0

my IT is called after receiving 8 bytes:

HAL_UART_Receive_IT(&huart2, buffer1, 8);

where buffer1 has length 8.

in my callback function I copy the 8 bytes to a seperate buffer2 and empty buffer1 & restart reception. this way i am continuously receiving:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){

    for(int i=0; i<8; i++){
        buffer2[i] = buffer1[i];
        buffer1[i] = '\0';
    }

    HAL_UART_Receive_IT(&huart2, buffer1, 8);

}

Problem: if I enter more than 8 bytes into my serial port, the callback function is executed after 8 bytes, then I get an overrun error and can no longer receive

my behaviour is as follows, IT is called after 8 bytes, then, after the restart i seem to be reading one more byte into my buffer1, after that the error occurs. it seems to me that the reaming bytes are stored somewhere, is there a way to discard the overflow?

i know that the uart function works my buffer1 in a circular manner, my expectation therefore:

example- receiving 10 bytes. IT after 8 bytes restart receive read the remaining 2 bytes.

artless noise
  • 21,212
  • 6
  • 68
  • 105
  • Does this answer your question? [How receive data with HAL\_UART?](https://stackoverflow.com/questions/56384201/how-receive-data-with-hal-uart) – Sam Mason May 02 '23 at 09:28
  • not really, i want to receive 8 bytes at a time, i think this is more efficient than having an IT after every charackter. the problem(and solution) youre referncing receives one byte at a time.. my programm runs well, my only problem is dealing with too much input – Patrick Daly May 02 '23 at 12:30
  • I'd suggest tagging your question with relevant tags otherwise it's not going to get visibility by the right people. i.e. I presume this is C code, so should be tagged as such. the function names suggests you're working with a STM microcontroller and there are probably relevant tags there as well. people tend to watch to tags so getting this right might result in somebody with the right knowledge finding your question. – Sam Mason May 02 '23 at 13:25
  • 1
    also, what's "IT"? I'm guessing interrupt, but that doesn't seem obvious from just what you've written – Sam Mason May 02 '23 at 13:27

1 Answers1

1

When you receive 8 chars, your MCU triggers interrupt after each individual character. Your MCU UART has no input buffer, the one and only thing is 1 char sized data register (DR for unified data register or TDR/RDR if they are separate for send/receive). If you had overrun characters, then by definition, all the data that was sent, was lost forever (was never saved anywhere). Your UART is soft-locked until you remove overrun error by reading data register (the act of reading clears overrun flag and unlocks the peripheral as per F103's reference manual, check yours, I'd expect it to be similar or the same).

Basically, the problem is that you never handle overrun error, and it's not very clear from your description what you want to do about it. If you want to discard overrun data and never save it as if it was never sent to MCU, you can clear overrun flag before calling interrupt receive again:

for(int i=0; i<8; i++){
        buffer2[i] = buffer1[i];
        buffer1[i] = '\0';
    }

 volatile uint8_t temp = USART2->DR; //or RDR depending on what register you have
 (void)temp;

 HAL_UART_Receive_IT(&huart2, buffer1, 8);

If there was no overrun, it will do nothing at all. If there was an overrun, it will read the first overrun byte that was received while data register was empty (other data was lost), it will clear overrun flag and return the peripheral to operational condition, but if and only if there is no incoming data while calling HAL_UART_Receive_IT(&huart2, buffer1, 8);. We could easily have timing issues - let's not forget that MCU executes this in microseconds, while physically UART could still be sending/receiving. So I would modify my solution the following way:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){

 for(int i=0; i<8; i++){
        buffer2[i] = buffer1[i];
        buffer1[i] = '\0';
    }
 while(UART2_Busy());

 volatile uint8_t temp = USART2->DR; //or RDR depending on what register you have
 (void)temp;

 HAL_UART_Receive_IT(&huart2, buffer1, 8);
 }

Where Busy() checks if there is an ongoing transmission. Basically, if the line is not idle, you want to let it finish communication first, then clear overrun flag knowing that nothing new will be received while you're clearing it. You can enable IDLE interrupt for that, if you don't want to have it block anything (which makes sense if you use interrupt solution)

How about this logic:

  1. You call interrupt receive function.
  2. Immediately enable line IDLE interrupt (won't trigger immediately as per STM32F103 reference manual I'm consulting right now, needs to receive something for IDLE to trigger interrupt)
  3. Your interrupt receiver function will receive 8 characters and stop handling new incoming data, will copy buffer1 to buffer2 as per callback function.
  4. After, say, 12 characters (8 of them were processed into a buffer), IDLE interrupt occurs, where you clear overrun flag, call interrupt receive function again.

Warning: be sure to handle underrun. If only 5 characters are sent and not 8, IDLE will trigger too, make sure it won't cause a problem (it probably won't, just keep it in mind). Alternatively, you can try to enable IDLE interrupt only after receiving 8 chars.

Make sure you check reference manual on how to clear IDLE flag. STM32F103, which I have next to me, says one needs to read USART_SR and then USART_DR, and it will clear IDLE flag.

Ilya
  • 992
  • 7
  • 14