Building Board Support Package (BSP) for STM32F411-Nucleo64 Part8: UART RX in interrupt mode

In this eighth part of Board Support Package, we shall develop interrupt driver for UART2 to receive a single character and toggle an LED to indicate that STM32 received the character.

In this guide, we shall cover the following:

  • Developing the header file.
  • Developing the source code.
  • Main code.
  • Results.

1. Developing the header file:

From the previous guide about UART in TX and RX here.

For more information about how character reception in interrupt mode, please refer to this guide here.

Open uart_bsp.h.

Declare the following enum which define the interrupt source and if the main interrupt is enabled or not:

typedef enum
{
	Interrupt_disabled=0, /*No interrupt*/
	Interrupt_Enabled=1, /*Interrupt enabled*/

	TXEIE_Enable=1, /*Transmit buffer is empty*/
	RXNEIE_Enable=1, /*Receiver buffer is not empty*/
	IDLEIE_Enable=1, /*IDLE Line detection enabled*/

}Interrupt_Source_Typedef;

Since STM32F411 has three UART as following:

  • USART1.
  • USART2.
  • UART6.

We shall declare enum to handle which uart to be used as following:

typedef enum
{
	usart1=0,
	usart2=1,
	uart6=2,
}USART_NumberTypedef;

This for enabling the interrupt in NVIC.

Within UART_ConfigTypedef datastruct, include the following:

	uint8_t InterrupteEnbable;
	uint8_t TX_InterruptEnable;
	uint8_t RX_InterruptEnable;
	uint8_t IDLE_Line_Interrupt_Enable;
	uint8_t uart_number;

Hence, the updated data structure as following:

typedef struct
{
	uint32_t busSpeed;
	uint32_t buadRate;
	uint8_t mode;
	uint8_t InterrupteEnbable;
	uint8_t TX_InterruptEnable;
	uint8_t RX_InterruptEnable;
	uint8_t IDLE_Line_Interrupt_Enable;
	uint8_t uart_number;
}UART_ConfigTypedef;

Thats all for the header file.

2. Developing the source code:

Open uart_bsp.c source file.

Within this function:

void BSP_UART_Init(USART_TypeDef *uart, UART_ConfigTypedef *config)

Before enabling the UART:

Check if the interrupt is enabled or not:

if (config->InterrupteEnbable==Interrupt_Enabled)

If it is enabled, enable the required one in NVIC:

switch (config->uart_number)
		{
			case usart2: NVIC_EnableIRQ(USART2_IRQn); break;
			case usart1: NVIC_EnableIRQ(USART1_IRQn); break;
			case uart6: NVIC_EnableIRQ(USART6_IRQn); break;
			default: break;

		}

Check which interrupts are needed:

		if (config->TX_InterruptEnable==TXEIE_Enable)
		{
			uart->CR1|= USART_CR1_TXEIE;
		}

		if(config->RX_InterruptEnable==RXNEIE_Enable)
		{
			uart->CR1|= USART_CR1_RXNEIE;
		}

		if(config->IDLE_Line_Interrupt_Enable==IDLEIE_Enable)
		{
			uart->CR1|=USART_CR1_IDLEIE;
		}

Hence, the updated version of BSP_UART_Init as following:

void BSP_UART_Init(USART_TypeDef *uart, UART_ConfigTypedef *config)
{
	switch (config->mode)
	{
		case UART_TX_Only: 	uart->CR1=USART_CR1_TE; break;

		case UART_RX_Only:  uart->CR1=USART_CR1_RE; break;

		case UART_TX_RX:	uart->CR1=USART_CR1_RE|USART_CR1_TE; break;

		default : break;
	}

	uart_set_baudrate(uart, config->busSpeed, config->buadRate);

	
	if (config->InterrupteEnbable==Interrupt_Enabled)
	{
		switch (config->uart_number)
		{
			case usart2: NVIC_EnableIRQ(USART2_IRQn); break;
			case usart1: NVIC_EnableIRQ(USART1_IRQn); break;
			case uart6: NVIC_EnableIRQ(USART6_IRQn); break;
			default: break;

		}

		if (config->TX_InterruptEnable==TXEIE_Enable)
		{
			uart->CR1|= USART_CR1_TXEIE;
		}

		if(config->RX_InterruptEnable==RXNEIE_Enable)
		{
			uart->CR1|= USART_CR1_RXNEIE;
		}

		if(config->IDLE_Line_Interrupt_Enable==IDLEIE_Enable)
		{
			uart->CR1|=USART_CR1_IDLEIE;
		}
	}


	uart->CR1|= USART_CR1_UE;

}

For the interrupt handler:

void USART1_IRQHandler(void)
{
	uart1_interrupt_handler();
	NVIC_ClearPendingIRQ(USART1_IRQn);
}


void USART2_IRQHandler(void)
{
	uart2_interrupt_handler();
	NVIC_ClearPendingIRQ(USART2_IRQn);
}

void USART6_IRQHandler(void)
{
	uart6_interrupt_handler();
	NVIC_ClearPendingIRQ(USART6_IRQn);
}

For interrupt handler to be implemented by the user:

__WEAK void uart1_interrupt_handler(void){}
__WEAK void uart2_interrupt_handler(void){}
__WEAK void uart6_interrupt_handler(void){}

Thats all for the source code.

3. Main code:

In main.c:

#include "bsp.h"
#include "uart_bsp.h"
#include "exti_bsp.h"

GPIO_Output_Typedef LED;


void clock_config(void);



int main()
{

	clock_config();

	GPIOA_CLOCK_ENABLE();
	GPIOC_CLOCK_ENABLE();

	GPIO_Configure_Typedef LED_Config;
	LED_Config.PinNumber=pin5;
	LED_Config.Mode=OUTPUT;
	LED.pinNumber=pin5;
	GPIO_Initialization(GPIOA,&LED_Config);

	GPIO_Configure_Typedef PA3_Config;
	PA3_Config.PinNumber=pin3;
	PA3_Config.Mode=Alternate_function;
	PA3_Config.AlternateType=AF7;
	GPIO_Initialization(GPIOA,&PA3_Config);

	UART2_CLOCK_ENABLE();

	UART_ConfigTypedef uart;

	uart.buadRate=115200;
	uart.busSpeed=50000000;
	uart.mode=UART_RX_Only;
	uart.InterrupteEnbable=Interrupt_Enabled;
	uart.RX_InterruptEnable=RXNEIE_Enable;
	uart.uart_number=usart2;
	BSP_UART_Init(USART2,&uart);


	BSP_Ticks_Init(100000000);


	while(1)
	{


	}

}

void clock_config(void)
{
	Clock_Config_Typedef clockConfig;

	clockConfig.PLL_M= 4;
	clockConfig.PLL_N= 200;
	clockConfig.PLL_P= 4;

	clockConfig.AHB1Prescaler=AHB1_Prescaler1;
	clockConfig.APB1Prescaler=APB1_Prescaler2;
	clockConfig.APB2Prescaler=APB2_Prescaler1;

	clockConfig.clockSourc=External_Oscillator;
	clockConfig.flash_latency= Three_wait_state;

	Clock_Configuration(&clockConfig);
}

void uart2_interrupt_handler(void)
{
	if (USART2->SR & USART_SR_RXNE) /*Check if the interrupt source is RXNE*/
	{
		(void)USART2->DR;			/*Read the DR register to clear everything*/

		GPIO_TogglePin(GPIOA,&LED); /*Toggle the LED to for character reception*/

	}

}

Since we are interested only in RX part, no need to declare the UART as full duplex.

4. Results:

Open any terminal program and set the buadrate to be 115200 and send any character and the built-in LED should toggle each time you send a character.

Happy coding 🙂

Add Comment

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