Shop OBEX P1 Docs P2 Docs Learn Events
Changing PLL multiplier causes servo to be not centered — Parallax Forums

Changing PLL multiplier causes servo to be not centered

W9GFOW9GFO Posts: 4,010
edited 2007-08-07 18:41 in Propeller 1
Ok, I've made up a five cell NiMH battery pack for my Protoboard BoeBot. I can now reliably control the servos with my simple testservo.spin program.

Using trial and error I found 1460 to be the magic number for centering the servos. Since I take this number and multiply it by (clkfreq/1_000_000) I assume it to be 1.46 ms.

If I change PLL16x to PLL8x, the servos no longer center. I have to adjust the center number for each different PLL multiplier. It ranges from 1460 @ 16x to 880 @ 1x.

What am I missing here? I expected the use of (clkfreq/1_000_000) * width to equal the same value at any PLL multiplier.
CON

  _clkmode = xtal1 + pll16x
  _xinfreq = 5_000_000

  center = 1460          ' 1460 @ 16x, 1425 @8x, 1345 @ 4x, 1190 @ 2x, 880 @ 1x
    
  Servo1 = 5
  MotorR = 6
  MotorL = 7
  
  LED1   = 12
  LED2   = 13
  LED3   = 14
  LED4   = 15
        
PUB Main
  
  repeat  
    PWM(motorL, center)
    PWM(motorR, center)
    PWM(servo1, center)
    waitcnt((clkfreq/1_000) * 20 + cnt)
  
  
PUB PWM(pin, width)

  dira[noparse][[/noparse]pin]~~                                                                       
  outa[noparse][[/noparse]pin]~~                                                                       
  waitcnt((clkfreq/1_000_000)* width + cnt)                                                          
  outa[noparse][[/noparse]pin]~

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2007-08-07 04:48
    That's definitely very very strange. CLKFREQ is supposed to adjust based on _XINFREQ and PLLx (from _CLKMODE) and appears to do so for me.
  • deSilvadeSilva Posts: 2,967
    edited 2007-08-07 05:07
    Check your signals with a 'scope! I think 20 mys waittime will be to short a time for 40 MHz SPIN programs
  • CJCJ Posts: 470
    edited 2007-08-07 05:33
    it is really quite logical, the spin overhead at the lower speeds doesn't scale down, and becomes more and more of an offset from your setpoint.

    1us at 80Mhz
    2us at 40Mhz
    4us at 20Mhz
    8us at 10Mhz
    16us at 5Mhz

    all of these are 80 clocks, just as an example of how it doesn't scale with the clock speed, not actual spin numbers,

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Parallax Forums - If you're ready to learn, we're ready to help.
  • Graham StablerGraham Stabler Posts: 2,507
    edited 2007-08-07 08:29
    deSilva, I am sure you mean millisecond when you say mys but as there is no y in millisecond (at least in English) then it can be somewhat confusing, perhaps ms would be better.

    Graham
  • W9GFOW9GFO Posts: 4,010
    edited 2007-08-07 15:04
    Isn't 20 ms equal to 800,000 clock cycles at 40 mhz? That seems like a long time to me.

    I am having to adjust my center number by .58 ms between PLL16x and PLL1x. Isn't that 580 us? I think 580 us is about 3,000 clock cycles @ 5 mhz.

    ms = .001 second
    us = .000001 second

    I would not be surprised at all if there are errors in my math...
  • LawsonLawson Posts: 870
    edited 2007-08-07 15:34
    I've seen this too, and I agree with CJ that it is the spin overhead that's causing the pulse width to change with operating frequency. It's pretty easy to fix too.

    Pub Pulseout_uS(Pin, Duration) | SyncCNT
    ''generate a pulse on Pin, Duration microseconds long.
      Duration := Min_uS #> Duration <# Max_uS     'limit Duration to valid values                                                     
      dira[noparse][[/noparse]pin]~~                                  ' Set to output                                         
      SyncCNT := cnt + 1000                        'snag value of cnt + some time
      waitcnt(SyncCNT)                             'sync to cnt
      !outa[noparse][[/noparse]pin]                                   'set Pin to opposite state                                 
      {do math while we wait for CNT to reach our target}                
      SyncCNT += ((Duration * us) #> 1000)       'modify sync point to a future time. duration*(clk cycles for us)
      waitcnt(SyncCNT)                             'wait until clk gets there                             
      !outa[noparse][[/noparse]pin]                                   'return Pin to orig. state
    



    This code assumes 'us' is the number of clock cycles in one micro-second. It also limits the pulse duration using the constants Min_uS and Max_uS

    This code keeps the pulse length identical as clock frequency changes because the delay due to spin overhead has the exact same effect on the start and end of the pulse. (I.e. same code is used)

    Marty

    P.S. I just estimated how many clocks of overhead you were seeing, 2,800 clocks. That's a LOT of clocks to add to a delay. Quite reasonable once you dig down into what your short snippet of code brakes down into. (i.e. just the simple OUTA[noparse][[/noparse]pin]~~ probably generates 5-10 spin byte-codes, each code taking 200-300 clocks to execute)

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Lunch cures all problems! have you had lunch?

    Post Edited (Lawson) : 8/7/2007 3:52:20 PM GMT
  • deSilvadeSilva Posts: 2,967
    edited 2007-08-07 15:58
    1 mys is what you like to write down as 1 us, which is fine for you. "Mys" is fine for me and billions of other people smile.gif

    But back to the topic: It is generally ignored that a typical SIMPLE SPIN instruction takes around 10 mys @ 80 MHz (note the upper case letters; it is also mA... The international standards require a capitalization of all units derived from person's names as Andr
  • W9GFOW9GFO Posts: 4,010
    edited 2007-08-07 16:59
    Thanks for the code example! now I can use 1500 as the center (as it should be).

    I made slight modifications
    Pub PulsOut(Pin, Duration) | SyncCNT                    'generate a pulse on Pin, Duration microseconds long.
                                                         
      Duration := Min_uS #> Duration <# Max_uS              'limit Duration to valid values                                                     
      dira[noparse][[/noparse]pin]~~                                           ' Set to output                                         
      SyncCNT := cnt + 1000                                 'snag value of cnt + some time
      waitcnt(SyncCNT)                                      'sync to cnt
      !outa[noparse][[/noparse]pin]                                            'set Pin to opposite state
                                       
            {do math while we wait for CNT to reach our target}
                              
      SyncCNT += ((Duration * (clkfreq/1_000_000)) #> 1000) 'modify sync point to a future time. duration*(clk cycles for us)
      waitcnt(SyncCNT)                                      'wait until clk gets there                             
      !outa[noparse][[/noparse]pin]                                            'return Pin to orig. state
    



    Is it possible for SyncCnt to reach SyncCNT += 1000 before reaching waitcnt(SyncCnt)? Maybe if it has too much math to do?
    Perhaps that is why the conversion to "us" is made at the top of the method, unlike what I am doing here.
  • LawsonLawson Posts: 870
    edited 2007-08-07 18:41
    W9GFO said...
    Is it possible for SyncCnt to reach SyncCNT += 1000 before reaching waitcnt(SyncCnt)? Maybe if it has too much math to do?
    Perhaps that is why the conversion to "us" is made at the top of the method, unlike what I am doing here.

    don't know! I just chose 1000 as a number small enough not to waste too much time, but large enough to prevent missing the targeted time. I calculated "us" in an Init method because that's how it was done in the BS2_functions library that I based this code on. It worked the first time so I just left it alone [noparse]:)[/noparse]

    Marty

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Lunch cures all problems! have you had lunch?
Sign In or Register to comment.