Changing PLL multiplier causes servo to be not centered
W9GFO
Posts: 4,010
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.
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]~
spin
803B

Comments
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
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...
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. stateThis 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
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
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. stateIs 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?