STM32 Blue Pill with Stepper Motor (28BYJ-48 and ULN2003 Motor Driver)

In this tutorial, we will learn to use STM32 Blue Pill with 28 BYJ-48 steeper motor and control it using the ULN2003 motor driver using STM32Cube IDE. Firstly, we will briefly introduce you to 28BYJ-48 Stepper Motor and ULN2003 Stepper Motor Driver Module. Then, we will interface the stepper motor with the Blue Pill using a UNL2003 driver and program it in Cube IDE to demonstrate the movement of the motor.

We have similar guides with other microcontrollers also:

Stepper Motors Introduction

Stepper motors are DC brushless and synchronous motors. They rotate in discrete steps of predefined values and are able to rotate both clockwise and anticlockwise. Unlike other DC motors, they provide a precise position control according to the number of steps per revolution for which the motor is designed. That means a complete one revolution of a stepper motor is divided into a discrete number of steps. They are commonly used in CNC machines, Robotics, 2D and 3D printers.

For this guide, we will use a 28BYJ-48 stepper motor and control it through ULN2003 motor driver.

28BYJ-48 Stepper Motor

This is the most commonly used stepper motor in low power industrial and most famously in hobbyist projects.

8BYJ-48 steper motor

28BYJ-48 is a uni-polar 5V stepper motor that takes electrical signals as input and rotates by converting these input signals into mechanical rotation. It consists of 4 stationary coils rated at +5V. These coils are known as a stator and make a ring around the rotor. Because of 5 volts operating voltage, we can easily drive this motor from any microcontroller such as STM32, ESP32, ESP8266, Arduino or TM4C123 Tiva Launchpad, etc. It has a 1/64 reduction gear set and therefore moves in precise 512 steps per revolution. These motors are silent in comparison to other DC motors and servo motors. You can achieve positional control easily without needing extra circuitry and components.

Stride Angle

This stepper motor has a stride angle of 5.625 degrees. That means 28BYJ-48 will complete one revolution in (360/5.625) 64 steps by taking one step at a time and in one step it covers a 5.625-degree distance. However, the stepper motor can also be used in full-step mode. In full-step mode, the angle of each step is 11.25 degrees. That means the motor completes its one revolution in 32 steps instead( 360/11.25).

Therefore, in order to move one step forward or backward, the coils of the motor energize with a particular sequence.

Steps per Revolution & Step Angle

The output shaft of this particular stepper motor is driven through a gear ratio of 64:1 that is also known as the speed variation ratio. This suggests that after the inside motor rotates 64 times then the shaft will complete one rotation.

Therefore we can conclude that:

  • In order to complete one full rotation of the shaft a total of 2048 steps will be required. This is known as the steps per revolution calculated by multiplying 32 and 64 (32×64=2048).
  • Moreover, this will enable the motor to have a step angle of 360º/2048 steps = 0.18º/step.

Specifications

  • It is a unipolar 5 pin coil with a rated DC voltage of 5V.
  • Has 4 phases with a stride angle of 5.625°/64.
  • Speed variation ratio is 1/64
  • The frequency of this stepper motor is 100Hz and insulated power is 600VAC/1mA/1s.
  • The half-step method is recommended for driving this stepper motor.
  • The value of pull in torque for a stepper motor is 300 gf-cm.

Pinout

The following figure shows the pinout diagram of 28BYJ-48 stepper motor. It consists of 5 pins. Out of these 5 pins, four pins are used to provide sequence logic to the coils and one pin is a +5 volts supply pin.

28BYJ-48 stepper motor coils pins

Pin Configuration Details

Pin NumberCoil NumberColour
14Blue
22Pink
33Yellow
41Orange
5VccRed

Coil 1-Coil 4: These are coils used to control the step sequence of the stepper motor. One end of each coil is connected with +5V and the other end will be connected with ULN2003 driver output.

Vcc: Used to apply +5 volt supply to the stepper motor. This voltage appears across the coils when a specific coil is ground through a control sequence.

ULN2003 Stepper Motor Driver Module

To use a 28BYJ-28 stepper motor with the STM32 Blue Pill, we will be required to attach it with the ULN2003 motor driver. This is necessary because the current consumption of 28BYJ-48 is around 240mA. That means the current required to drive coils by applying a sequence of control signals is also almost 200mA. GPIO pins STM32 Blue Pill can not provide current of this magnitude. Therefore, we need a ULN2003 driver which translates low current output of Blue Pill GPIO pins into higher current that meets the requirement of stepper motor control signals.

ULN2007 driver

The ULN2003 breakout board has high current and voltage than a single transistor and therefore it can drive a stepper motor easily by enabling our STM32 board.

Recommended reading: ULN2003 introduction, pinout, example and features

Stepper Motor Driver Module Pinout

ULN2003 driver IC consists of 7 Darlington pair transistor outputs. Each output can drive 500mA and 50V load. The input to each 7 Darlington pair transistor will be a signal from our microcontroller. To drive a stepper motor, this driver board uses only four input pins (IN1, IN2, IN3, and IN4).

The following diagram shows the ULN2003 motor driver board:

ULN2003 Stepper Motor driver board

Motor Connector Header is used to connect the stepper motor. It provides output from four Darlington pair transistors.

8BYJ-48 steper motor and ULN2003 Driver
PinDescription
1N1 to IN4These are input pins used to provide control signals to the stepper motor such as control sequences. We will connect these pins with the output pins of STM32.
VCC and GNDVcc is a power supply pin and it is used to provide 5 volts power to the stepper motor from an external power source.

The 28BYJ-48 stepper motor requires 240mA current to operate and it also consumes power at an idle condition. Therefore, it is recommended not to power the 28BYJ-48 stepper motor directly from any microcontroller, instead use an external 5 volts power supply, although STM32 Blue Pill provides an onboard 5 volts signal. We will use an external power supply of 5V to power the motor driver.

Interfacing STM32 with 28BYJ-48 Stepper Motor and ULN2003 motor driver

We will require the following components:

  • STM32 Blue Pill board
  • One 28BYJ-48 Stepper Motor with ULN2003 motor driver
  • External 5V power supply
  • Connecting Wires

To connect the STM32 Blue Pill with the stepper motor and driver we will use the input pins IN1-IN4, the power supply pins, and the motor connection header. The 28BYJ-48 stepper motor already comes attached with the motor driver via the motor connector header. Now we will connect four output pins of our STM32 board with the input pins (IN1-IN4) of the driver. We have used the following pins to connect with each input pin.

STM32 Blue PillMotor Driver
PA1IN1
PA2IN2
PA3IN3
PA4IN4

Power the motor driver with a 5V external power supply. Both the grounds of the power supply and the STM32 board will be in common.

The connection diagram is shown in the picture below.

STM32 Stepper Motor with ULN2003A motor driver and stepper motor connection diagram
STM32 Stepper Motor with ULN2003A motor driver and stepper motor

STM32 Blue Pill Stepper Motor Project

Let’s create and build a project in STM32 CubeIDE through which we will be able to control our stepper motor using STM32 Blue Pill and ULN2003A motor driver.

Open the CubeIDE and head over to a new project.

Then for the target selection, specify the STM32 Blue Pill board number. After that click on any column as shown in the picture below. Then click the ‘Next’ button.

Blue Pill STM32 using STM32Cube creating project pic 3

Specify the name of your project then click ‘Finish’ to complete the setup of your project.

Go to System Core > RCC then select ‘Crystal/Ceramic Resonator’ in from the High Speed Clock feature.

STM32 Blue Pill Timer in PWM Mode LED Dimmer Set RCC

Now we have enabled the RCC external clock source.

Head over to Timers > TIM1 and set the clock source as ‘Internal Clock.’ Set the Prescaler value as 71.

STM32 Stepper Motor with ULN2003A configure timer

Now setup PA1, PA2, PA3 and PA4 as GPIO_Output pins.

STM32 Stepper Motor with ULN2003A configure GPIO outputs

Clock Configuration

Next go to the Clock Configuration found at the top. This will open the following window. Here we will select the clock frequency.

STM32 Blue Pill UART DMA Clock Configuration

You can specify your system clock. We will set it as 72 MHz. These are the configurations we have set:

Blue Pill STM32 Creating project Digital Input picture 9

Now we will save our file. Press Ctrl + S. The following window will appear. Click ‘Yes.’ This will generate a template code for you.

Blue Pill STM32 using STM32Cube creating project pic 11

Another window will appear that will ask if you want to open the perspective. Click ‘Yes.’

Blue Pill STM32 Creating project Digital Input picture 11

STM32 Blue Pill Stepper Motor Code

We will set up a code in which the stepper motor moves with a delay.

Now let us look at our main.c file that was generated. Inside the main.c file, make sure the following code is part of your script by including the lines of code given below.

#include "main.h"

TIM_HandleTypeDef htim1;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM1_Init(void);

#define IN1_PIN GPIO_PIN_1
#define IN1_PORT GPIOA
#define IN2_PIN GPIO_PIN_2
#define IN2_PORT GPIOA
#define IN3_PIN GPIO_PIN_3
#define IN3_PORT GPIOA
#define IN4_PIN GPIO_PIN_4
#define IN4_PORT GPIOA

void microDelay (uint16_t delay)
{
  __HAL_TIM_SET_COUNTER(&htim1, 0);
  while (__HAL_TIM_GET_COUNTER(&htim1) < delay);
}

void move_anticlockwise (int steps, uint16_t delay)
{
  for(int i=0; i<steps; i=i+1)
  {
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);   // IN1
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); // IN2
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); // IN3
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); // IN4
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);   // IN1
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);   // IN2
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); // IN3
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); // IN4
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); // IN1
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);   // IN2
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); // IN3
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); // IN4
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); // IN1
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);   // IN2
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);   // IN3
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); // IN4
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); // IN1
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); // IN2
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);   // IN3
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); // IN4
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); // IN1
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); // IN2
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);   // IN3
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_SET);   // IN4
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); // IN1
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); // IN2
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); // IN3
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_SET);   // IN4
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);   // IN1
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); // IN2
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); // IN3
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_SET);   // IN4
    microDelay(delay);
  }
}

void move_clockwise (int steps, uint16_t delay) // CV - Clockwise
{
  for(int i=0; i<steps; i=i+1)
  {
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);   // IN1
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); // IN2
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); // IN3
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_SET);   // IN4
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); // IN1
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); // IN2
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); // IN3
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_SET);   // IN4
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); // IN1
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); // IN2
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);   // IN3
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_SET);   // IN4
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); // IN1
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); // IN2
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);   // IN3
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); // IN4
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); // IN1
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);   // IN2
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);   // IN3
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); // IN4
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); // IN1
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);   // IN2
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); // IN3
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); // IN4
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);   // IN1
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);   // IN2
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); // IN3
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); // IN4
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);   // IN1
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); // IN2
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); // IN3
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); // IN4
    microDelay(delay);
  }
}

int main(void)
{
  HAL_Init();

  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM1_Init();

  HAL_TIM_Base_Start(&htim1);

  while (1)
  {
	  move_clockwise(256, 1000);  // 256 half revolution
	  HAL_Delay(100);
	  move_anticlockwise(128, 1000); // 128 quarter revolution
	  HAL_Delay(100);
  }

}

Working of the Code

We start off by defining the four GPIO pins of STM32 Blue Pill that will be used to connect with the input pins of the motor driver. We have configured GPIO pins 1, 2, 3 and 4 to connect with IN1, IN2, IN3 and IN4 respectively. Moreover, the selected port is GPIOA for all the four pins. These are the four pins that we setup as GPIO_Output in the configuration.

#define IN1_PIN GPIO_PIN_1
#define IN1_PORT GPIOA
#define IN2_PIN GPIO_PIN_2
#define IN2_PORT GPIOA
#define IN3_PIN GPIO_PIN_3
#define IN3_PORT GPIOA
#define IN4_PIN GPIO_PIN_4
#define IN4_PORT GPIOA

Next, we have the microDelay() function, where we set the timer counter register value on runtime as ‘0.’ It continues to run while the timer counter register value remains less than the ‘delay’, which is the single parameter of this function.

void microDelay (uint16_t delay)
{
  __HAL_TIM_SET_COUNTER(&htim1, 0);
  while (__HAL_TIM_GET_COUNTER(&htim1) < delay);
}

Then, we have the move_anticlockwise function which will be responsible for moving the stepper motor in an anticlockwise (counter clockwise) manner. This function takes in two parameters, the number of steps and the delay. A lesser delay value constitutes to a faster movement of the motor.

Inside this function, we continuously call the HAL GPIO function till the step count provided by the user. Each input pin IN1-IN4 is either set or reset by calling the HAL_GPIO_WritePin() function with delays in between to create the anticlockwise motion.

void move_anticlockwise (int steps, uint16_t delay)
{
  for(int i=0; i<steps; i=i+1)
  {
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);   
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); 
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);   
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);   
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); 
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);   
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); 
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);   
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);   
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); 
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);   
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); 
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);   
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_SET);   
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_SET);   
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);   
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_SET);   
    microDelay(delay);
  }
}

Similarly, we have the move_clockwise function which will be responsible for moving the stepper motor in a clockwise manner. This function takes in two parameters, the number of steps and the delay. A lesser delay value constitutes to a faster movement of the motor.

Inside this function, we continuously call the HAL GPIO function till the step count provided by the user. Each input pin IN1-IN4 is either set or reset by calling the HAL_GPIO_WritePin() function with delays in between to create the clockwise motion. Note that in this function all the input pins are set/reset in the opposite way as in the move_anticlockwise() function, in order to move the stepper motor in an opposite direction.

void move_clockwise (int steps, uint16_t delay) 
{
  for(int i=0; i<steps; i=i+1)
  {
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);   
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_SET);   
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_SET);   
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);   
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_SET);   
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);   
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); 
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);   
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);   
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); 
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);   
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); 
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);   
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);   
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); 
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);   
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET); 
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET); 
    microDelay(delay);
  }
}

Inside the main() function, we initialize all the peripherals, configure the system clock, initialize all the configured peripherals. After that we enable the time. Firstly, call MX_TIM1_Init() function to initialize Timer1 on the set Prescaler value. Then call HAL_TIM_Base_Start() to start the TIM Base generation. It takes in a single parameter which is the pointer to the TIM_HandleTypeDef structure that holds the configuration parameters for the timer module. It is ‘&htim1’ in our case. Inside the infinite while loop, we first call move_clockwise() function for 256 steps, then add a delay and call the move_anticlockwise() function for 128 steps and the loop repeats continuously.

int main(void)
{
  HAL_Init();

  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM1_Init();

  HAL_TIM_Base_Start(&htim1);

  while (1)
  {
	  move_clockwise(256, 1000);  // 256 half revolution
	  HAL_Delay(100);
	  move_anticlockwise(128, 1000); // 128 quarter revolution
	  HAL_Delay(100);
  }

}

main.c file

This is how a complete main.c file will be after modification.

#include "main.h"

TIM_HandleTypeDef htim1;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM1_Init(void);

#define IN1_PIN GPIO_PIN_1
#define IN1_PORT GPIOA
#define IN2_PIN GPIO_PIN_2
#define IN2_PORT GPIOA
#define IN3_PIN GPIO_PIN_3
#define IN3_PORT GPIOA
#define IN4_PIN GPIO_PIN_4
#define IN4_PORT GPIOA

void microDelay (uint16_t delay)
{
  __HAL_TIM_SET_COUNTER(&htim1, 0);
  while (__HAL_TIM_GET_COUNTER(&htim1) < delay);
}

void move_anticlockwise (int steps, uint16_t delay)
{
  for(int i=0; i<steps; i=i+1)
  {
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET);
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET);
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET);
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET);
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET);
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_SET);
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_SET);
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_SET);
    microDelay(delay);
  }
}

void move_clockwise (int steps, uint16_t delay)
{
  for(int i=0; i<steps; i=i+1)
  {
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_SET);
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_SET);
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_SET);
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET);
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET);
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET);
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET);
    microDelay(delay);
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET);
    microDelay(delay);
  }
}

int main(void)
{
  HAL_Init();

  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM1_Init();

  HAL_TIM_Base_Start(&htim1);

  while (1)
  {
	  move_clockwise(256, 1000);  // 256 half revolution
	  HAL_Delay(100);
	  move_anticlockwise(128, 1000); // 128 quarter revolution
	  HAL_Delay(100);
  }

}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief TIM1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM1_Init(void)
{

  /* USER CODE BEGIN TIM1_Init 0 */

  /* USER CODE END TIM1_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM1_Init 1 */

  /* USER CODE END TIM1_Init 1 */
  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 71;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 65535;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM1_Init 2 */

  /* USER CODE END TIM1_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4, GPIO_PIN_RESET);

  /*Configure GPIO pins : PA1 PA2 PA3 PA4 */
  GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

Save the main.c file after modifying it. Now we are ready to build our project.

Building the Project

To build our project press Ctrl + B or go to Project > Build All.

Your project will start building. After a few moments, your project will be successfully built if there are no errors.

Connecting ST-Link Programmer with STM32

Now as we have successfully built our project let us move ahead and upload the code to our STM32 board. To do that, first we will have to connect our Blue Pill STM32 with a ST-Link programmer. We will be using ST-Link V2.

ST-Link V2 programmer

This will provide an interface between our computer and our STM32 board. It consists of 10 pins. We will be using pin2 SWDIO, pin6 SWCLK, pin4 GND, and pin8 3.3V to connect with our STM32 board. The SWDIO is the data input/output pin and the SWCLK is the clock pin. Follow the pin configuration given on the ST-LINK V2 to identify each pin.

Follow the table below to connect both devices correctly.

STM32ST-LINK V2
VCC 3.3V pinpin8 3.3V
SWDIO pinpin2 SWDIO
SWCLK pinpin6 SWCLK
GND pinpin4 GND
ST-Link V2 with STM32 connection

Additionally, move the BOOT jumper to the right to enable the microcontroller to go into programming mode.

STM32 in programming mode
  • Now connect your ST-LINK V2 with your computer via the USB port. Both devices will power ON.
  • Next press the RUN button in the IDE. The ‘Edit configuration’ window will open up. Click ‘OK’.
  • After a few moments, the code will be successfully sent to the STM32 board. Otherwise, press the RESET button on your STM32 board.
  • Now to bring the Blue pill back to normal mode make sure you bring the BOOT jumper back at its place.

Press the Reset button of STM32. The stepper motor will start rotating.

You may also like to read:

Leave a Comment