Tachyon's 32 channel PWM as an arbitrary or analog waveform generator
Peter Jakacki
Posts: 10,193
I was just thinking that since the 32 channel PWM uses a 256 long table to store its PWM samples and that since the table is read at an almost 2MHz rate that it would be an easy thing to add resistors to the port pins as ladder DACs (one or more) of variable width. Doing this means it's easy then to generate all kinds of waveforms simply by accessing the table.
Using a lower frequency will yield very smooth audio waveforms etc. At present there are a few words that calculate and write the values to the table whenever you set the duty cycle you want on those channels.
30 2 SETPWM \ Set P2 to 30/256 duty cycle
75 % 3 SETPWM \ Set P3 to 75 % duty cycle
50 % 8 16 SETPWMS \ Set P8..P16 to 50 % duty cycle
This has also meant that I can downgrade the resolution of individual channels and increase the update rate accordingly so that turning that pin into a 4-bit PWM results in >120kHz update rate.
4 16 1 SETPWMW \ Set P1 with a duty of 4/16 so that it repeats every 16 PWM cycles (>120KHz update)
Now I can add modes to specify which pins form a ladder DAC and generate sine waves etc, without affecting the other channels, some can be 8-bit PWMs, some faster 6-bit, and some analog waveforms. The waveforms can even be set from a table loaded into from SD if needed.
Some possible ways of specifying an analog waveform
256 16 23 SINWAVE \ Generates a sine wave over the full 256 samples on P16..P23
16 4 8 COSWAVE \ generates a cosine wave over 16 samples onto P4..P8
noise 64 16 23 WAVE \ copy 64 values (at a time) from "noise" table (just an address in memory) to P16..P23
When I have some time I will set it up and generate some sine/cosine waves alongside 8-bit/4-bit PWMs etc
BTW, this is all interactive so I can type and change it on the fly.
Using a lower frequency will yield very smooth audio waveforms etc. At present there are a few words that calculate and write the values to the table whenever you set the duty cycle you want on those channels.
30 2 SETPWM \ Set P2 to 30/256 duty cycle
75 % 3 SETPWM \ Set P3 to 75 % duty cycle
50 % 8 16 SETPWMS \ Set P8..P16 to 50 % duty cycle
This has also meant that I can downgrade the resolution of individual channels and increase the update rate accordingly so that turning that pin into a 4-bit PWM results in >120kHz update rate.
4 16 1 SETPWMW \ Set P1 with a duty of 4/16 so that it repeats every 16 PWM cycles (>120KHz update)
Now I can add modes to specify which pins form a ladder DAC and generate sine waves etc, without affecting the other channels, some can be 8-bit PWMs, some faster 6-bit, and some analog waveforms. The waveforms can even be set from a table loaded into from SD if needed.
Some possible ways of specifying an analog waveform
256 16 23 SINWAVE \ Generates a sine wave over the full 256 samples on P16..P23
16 4 8 COSWAVE \ generates a cosine wave over 16 samples onto P4..P8
noise 64 16 23 WAVE \ copy 64 values (at a time) from "noise" table (just an address in memory) to P16..P23
When I have some time I will set it up and generate some sine/cosine waves alongside 8-bit/4-bit PWMs etc
BTW, this is all interactive so I can type and change it on the fly.
Comments
It seems to me that if your table is preloaded for a repetitive waveform, 10Mhz or so just moving the table value to the outa and incrementing the table pointer should be doable. maybe a small glitch when the table pointer rolls over to do it again. Then again, 256 points may be a bit ugly at lower frequencies. Using twelve bits to address a pre-loaded table in an external ram whose output goes to an R2R DAC may give a more accurate waveform from having many more points to reconstruct per unit time.
Oh for more time to play........
I don't think it's possible to do 10MHz at all even if the table were loaded into cog ram as a mov+incptr+outa+jmp gives a maximum 5MHz update rate. The PWM routine however reads from hub ram which can be modified on the fly by another cog so hub access slows things down a lot, however kuroneko rewrote the whole PWM and increased the read rate from 1.28MHz to almost 2MHz.
The main point about using the PWM this way is that it is a very versatile module because you can use your PWMs in the usual way on any and as many pins as you choose and also you can increase the update rate by decreasing the resolution on any pin without affecting the resolution on other pins. Being able to throw a few resistors on it so to speak means you can have analog waveform generators for peanuts. In this regards I find it very easy to use smd resnets as they normally four individual resistors and it's very easy to build cheap and compact R2Rs with these.
Peter,
I'd love to see that in stand-alone form. Is it available, or do I have to (attempt to) unwind it from the Tachyon source?
It's just as easy to see the source document but here's a link to the bookmarked webpage document which gets republished automatically from the source document. The formatting on the original is better but you need to sign in.
The bottom channel is running at 25% duty cycle but at four times the main PWM frequency, so that's 30.4kHz. Now the two channels up above it are out of phase with the other pwm channels so that might be useful for a dead band etc.
[video=youtube_share;QKN7ggWGnQk]
Here's the sample code I used to demonstrate it:
Really Neat! Thanks for the video
Even in TF itself this operation would take around 1ms but I have a small PASM module loaded into the TF cog which takes 100us, certainly not a problem for control purposes. I think if you make the Spin loop count down that it is more efficient but if an application needed many fast changes to the speed the standard brute force loop in Spin might not be suitable and instead I would look at a tracking method which would only make the necessary incremental changes.
BTW, I drafted this post the other day and forgot to send it.