cancel
Showing results for 
Search instead for 
Did you mean: 

Firing multiple printfs with UART DMA Tx -> crap -> printf memory alloc?

LCE
Principal

Okay, thread number 371982 about the STM's UART... - but maybe not?

I recently found that I'm using some blocking printf / UART version, so I wanted to change that to non-blocking via DMA.

So far so good, works flawlessly.

Until I start "firing" the printfs, then it looks like parts of the previous strings are overwritten by the following printfs.

I think that I wait for all the right flags before starting a new DMA TX (I know that if the DMA says it's finished that there might still by a byte in the peripheral) in _write(), and the single printf (or more with some pauses) shows me that it's generally working.

So this works:

printf("string1");

... do other stuff...

printf("string2");

This doesn't:

printf("string1");

printf("string2");

So after having checked UART and DMA again, could it be GCC's printf memory handling? So that rapid printfs use the same memory space, and older data is overwritten by newer printfs?

Here's my _write() function:

Note: if I put the DMA transmit before the while loop, everything works fine, but then I'm back in blocking mode for a single printf.

int _write(int file, char *data, int len)
{
	HAL_StatusTypeDef HalRet = HAL_OK;
	uint32_t TickUartStart = HAL_GetTick();
 
	/* wait for uart ready with timeout */
	while( 1 )
	{
		if( (HAL_GetTick() - TickUartStart) > UART_TX_DMA_TIMEOUT_MS )
		{
			u8Uart3TxTimeout++;
			return 0;
		}
 
		if( (u8Uart3TxActive == 0) &&
			(USART3->ISR & (USART_ISR_TC | USART_ISR_TXE)) &&
			(huart3.gState == HAL_UART_STATE_READY) )
		{
			break;
		}
	}
 
	u8Uart3TxActive = 1;  // reset in DMA complete callback
	HalRet = HAL_UART_Transmit_DMA(&huart3, (uint8_t *)data, len);
 
	/* return # of bytes written */
	if( HalRet != HAL_OK ) return 0;
	return len;
}

1 ACCEPTED SOLUTION

Accepted Solutions

Right, DMA has longer term scope.

You can't use memory that is transient, ie auto/local or allocated and released.

Use sprintf(), put into a permanent buffer, or at least one you don't deallocated until AFTER the DMA has completed.

You can't make assumptions about what happens to this memory after it leaves _write(), move it to your own output buffers, and manage those.

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

View solution in original post

2 REPLIES 2

Right, DMA has longer term scope.

You can't use memory that is transient, ie auto/local or allocated and released.

Use sprintf(), put into a permanent buffer, or at least one you don't deallocated until AFTER the DMA has completed.

You can't make assumptions about what happens to this memory after it leaves _write(), move it to your own output buffers, and manage those.

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

Thanks!

Until recently I never used printf, mostly because I was only using 8 bit controllers with very limited memory. So back to filling output buffers - at least with the STM32 I can "afford" now to use sprintf.

Edit: out of habit, I had an uart output buffer declared already, but never used it, so I just copy data there before output. Still some copying, but surely not as bad as the blocking version, even at 921.6 kbaud (on a STM32F7 at 216 MHz).

// in _write()
	for( uint16_t i = 0; i < len; i++ ) u8Uart3TxBuf[i] = (uint8_t)data[i];
	HalRet = HAL_UART_Transmit_DMA(&huart3, u8Uart3TxBuf, len);