[beagleboard] PWM update rate / alternate way to drive "analog" from Beaglebone?

I am working on a project to build a Beaglebone demo/development cape for more advanced haptics devices (not vibromotor; more like very small linear actuators). To drive them requires the output of up to several analog waveforms in the range of 20-300Hz, ideally synchronously. I've started down a couple possible paths (PWM, I2C), but wanted to pick the brains of the experts before proceeding further. It looks like there is theoretically some sort of audio codec on the AM335x (mcasp0), but I'd like to save it for actual audio if possible (and according to my google-fu, nobody has yet gotten it to work on the 'Bone).

The dvi+audio cape is working nicely nowadays.

Ideally, I'd like to come up with a solution that's accessible for end users via one of the low entry-bar programming interfaces (node.js or python), but understand this may not be a realistic goal :slight_smile:

PWM approach: A tried-and-true approach on more deeply embedded platforms is to make a software wavetable (say, a 128-point array representing e.g. a sine wave) and step the PWM output through it, varying the step rate (or stride length) to change wave output frequency. I've gotten a Python-based PWM test up and running thanks to the filesystem access now available in angstrom and an excellent tutorial by Dan Watts [http://www.gigamegablog.com/2012/03/16/beaglebone-coding-101-buttons-and-pwm/] . This makes userland access very straightforward, but having to open-write-close the file for every update is a lot of overhead. A quick benchmark (see file attached) shows 100 PWM duty cycle updates via this method take about 25.7ms (~ 30Hz absolute max update rate for a 100-point wavetable). Predictably, an alternate approach using OS calls (echo duty > /sys/class/pwm...) are even much slower, about 1Hz. I tried streaming multiple updates to the open file (e.g. "20\n40\n50\n80\n100\n"), but no joy. Can I assume the duty_percent 'file' must be closed and re-opened for each update? Does anyone have experience with a suitably faster/better approach to driving PWM updates at a higher speed? (What is the overhead of 'file' access via direct mmap() register-banging? Seems awfully hacky and likely to break though.)

I2C approach: An uglier, but maybe easier (from the Beaglebone side) approach is to offload the actual waveform generation to some tiny/cheap MCUs (e.g. PIC12) where a waveform could be more leisurely uploaded via I2C (or SPI, etc.), then commanded to start playing at a prescribed time. The paths I found for I2C access seem to require either the extremely slow OS-call-per-byte approach (i2cget/i2cset command-line programs), or writing a custom kernel driver(!) (https://groups.google.com/d/msg/beagleboard/H_4gED5aJw4/HYr9qTgnnRwJ). Not to mention the MCU code itself of course. Are there some better and/or less labor-intensive approaches that I am overlooking?

Yes, write a kernel driver that interfaces with the PWM subsystem.

Thanks for the reply.
That is the answer I was secretly hoping I wouldn’t hear. I’m a hardware engineer who happens to dabble in software a bit (mostly for 8-bit micros) - Linux kernel driver hacking is sky-high above my current position on the learning curve!

But looking down that rabbit hole…
While googling around for the source of the current driver (which I presume is what provides the /sys/class/pwm/* interfaces) to see what I would be in for, I stumbled across this project:


It mainly appears to provide an equivalent filesystem access to the PWM module for generic OMAP-based boards (Gumstix, BeagleBxxxx), however, it contains a source file labeled “PCM module for pwm speaker driver”, which at first glance sounds pretty much exactly like the problem I am trying to solve. However, it is 2 years old, rather undocumented, and I can find no evidence that anyone has ever gotten this portion working (possibly including its author, ref: http://beagleboard-pwm.blogspot.com/2010_08_01_archive.html). My ham-handed attempt to check “is this genetically related to the existing driver” suggests not: omap3-pwm generates a user-loadable module (.ko), while the shipping 'Bone kernel “appears to” bake the PWM driver in (no PWM-related references in modprobe -l output).

If I were to try building and installing this module, will it conflict with the existing driver? How do I resolve that, and/or force the use of a specific one? More importantly, would the end solution be accessible to end users? (The last thing I need the cape’s user manual to start with is “First, set up a cross-compiler toolchain, then apply these kernel patches…”)

Aside: A note at the very bottom of the omap3-pwm readme says to patch (defconfing) and recompile the kernel, citing a kernel “power saving feature” (CONFIG_OMAP_RESET_CLOCKS) which sounds like it results in necessary clock resources being disabled by default. This sounds suspiciously reminiscent of the register-poking kludges (mmap(), setPWMReg.py) I have seen used to enable clocks for the PWM modules. Are these in fact related, or separate issues? If related, does this mean it is not really necessary to build a custom kernel to experiment with this driver?

Thanks, and please excuse the noob questions!