UART/USART CONFIGURATION IN STM32F446RE

Anumonjacob
7 min readMay 26, 2023

--

In this tutorial, we will cover the STM32 USART peripheral and how to handle UART protocol in STM32. We will create an example project in interrupt mode using the STM32 NUCLEO-F446RE development board. The project will transmit and receive data between STM32 and the host computer via the USB port.

The STM32 USART peripheral supports both USART and UART protocols. It enables the device to communicate using a serial protocol. In this tutorial, we will be using UART for communication.

For more detailed information about the peripheral, please refer to the datasheet of your selected STM32 microcontroller.

Some of the main features of the STM32 USART peripheral include:

  • Full duplex, asynchronous communications: The USART can communicate in full duplex mode, which means that it can send and receive data at the same time. This is in contrast to half duplex mode, where the device can only send or receive data at a time.
  • NRZ standard format (Mark/Space): The USART uses the NRZ standard format for data transmission. This means that the data is represented by the presence or absence of a signal.
  • Programmable data word length (8 or 9 bits): The USART can be configured to transmit and receive data words of 8 or 9 bits. This allows for a greater range of data to be transmitted.
  • Configurable stop bits — support for 1 or 2 stop bits: The USART can be configured to use 1 or 2 stop bits. This determines the amount of time that is between each data word.
  • Transmitter clock output for synchronous transmission: The USART can output a clock signal that can be used to synchronize data transmission with another device.
  • Single-wire half-duplex communication: The USART can be configured to communicate in single-wire half-duplex mode. This means that the device can share a single data line with another device to send and receive data.

There are three ways to handle UART in STM32: polling mode, interrupts mode, and DMA mode.

Polling mode is the simplest way to handle UART. In this mode, the CPU polls the UART hardware to see if it is ready to transmit or receive data. This is not an efficient way to handle UART, as the CPU will spend a lot of time waiting for the UART to be ready.

Interrupt mode is a more efficient way to handle UART than polling mode. In this mode, the UART hardware will generate an interrupt when it is ready to transmit or receive data. The CPU can then handle the data transfer in the interrupt handler. This frees up the CPU to do other things while the UART is handling the data transfer.

DMA mode is the most efficient way to handle UART. In this mode, the UART hardware will transfer data directly to or from memory without involving the CPU. This frees up the CPU to do other things while the UART is handling the data transfer.

The best mode to use depends on the application. If the application is not time-critical, then polling mode may be sufficient. If the application is time-critical, then interrupts mode or DMA mode may be necessary.

Here are some additional details about each mode:

Polling mode:

Pros:

  • Simple to implement
  • No additional hardware required

Cons:

  • Inefficient
  • CPU can be blocked for long periods of time

Interrupts mode:

Pros:

  • More efficient than polling mode
  • CPU is not blocked while data is being transferred

Cons:

  • Requires additional hardware (interrupt controller)
  • Interrupt handler can be complex

DMA mode:

Pros:

  • Most efficient mode
  • CPU is not involved in data transfer
  • Requires additional hardware (DMA controller)

Cons:

  • Most complex mode to implement

UART configuration in STM32CubeIDE

Open your stm32CubeIDE the go to File>New>Stm32 Project. Then open the Board Select tab and Enter your stm32 development board name. In this tutorial, we are using the NUCLEO-F446RE board. Then click Next.

After that add your Project Name on the next page and click Finish.

By default, STM32CubeIDE set up all the necessary files and peripherals under the HAL (Hardware Abstraction Layer) when you create the project. The NUCLEO-F446RE development board has a built-in ST-Link debugger and STM32CubeIDE configure PA2 and PA3 as UART Tx and Rx pin respectively.

We also need to enable the global interrupt from the NVIC settings as we are going to use the interrupt mode for this example.

For transmitting data in interrupt mode, we use a HAL function called HAL_UART_Transmit_IT()

HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size)

For receiving data in interrupt mode, we use a HAL function called HAL_UART_Receive_IT()

HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

HAL transmit complete callback function

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart){
// write some code when transmission is complete
}

HAL reception complete callback function

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
// write some code when reception is complete
}

Here is the full code

main.c file

#include "main.h"
#include<string.h>
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
UART_HandleTypeDef huart2; //UART_2 handle variable
char *user_data = "The application is running\r\n"; //demo data for transmission-
uint8_t data_buffer[100]; // data buffer
uint8_t recvd_data; // receive buffer
uint32_t count=0; // count how many bytes are received
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
HAL_UART_Transmit_IT(&huart2,(uint8_t*)user_data,strlen(user_data)); //Transmit data in interrupt mode
HAL_UART_Receive_IT(&huart2,&recvd_data,1); //receive data from data buffer interrupt mode
while (1){
HAL_GPIO_TogglePin(GPIOA, LD2_Pin); //loggle the user led which is connected to GPIO PA5
HAL_Delay(500); //delay 500 milisecond
}
}
//UART 2 transmission complete callback
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
memset(user_data, 0, strlen(user_data)); //empty the transmission data buffer
}
//UART 2 receive complete callback
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(recvd_data == '\r') //when enter is pressed go to this condition
{
data_buffer[count++]='\r';
HAL_UART_Transmit(huart,data_buffer,count,HAL_MAX_DELAY); //transmit the full sentence again
memset(data_buffer, 0, count); // enpty the data buffer
}
else
{
data_buffer[count++] = recvd_data; // every time when interrput is happen, received 1 byte of data
}
HAL_UART_Receive_IT(&huart2,&recvd_data,1); //start next data receive interrupt

}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 16;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
RCC_OscInitStruct.PLL.PLLQ = 2;
RCC_OscInitStruct.PLL.PLLR = 2;
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 USART2 Initialization Function
* @param None
* @retval None
*/
static void MX_USART2_UART_Init(void)
{
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != 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_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin : B1_Pin */
GPIO_InitStruct.Pin = B1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : LD2_Pin */
GPIO_InitStruct.Pin = LD2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LD2_GPIO_Port, &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 */

As I am using the Windows operating system, I use XCTU(multi-platform application) to communicate between our microcontroller and PC. You can download XCTU from this LINK and also you can download any other terminal or serial console like Tera Term, PuTTY, etc.

--

--