2025-01-02 02:02 PM
Hello everyone!
I have a problem with filling the buffer with data when I receive it via UART using an interrupt. I use a microcontroller of the STM32G03 series. Packets are sent at a frequency of 4 times per second. As a result, the buffer is filled not from the first byte, but somehow randomly (from the middle, from the end, and sometimes coincides with the first byte). Although the order of the bytes is correct. The packet size is 15 bytes. It seems that the synchronization is broken. What could be the reason?
I would be grateful for the answer!
2025-01-02 02:23 PM - edited 2025-01-02 02:29 PM
This is typically why you have protocols with preamble or synchronization bytes so you can recover sync and find the front on a packet if you lose bytes, or start listening mid-stream.
Should really interrupt on a per byte basis, assemble to a buffer, and state-fully process in a way that lets you confirm sync, and resynchronize if necessary, and determine you've got to the end of the packet.
For integrity you might want to communicate length in the protocol, if not fixed, and append checksum or CRC to the end of the framing.
If you're not continuously ready to accept characters, you'll likely be missing characters when you weren't listening, or in phase with the transmitter.
ie don't just use HAL_UART_ReceiveIT(huart, buffer, 15) when you're ready to get a 15 byte packet.
2025-01-02 02:41 PM
My packet starts and ends with characters. So I can track the beginning and end of the packet.
Tell me more: is the void USART2_IRQHandler(void) function called when processing each byte? Or not?
2025-01-02 03:46 PM
Yes, typically the STM32's call the IRQHandler once, per byte, unless there is a FIFO in use/enabled, and then the IRQHandler calls into the HAL call-ins, which can buffer more, and do a call-back to your handlers when the requested byte(s) have filled the buffer.
I don't much care for the HAL to do this, lot of overhead, and doesn't fit my paradigm, so I manage the buffering directly in the IRQHandler. Basically buffer, and leave, and process elsewhere.
Using a STM32G031K8 (UFQFPN32) here, multiple USART streaming
2025-01-02 04:07 PM
Here is my handler code. What do you think? Will it work correctly? Here I used a ring buffer and a separate array for the final data.
#define ARRAY_SIZE 15
#define BUFFER_SIZE 45
uint8_t receivedBytes[ARRAY_SIZE];
uint8_t rxBuffer[BUFFER_SIZE];
uint8_t readIndex = 0;
uint8_t packetReady = 0;
void USART2_IRQHandler(void)
{
if (USART2->ISR & USART_ISR_RXFF)
{
uint8_t data = USART2->RDR;
rxBuffer[readIndex] = data;
readIndex = (readIndex + 1) % BUFFER_SIZE;
static uint8_t packetIndex = 0;
receivedBytes[packetIndex] = data;
packetIndex++;
if (packetIndex >= ARRAY_SIZE)
{
packetReady = 1;
packetIndex = 0;
}
}
}
2025-01-03 12:12 AM
Use RXNE/RXFNE flag instead of RXFF!
Also, why don't you check for packet start/end in the ISR? That way the ISR could properly recognize/assemble the packets and align them in the packet buffer.
2025-01-03 09:56 AM - edited 2025-01-03 09:58 AM
The DIV / MOD will be expensive on the CM0(+), for a ring buffer use a power of two so the wrap can be optimized to an AND
I don't see any resync effort on the packet, and you should probably move the complete data to it's own holding buffer so as not to over-write the active buffer with live / partial data.
packetReady should be volatile
I wouldn't use uint8_t for flags, uint32_t or int would be more efficient in most cases