1

I have a project where I need to read in some UART data (in this case, multiple six-byte packets), do some (very minor) processing, then transmit it out on a different UART. I'm having an issue with some of the TX data being corrupted mid-stream sometimes. It basically will send half of packet 1, then half of packet 2. So if packet 1 is ABCDEF, and 2 is UVWXYZ, I get ABCXYZ. I am using STM's HAL for the STM32L0x0 to read it one byte at a time (my packets have a known SOF and EOF byte), with a simple state machine to capture the full six-byte packet in a circular buffer. Note that the circular buffer always seems to have the correct contents; I don't see this corruption there.

The way I am reading the data is basically:

while(1)
{
    HAL_UART_Receive_IT(UART, Buffer, 1);
}

However, there isn't really much else going on in the code until I get a full packet, so this function ends up getting called over and over. With a data rate of only 57.6kbps, even one byte takes 174uS with overhead. This function could get called dozens of times in that time period.

I guess what I am asking is what does the processor do when a function like this is repeatedly called? Is it "safe"; does the processor simply re-start the RX process? Older versions of the HAL for my part (STM32L0x0) used to have a bunch of Lock/Unlocks inside these calls, but they're gone in the newer versions.

Either way I plan to move the HAL_UART_Receive_IT(UART, Buffer, 1) call to a callback routine that only fires after successful data reception. I was just curious and Google wasn't giving me the answer I was looking for.

artless noise
  • 21,212
  • 6
  • 68
  • 105
nobby
  • 373
  • 1
  • 3
  • 15
  • The best way would be to not use the HAL at all. Just enable the receive interrupt on the UART and handle the bytes yourself. So you'd not be calling `HAL_UART_Receive_IT()` ever (or any other HAL_UART... function). – pmacfarlane May 12 '23 at 19:17
  • To answer your question, for most of your calls to `HAL_UART_Receive_IT()` it is probably just returning `HAL_BUSY`. – pmacfarlane May 12 '23 at 19:19

1 Answers1

2

What happens, if you're in the middle of the UART receive configuration (inside while(1)), and suddenly a char arrives? Interrupt handler might clear some bit in the configuration (since it was the one and only char to receive), which has just been set by UART receive function, or the other way around. In other words, you have two entities (while(1) UART IT receive call) and the interrupt handler fight for the same UART registers.

Most of the time it works however, because HAL marks peripheral as busy, but if things happen just at the right (wrong) time, then both functions try to configure registers and can ruin each other's configuration.

The solution is that UART IT receive function is called once and only once until it's done receiving. One of the common ways to achieve that would be to call it early in main() for the first time, and then call it every time in the receive interrupt callback - meaning, just after reception of the last requested UART char has finished, you call it again, and do it only once.

Ilya
  • 992
  • 7
  • 14
  • Thanks, I have made this change. As I suspected, it didn't help my issue on the TX side, but is certainly the better way to handle the RX side. – nobby May 12 '23 at 20:00
  • Do you have two UART peripherals use the same memory as a buffer? What exactly is the issue now? – Ilya May 12 '23 at 20:03
  • Two UARTS, yes, but not quite using the same buffer: one puts multipe 6-byte packets in a circ RX buffer. Code then grabs the next from the RX buffer, does basic math on it, and places it in a separate TX buffer. Issue is that somehow, partway through TX, the tail end of the packet gets overwritten with data from a new packet. What is odd is I can send any number of packets in a row w/ no issues. It's only the LAST packet in the string that has this corruption. This is a UI application, so it's common to get say, 25 packets, then have a pause of 50-100mS, then another sequence of 25 packets. – nobby May 12 '23 at 20:42
  • The last packet gets intermixed with pre-last? What's the system clock? Just to be sure? Do you start data processing only after the last byte of the packet is received? Or you process it every byte? – Ilya May 12 '23 at 20:48
  • Basically, yes, they get mixed. Sys_Clk is 32Mhz. I only do any math processing after receiving a full packet. Otherwise, it's a simple state machine while I wait for the EOF packet. – nobby May 12 '23 at 20:52
  • 1
    Do you keep track of how many packets you received per packet burst? It could be <= instead of < in packet counter handler – Ilya May 12 '23 at 20:57
  • I do have some temp counters to track that, and the counts match. What's even stranger: if I switch the HAL_UART_Transmit call to a blocking type (no DMA/ISR), it works correctly. So it sure seems like something is interrupting it. But the only other interrupt (besides Sys-Tick) is the other UART. However, changing priorities to make the RX UART lower (numerically higher) had no effect. – nobby May 12 '23 at 21:03
  • Don't forget that the interrupt/DMA transmit routines return immediately, without waiting for completion. If you're (for example) advancing the tail pointer in the circular buffer of the RX queue after you've done such a transmit, you've done it too early. – pmacfarlane May 12 '23 at 23:18
  • 1
    "if I switch the HAL_UART_Transmit call to a blocking type (no DMA/ISR), it works correctly" - just do that then. I don't know why you'd want to use DMA or interrupts for the transmit, unless you're a masochist. From my experience, the interrupt driven HAL transmit routines have _absolutely no advantages_. The DMA ones can have some advantages, but only if you need the CPU to be doing something else while it happens. – pmacfarlane May 12 '23 at 23:21
  • @pmacfarlane: My issue is I could have data coming in on the RX side that I don't want to miss. – nobby May 15 '23 at 16:10
  • @nobby You can use interrupts for the receive, and not for transmit, no problem. – pmacfarlane May 15 '23 at 16:23