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
}

13 REPLIES 13

@Guenael Cadier​ Thank you for the diagram that you provided. I have a better understanding of DMA now.

I declared a volatile variable as you suggested so that DMA_complete is 0 at the beginning and only becomes 1 after DMA transfer is complete. What I found is that after executing the first HAL_UART_Transmit_DMA, the process is still running and hence DMA_complete never becomes 1. When adding a HAL_Delay after it, it works however. I am still stuck with the same problem. How do I make the DMA process finish without having to add delay?

It seems to me that DMA is somehow slower than polling...

Something is preventing DMA process from finishing and it remains on hold...If I add a HAL_GPIO function after HAL_UART_DMA, then I see the process completed...I am so confused why this happen this way...

volatile int DMA_complete = 0;
 
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	DMA_complete = 1;
}
 
Main:
HAL_UART_Transmit_DMA(&huart3, TX_String, sizeof(TX_String));
//HAL_Delay(1); //When added, it makes DMA process to complete
  if (DMA_complete == 1) //Never enters this statement as DMA_complete remains 0, DMA process not completed...
  {
	  HAL_UART_Transmit_DMA(&huart3, TX_String2, sizeof(TX_String2));
  }
 
if (DMA_complete == 1)
{
	  HAL_UART_Transmit_DMA(&huart3, TX_String3, sizeof(TX_String3);
}

Also how to check HAL_Status after each HAL_UART_Transmit_DMA?

Krautermann
Senior II

@Community member​ For SendData2MCU, I used a different array:

Basically the user enter a string on the LCD, that string is stored as unicode (in TX_TextAreaBuffer) and converted to ASCII (in TX_Buffer) and then this is stored in another buffer DataBuffer which is used in the HAL_UART_Transmit_DMA function.

I tried several times but I cannot see how the DataBuffer is getting modified in the middle of the DMA process. Whenever the user write another string on the LCD, DataBuffer is only modified when the user press on the Send Button.

void Screen2View::OKClicked()
{	//hide gui_keyboard and ok and exit button
	gui_keyboard.setVisible(false);
	gui_keyboard.invalidate();
	OKButton.setVisible(false);
	OKButton.invalidate();
	EXITButton.setVisible(false);
	EXITButton.invalidate();
	SendButton.setVisible(true);
	SendButton.invalidate();
	if (TX_mod)
	{
		Unicode::strncpy(TX_TextAreaBuffer, gui_keyboard.getBuffer(), TX_TEXTAREA_SIZE);
		TX_TextArea.invalidate();
	}
 
	gui_keyboard.clearBuffer();
}
 
void Screen2View::SendClicked()
{
	if(UARTToggleButton.getState()==0)
	{
		UART = 0;
		if(DMAToggleButton.getState()==0)
		{
			DMA = 0;
		}
		else
		{
			DMA = 1;
		}
	}
	else if(UARTToggleButton.getState()==1)
	{
		UART = 1;
		if(DMAToggleButton.getState()==0)
		{
			DMA = 0;
		}
		else
		{
			DMA = 1;
		}
	}
	int i=0;
	while(TX_TextAreaBuffer[i]!=0)
	{
		TX_Buffer[i] = (char)TX_TextAreaBuffer[i];
		i++;
	}
 
	sprintf((char*)DataBuffer, "%s", TX_Buffer);
	//memcpy(DataBuffer, TX_Buffer, sizeof(TX_Buffer));
 
	presenter->SendData2MCU(UART, DMA, DataBuffer);
 
	memcpy(TX_Buffer, "\0", TX_TEXTAREA_SIZE);
}

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
}

@Guenael Cadier​ Thank you so much for your continuous support!!! Now it works!!!

I can now see all 3 strings via DMA instantly!

  DMA_complete = 0;
  HAL_UART_Transmit_DMA(&huart3, TX_String, sizeof(TX_String));
  while(DMA_complete==0){};
  DMA_complete = 0;
  HAL_UART_Transmit_DMA(&huart3, TX_String1, sizeof(TX_String1));
  while(DMA_complete==0){};
  DMA_complete = 0;
  HAL_UART_Transmit_DMA(&huart3, TX_String2, sizeof(TX_String2));
  while(DMA_complete==0){};