STM32 Blue Pill Timer Interrupt with STM32Cube IDE and HAL Libraries

In this STM32 Blue Pill tutorial, we will learn how to configure and handle timer interrupts using HAL Library in STM32Cube IDE. We will demonstrate this through an example by toggling an LED after a set number of time.

Timer interrupts in STM32 pause the sequential execution of a program loop() function for a predefined number of seconds (timed intervals) to execute a different set of commands. After the set commands are executed, the program resumes again from the same position. The Blue Pill STM32F103C8 comes with four timers known as TIM1, TIM2, TIM3, and TIM4. They act as a clock and are used to keep track of time based events. We will show you how to program these timers in STM32Cube IDE using HAL Library.

STM32 Blue Pill Timer Interrupt with STM32Cube IDE and HAL Libraries

By the end of this article you will be able to know about:

  • How to set up timer interrupt in STM32
  • Configure GPIO Output Pin using STM32Cube IDE
  • Toggling the output pin after every 500 milliseconds by setting Prescaler and Preload value
  • ISR that toggles the LED after every interrupt

We will guide you step by step on how to configure timer interrupts for Blue Pill STM32 using STM32Cube IDE. Our aim will be to toggle an LED after every interrupt.

Timer Interrupt to toggle LED

Now we will learn how to handle interrupts in the Blue Pill STM32 to toggle an LED. The LED will be set up as a digital output. The LED will be toggled after a set number of intervals.

The following components are required:

  • Blue Pill STM32 board
  • One 5mm LED
  • One 220 ohm resistor
  • Breadboard
  • Connecting Wires

Assemble your circuit as shown below:

STM32 with LED connection diagram
Blue Pill STM32 with LED

In the above diagram, we can see that pin A11 is connected with the anode pin of LED, and the cathode pin is connected with the common ground through the 220-ohm resistor.
You can use any appropriate Blue Pill STM32 digital pin to connect with the LED.

Program Timer Interrupts with STM32 Blue Pill in STM32Cube IDE

We will use STM32Cube IDE to program our STM32 board. Open the IDE and head over to a new project.

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

Blue Pill STM32 using STM32Cube creating project pic 3

The following window will open. Here specify the name of your project then click ‘Finish’ to complete the setup of your project.

STM32 Timer Interrupts Creating project 1

Device Configuration Tool

The Device Configuration Tool will now open. Here we will configure the output pin. For the output pin we have chosen the digital pin PA11.

PA11 is connected with the LED as mentioned before.

Click at PA 11 and set it as GPIO_Output.

STM32 Timer Interrupts Creating project 2

Configuring TIM2

Here comes the important part where we will configure the timer interrupt. For this project we will use TIM2. So head over to Timers > TIM2 and set the clock source as ‘Internal Clock.’

Our aim is that TIM2 should generate an interrupt after every 500ms (0.5s). To set a particular output time we will have to set the Prescaler value, the clock frequency, and the timer preload register’s value.

Tout= (Prescaler x Preload)/Clock Frequency

As the Blue Pill STM32 microcontroller has a maximum clock speed of 72 MHz therefore let us use this frequency. When using 72 MHz Clock frequency, Precaler value as 1000 and output time as 500ms, the Preload value will come out to be (0.5*72*1000000)/1000 = 36000.

You may use appropriate values according to your project.

Next we will specify the Prescaler and Preload (Counter Period) values in the Parameter Settings. We have set 1000 as the Prescaler and 36000 as the Preload. Also, enable the auto-reload preload feature as shown below.

STM32 Timer Interrupts Creating project 3

Now click NVIC Settings and enable the interrupt for TIM2 global interrupt as shown below.

STM32 Timer Interrupts Creating project 4

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

We have enabled the RCC external clock source.

STM32 Timer Interrupts Creating project 5

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.

Blue Pill STM32 Creating project Digital Input picture 8

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 Creating project Digital Input picture 10

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

Timer Interrupts with STM32 Blue Pill Code

Now the following page opens. On the right side you will be able to view the Outline of the code. This happened because we opened our code with perspective.

In the centre you can view the main.c file and on the left you can view the Project Explorer. If you want to go to the Device Configuration Tool, then click the .ioc tab.

STM32 Timer Interrupts Creating project 6

Now let us look at our main.c file that was generated.

Look for the MX_GPIO_Init() function.

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_11, GPIO_PIN_RESET);

  /*Configure GPIO pin : PA11 */
  GPIO_InitStruct.Pin = GPIO_PIN_11;
  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);

}

This is the function that initializes the GPIO pin that we have set up in the Device Configuration Tool. Here as you can see Pin A11 is configured as an output pin with no pull. This means that the pin A11 is setup in high impedance. The set up speed is set to low frequency.

The HAL_GPIO_Init() function is then called for the pin. This function takes in two parameters. The first parameter is the GPIO Port which is GPIOA in our case. The second parameter is &GPIO_InitStruct.

Look for the MX_TIM2_Init() function.

static void MX_TIM2_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 1000;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 36000;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }


}

This is the function that initializes the TIM2 interrupt on the set Prescaler, Preload and Clock frequency values.

Modifying Code

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 htim2;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM2_Init(void);

int main(void)
{

  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM2_Init();
  HAL_TIM_Base_Start_IT(&htim2);
  while (1)
  {
  }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim)
{
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_11);
}

We will first enable the timer in the following lines:

TIM_HandleTypeDef htim2;
HAL_TIM_Base_Start_IT(&htim2);

Then we will add the timer interrupt ISR handler callback function. It is responsible to check the interrupt pin source, then toggle the output GPIO pin accordingly.

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim)
{
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_11);
}

You can view the the timer interrupt handler in the stm32f1xx_it.c file.

void TIM2_IRQHandler(void)
{
  HAL_TIM_IRQHandler(&htim2);
}

The timer interrupt ISR handler callback function name is provided in the timer interrupt handler routine.

void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)
{
  /* TIM Update event */
  if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET)
  {
    if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_UPDATE) != RESET)
    {
      __HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE);
      #if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
        htim->PeriodElapsedCallback(htim);
      #else
        HAL_TIM_PeriodElapsedCallback(htim);
      #endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
    }
  }
}

main.c file

#include "main.h"

TIM_HandleTypeDef htim2;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM2_Init(void);

int main(void)
{

  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM2_Init();
  HAL_TIM_Base_Start_IT(&htim2);
  while (1)
  {
  }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim)
{
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_11);
}

/**
  * @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 TIM2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM2_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 1000;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 36000;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }


}

/**
  * @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_11, GPIO_PIN_RESET);

  /*Configure GPIO pin : PA11 */
  GPIO_InitStruct.Pin = GPIO_PIN_11;
  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);

}


/**
  * @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.

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 the 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 the devices will power ON.

Next press the RUN button in the IDE. The following ‘Edit configuration’ window will open up. Click ‘OK’.

STM32 Timer Interrupts Creating project 7

After a few moments, the code will be successfully sent to the STM32 board. You can view the message in the Console terminal.

Otherwise, press the RESET button on your STM32 board.

STM32 Timer Interrupts Creating project 8

Now to bring the Blue pill back to normal mode make sure you bring the BOOT jumper back at its place. After doing that press the RESET button on the board.

The LED will start blinking every 0.5 seconds.

Watch the demonstration video below to have a better insight.

You may also like to read:

Leave a Comment