I wonder what kind of performance you would get from a pure software loop generating all waveforms in parallel. Obviously, any kind of work other than generating the waveforms -- changing any frequency, responding to an USB packet, anything -- would cause jitter on
all waveforms generated. I'm too lazy to test for myself -- also, I don't have an oscilloscope (good enough for measuring something like this).
If a "pause" or a "glitch" whenever changes are applied or USB communications occur is acceptable, this might be an acceptable solution.
For 50% duty cycle, for each waveform:
Code:
uint32_t accumulator, rate;
{
const uint32_t old = accumulator;
accumulator += rate;
if (accumulator < old)
PORT ^= MASK;
}
The idea is that whenever the accumulator wraps around, the output pin is flipped. This can be written in very tight assembly code, too, especially if the pins are consecutive in the same port.
Initially,
accumulator=0, and
rate=2147483648.0*wave_freq/loop_freq, where
wave_freq is the desired frequency for the waveform, and
loop_freq is the frequency the above code is run. Lower frequencies are more precise, as the frequencies are quantized to the nearest integer rate; the theoretical frequency being
wave_freq=rate*loop_freq/2147483648.0 . The inherent jitter is
2/loop_freq , as the pulse length varies between 2
n and 2
n+2 loop iterations.
With slightly more complex code, and 2
N+1 additional 32-bit unsigned integers per waveform, you could generate
N-pulse trains, too.
I don't have an oscilloscope, and I'm anyway just waving my hands here (I'd put this rambling at 85% reliability), but I think you could get
loop_freq to about 1 MHz for up to sixteen waveforms (two ports). Assuming that was the exact rate (I'd measure it in practice!),
rate=32212 would correspond to
wave_freq=14.9999 Hz, and
rate=42949673 would correspond to
wave_freq=20000.0000 Hz.
Another approach is to use a single timer interrupt, reprogramming it to the next pulse edge, and do all work in the timer interrupt. This would let the Teensy do other work at the same time. There would be slightly more work in the timer interrupt, so it might cause jitter to everything else; I'm not sure if it would negatively affect other stuff (especially USB). Would need testing to verify.