2024-10-25 07:29 AM
Hello,
Using HAL_UART_Receive_DMA() function and expecting an N length message, so have set the buffer length and transfer size to N.
The communicating equipment sometimes adds an extra character / or drops a character which is taking my DMA out of synchronisation.
Once out of synchronisation it is then staying like this until a reset occurs.
I can capture that it is out of sync and log a data error. When this occurs I would like to restart the DMA so that the data is resynchronised.
Example of the problem below:
Normal Operation Receive Bytes: 1, 2, 3, 4, ... N
When out of Sync every receive now looks like: 2, 3, 4, ... N, 1
What functions are in the HAL that allow me to resynchronise the DMA in the best way?
Thanks in advance.
PS - Have moved away from using DMA to IDLE as the connecting equipment sometimes drips feeds bytes in a disorderly fashion.
2024-10-25 03:06 PM
You didn't mention if you're using normal or circular mode for the DMA. You haven't shown any code so we don't know what you're doing in the callback, which is probably the issue if you're getting out of sync.
You should try HAL_UARTEx_ReceiveToIdle_DMA and HAL_UARTEx_RxEventCallback and save the packet of data to a queue buffer. If you do truly receive a bad packet, at least on the next good packet it'll be in sync again.
.
2024-10-25 11:31 PM
Hi Karl,
As I said in my original post, I have used HAL_UARTEx_RecevieToIdle_DMA which isn't satisfactory as the communicating equipment inserts idle conditions periodically inbetween bytes - makes the problem worse.
I am currently using HAL_UART_Receive_DMA() in Normal mode. The DMA is pointed to an RXBuffer, then copied in the callback to a ProcessBuffer.
My callback has this form:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == UARTX)
{
if (huart->RxEventType == HAL_UART_RXEVENT_TC)
{
/* memcpy to a ProcessBuffer */
/* set a flag to Process in the main loop */
HAL_UART_Receive_DMA(...)
}
All of this currently works in normal conditions. I've confirmed the problem occurs when the communicating equipment aborts a transmission on purpose, e.g. sends 5 characters instead of 6. The DMA still sits and waits for a 6th character.
To clarify, my question is really what HAL functions do I use to efficiently restart the UART/DMA? e.g. from the functions below and or others:
Pause the DMA Transfer using HAL_UART_DMAPause()
Resume the DMA Transfer using HAL_UART_DMAResume()
Stop the DMA Transfer using HAL_UART_DMAStop().
Failing this I shall have to move back and process each incoming byte on interrupt without DMA.
TIA.
2024-10-26 07:55 AM
In your callback you essentially restarted the DMA by calling HAL_UART_Receive_DMA. So the problem looks to be in your processing function.
Instead of DMA in normal mode, use circular mode. That way you get half and complete callback. You then copy either half to a larger buffer. To be sure you stay in sync when parsing from the larger buffer you should have a checksum, preferably a 16 bit checksum. You already know the fix length of data. So with the fix length and the checksum you can parse your good packet from the larger buffer.
2024-10-28 04:03 AM
Hello @######
Did you try using the HAL_UART_Abort() API?
This API allows you to:
2024-10-31 09:07 AM
Hi Karl, thanks again for taking the time to reply. With respect though (and not wanting to be inflammatory) changing to half / full complete and circular IMO does not actually solve the problem, it just means the problem would manifest in a similar but different way.
For example:
if my message is 6 characters ABCDEF and I have a circular buffer that is 12 characters long. I receive my first message so my buffer looks like:
ABCDEF
and the half transfer complete flag goes, and then I copy this and process it etc. this is fine, and equivalent to what I am currently doing.
I then get a rogue character from an aborted transmission e.g. another A, so my buffer would now look like:
ABCDEFA
I then get sent a proper message, so my buffer would look like the following (including the circular wraparound):
FBCDEFAABCDE
The transmit complete flag goes when the buffer is filled, but the second half of the buffer now is not a valid message AABCDE as it contains the aborted character.
This is essentially what I see with my current implementation anyway.
Regarding a checksum: I can't add a checksum as the protocol from the communicating equipment is fixed and is not in my design responsibility.
I have been thinking about re-using DMA to idle however (as you previously suggested), but trying to process incoming bytes one by one rather than hoping a full message comes inbetween idle characters. If i'm successful I will update this thread to describe how I managed it.
2024-10-31 09:10 AM
Thanks Omar for replying. A couple of questions please?
After I do HAL_DMA _Abort() how do I restart the DMA and UART interrupts?
Is this done in HAL_UART_Receive_DMA()? Or do I need to reinit the UART and DMA again?
Is the DMA pointer reset to the start of the buffer?
Many thanks.
2024-10-31 11:27 AM
How do you keep track when there is an abort and when there is start of a new packet? At what point do you know you need to reset the DMA?
It seems the device sending the packet is missing some key elements for the receiving device to parse a good packet. Something like a start byte, the amount of bytes proceeding and ideally a checksum. I'm sorry to say but the device sending the packet is a bad design.
2024-10-31 10:38 PM
Hi,
The best way - don't use DMA. Use Interrupts. Even better if you know how to write threadsafe code directly into the handler...
Kind regards
Pedro
2024-11-01 02:40 AM
Hello @######
HAL_UART_Receive_DMA() is used to reconfigure the DMA with the new buffer and length, which will reset the DMA pointer to the start of the buffer. The UART and DMA interrupts are then enabled to handle the new transfer.
You don't need to reinit again the UART and DMA.