Servo Pulse Width Modulation PWM and Timer Resolution for 1ms-2ms and 600us-2300us
I moved an app from an ESP8266 to a Raspberry Pi Pico processor and none of my servos worked correctly. The servos moved but not the right amount. Then I google-found someone that had given a completely different set of microsecond ranges. It turns out they fixed the problem but did not understand why they had the problem.
The following type of explanation is not correct
The setServoCycle(position) function can be used to set the position of the servo by passing the position parameter from 1000 to 9000. The values for duty_u16 are in microseconds instead of degrees. The servo values 1000-9000 represent 0-180 degrees
Servo control is based on Pulse Width Modulation where the width of a pulse tells the servo its position. The control pulse is between 1-2 msec in a 50hz cycle. That 50hz translates into a 20msec period. So basically the control pulse ranges from 5%-10% of the period.
Timer Basics
- Full Frequency / Period, in our case 50hz/20msec. Timers are counters and we program them to periodically roll over and restart. In our case, they roll over every 20msec.
- The portion of the period that is on. This is the length of the pulse inside the full period. We tell the timer how many tics long this pulse is.
- The timer rollover rate is 50hz
- The internal clock rate is 50*100 or 5000 tics/second.
- A 10% pulse would span 1/10 of the total counter 100 tic size or 10 timer tics.
- A 5% pulse would span 1/20 of the total 100 counter tics in the period or 5 timer tics.
References
Reference Code
def setServoAngle(servo, angle, pulse_min_usec = 600, pulse_max_usec=2400, timer_bits=16): timer_resolution=2**timer_bits - 1 pulse_range_usec=pulse_max_usec-pulse_min_usec angle = max(min(angle, 180),0) pulse_target = (angle * pulse_range_usec // 180) + pulse_min_usec tics = pulse_target * timer_resolution // 20000 print(pulse_min_usec, pulse_max_usec, timer_bits, timer_resolution, pulse_range_usec, angle, pulse_target, tics) ##servo.duty_u16(tics)
Formulas
- duty-tics = (pulse width in seconds) * (scale: number of timer steps) * (frequency in Hz)
- duty-tics = (pulse width in seconds) * (scale: number of steps) / (Period in seconds)
Millisecond Math
- duty-tics = (pulse width in seconds * 1000) * scale * freq) / 1,000
Change the units to milliseconds taking into account multiplication or division by 1,000
- duty-tics = ((pulse width in ms) * scale * 50)/1000
- duty-tics = (pulse width in ms) * scale / 20
Microsecond Math
- duty-tics = (pulse width in s seconds * 1000) * scale * freq) / 1,000
- duty-tics = (pulse width in seconds* 1,000,000) * scale * freq) / 1,000,000
We change the unit to microseconds, Microsecond, because most of the APIs accept microseconds and not seconds in order to stay with integer math.
- duty-tics = ((pulse width in usec) * scale * 50) / 1,000,000
- duty-tics = (pulse width in usec) * scale / 20,000
Taking it to the Timer (house)
Device | Timer Size | Rollover Timer Tics |
---|---|---|
ESP8266 | 10 | 1024 |
ESP32 | 8, 10, 12, 15 | 256, 1024, 4096, 32K |
Avtel Arduino | 8 | 256 |
Pico | 16-ish | 65205 |
10-Bit Timer Tic Examples
Some machines have 10-bit timers or 1024 steps. We can calculate the number of timer tics required for pulses of these widths
- duty-tics = 1ms * 1024 * 50 / 1000 ==> 51
- duty-tics = 2ms * 1024 * 50 / 1000 ==> 102
- duty-tics = 1000us * 1024 / 20000 = 51
- duty-tics = 2000us * 1024 / 20000 = 102
16-Bit Timer Tick Examples
Some machines have 16-bit timers or cascaded 8-bit timers resulting in 65535 or 65205 (255*255) steps. We calculate the number of timer tics required for a couple pulse widths.
- duty-tics = 1000us * 65535 // 20000 = 3276
- duty-tics = 2000us * 65535 // 20000 = 6553
Real world Servo pulse lengths
Video
Revision History
Created 2023 01
Updated 2023 01
Comments
Post a Comment