How to implement scanf() with STM32 Blocking and Non-Blocking Input

STM32 HAL Tutorial: UART Input Handling for Embedded Projects

Abstract

Learn how to implement scanf in STM32 projects using HAL. Handle UART input in blocking and non-blocking modes for real-time applications.

1. Introduction

scanf allows STM32 programs to receive input from UART, enabling:

  • Interactive debugging

  • Command-based control of embedded systems

  • Dynamic configuration during runtime

Two main modes:

  1. Blocking mode: MCU waits until input is fully received

  2. Non-blocking mode: MCU continues executing while receiving data using interrupts

2. Prerequisites

  • STM32 board with UART and a serial2USB converter
  • STM32CubeIDE and/or VS Code installed
  • Knowledge of HAL and UART
  • Serial terminal (e.g., CubeIDE Serial Monitor, PuTTY, minicom)

3. Blocking scanf via UART

Implementation Steps:

  1. Redirect _read() function to UART

  2. Use scanf to read formatted input

Example:

				
					/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */

				
			
				
					/* USER CODE BEGIN 0 */
int __io_getchar(void)
{
	uint8_t ch = 0;
	__HAL_UART_CLEAR_OREFLAG(&huart2);
	HAL_UART_Receive(&huart2, (uint8_t *)&ch, 1, 10000);
	return ch;
}
int __io_putchar(int ch)
{
	HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 100);
	return ch;
}
/* USER CODE END 0 */

				
			
				
					/* USER CODE BEGIN 2 */
 setvbuf(stdin, NULL, _IONBF, 0);
 /* USER CODE END 2 */

				
			
				
					 /* USER CODE BEGIN WHILE */
 while (1)
 {
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
	  printf("type something! \r\n");
	  scanf("%5s", data);
	  printf("typed: %5s \n", data);
 }
 /* USER CODE END 3 */

				
			
  • HAL_UART_Receive() blocks until each byte is received

  • Simple and works for interactive debugging

4. Non-Blocking scanf via Interrupt

Implementation Steps:

  1. Use HAL_UART_Receive_IT() to receive bytes asynchronously

  2. Store bytes in a buffer until Enter (\n) is pressed

  3. Parse buffer when full

Example:

				
					/* USER CODE BEGIN PD */
#ifdef USE_NON_BLOCKING_IT
#define RX_BUFFER_SIZE 20
uint8_t rxBuffer[RX_BUFFER_SIZE];
uint8_t rxIndex = 0;
#endif
/* USER CODE END PD */

				
			
				
					/* USER CODE BEGIN 2 */
 setvbuf(stdin, NULL, _IONBF, 0);
#ifdef USE_NON_BLOCKING_IT
 printf("type something! \r\n");
 HAL_UART_Receive_IT(&huart2, &rxBuffer[rxIndex], 1);
#endif
 /* USER CODE END 2 */

				
			
				
					/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
   if(huart->Instance == USART2)
   {
       if(rxBuffer[rxIndex] == '\n')
       {
           rxBuffer[rxIndex] = 0; // Null-terminate
           printf("You entered (non-blocking): %s\n", rxBuffer);
           rxIndex = 0; // Reset buffer
       }
       else
       {
           rxIndex++;
       }
       HAL_UART_Receive_IT(&huart2, &rxBuffer[rxIndex], 1);
   }
}
/* USER CODE END 4 */

				
			
  • MCU continues running while waiting for input

  • HAL_UART_RxCpltCallback handles each byte

  • Buffer is processed once Enter key is detected

5. Hands-On Lab Example

1. Configure the UART with IT

2. Implement either blocking or interrupt

3. Type the message and/or monitor the buffer

4. Test non-blocking mode by performing other tasks (e.g., toggling LED) while receiving input

Tip: For non-blocking modes, always ensure buffer overflow protection

6. Advantages

  • Blocking scanf: simple and easy for interactive debugging

  • Interrupt/DMA: non-blocking, suitable for real-time embedded systems

  • Works with STM32CubeIDE, VS Code, or PlatformIO

  • Compatible with HAL UART API

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top