Shop OBEX P1 Docs P2 Docs Learn Events
Fast PWM (up to 1.6 MHz) — Parallax Forums

Fast PWM (up to 1.6 MHz)

Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
edited 2015-11-12 20:13 in Propeller 1
Fast PWM seems to be all the rage lately, so I thought I'd give it a try. The attached object provides PWM rates from 2.44 kHz to 1.6 MHz, with 12.5ns resolution (80 MHz clock) and full 0-100% duty cycles. It requires one pin, one extra cog, and no external circuitry. The on and off times can also be set separately. Changes to the timing parameters are done instantly, without stopping or otherwise interrupting the flow of the output.

Enjoy!
-Phil

Corrected archive attached ...
Improved archive attached ...

Comments

  • jmgjmg Posts: 15,173
    Nice specs...
  • Definitely up to par. This is what I like about the Propeller, the job is done with such a a concise piece of code.
  • This is what I like about the Propeller, ...
    That makes two of us. The P1 combines power with such simple elegance, it almost takes one's breath away. The counters and PLLs are everything, really. Without them, even an 8x clock boost wouldn't amount to much, IMO.

    -Phil
  • evanhevanh Posts: 15,918
    edited 2015-11-12 10:26
    Oh, wow, it's damn clean! I was suspiciously looking for that corner case or jittery control but nope none of that at all. Beautiful control using WAITCNT to precisely dominate the Cog counter which then produces the subsequent precise length of high time. Ace.
  • evanhevanh Posts: 15,918
    Gives me the willies that the output pin is exact right down to a single 12.5ns pulse. That's so good. :)
  • AribaAriba Posts: 2,690
    Maybe I miss something, but is this not the classical way we are doing PWM since the beginning of the Propeller era?

    Andy
  • evanhevanh Posts: 15,918
    Oh? Me goes looks ... I see, yes, just not in such a tight loop. Dang, still cool though. Shows my lack of actual Prop time doesn't it. :blush:
  • evanhevanh Posts: 15,918
    edited 2015-11-12 11:32
    Lol, made a right fool of myself. Found an even tighter one posted in 2013 - http://obex.parallax.com/object/198 . It doesn't provide for dynamic period though.
  • AribaAriba Posts: 2,690
    edited 2015-11-12 14:12
    Yeah the dynamic period is the difference to other objects.
    If you do in Spin only, it goes up to 60 kHz @80 MHz clock:
    CON
      _clkmode  = xtal1 + pll16x
      _xinfreq  = 5_000_000
    
      PWMOUT = 16    'pin
      
    VAR
      long  period, width
      long  stack[10]
      
    PUB Main : w
      period := 1330                '60 kHz max
      cognew(pwm,@stack)
      repeat
        repeat w from 0 to period
          width := -w               'we need a negative width value
          waitcnt(clkfreq/200 + cnt)
    
    PRI pwm : time
      ctra := %00100<<26 + PWMOUT
      frqa := 1
      dira[PWMOUT] := 1
      time := cnt
      repeat
        waitcnt(time += period)
        phsa := width
    

    Andy
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2015-11-12 18:38
    Phil,
    One correction in the posted code ...
    Start(pwm_pin) 
       pwm_pin := 1   '  huh?!
    

    also made the direction |= instead of := so as to not affect other direction bits.
    dira0 |= 1 << pwm_pin
    
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2015-11-12 18:44
    Oh, it does work great out to the ends of the range. I like the way you packed both the period and width into one long.

    Playing with it, I changed the first two lines in the Start method:
    Start(pwm_pin)
      ctra0 := %00101 << 26 | ((pwm_pin+1)<<9) | pwm_pin
      dira0 |= 3 << pwm_pin
    
    and now have fully complementary output from pin and pin+1. That would be applicable say to driving an h-bridge or bridge tied load.


  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2015-11-12 20:15
    pwm_pin := 1 ' huh?!
    Oops! I left some test code in there by mistake. Thanks for the catch. I've corrected the archive in the first post.
    dira0 |= 1 << pwm_pin
    That's not necessary, as the dira is exclusive to the PASM cog, which uses only one output. In fact, it would be harmful in the case where two instances of the object are started at once, since it operates on the common hub variable before it's copied to the cog.

    'Nice extension for complementary outputs, BTW!

    -Phil
  • I've updated the program in the first post to accommodate Tracy's complementary-output idea. I've also wrapped some security around the (now) two start methods to prevent starting the same instance twice.

    -Phil
  • While the technique of generating PWM by retarding phsx may be nearly as old as the P1, it never hurts to bring attention to it and to recast it. After all, a large benefit of these fora is to teach and learn from one another.

    One nice application of this technique is embedded in the wave player introduced here. It uses this standard PWM and noise shaping in order to avoid the subharmonic hash noise that arises from counter DUTY mode. In that application of course the technique is buried deep within the sampling loop.



  • While the technique of generating PWM by retarding phsx may be nearly as old as the P1 ...
    Also, one of the advantages of having glitchy long-term memory is that I'm constantly making "new" discoveries! :)

    -Phil
Sign In or Register to comment.