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
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.