You will learn how to control a standard miniature servo with the STM32 System on Chip. Standard miniature analogue servo is controlled with a PWM signal of frequency 50Hz with a duty cycle period between 1 ms (rotate to 0) and 2 ms (rotate to 180 degrees), where 1.5 ms corresponds to 90 degrees. Some servos have other duty cycle minimum and maximum values, always refer to the documentation.
A servo has a red arrow presenting the gauge's current position.
The servo is an example of a physical actuator. It requires some time to operate and change the position. Please give it time to set a new position between consecutive changes of the control PWM signal. Moreover, because of the observation via camera, too quick rotation may not be observable at all depending on the video stream fps. A gap of 2s between consecutive rotations is usually a reasonable choice.
A good understanding of the PWM signal and duty cycle is necessary. In this scenario, we use built-in timers to control the PWM hardware channels of the STM32WB55 chip. In this case, we do not use any external library; instead, we use the hardware timer library built in the Arduino Core STM32 framework for STM32WB55 (stm32duino)[1] so that no additional external libraries will be included in the project.
Some servos tend to go below 1 ms and above 2 ms to achieve a full 180-degree rotation range. For example, the servo in our laboratory (MG90/SG90) accepts values starting from 500 ms, and ending at 2500 ms. If there is a need for a high accuracy of rotation, it is possible to fine-tune the minimum and maximum duty cycle values.
Rotate the servo to the following angles: 0, 90, 180, 135, 45 and back to 0 degrees.
In the laboratory equipment, the servo and the fan are connected to the same hardware timer instance. It means that both elements use the same base frequency of the PWM signal. If you use a fan and servo in the same project the frequency of the PMW signal needs to match the servo requirements.
The hardware timer library implements functions which allow us to control the duty cycle of the PWM signal and express it in different formats, including percentages. In the case of setting the PWM duty cycle expressed in percentages, the minimum value is 0, and the maximum is 100. We can also express the duty cycle in microseconds which is easier to calculate for the servo.
Write your application all in the setup()
function, leaving loop()
empty.
Include Arduino and timer libraries, specific to STM32. The servo is controlled with GPIO PA_10 (the PA_10 name is the STM Nucleo naming convention). Define the pin name constant.
#include "Arduino.h" #include <HardwareTimer.h> #define SERVO_PWM_PIN PA0
We will also need some variables:
// PWM variables definitions TIM_TypeDef *SRVInstance; //General hardware instance uint32_t channelSRV; //2 channel for the servo HardwareTimer *MyTimServo; //Hardware Timer class for Servo PWM
The hardware timer library uses internal timer modules and allows us to define channels attached to the timer. Channels represent the output pins connected to the hardware elements. Our laboratory board uses the same timer to control the servo and fan. In this example, we will use one channel for the servo only setting the proper PWM frequency of the timer. The channel connected to the servo controls the PWM duty cycle.
Create the timer instance type based on the servo pin:
TIM_TypeDef *SRVInstance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(SERVO_PWM_PIN), PinMap_PWM);
Define the channel for servo:
channelSRV = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(SERVO_PWM_PIN), PinMap_PWM));
Instantiate HardwareTimer object. Thanks to 'new' instantiation, HardwareTimer is not destructed when setup() function is finished.
MyTimServo = new HardwareTimer(SRVInstance);
Configure and start PWM
MyTimServo->setPWM(channelSRV, SERVO_PWM_PIN, 50, 5); // 50 Hertz, 5% dutycycle.
MG 90 servos that we use in our lab are specific. As mentioned above, to achieve a full 180-degree rotation range, their minimum and maximum duty cycle timings go far beyond standards. We will create a function for calculating the duty cycle for the servo with the angle as the input parameter.
void fSrvSet(int pAngle) { if (pAngle<0) pAngle=0; //check boudaries of angle if (pAngle>180) pAngle=180; int i_srv=500+(200*pAngle)/18; //minimal duty cycle is 0,5ms, maximal 2,5ms MyTimServo->setCaptureCompare(channelSRV, i_srv, MICROSEC_COMPARE_FORMAT); //modify duty cycle };
Rotating a servo is as easy as calling our function with the desired angle as the parameter, e.g.:
fSrvSet(90); delay(2000);
How do I know minimum and maximum values for the timings for servo operation?: Those parameters are provided along with servo technical documentation, so you should refer to them. Our configuration reflects the servos we use (MG90/SG90), and as you can see, it goes far beyond the standard servo rotation control that is a minimum of 1000us and a maximum of 2000us. Using standard configuration, your servo won't rotate at full 180 degrees but at a shorter rotation range. Would it be possible to control the servo and fan with the same program?: Yes, but you have to remember that servo has very strict timing requirements. Because both elements share the same timer, they also share the same frequency which must be set according to the servo requirements.
Observe the red arrow to rotate accordingly. Remember to give the servo some time to operate.