cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 problem with UART Rx IT callback

CAdat.1
Associate II

Hello everyone!

After many hours of grinding through forums & discussing with ChatGPT (really, ChatGPT is like my best friend now), I still fail to understand what must be a simple notion with an easy solution. This is why I'm posting this in forums.

 

I have a device which is transmitting data constantly (with about 4-5 Mbits/sec rates). I use HAL_UART_Transmit_DMA method for it. This part works correctly.

 

I also need to receive some commands from the PC to configure or signal the device. The frequency of this command reception is very low (about 1-2 commands per 10 seconds, usually). I try using HAL_UART_Receive_IT method for it (because from what I understand, it is best practice for such a requirement).

 

I want the UART interrupt to trigger only on actual data reception (like RXNE - Receive Data Register Not Empty). I have a buffer of 1 byte long and I try filling it via the following command -> 

uint8_t rxBuffer[1];

__HAL_USART_ENABLE_IT(&husart3, USART_IT_RXNE);

HAL_USART_Receive_IT(&husart3, (uint8_t*)rxBuffer, 1);

 

I need the callback function to fire only when I get data in the buffer. Instead, I discover in debugging that it fires repeatedly, messing up with the data transmission DMA. This is my callback function: 

void HAL_USART_RxCpltCallback(USART_HandleTypeDef *husart)
{
commandRecieved = 1;
command = rxBuffer[0];
memset(rxBuffer, 0, sizeof(rxBuffer));
HAL_USART_Receive_IT(&husart3, (uint8_t*)rxBuffer, 1);
}

(my plan was to use the commandRecieved flag and command in the main while(1) function, then clear them after handling the command)

What am I missing here? What can I do so that callback function only fires when the buffer is full (i.e. I received a command from PC) instead of firing all the time?

Regards

1 ACCEPTED SOLUTION

Accepted Solutions
CAdat.1
Associate II

Ok, for some weird reason, I created a brand new project, wrote the exact same code and it somehow worked this time. Right now a circular HAL_UART_Receive_DMA callback only fires when the rxBuffer is full, which was my intention all along.

In debugging, for some weird reason, I discovered (in my first failed project), the uint8 rxBuffer (populated via HAL_UART_Receive_DMA or IT) was somehow getting filled with the number "127" continuously (01111111) - I (or chatGPT) haven't got the slightest idea as to why, or why it resolved itself when I opened a brand new project (which happened entirely by chance, by the way).

 

In any case, thank you everyone for your help 🙂 I don't think this was engineering at its finest, but in any case, I resolved it somehow...

View solution in original post

5 REPLIES 5
Pavel A.
Evangelist III

For your scenario the best is a custom interrupt handler (no HAL, especially no HAL_USART_Receive_IT). Just enable the RXNE interrupt and keep it enabled. Another option is a continuous circular DMA with RX timeout detection, since you already use the DMA.

Note that H7 UARTs have a FIFO, it can be helpful. But IIRC the FIFO is not compatible with the disable overrun option  (at least in older H7's such as 743/753/750).

AScha.3
Chief II

i use (on H743) almost same : HAL_USART_Receive_IT(&husart3, (uint8_t*)rxBuffer, 1);

to receive data at 115k ; works without problems (most time...so i include error callback also)

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)

{

// *** egal...

HAL_UART_Receive_IT(&huart7, &ESP_RX[ESP_RX_COUNT], 1); // set receive buf to 120 char. max

}

so check with a scope, whats real on rx pin of cpu.

If you feel a post has answered your question, please click "Accept as Solution".

So you are recommending to use no HAL method such as HAL_USART_Receive_IT if I use the custom interrupt handler approach, did I get this right? If possible, can you tell me where can I learn how to create a custom interrupt handler?

Correct. There are many UART projects on github, and even chatgpt (or MS Bing/Copilot) these day can quickly offer an example.

Assume UART3 is used,then

 

 

void handle_RX_byte(uint8_t b)
{ .... }

void USART3_IRQHandler(void)
{
    USART_TypeDef *U = USART3;

    uint32_t isrflags = U->ISR; // status
    if (isrflags & USART_ISR_ORE) {
      U->ICR = UART_CLEAR_OREF;
    }
    if (isrflags & USART_ISR_RXNE_RXFNE) {
        uint8_t recvd_byte = U->RDR;
        handle_RX_byte(recvd_byte);
    }
}

int main(void)
{
    //..... initialize HAL, clocks...........
    //..... initialize UARTs...........
    
    // Enable USART receive interrupt
    USART3->CR1 |= USART_CR1_RXNEIE;
    while (1) {
        // Your main application logic here
    }
}

 

 

CAdat.1
Associate II

Ok, for some weird reason, I created a brand new project, wrote the exact same code and it somehow worked this time. Right now a circular HAL_UART_Receive_DMA callback only fires when the rxBuffer is full, which was my intention all along.

In debugging, for some weird reason, I discovered (in my first failed project), the uint8 rxBuffer (populated via HAL_UART_Receive_DMA or IT) was somehow getting filled with the number "127" continuously (01111111) - I (or chatGPT) haven't got the slightest idea as to why, or why it resolved itself when I opened a brand new project (which happened entirely by chance, by the way).

 

In any case, thank you everyone for your help 🙂 I don't think this was engineering at its finest, but in any case, I resolved it somehow...