cancel
Showing results for 
Search instead for 
Did you mean: 

Sequential calls to HAL_UART_Transmit_DMA, only last call works

YCHTI.1
Associate II

Hello,

I use SM32F103ZH6 MCU.

I'm trying to send log (i.e. text) messages from my board to host PC on a UART2 using HAL_UART_Transmit_DMA

problem: 2 successive calls to HAL_UART_Transmit_DMA, only last call gets executed.

logging function (sends text to host PC)

void LogEvent(const unsigned int EventType, const char* format, ...)

{

  va_list arg_list;

  va_start(arg_list, format);

  vsnprintf(LogString, MaxEventTextLength, format, arg_list);

  if (EventType == EventType_Info)

    strcpy(pcLog, "Info>");

  else if (EventType == EventType_Warning)

    strcpy(pcLog, "Warning>");

  else if (EventType == EventType_Error)

    strcpy(pcLog, "Error>");

  strcat(pcLog, LogString);

  strcat(pcLog, "\n\r");

  HAL_UART_Transmit_DMA(&huart2, (uint8_t*)pcLog, strlen(pcLog));

}

UART2 is setup as following

hdma_usart2_tx.Instance = DMA1_Channel7;

hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;

hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE;

hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE;

hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;

hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;

hdma_usart2_tx.Init.Mode = DMA_NORMAL;// ;

hdma_usart2_tx.Init.Priority = DMA_PRIORITY_HIGH;

void HAL_UART_TxCpltCallback(UART_HandleTypeDef* huart)

{

  if (huart->Instance == USART2)

  {

    //LogEvent(EventType_Info, "HAL_UART_TxCpltCallback,huart2");

    CallBacksUART2++;

    huart->TxXferCount = 0;

    /* Disable the DMA transfer for transmit request by setting the DMAT bit

    in the UART CR3 register */

    CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAT);

  }

}

Test sequence (in main.c):

LogEvent(EventType_Info, "here1");

//DelayMs(2);

LogEvent(EventType_Info, "test2");

In test sequence: HAL_UART_TxCpltCallback (function presented above) is reached twice.

when I call LogEvent with "here1" and then LogEvent with "test2", only "test2" is received on host PC.

If I put a breakpoint (at line LogEvent(EventType_Info, "test2"); ) or add a delay between the two LogEvent calls, it works fine (both "here1" and "test2" are received))

any idea how to fix this?

I need to use non-blocking transmit function.

I cant afford a delay between different logs..

thanks

1 ACCEPTED SOLUTION

Accepted Solutions
YCHTI.1
Associate II

thank you KMora and David for your responses.

I used before sending any new HAL_UART_Transmit_DMA

  while (hdma_usart1_tx.State != HAL_DMA_STATE_READY) {

    // some other code and work here...

     //if timeout is reached (exhausted), poll for transfer would still return dma ready!

    HAL_DMA_PollForTransfer(&hdma_usart1_tx, HAL_DMA_FULL_TRANSFER, 100);//cnDMAPollForTransferTimeoutMs);// 10);//timeout in ms

  }

  if (hdma_usart1_tx.State == HAL_DMA_STATE_READY)

    huart1.gState = HAL_UART_STATE_READY;

///

this works good for my uart2

problem fixed.

thanks

View solution in original post

3 REPLIES 3
KiptonM
Lead

I did not step through your code line by line.

But if you call HAL_UART_Transmit_DMA before the previous one is finished, (or started) it will reset the DMA and start writing the last thing you told it to do.

So you have to have enough buffers to hold all the data you are logging because I am guessing the logging data is bursty and the UART is not.

At some point you have to be able to write to the UART faster than you generate logs. And you have to have the buffers to support it. And you cannot write to the UART with the DMA until it has finished the previous action.

The one that caught me, was you cannot write to the same bufffer as the last write, because the DMA uses that buffer. So it just writes the latest data in the buffer.

I got around it by adding a routine that moves the data to a seperate buffer, then writes with the DMA,.

void write_debug_str(void *str)
{
	static char tcbuffer1[257];
	while (UART1Busy)
	{
		HAL_Delay(1);
	}
	strcpy(tcbuffer1,str);
	UART1Busy = true;
	HAL_UART_Transmit_DMA(&huart1, (uint8_t *)tcbuffer1, strlen((char *)str));
}

UART1Busy is set to false in the interrupt call back.

Hope this helps.

DavidAlfa
Senior II

Check HAL_DMA_PollForTransfer.

It will return error if there's no transfer going on.

This is ok only if you check it right after running the DMA.

// Poll huart1 TX DMA stream for transfer complete flag, 100mS timeout
HAL_DMA_PollForTransfer(huart1.hdmatx, HAL_DMA_FULL_TRANSFER, 100);

However you can check the DMA handler state :

// While DMA not ready
while(huart1.hdmatx->State != HAL_DMA_STATE_READY){
 
}

That way you don't even need to make the callback.

YCHTI.1
Associate II

thank you KMora and David for your responses.

I used before sending any new HAL_UART_Transmit_DMA

  while (hdma_usart1_tx.State != HAL_DMA_STATE_READY) {

    // some other code and work here...

     //if timeout is reached (exhausted), poll for transfer would still return dma ready!

    HAL_DMA_PollForTransfer(&hdma_usart1_tx, HAL_DMA_FULL_TRANSFER, 100);//cnDMAPollForTransferTimeoutMs);// 10);//timeout in ms

  }

  if (hdma_usart1_tx.State == HAL_DMA_STATE_READY)

    huart1.gState = HAL_UART_STATE_READY;

///

this works good for my uart2

problem fixed.

thanks