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