cancel
Showing results for 
Search instead for 
Did you mean: 

HAL_UART_Transmit_DMA with unwanted behaviour

Krautermann
Senior II

I am working with the STM32H745XIH6 disco board to use HAL_UART_Transmit_DMA and send 3 different strings. But what I receive on the terminal are corrupted data. At least on the 1st send of every string, the string are not shown correctly.

Let's say the strings are as follows:

uint8_t TX_String[] = "Hello World";

uint8_t TX_String1[] = "WholeCallback";

uint8_t TX_String2[] = "Half";

What I see on the 1st send on the terminal is: a bunch of NULL characters when sending the 1st string. Then if I send the 1st string again, it appears correctly. On sending the 2nd string I get a mixed of 1st and 2nd string. Same happen for 3rd string. It seems that the buffer responsible for DMA transmit is not storing all the characters from my global variable on the 1st go.

This is what is seen on the terminal:

\0\0\0\0\0\0\0orld
Hello wllbackÿÿÿ
Whol

How can I fix that?

#include <gui/model/Model.hpp>
#include <gui/model/ModelListener.hpp>
 
#ifndef SIMULATOR
#include "main.h"
#include "string.h"
extern "C"
{
	extern UART_HandleTypeDef huart1;
	extern UART_HandleTypeDef huart3;
	extern uint8_t RX_String[512];
}
#endif
 
Model::Model() : modelListener(0)
{
 
}
 
void Model::tick()
{
#ifndef SIMULATOR
	strncpy(RXData, (char*)RX_String, sizeof(RXData));
	modelListener->RX_Data(RXData);
#endif
}
 
void Model::SendData2MCU(int UART, int DMA, char* data)
{
#ifndef SIMULATOR
	if(UART==0)
	{
		if(DMA==0)
		{
			HAL_UART_Transmit(&huart3, (uint8_t*) data, strlen(data), 100);
		}
		else if(DMA==1)
		{
			HAL_UART_Transmit_DMA(&huart3, (uint8_t*) data, strlen(data));
		}
	}
	if(UART==1)
	{
		if(DMA==0)
		{
			HAL_UART_Transmit(&huart1, (uint8_t*) data, strlen(data), 100);
		}
		else if(DMA==1)
		{
			HAL_UART_Transmit_DMA(&huart1, (uint8_t*) data, strlen(data));
		}
	}
#endif
}

1 ACCEPTED SOLUTION

Accepted Solutions

Hi @Krautermann​ 

Again, just after exiting the first call of HAL_UART_Transmit_DMA(), depending of transmitted buffer size and baudrate, I think the transfer is not complete, so DMA_complete is still 0 => you do not enter the if() statements.

Instead, if you want to wait the first transfer is completed, before requesting the second one, you need rather to have a while. Something like :

// 1st buffer
DMA_complete = 0;
HAL_UART_Transmit_DMA();
while (DMA_complete == 0) {};
 
// 2nd buffer
DMA_complete = 0;
HAL_UART_Transmit_DMA();
while (DMA_complete == 0) {};
 
// 3rd buffer
DMA_complete = 0;
HAL_UART_Transmit_DMA();
while (DMA_complete == 0) {};

 Regarding the second question, on how to check result of HAL call, you could try :

if (HAL_OK != HAL_UART_Transmit_DMA(...))
{
  // something wrong happens
}

View solution in original post

13 REPLIES 13

> Let's say the strings are as follows:

> uint8_t TX_String[] = "Hello World";

> uint8_t TX_String1[] = "WholeCallback";

> uint8_t TX_String2[] = "Half";

What's that, global variables or local variables or what?

How exactly do you start transmission and how do you wait until transmission of one string ends?

JW

Guenael Cadier
ST Employee

Just an idea : could it be that the data present in the buffer you use for transmission, is updated during the transmission ?

The buffers you are using to send data with HAL_UART_Transmit() or HAL_UART_Transmit_DMA() have to contain the data to be transmitted till the transmission is complete.

For HAL_UART_Transmit(), buffer should not be reused or altered by application until you exit the function.

For HAL_UART_Transmit_DMA(), buffer should not be reused or altered by application until the TxComplete callback is executed.

Just for a try, if you declare your TX_String as const (global array) and try to directly use TX_String as parameter of HAL_UART_Transmit() or HAL_UART_Transmit_DMA(), what is the result ?

The routine returns immediately, not when the transmission has finished. It doesn't handle / manage concurrent operation, you have to do that.

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

Hi @Community member​ , thank you for your reply.

They are global variables because I declared them before int main() and they remain unchanged throughout the whole process.

I have written the following in int main():

HAL_UART_Transmit_DMA(&huart3, TX_String, sizeof(TX_String));
  HAL_UART_Transmit_DMA(&huart3, TX_String1, sizeof(TX_String1));
  HAL_UART_Transmit_DMA(&huart3, TX_String2, sizeof(TX_String2));

And what I see on the terminal is only the 1st string, why don't I see the 2nd and 3rd String? when using regular HAL_UART_Transmit function, I can see all 3 strings in the right order instantly.

To your question: How exactly do you start transmission and how do you wait until transmission of one string ends?

Pressing reset button on the disco board starts the transmission. As for waiting till transmission ends, I don't know how to do that. Please help!

I don't know what to write inside void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) to make DMA work properly.

Hi @Guenael Cadier​ 

Thank you for your reply. I have been trying for the past few days to get it working but in vain. When I use HAL_UART_Transmit() with global variable, I can see all 3 strings correctly displayed on the terminal. But for HAL_UART_Transmit_DMA(), I only see the 1st string and not the 2nd and 3rd string.

I have a feeling that DMA is way slower than polling. How can I see all through strings via DMA if I write the following in my main?

  HAL_UART_Transmit_DMA(&huart3, TX_String, sizeof(TX_String));
  HAL_UART_Transmit_DMA(&huart3, TX_String1, sizeof(TX_String1));
  HAL_UART_Transmit_DMA(&huart3, TX_String2, sizeof(TX_String2));

I notice that I only see the 3 strings if I add a small delay HAL_Delay(1) in between each HAL_UART_Transmit_DMA(). In debug, the DMA works fine, but in run mode, it does not.

Hi @Krautermann​ 

HAL_UART_Transmit_DMA() is a non blocking API : it means that when you exit the call of HAL_UART_Transmit_DMA(), transfer is not complete.

From what I understood from your answers, you need to wait for the execution of the TxComplete callback (end of the 1st transfer) before being able to call again the same API.

(this is also highlighted by the fact that inserting a delay is allowing to have correct result.

In your case, second and third call might be useless, as you will exit with HAL BUSY code (i.e. 1st transfer still ongoing).

To confirm this hypothesis, you could check the return code of each of your HAL_UART_Transmit_DMA() calls.

Regards

@Guenael Cadier​ I have tested it and HAL seems busy still after 1st transfer, how can i deal with this then? How can I make 1st transfer complete without using HAL_Delay?

I seem to be missing something and I don't know what it is.

Hi @Krautermann​ 

I briefly draw a chart to try to explain behavior differences between a blocking transmit API (HAL_UART_Transmit) and non blocking one (HAL_UART_Transmit_DMA). See below.


_legacyfs_online_stmicro_images_0693W00000blFOeQAM.png 

In case of DMA transfer, the end of transfer is notified thanks to HAL_UART_TxCpltCallback() callback execution. this callback is declared as weak in HAL (empty one), so you could implement your own callback in your code.

For instance setting a flag transmit_ok to TRUE in the callback, and waiting for it to be TRUE in your main could be a basic mechanism for synchronizing transfer end with further actions.

Please make sure to declare your sync variable as volatile, as it will be accessed from both main and IT routines, which could be problematic in some compiler optimizations configurations.

Karl Yamashita
Lead II

You don't show how you're calling SendData2MCU? I'm guessing that you're using the same data array but copying the 3 TX_String to it first?

Also, you don't check HAL status which is very important.

If you find my answers useful, click the accept button so that way others can see the solution.