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.
HI this code is not working on Nucleo board L073RZ i have used the same code and made necessary changes but the stepper motor is not rotating, atleast itsnot vibrating, the stepper motor with ULN2003AN is working fine with ESP32
The code works well if you want to move the motor for a number of sequences. How would one move steps less than a sequence? Lets say I want to move 5 steps, which is less than a full sequence, how will this be done? Or if I want to move 9 steps, which is one full sequence plus one step.
Hi,
I have copied the code as is and made the connections which I have double checked. When I run the code, my motor vibrates but doesn’t rotate.
I have tested with two different motors and driver boards but no luck.
Can anyone please help.
Resolved the issue. My wiring was not right. Swapper IN1 and IN2 and the motor started rotating.
Hi, what is that 60000000 in delay function that is used in stepper_set_rmp function?
from where that number is written?
Hi, my motor is vibrating but no rotating, do you know why ?
Did you solve ur problem?
Check your wire connection
Can you suggest me what I need to drive three stepper motors with stm32f1
well 4 pins are used to drive 1 motor. So you need to use 12 pins
Couldn’t download the stepper motor code. I guess the application/user folder is missing. The download contains only the auto generated Core and driver folders.
check the main.c file
What consideraions should we have when we work with a NEMA 17.
I couldn’t do the connection, can you help me
What’s there to do the connection? Stepper comes along a fixed connector, and you can connect it only one way.
Is it possible to reverse direction of the rotation?
yes. In thew function stepper_step_angle , second parameter is direction. I have commented the values in the code.
1
your mathematic ist wrong
Ok. Can u elaborate ?
where is your Main function?
you can download the entire code.