Skip to main content
Associate
September 25, 2023
Solved

Unintentional splitting of messages sent via UART using DMA

  • September 25, 2023
  • 6 replies
  • 7057 views

Hi, I'm having a problem with unintentionally splitting a message I'm trying to send over the UART using DMA . I am using one function to send the message:

const uint32_t max_value_of_counter_loop = 100; /* in case systick jams */
volatile bool was_u1_tx_cplt = true;
HAL_StatusTypeDef OwnTransmitDma(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size)
{
/* Wait on uart to be ready, but no more than 3ms */
uint32_t start_of_try_to_send = HAL_GetTick();
uint32_t counter_while_loop = 0; /* in case systick jams */
while((huart1.gState != HAL_UART_STATE_READY) && (!was_u1_tx_cplt))
{
counter_while_loop++;
uint32_t now_get_tick = HAL_GetTick();
if(now_get_tick > (start_of_try_to_send+2)) break; /* wait no more than 3ms */
if(counter_while_loop > max_value_of_counter_loop) break; /* in case systick jams */
}
was_u1_tx_cplt = false;
return HAL_UART_Transmit_DMA(huart, pData, Size);
}

I call it like this:

uint8_t SendInfoAboutNumberOfStepsMadeByHighPrecisionMotors(uint8_t number_of_axis)
{
int number_of_steps = 0;
uint8_t axis = number_of_axis;

MG_GetNumberOfSteps(axis, &number_of_steps);

Length = sprintf((char*)Message4, "MG%d=%d\n", axis, number_of_steps);
send_status = OwnTransmitDma(&huart1, Message4, Length);

if(send_status != HAL_OK) return RESPONSE_TRY_ERROR;
else return RESPONSE_TRY_OK;
}

 

I set the was_u1_tx_cplt variable in a callback:
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart == &huart1) was_u1_tx_cplt = true;
}


Message4 is a global array located in this file:
uint8_t Message4[16];

Not every message is split, moreover 99% of messages are sent without any problems, but sometimes messages are split into two parts and I don't see the dependency when this happens. I don't use RTOS.
I'm using this DMA only for this UART, but also for TX and RX, at first I thought the RX stream was interrupting the TX stream, but I set the TX stream priority to DMA_PRIORITY_MEDIUM where RX has DMA_PRIORITY_LOW and that didn't help.

UART and DMA configuration:

grados73_0-1695638586953.png

grados73_1-1695638653997.png

Examples of splitting messages:

grados73_2-1695638696291.png

 
 

grados73_5-1695638747552.png

Unfortunately, I have no idea what can cause the fact that the significant majority of messages are sent correctly, but some are sent incorrectly. If you have any ideas, please help me with this issue.
STM32F407VGTx,
STM32CubeIDE Version: 1.13.1

 

 
This topic has been closed for replies.
Best answer by MS.9

But when you wait for HAL_UART_TxCpltCallback , Message4 already has been loaded with the new message.

The DMA will be merrily transmitting the 'old' contents of Message4 and your code overwrites it part way through then waits for DMA to compelte. Sometimes the DMA will be part way through transmitting it, sometimes not.

6 replies

MS.9
Associate III
September 25, 2023

looks like you calling sprintf( Message4 ...) while Message4 is still being sent out by DMA.

suggest you wait for DMA to complete before loading the next msg into Message4 

grados73Author
Associate
September 25, 2023

Waiting for HAL_UART_TxCpltCallback doesn't get the job done? Which interrupt should I wait for?

ONadr.1
Senior III
September 25, 2023

Try to mark this Message array as volatile.

ONadr.1
Senior III
September 25, 2023

Anad variable was_u1_tx_cplt too.

MS.9
MS.9Best answer
Associate III
September 25, 2023

But when you wait for HAL_UART_TxCpltCallback , Message4 already has been loaded with the new message.

The DMA will be merrily transmitting the 'old' contents of Message4 and your code overwrites it part way through then waits for DMA to compelte. Sometimes the DMA will be part way through transmitting it, sometimes not.

grados73Author
Associate
September 25, 2023

MS.9
Oki, so I understand that I should wait for the HAL_UART_TxCpltCallback not only before sending, but also before the sprintf() function?

ONadr.1
Senior III
September 25, 2023

You should check the was_u1_tx_cplt flag before sprintf and set it to false after sprintf and then start Tx with DMA. When the Tx is complete, the flag is set again in the callback.

Tesla DeLorean
Guru
September 25, 2023

Is it actually splitting it? Or is this how your receiver times out, buffers, or granularity/resolution there.

Check signals on a scope or logic analyzer.

Expect to have to reconstruct data, and resync, on the host. If there are no additional characters inserted, I'd look at other expectations and synchronization

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
grados73Author
Associate
September 28, 2023

Unfortunately, the problem hasn't gone away, but it didn't occur during my tests, so I can't solve it with an oscilloscope or logic analyzer. We think it may be a Windows problem with the handling of messages coming to the computer's COM port. This is now solved using the message joining procedure in the PC application, but this is a temporary solution. Hopefully, during the next tests, we will be able to catch this definitively and make sure that the problem is not on the STM side. By the way, the bug with the wrong place to check if the transmission is finished has been fixed, and I thank you for your help. If something is clarified I will write in the comment thread, maybe it will be useful to someone someday.

Tesla DeLorean
Guru
September 28, 2023

The STM32 with DMA to a USART should not have any significant inter-symbol gaps or pauses, the hardware is pretty simple/dumb so it's going to funnel data consistently, the bandwidth of the serial data being such that any contention is not going to be visible as you've got 8-10 bit times of slop.

What's definitely going to be an issue is the synchronization between the sender and the receiver, they are operating in entirely different time domains, so if one is collecting data say every 100ms to process, or busy with other tasks/threads, where a larger transfer might be bisected is anyone's guess. The receiving end will need to reassemble packets, and you'll want a format that's able to self-synchronize, and detect errors and corruption, and be able to recover. Expecting to send/receive a dozen bytes in lock-step, indefinitely is going to break.

DMA has secondary coherency issues that must be considered, caching and scope. The data can't come from local/auto variables as the scope of these generally collapses immediately, and you end up sending whatever's in memory at the time the data bytes are fetched. You can't reuse the buffer whilst it's active. You have to manage that, using TC / HT interrupts, or chain lists of buffers in the interrupt/callback. Don't start new DMA transfers while one is currently active.

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..