Shop OBEX P1 Docs P2 Docs Learn Events
8-channel PWM output, one cog, 23KHz for 8-bit resolution (updated 20 Jan 10) — Parallax Forums

8-channel PWM output, one cog, 23KHz for 8-bit resolution (updated 20 Jan 10)

Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
edited 2010-01-21 19:36 in Propeller 1
There have been several threads regarding PWM output from the Propeller. Although the Propeller's counters have DUTY mode outputs, which are easily filtered to provide an analog voltage, the waveforms produced by these outputs are not true PWM. As such, they are unsuitable, say, for gating a MOSFET to drive an inductive load, due to the extremely short pulses (12.5ns) that are possible.

Generating PWM outputs by bit-banging individual port pins is possible, but the output frequency is quite limited with even moderate resolutions. For this reason, I decided to try writing a PWM driver using the Propeller's video output circuitry instead. The video generator can output either two- or four-color video in both NTSC and VGA modes. I chose VGA output, since it allows individual control over eight separate pins. In four-color mode, each WAITVID can output up to sixteen "pixels" of 8-bit "color", each selected from one of four indexed colors. However, because more than four "pixels" may require more than four distinct "colors" during PWM generation, the length of the vector has to be limited to four pixels, instead of sixteen.

The idea is to manage a vector of "color" data in hub RAM, so that it forms a PWM sequence in each bit slice. The vector is then cycled through repeatedly in a separate cog, using the video generator to output the PWM data to the individual pins. The vector length needs to equal resolution / 4 longs and represents the "color" tables only. Each 32-bit element of the vector holds four 8-bit "colors". The "pixel" data is the same for each WAITVID, namely: %11_10_01_00. In other words: color 0, color 1, color 2, then color 3, indexed in sequence from the four-color color table. For this reason, only one RDLONG is required for every four PWM increments, which provides a faster response than bit-banging alone would.

The length of the PWM loop is 52 processor clocks, worst case. It might be tempting to try arranging instructions to hit the hub access sweet spots, thus reducing the actual execution time of the loop. But this is impossible, due to the complex interplay between hub accesses and the completely independent video clock. Moreover, since the PLL that drives the video can exhibit considerable jitter at certain frequencies, it's necessary to add a protective buffer to the timing computations to accommodate the effects of that jitter. For example, from the loop length alone, one can determine that the maximum frequency for an 8-bit (256) resolution PWM would be over 24KHz. But the practical limit due to PLL jitter, determined through experimentation, is more like 23KHz. Now, it's true that there are "islands of stability" above 24KHz for 8-bit PWMs. In fact, 30KHz is one of them. But predicting where they are algorthmically should be considered intractable for most practical purposes.

The code I came up with allows a PWM resolution of any length that's an integer multiple of four. It doesn't have to be a power of two. So 100 and 1000 are acceptable resolutions. It uses a single buffer for the PWM data. What this means is that the object's "duty" method modifies data that's being "displayed" on the fly. While, for universal function generation, this could lead to corrupt, runty, or hole-riddled cycles, that doesn't happen here. This is prevented by keeping track of the previous duty value for each channel and modifying the PWM vector in such a way that doesn't leave gaps or runts in any cycle. There could exist intervals whose duty cyles are intermediate between the prior and currently programmed values. But any intermediate values, if they exist at all, will change monotonically from one value to the next, so the effect will be benign in most applications. To code a double-buffered implementation would certainly be possible, but it would require an additional hub read and hub write for each time through the PWM loop to do the necessary handshaking, slowing the loop unacceptably.

Attached is a zip archive of the object, along with a demo program. Also attached is a scope capture from the demo program. This object was written to be a candidate for the Propeller Object Exchange and is licensed accordingly. I'll give it some time to percolate here first, in case there are any bugs. So feedback is most welcome!

-Phil

Update: There was a bug in the original object that prevented its use with base pins > 0. This has now been fixed, and an updated archive is attached. The new demo program uses pins 16-23 which, on the Demo Board, activiate the eight LEDs, so you can see the PWM effect without a scope. The attached scope trace applies to the original demo program, which used pins 0-3.

Update (2010.01.20): Fixed a bug dealing with duty modes of 256 (full on).

Post Edited (Phil Pilgrim (PhiPi)) : 1/20/2010 8:00:44 PM GMT

Comments

  • Graham StablerGraham Stabler Posts: 2,507
    edited 2008-06-07 21:34
    This is EXTREMELY useful, nice one Phil!

    Graham
  • Cluso99Cluso99 Posts: 18,069
    edited 2008-06-07 22:10
    Great one Phil roll.gif

    I knew there would be more uses for the VGA counters blush.gif

    P.S. I like your CRO output - what do you use?
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2008-06-07 22:52
    Cluso99,

    Thanks. The scope is a Tek TDS3034, which writes screen images as TIFF files to a built-in floppy drive. I use Corel PhotoPaint to rotate the images and convert to GIFs.

    -Phil
  • scottascotta Posts: 168
    edited 2008-06-09 13:29
    Wow.

    I barely can just see how it works, but it
    works fantastic !

    Scott
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2008-07-17 18:36
    There was a bug in the original object when it was applied to base pins > 0. This has been fixed.

    -Phil

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    'Still some PropSTICK Kit bare PCBs left!
  • GuntherGunther Posts: 3
    edited 2008-07-18 08:25
    Phil is far too modest.

    If there was a bug in the PWM it was between my ears. Phil being the nice guy that he is updated his demo application to drive the demo board LEDS based on a conversation e had. The PWM_x8 output object worked fine, I just didn't understand how to call it properly.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2008-07-18 16:02
    Gunther,

    Thanks for your vote of confidence, but there really was a bug in the PWM object — two of them, to be exact. One miscalculated the pin mask for DIRA; the other, the pin group argument for VCFG. I rewrote the demo, in part, to track down the problem, since the original demo worked fine with the faulty code. I didn't even notice that you may have made errant method calls until I was finished. smile.gif

    -Phil

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    'Still some PropSTICK Kit bare PCBs left!
  • timatrontimatron Posts: 16
    edited 2008-08-12 23:20
    Hey Phil,

    I asked you about controling 100+ gauges from a computer a while back, i loved all of the suggestions people came up with. Because I could get the led driver chips from TI as samples, i grabbed some and tried them out (TLC5940), unfortunately, I couldn't drive enough current and they didnt have enough resolution for me to be useful. So I'm back to trying to get 24 PWMs out of a single propeller chip and bumping the juice with darlington transistors. I'm using the code you've written above and i'm very thankful for making my life so much easier[noparse]:)[/noparse] But one question, is it simple enough to just use more than 8 pins? If i just specify the other pins will it work just the same?

    Thanks for all of your help!

    Cheers,
    tim
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2008-08-13 03:03
    Tim,

    You can start several instances of pwmx8, thus:

    [b]OBJ[/b]
    
      pwm[noparse][[/noparse]3] : "pwm_x8"
    
    [b]PUB[/b] Start | j
    
      [b]repeat[/b] j [b]from[/b] 0 to 2
        pwm[noparse][[/noparse]j].Start(j << 3, $ff, 5000)
    
    
    


    You can then define your own Duty method, thus:

    [b]PUB[/b] Duty(pin, amt)
    
      pwm[noparse][[/noparse]pin >> 3].Duty(pin, amt)
    
    
    


    -Phil

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    'Still some PropSTICK Kit bare PCBs left!
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2008-08-13 03:07
    Tim,

    Since you mentioned Darlingtons, make sure to use the lowest PWM frequency that doesn't cause noticeable flicker. The reason is that Darlingtons switch rather slowly. If the PWM frequency is too high, the transistors will spend a larger percentage of time in their linear region, possibly overheating.

    -Phil

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    'Still some PropSTICK Kit bare PCBs left!
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2008-08-19 23:49
    This object (PWMx8) is now available from the Object Exchange in the "Signal Generation" section.

    -Phil

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    'Still some PropSTICK Kit bare PCBs left!
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-01-20 20:01
    I've found and repaired a bug in the PWMx8 object that occurred when a duty mode of 256 (full on) was specified. It caused a buffer overflow, which was fixed by extending the buffer. The updated version is attached at the top of this thread and has been uploaded to the OBEX. Anyone using this object should update their copy.

    -Phil
  • lcyepizlcyepiz Posts: 26
    edited 2010-01-20 20:31
    thanks phil

    good job
  • ElectricAyeElectricAye Posts: 4,561
    edited 2010-01-20 22:13
    Thanks, Phil.

    smile.gif
  • mikedivmikediv Posts: 825
    edited 2010-01-21 19:36
    Hey Phil as always thank you very much for sharing your stuff with us. I have a whole directory on my drive called Phils stuff lol thanks man
Sign In or Register to comment.