on
2023-08-11
03:18 AM
- edited on
2024-03-11
02:14 AM
by
Laurids_PETERSE
In this article, we cover the steps needed to use the Register callback's feature in STM32. The NUCLEO-H503RB (with an STM32H503RBT6 microcontroller) board is used, but the steps can be easily tailored to another MCU. All the implementation was done over the STM32CubeIDE v1.13.1 but can be implemented in any other version with minor step changes.
In this demonstration, we use the STLINK VCOM Port connected to the MCU's USART3 (figure 2). Furthermore, we use the TIM2 and the board’s user LED (figure 3). Two callbacks are registered: one for the UART_Tx and the other one for the timer overflow, also known as period elapsed interrupt. By the end of this article, you should have the necessary knowledge to do your own Register callback implementations in any kind of application. So, grab your coffee, settle into your chair, and let us start coding!
Create a project for the STM32H503RB using the STM32CubeIDE. In this project, the following settings are applied:
The next changes are made at the clock configuration tab. In this tab, the HCLK frequency needs to be changed to 250 MHz (this frequency matches the TIM2 settings for the 1 second time base).
The last step, before the code generation, is to enable the register callback. For that, the UART and TIM register callbacks should be enabled under project manager -> advanced settings as shown in figure 6.
Now, that all the settings are done, the code can be generated by clicking on the respective button or just pressing the shortcut alt + k.
In the main.c file, we should register the callback functions for the peripherals we want. To apply it for the UART and TIM, the following functions should be called:
HAL_StatusTypeDef HAL_UART_RegisterCallback(UART_HandleTypeDef *huart, HAL_UART_CallbackIDTypeDef CallbackID, pUART_CallbackTypeDef pCallback)
Also:
HAL_StatusTypeDef HAL_TIM_RegisterCallback(TIM_HandleTypeDef *htim, HAL_TIM_CallbackIDTypeDef CallbackID, pTIM_CallbackTypeDef pCallback)
Note that we have some parameters to fill. The first one is a pointer to the peripheral handler, &huart3 and &htim2 for the UART3 and TIM2 respectively. The second parameter is a CallBack ID, which points to which the interrupt source calls the given CallBack. A list of these IDs can be found in the respective peripheral HAL driver headers. See figures 7 and 8.
Finally, the last parameter is a pointer to your custom CallBack function. Just make sure that your function respects the same structure as the HAL excepts. You can find the correct structure in the p[Peripheral]_CallbackTypeDef (like pUART_CallbackTypeDef). See the examples below:
Now, that all the preparations for using the Register callback feature are ready, let us dive into an example using that.
Going back to the main.c file, define the following variables:
/* USER CODE BEGIN PV */
uint8_t message[16] = "Period Elapsed\n\r";
FlagStatus periodElapsed = RESET;
/* USER CODE END PV */
The message array stores the message that is sent through the UART. The periodElapsed variable is used to control the application flow based on timer interruptions.
Add the function’s prototypes for each custom callback (one for the UART and another for the timer). You can select any name you want, just make sure to follow the function’s arguments and return method as mentioned before.
/* USER CODE BEGIN PFP */
void User_UartCompleteCallback(UART_HandleTypeDef *huart);
void User_TIMPeriodElapsedCallback(TIM_HandleTypeDef *htim);
/* USER CODE END PFP */
In the main function, we need to associate the callback with the interrupt source by using the Register callback function. This should be done after the MX peripheral initialization, please rely on the USER CODE comment label as a reference.
/* USER CODE BEGIN 2 */
HAL_UART_RegisterCallback(&huart3, HAL_UART_TX_COMPLETE_CB_ID, User_UartCompleteCallback);
HAL_TIM_RegisterCallback(&htim2, HAL_TIM_PERIOD_ELAPSED_CB_ID, User_TIMPeriodElapsedCallback);
HAL_TIM_Base_Start_IT(&htim2);
/* USER CODE END 2 */
Note the HAL_UART_TX_COMPLETE_CB_ID was selected for the UART, to link the TX complete Interrupt to the custom function. Additionally, the HAL_TIM_PERIOD_ELAPSED_CB_ID, for the timer period elapsed Interrupt.
After registering the callbacks, the code called a function to start timer 2 as a time base, in interruption mode. This will generate a period elapsed Interrupt every 1 second, as previously set.
In the endless loop, add the following code, which is responsible for transmitting the predefined message by pooling the periodElapsed flag:
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(periodElapsed == SET)
{
HAL_UART_Transmit_IT(&huart3, message, sizeof(message));
periodElapsed = RESET;
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
Finally, add your custom callback functions code, again, use the USER CODE comment to locate the proper place to add the code snippet.
/* USER CODE BEGIN 4 */
void User_UartCompleteCallback(UART_HandleTypeDef *huart)
{
HAL_GPIO_TogglePin(USER_LED_GPIO_Port, USER_LED_Pin);
}
void User_TIMPeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
periodElapsed = SET;
}
/* USER CODE END 4 */
Now that you have completed all the steps to implement the Register callback feature, you can build the code, flash it onto your microcontroller, open a terminal and see the application in action.
This application utilizes timer 2 to generate a 1-second time base, resulting in an interruption being generated every second. This interrupt calls the custom period elapsed callback function, which sets the periodElapsed flag. This flag is used in the infinite while loop in the main function. Once the periodElapsed flag is set, the code within the main loop transmits the defined message through the UART in interruption mode (non-blocking). After the message is queued to be sent, the periodElapsed flag is reset. Finally, once the message transmission is complete, a Tx complete callback is generated, calling the UART custom callback function, which toggles the LED status.
In summary, this application sends a message through the UART every second. The application changes the LED status once the message is fully sent, utilizing the custom callback functions that were created.
With this example you can create custom callback Functions and register it as shown above. This can be done for all peripherals which are capable of being managed by the STM32 HAL.
We hope this content was helpful for you!
Best wishes for your future developments.
Description of STM32H5 HAL and low-layer drivers - User manual
STM32CubeIDE - integrated development environment for STM32 - STMicroelectronics
How it can use in DMA complete transfer
XferCpltCallback // how can use this function
Thanks for this tutorial. I followed each step even though I didn't thoroughly understand it all. I think I need to understand pointers a little better (that is where I became a little confused). To my own surprise, the example worked first time. Happy me!