Stm32F401 UART  receive interrupt? - Page 1
Forum section: electronics / general

author: kgavionics
author_rank: Regular Contributor
timestamp: March 18, 2024, 09:06:14 pm
content: Hello guysI coded a function that receives over UART, and it's working fine using polling. Now, I want to make it using interrupts, I'm not being able to receive anything. With Stm32, I played a lot using external interrupt, but never another type of interrupts, so I'm a bit stuck. I know with external interrupt, you have to clear them in the IRQ handler, but I didn't find a way to do it with Uart. I hope I'm not doing something stupid here! Can someone tell me what I'm doing wrong, please?TIAThis is my code:Code:void usart_init(void){/* alternate function 7usart6_TX PA11usart6_RX PA12 */static uint16_t usart_tx=PIN('A',11);static uint16_t usart_rx=PIN('A',12);gpio_init(usart_rx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_INSANE,GPIO_PULL_NONE, 8);gpio_init(usart_tx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_INSANE,GPIO_PULL_NONE, 8);RCC->APB2ENR |=RCC_APB2ENR_USART6EN ;//enable usart6 clock/*oversamling 16div=84MHZ/9600=8750=0x222e */USART6->BRR =0x222e;USART6->CR1 |=USART_CR1_RE | USART_CR1_TE |USART_CR1_RXNEIE ;//enable receiver , transmitter and interrupt enabledNVIC_EnableIRQ(USART6_IRQn);USART6->CR1 |=USART_CR1_UE; //enable usartthe IRQ handler:Code:void USART6_IRQHandler (void){if(USART6->SR & USART_SR_RXNE)usart_itp=1;}the receiving functionCode:uint8_t usart_receive_char(void){if(usart_itp)usart_itp=0;return USART6->DR;}

author: tellurium
author_rank: Frequent Contributor
timestamp: March 18, 2024, 10:59:51 pm
content: Why don't you read inside the handler?Something like this:Code:static void uart_callback(uint8_t c) {uart_write_byte(USART6, c);}void USART6_IRQHandler(void) {if (uart_read_ready(USART6)) {uint8_t c = uart_read_byte(USART6);uart_callback(c);}}

author: kgavionics
author_rank: Regular Contributor
timestamp: March 19, 2024, 08:34:07 pm
content: Quote from: tellurium on March 18, 2024, 10:59:51 pmThank you for your suggestion. I try it out and I will report back!

author: mwb1100
author_rank: Frequent Contributor
timestamp: March 19, 2024, 10:04:28 pm
content: Quote from: kgavionics on March 18, 2024, 09:06:14 pmThe key thing you need to be aware of is this information from theSTM32F401 reference manual:QuoteIf you don't clear the RXNE status the receive data interrupt will not clear and the processor will continually call the interrupt handler.When writing an interrupt handler, you need to lookup the information for what exactly triggers the interrupt and how to clear that. Sometimes it's simply reading the status register. But in this case you need to actually read the received data (or manually clear the status bit, which I think is not a common way to handle clearing the received data interrupt unless for some reason you don't care about the data).

author: tellurium
author_rank: Frequent Contributor
timestamp: March 19, 2024, 10:14:55 pm
content: I made this quick experiment to read from the IRQ handler to the circular buffer.Then, the main program can fetch data from the circular buffer.The difference with the previous callback approach is that callback executes in the interrupt context, so that means it cannot run some lengthy operations. This circular buffer approach does not have this limitation. The code that reads the UART data, executes in the main context, so it can do whatever.Note that this code is absolutely untested, written as a quick experiment.Code:// Copyright (c) 2024 Cesanta Software Limited// All rights reserved#include "hal.h"// Circular bufferstruct cb {_Atomic size_t head;_Atomic size_t tail;uint8_t buf;};// Append one byte to the circular bufferstatic bool cb_put(struct cb *cb, uint8_t c) {if (cb->head + 1 == cb->tail) return false; // No space, failcb->buf[cb->head] = c; // Append to the buffercb->head = (cb->head + 1) % sizeof(cb->buf); // Advance/wrap headreturn true;}// Read one byte from the circular bufferstatic bool cb_get(struct cb *cb, uint8_t *c) {if (cb->tail == cb->head) return false; // No data, fail*c = cb->buf[cb->tail]; // Read bytecb->tail = (cb->tail + 1) % sizeof(cb->buf); // Advance/wrap tailreturn true;}static struct cb s_uart_cb; // UART circular buffervoid USART3_IRQHandler(void) {if (uart_read_ready(USART3)) { // Have some data?uint8_t c = uart_read_byte(USART3); // Yes! Read itcb_put(&s_uart_cb, c); // Append to the circular buffer}}int main(void) {hal_init();USART3->CR1 |= USART_CR1_RE | USART_CR1_TE | USART_CR1_RXNEIE;NVIC_EnableIRQ(USART3_IRQn);// Main superloopfor (;;) {uint8_t c;if (cb_get(&s_uart_cb, &c)) { // Have some data in the UART buffer?gpio_toggle(LED3); // Yeah. Blink LEDuart_write_byte(USART3, c); // And echo it}}return 0;}

author: peter-h
author_rank: Super Contributor
timestamp: March 20, 2024, 09:17:59 am
content: If no int is occurring, it is probably not enabled to start with.I looked into my project and there is a ton of code relating to the NVIC but I could not find a usable way to separate out a clear example just for the UARTs. It is more than just enabling the UART interrupts; you have to config the NVIC.Note also that several interrupt sources go via the same vector, so you need to check which one to process:Code:// Common interrupt handler for all USART interruptsstatic void usart_common_IRQHandler(UART_HandleTypeDef *huart, uint8_t port){// check if we are here because of RXNE interruptif (huart->Instance->SR & USART_SR_RXNE){//if RX is not emptyserial_receive_irq_handler(huart, port);}// check if we are here because of TXEIE interruptif (huart->Instance->SR & USART_SR_TXE){//if TX is emptyserial_transmit_irq_handler(huart, port);}// check if we are here because of the TC interruptif (huart->Instance->SR & USART_SR_TC){//if TX completeserial_transmit_complete_irq_handler(huart, port);}}The interrupt handler merely needs to read the byte out of DR to clear the RX interrupt.

author: kgavionics
author_rank: Regular Contributor
timestamp: March 20, 2024, 09:39:58 pm
content: Thank you guys, so by reading from the DR register inside the ISR, I was able to clear the interrupt.

author: westfw
author_rank: Super Contributor
timestamp: March 21, 2024, 02:41:33 am
content: Hmm. Does the tail-chaining NVIC behavior in cortex-M mean that not clearing an interrupt flag is going to result in an infinite loop in the ISR?On a bunch of the 8bit cpus (at least AVR), returning from interrupt state allows one instruction to execute, so that the symptom of failing to clear the flags is that code runs much slower than normal (one ISR pass per regular instruction.) I don't know for sure that I'd call this better (sometimes it goes unnoticed!), but it's a difference to watch out for, I guess.