Author Topic: Stm32F401 UART receive interrupt?  (Read 867 times)

0 Members and 1 Guest are viewing this topic.

Offline kgavionicsTopic starter

  • Regular Contributor
  • *
  • Posts: 195
  • Country: ca
Stm32F401 UART receive interrupt?
« on: March 18, 2024, 09:06:14 pm »
Hello guys
I 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?TIA
This is my code:
Code: [Select]
void usart_init(void)
{
/* alternate function 7
usart6_TX PA11
usart6_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 16
div=84MHZ/9600=8750=0x222e */
USART6->BRR =0x222e;
USART6->CR1 |=USART_CR1_RE | USART_CR1_TE |USART_CR1_RXNEIE ;//enable receiver , transmitter and interrupt enabled
NVIC_EnableIRQ(USART6_IRQn);
USART6->CR1 |=USART_CR1_UE; //enable usart

the IRQ handler:
Code: [Select]
void USART6_IRQHandler (void)
{
if(USART6->SR & USART_SR_RXNE)
usart_itp=1;


}

the receiving function
Code: [Select]
uint8_t usart_receive_char(void)
{

if(usart_itp)
usart_itp=0;
return USART6->DR;
}
 

Online tellurium

  • Regular Contributor
  • *
  • Posts: 231
  • Country: ua
Re: Stm32F401 UART receive interrupt?
« Reply #1 on: March 18, 2024, 10:59:51 pm »
Why don't you read inside the handler?
Something like this:

Code: [Select]
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);
  }
}
Open source embedded network library https://mongoose.ws
TCP/IP stack + TLS1.3 + HTTP/WebSocket/MQTT in a single file
 
The following users thanked this post: kgavionics

Offline kgavionicsTopic starter

  • Regular Contributor
  • *
  • Posts: 195
  • Country: ca
Re: Stm32F401 UART receive interrupt?
« Reply #2 on: March 19, 2024, 08:34:07 pm »
Why don't you read inside the handler?
Something like this:

Code: [Select]
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);
  }
}
Thank you for your suggestion. I try it out and I will report back!
 

Offline mwb1100

  • Frequent Contributor
  • **
  • Posts: 529
  • Country: us
Re: Stm32F401 UART receive interrupt?
« Reply #3 on: March 19, 2024, 10:04:28 pm »
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.

The key thing you need to be aware of is this information from the STM32F401 reference manual:

Quote
Bit 5 RXNE: Read data register not empty

This bit is set by hardware when the content of the RDR shift register has been transferred to the USART_DR register. An interrupt is generated if RXNEIE=1 in the USART_CR1 register. It is cleared by a read to the USART_DR register. The RXNE flag can also be cleared by writing a zero to it. This clearing sequence is recommended only for multibuffer communication

If 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).
 
The following users thanked this post: kgavionics

Online tellurium

  • Regular Contributor
  • *
  • Posts: 231
  • Country: ua
Re: Stm32F401 UART receive interrupt?
« Reply #4 on: March 19, 2024, 10:14:55 pm »
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: [Select]
// Copyright (c) 2024 Cesanta Software Limited
// All rights reserved

#include "hal.h"

// Circular buffer
struct cb {
  _Atomic size_t head;
  _Atomic size_t tail;
  uint8_t buf[10];
};

// Append one byte to the circular buffer
static bool cb_put(struct cb *cb, uint8_t c) {
  if (cb->head + 1 == cb->tail) return false;   // No space, fail
  cb->buf[cb->head] = c;                        // Append to the buffer
  cb->head = (cb->head + 1) % sizeof(cb->buf);  // Advance/wrap head
  return true;
}

// Read one byte from the circular buffer
static 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 byte
  cb->tail = (cb->tail + 1) % sizeof(cb->buf);  // Advance/wrap tail
  return true;
}

static struct cb s_uart_cb;  // UART circular buffer

void USART3_IRQHandler(void) {
  if (uart_read_ready(USART3)) {         // Have some data?
    uint8_t c = uart_read_byte(USART3);  // Yes! Read it
    cb_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 superloop
  for (;;) {
    uint8_t c;
    if (cb_get(&s_uart_cb, &c)) {  // Have some data in the UART buffer?
      gpio_toggle(LED3);           // Yeah. Blink LED
      uart_write_byte(USART3, c);  // And echo it
    }
  }

  return 0;
}
Open source embedded network library https://mongoose.ws
TCP/IP stack + TLS1.3 + HTTP/WebSocket/MQTT in a single file
 
The following users thanked this post: kgavionics

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 3700
  • Country: gb
  • Doing electronics since the 1960s...
Re: Stm32F401 UART receive interrupt?
« Reply #5 on: March 20, 2024, 09:17:59 am »
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: [Select]
// Common interrupt handler for all USART interrupts
static void usart_common_IRQHandler(UART_HandleTypeDef *huart, uint8_t port)
{
// check if we are here because of RXNE interrupt
if (huart->Instance->SR & USART_SR_RXNE)
{ //if RX is not empty
serial_receive_irq_handler(huart, port);
}

// check if we are here because of TXEIE interrupt
if (huart->Instance->SR & USART_SR_TXE)
{ //if TX is empty
serial_transmit_irq_handler(huart, port);
}

// check if we are here because of the TC interrupt
if (huart->Instance->SR & USART_SR_TC)
{ //if TX complete
serial_transmit_complete_irq_handler(huart, port);
}
}

The interrupt handler merely needs to read the byte out of DR to clear the RX interrupt.
« Last Edit: March 20, 2024, 10:38:02 am by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 
The following users thanked this post: kgavionics

Offline kgavionicsTopic starter

  • Regular Contributor
  • *
  • Posts: 195
  • Country: ca
Re: Stm32F401 UART receive interrupt?
« Reply #6 on: March 20, 2024, 09:39:58 pm »
Thank you guys, so by reading from the DR register inside the ISR,  I was able to clear the interrupt.
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4199
  • Country: us
Re: Stm32F401 UART receive interrupt?
« Reply #7 on: March 21, 2024, 02:41:33 am »
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.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf