DSPRelated.com
Forums

PWM trick

Started by Jim Thomas November 16, 2007
Hey guys,

A couple of months ago I found myself needing to generate an analog DC 
voltage on an AVR microcontroller's digital I/O pin as part of a test 
procedure.  I was not free to alter the hardware, so I had to work with 
what I had - I decided to use PWM.  My first stab looked something like 
this:

for(i=0; i<256; i++)
{
   if (sample < i) set_bit();
   else            clr_bit();
}

This generates a square wave whose duty cycle is controlled by the value 
of "sample".  Only problem was that the capacitance on the I/O line was 
not quite high enough to filter this sufficiently for my needs.  So I 
changed the code to bit reverse the 8 ls bits of "i" in the compare:


for(i=0; i<256; i++)
{
   if (sample < bit_rev[i]) set_bit();
   else                     clr_bit();
}

This jacked the frequency way up and the filter was easily able to 
handle it.  Instead of a nasty-looking sawtooth, I saw a nice smooth DC.

Now for the question - is bit-reversal optimal for maximizing the i/o 
frequency, or is there something better?

-- 
Jim Thomas            Principal Applications Engineer  Bittware, Inc
jthomas@bittware.com  http://www.bittware.com    (603) 226-0404 x536
Failure is always an option

Jim Thomas wrote:

> Hey guys, > > A couple of months ago I found myself needing to generate an analog DC > voltage on an AVR microcontroller's digital I/O pin as part of a test > procedure. I was not free to alter the hardware, so I had to work with > what I had - I decided to use PWM. My first stab looked something like > this: > > for(i=0; i<256; i++) > { > if (sample < i) set_bit(); > else clr_bit(); > } > > This generates a square wave whose duty cycle is controlled by the value > of "sample". Only problem was that the capacitance on the I/O line was > not quite high enough to filter this sufficiently for my needs. So I > changed the code to bit reverse the 8 ls bits of "i" in the compare: > > > for(i=0; i<256; i++) > { > if (sample < bit_rev[i]) set_bit(); > else clr_bit(); > } > > This jacked the frequency way up and the filter was easily able to > handle it. Instead of a nasty-looking sawtooth, I saw a nice smooth DC. > > Now for the question - is bit-reversal optimal for maximizing the i/o > frequency, or is there something better?
This is what delta sigma is for. Noise shaping 1: for(;;) { x = sample + carry_over; if(x & 0x80) set_bit(); else clr_bit(); carry_over = x&0x7F; } Noise shaping 2: for(;;) { x = sample + (carry_over0<<1) - carry_over1; if(x & 0x80) set_bit(); else clr_bit(); carry_over1 = carry_over0; carry_over0 = x&0x7F; } The noise shapings of the higher order are less interesting because they require multiplication. Vladimir Vassilevsky DSP and Mixed Signal Design Consultant http://www.abvolt.com
Jim Thomas <jthomas@bittware.com> wrote in news:13jreihamk8eu45
@corp.supernews.com:

> Now for the question - is bit-reversal optimal for maximizing the i/o > frequency, or is there something better?
Many microcontrollers have PWM modules that you set the frequency, and then update the duty cycle as needed. At the very least, consider using timers to flip your bits in interrupts. You should be able to go pretty fast then, and your timing will be much more dependable. -- Scott Reverse name to reply
Jim Thomas wrote:
> Hey guys, > > A couple of months ago I found myself needing to generate an analog DC > voltage on an AVR microcontroller's digital I/O pin as part of a test > procedure. I was not free to alter the hardware, so I had to work with > what I had - I decided to use PWM. My first stab looked something like > this: > > for(i=0; i<256; i++) > { > if (sample < i) set_bit(); > else clr_bit(); > } > > This generates a square wave whose duty cycle is controlled by the value > of "sample". Only problem was that the capacitance on the I/O line was > not quite high enough to filter this sufficiently for my needs. So I > changed the code to bit reverse the 8 ls bits of "i" in the compare: > > > for(i=0; i<256; i++) > { > if (sample < bit_rev[i]) set_bit(); > else clr_bit(); > } > > This jacked the frequency way up and the filter was easily able to > handle it. Instead of a nasty-looking sawtooth, I saw a nice smooth DC. > > Now for the question - is bit-reversal optimal for maximizing the i/o > frequency, or is there something better? >
See http://www.wescottdesign.com/articles/sigmadelta.html. There's no reason you can't do it with a one-bit output, and maybe you can use Vladimir's 2nd-order filter. -- Tim Wescott Wescott Design Services http://www.wescottdesign.com Do you need to implement control loops in software? "Applied Control Theory for Embedded Systems" gives you just what it says. See details at http://www.wescottdesign.com/actfes/actfes.html
On Nov 16, 8:42 am, Jim Thomas <jtho...@bittware.com> wrote:
> > Now for the question - is bit-reversal optimal for maximizing the i/o > frequency, or is there something better?
Cool - years ago I used a bit-reversed PWM to implement an analog output on a digital ASIC to close a clock tracking loop. It worked pretty well. I haven't seen it discussed though so I always wondered if I was missing something obviously wrong with it. Since then I've seen similar problems solved using the noise shaping technique Vladimir described. I'd guess the complexity is about the same, but noise shaping seems more elegant to me. EB
There is another similar trick that can be used to generate PWM.

if (sample > rand(scaled to same range as sample))  set_bit() else clr_bit();

This can be called at irregular intervals. I used this as an idle task on an application.


Regards

--
Walter Banks
Byte Craft Limited
Tel. (519) 888-6911
Fax (519) 746 6751
http://www.bytecraft.com
walter@bytecraft.com






Jim Thomas wrote:

> Hey guys, > > A couple of months ago I found myself needing to generate an analog DC > voltage on an AVR microcontroller's digital I/O pin as part of a test > procedure. I was not free to alter the hardware, so I had to work with > what I had - I decided to use PWM. My first stab looked something like > this: > > for(i=0; i<256; i++) > { > if (sample < i) set_bit(); > else clr_bit(); > } > > This generates a square wave whose duty cycle is controlled by the value > of "sample". Only problem was that the capacitance on the I/O line was > not quite high enough to filter this sufficiently for my needs. So I > changed the code to bit reverse the 8 ls bits of "i" in the compare: > > for(i=0; i<256; i++) > { > if (sample < bit_rev[i]) set_bit(); > else clr_bit(); > } > > This jacked the frequency way up and the filter was easily able to > handle it. Instead of a nasty-looking sawtooth, I saw a nice smooth DC. > > Now for the question - is bit-reversal optimal for maximizing the i/o > frequency, or is there something better? > > -- > Jim Thomas Principal Applications Engineer Bittware, Inc > jthomas@bittware.com http://www.bittware.com (603) 226-0404 x536 > Failure is always an option
Vladimir Vassilevsky wrote:
> This is what delta sigma is for. > > Noise shaping 1: > > for(;;) > { > x = sample + carry_over; > if(x & 0x80) set_bit(); > else clr_bit(); > carry_over = x&0x7F; > } > > Noise shaping 2: > > for(;;) > { > x = sample + (carry_over0<<1) - carry_over1; > if(x & 0x80) set_bit(); > else clr_bit(); > carry_over1 = carry_over0; > carry_over0 = x&0x7F; > } > > The noise shapings of the higher order are less interesting because they > require multiplication.
That's pretty cool Vladimir. Thanks for the reply. If this ever comes up again, I'll try it this way. -- Jim Thomas Principal Applications Engineer Bittware, Inc jthomas@bittware.com http://www.bittware.com (603) 226-0404 x536 The secret to enjoying your job is to have a hobby that's even worse - Calvin's Dad
Scott Seidman wrote:
> Jim Thomas <jthomas@bittware.com> wrote in news:13jreihamk8eu45 > @corp.supernews.com: > >> Now for the question - is bit-reversal optimal for maximizing the i/o >> frequency, or is there something better? > > > Many microcontrollers have PWM modules that you set the frequency, and then > update the duty cycle as needed.
Thanks for the suggestion. In my experience, the PWM features of most microcontrollers are limited to certain pins. I was not at liberty to rearrange any pins because the hw/sw in the "real" application was already doing exactly what it was supposed to be doing. My task was to write some test software for when this card was plugged into a test jig.
> > At the very least, consider using timers to flip your bits in interrupts. > You should be able to go pretty fast then, and your timing will be much > more dependable.
Since the PWM was the only thing going on at the time, I don't think an IRQ would make it go any faster - especially when the context switching overhead is taken into account. -- Jim Thomas Principal Applications Engineer Bittware, Inc jthomas@bittware.com http://www.bittware.com (603) 226-0404 x536 The secret to enjoying your job is to have a hobby that's even worse - Calvin's Dad
emeb wrote:
> On Nov 16, 8:42 am, Jim Thomas <jtho...@bittware.com> wrote: >> Now for the question - is bit-reversal optimal for maximizing the i/o >> frequency, or is there something better? > > Cool - years ago I used a bit-reversed PWM to implement an analog > output on a digital ASIC to close a clock tracking loop. It worked > pretty well. I haven't seen it discussed though so I always wondered > if I was missing something obviously wrong with it. > > Since then I've seen similar problems solved using the noise shaping > technique Vladimir described. I'd guess the complexity is about the > same, but noise shaping seems more elegant to me.
Yes, it does. Unfortunately for me, the bit-reversed PWM is working too well, so I cannot justify revisiting it. But if there's a next time... -- Jim Thomas Principal Applications Engineer Bittware, Inc jthomas@bittware.com http://www.bittware.com (603) 226-0404 x536 The secret to enjoying your job is to have a hobby that's even worse - Calvin's Dad
Jim Thomas <jthomas@bittware.com> wrote in news:13kll479jpttd22
@corp.supernews.com:

> Since the PWM was the only thing going on at the time, I don't think an > IRQ would make it go any faster - especially when the context switching > overhead is taken into account.
You're doing 2 comparisons, 1 increment, and a set or clear bit every pass through your loop! Compare this to two set or clear for each PWM period. You should be able to go many times faster, at least, using a timer, context switching or no. -- Scott Reverse name to reply