We'll use servo motors, specifically the SG90, to actuate our PTZ kit. Servo motors are driven by pulse width modulation(PWM) signals.
Table of Contents
- Servo
Implementation
PWM
- Final Code
Servo
The SG90 is a microservo that can rotate through 180 degrees. It is operated by pulse width modulation (PWM) signals oscillating at 50 Hz. A pulse with a period of 1.5 ms (7.5% duty cycle) will position it to 0 degrees, 2 ms (10% duty cycle) to 90 degrees, and 1 ms (5% duty cycle) to -90 degrees. Feeding a signal with duty cycles ranging from 5 to 10% should therefore sweep the motor from 0 to 180 degrees.
Under Pins configure the pin.
// Configure GP20 as PWM channel 1A
pads_bank0.gpio(20).modify(|_, w| w
.pue().set_bit()// pull up enable
.pde().set_bit()// pull down enable
.od().clear_bit()// output disable
.ie().set_bit()// input enable
);
io_bank0.gpio(20).gpio_ctrl().modify(|_, w| w.funcsel().pwm());// connect to matching pwm
The programming parameters for the RP2040's PWM are set using the below formula.
fPWM=fsysperiod=fsys(TOP+1)×(CSR_PH_CORRECT+1)×(DIV_INT+DIV_FRAC16)
\begin{split}
f_{PWM} &= {f_{sys} \over period}\\
&= {f_{sys} \over (TOP + 1) \times (CSR\_PH\_CORRECT + 1) \times (DIV\_INT + {DIV\_FRAC \over 16})}
\end{split}\\
fPWM=periodfsys=(TOP+1)×(CSR_PH_CORRECT+1)×(DIV_INT+16DIV_FRAC)fsys
Where:
- TOP is the upper limit count of the PWM's counter,
- CSR_PH_CORRECT is the phase correction selector that determines whether the PWM's counter counts backwards from TOP,
- and DIV_INT and DIV_FRAC are further used in dividing the counter to acquire a final PWM output.
For a wrap value of 50,000 and with phase correction enabled we can get the divider values below:
DIV_INT+DIV_FRAC16=fsys(TOP+1)×(CSR_PH_CORRECT+1)×fPWM=125,000,000(49,999+1)×(1+1)×50=25+0
\begin{split}
DIV\_INT + {DIV\_FRAC \over 16}
&= {f{sys} \over (TOP + 1) \times (CSR\__PH\_CORRECT + 1) \times f_{PWM}}\\
\\
&= {125,000,000 \over (49,999 + 1) \times (1 + 1) \times 50}\\
\\
&= 25 + 0\\
\end{split}
DIV_INT+16DIV_FRAC=(TOP+1)×(CSR_PH_CORRECT+1)×fPWMfsys=(49,999+1)×(1+1)×50125,000,000=25+0
We will therefore set our DIV_INT as 25 and DIV_FRAC as 0.
// pwm2 set up
let pwm2 = dp.PWM.ch(2);// Acquire handle for pwm slice 2
resets.reset().modify(|_, w| w.pwm().clear_bit());// Deassert pwm
// Configuring pwm2
pwm2.csr().modify(|_, w| w
.divmode().div()// free runnning counter determined by fractional divider
.ph_correct().set_bit()// enable phase correction
);
pwm2.top().modify(|_, w| unsafe { w.bits(50_000) });// sets the wrap value: TOP
// For a fpwm of 50Hz w/ top of 50000 div need to be 25
pwm2.div().modify(|_, w| unsafe { w.int().bits(25).frac().bits(0) });
pwm2.csr().modify(|_, w| w.en().set_bit());// Enable pwm2
Final Code
Rearranging the code we get this final copy.
In the next part we shall run an example showing PWM in operation.
SOCIAL SHARE CARD GENERATOR