Interface Stepper motor with STM32
A stepper motor divides full rotation into number of equal steps. It finds great application in the fields of Robotics. Today, in this tutorial, we will learn how to interface stepper motor with STM32.
Stepper motor 28BYJ-48 generally comes along with an IC ULN2003. This IC is used to drive motor because microcontroller pins are unable to provide sufficient current to drive these motors. There are three different types of stepping modes used for stepper motors:-
- Wave Drive
- Full Drive
- Half Drive
I have also covered how to control the angle of the stepper motor using the potentiometer. You can check out this tutorial also.
Wave Drive
In this mode only one stator electromagnet is energised at a time. It has the same number of steps as the full step drive. The motor takes 2048 STEPS to complete 1 revolution, which means 2048/4 = 512 sequences per Revolution.
Full Drive
In this mode two stator electromagnets are energized at a time and the motor runs at full torque. Two phases are energized at the same time, and motor have more Torque than the Wave Drive. The motor takes 2048 STEPS to complete 1 revolution, which means 2048/4 = 512 sequences per Revolution.
Half Drive
In this mode, one and two phases are energized alternatively. This mode is used to increase the angular resolution of the motor but the torque is reduced. The Torque remains somewhere in between the Wave Drive and Full Drive. The motor takes 4096 STEPS to complete 1 revolution, which means 2048/8 = 512 sequences per Revolution.
Connection & Configuration
Below is the image showing the connection between the stepper motor 28BYJ-48, ULN2003 and STM32F103.
The ULN2003 has 4 input pins. They are connected to the pins PA1, PA2, PA3 and PA4 of the STM32. The driver can be powered with 5-12V DC, so we will supply it 5V from the battery.
There are 5 pins in the output of the driver, which are connected to the motor.
Below is the image showing the clock configuration in cubeMX.
I am using the external crystal to provide the clock. The STM32F103C8 board has 8MHz crystal on it, and we will run the system at maximum 72MHz clock. Also note that the APB2 Timer clock is at 72MHz. This will be later used while configuring the timer.
We need the delay in microseconds to control the RPM of the motor. I have already covered a tutorial about how to generate delays in microseconds. Below is the image showing the timer configuration for the delay.
I am using TIM1 for the delay. The TIM1 is connected to the APB2 bus, which is running at 72MHz as shown in the clock configuration above. We need the Timer frequency to be 1MHz, so we will use the prescaler of 72 to bring down the clock from 72MHz to 1MHz. The ARR is set to maximum, 0xFFFF, as it is generally set for delays.
Below is the image showing the pin configuration for this project.
These pins, PA1 – PA4, are connected to the input of the driver ULN2003. We have set these pins as output.
Some Insight into the CODE
Below is the function to generate the delay in microseconds.
void delay (uint16_t us)
{
__HAL_TIM_SET_COUNTER(&htim1, 0);
while (__HAL_TIM_GET_COUNTER(&htim1) < us);
}
If you don’t understand this part please check out https://controllerstech.com/create-1-microsecond-delay-stm32/
Define the number of steps the motor takes for 1 revolution. We will use the wave drive in this tutorial, so we have a total of 4096 steps in 1 revolution.
#define stepsperrev 4096
Now we will define the function to set the RPM of the Motor. We have to use it instead of delay.
void stepper_set_rpm (int rpm) // Set rpm--> max 13, min 1,,, went to 14 rev/min
{
delay(60000000/stepsperrev/rpm);
}
The parameter of the function stepper_set_rpm
is the RPM with which we want the motor to rotate. This function basically generates a delay in microseconds, which depends on the 2 parameters. Here the value 60000000 represents the number of microseconds in 1 minute.
- The number of steps motor takes for 1 Revolution.
- The RPM of the motor we want.
Next is the function to drive 1 sequence in the Half Drive Mode. In this mode, there are a total of 8 steps required to generate 1 sequence. We have to set the pins HIGH in a particular order, which is shown below.
void stepper_half_drive (int step)
{
switch (step){
case 0:
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); // IN1
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); // IN2
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET); // IN3
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // IN4
break;
case 1:
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); // IN1
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); // IN2
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET); // IN3
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // IN4
break;
case 2:
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); // IN1
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); // IN2
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET); // IN3
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // IN4
break;
case 3:
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); // IN1
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); // IN2
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET); // IN3
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // IN4
break;
case 4:
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); // IN1
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); // IN2
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET); // IN3
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // IN4
break;
case 5:
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); // IN1
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); // IN2
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET); // IN3
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // IN4
break;
case 6:
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); // IN1
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); // IN2
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET); // IN3
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // IN4
break;
case 7:
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); // IN1
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); // IN2
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET); // IN3
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // IN4
break;
}
}
Once all these 8 steps are executes, 1 sequence will complete and the motor will rotate through an angle of 360/512 = 0.703125°.
The motor takes 512 sequences to complete 1 revolution. This means in each sequence, the motor rotates through an angle of 360/512 = 0.703125°. Now we will write a function to control the angle of rotation.
void stepper_step_angle (float angle, int direction, int rpm) //direction-> 0 for CK, 1 for CCK
{
float anglepersequence = 0.703125; // 360 = 512 sequences
int numberofsequences = (int) (angle/anglepersequence);
for (int seq=0; seq<numberofsequences; seq++)
{
if (direction == 0) // for clockwise
{
for (int step=7; step>=0; step--)
{
stepper_half_drive(step);
stepper_set_rpm(rpm);
}
}
else if (direction == 1) // for anti-clockwise
{
for (int step=0; step<=7; step++)
{
stepper_half_drive(step);
stepper_set_rpm(rpm);
}
}
}
}
The function to move the stepper by some angle. Here we can also control the direction and RPM.
Inside this function we will first calculate the number of sequence required. This depends on the Angle through which we want to rotate the motor. numberofsequences = angle/0.703125
.
Then we will call the function stepper_half_drive to drive the motor for 1 sequence. This will be repeated as many times as the numberofsequences
we have.
We can also control the direction of rotation of motor by reversing the steps in each sequence.
The function stepper_step_angle
can rotate the motor through an angle, but it does not consider the current position of the stepper motor. For eg– if you pass the angle 45 to the parameter and call the function twice, the motor will rotate through an angle of 90°.
We will write another function to rotate the motor through an angle, which will also consider the current position of the motor.
float currentAngle = 0;
void Stepper_rotate (int angle, int rpm)
{
int changeinangle = 0;
changeinangle = angle-currentAngle; // calculate the angle by which the motor needed to be rotated
if (changeinangle > 0.71) // clockwise
{
stepper_step_angle (changeinangle,0,rpm);
currentAngle = angle; // save the angle as current angle
}
else if (changeinangle <0.71) // CCK
{
changeinangle = -(changeinangle);
stepper_step_angle (changeinangle,1,rpm);
currentAngle = angle;
}
}
The variable currentAngle is defined as global variable and it will keep track of the current position of the motor.
Inside the function Stepper_rotate
, we will first calculate the changeinangle. If this change is greater than 0.71, we will call the function stepper_step_angle
to rotate the motor in the clockwise direction.
Similarly, If this change is lesser than -0.71, we will call the function stepper_step_angle
to rotate the motor in the counterclockwise direction.
The value 0.71 is chosen because the motor rotates through 0.703125° in 1 sequence. Therefore we can not rotate the motor through an angle, which is lesser than this value.
The main function
int main()
{
....
HAL_TIM_Base_Start(&htim1);
while (1)
{
for (int i=0; i<=360; i++)
{
Stepper_rotate(i, 10);
HAL_Delay(250);
}
for (int i=360; i>=0; i--)
{
Stepper_rotate(i, 10);
HAL_Delay(250);
}
}
}
Inside the main function we will start the timer, so to enable the delay function.
In the while loop we will call the function Stepper_rotate in a for loop to rotate the motor through the entire 360°. The motor will wait for 250ms between each angle. Once the motor reached 360°, it will start rotating in the counterclockwise direction.
Result
Watch the video below to see the complete working.