cancel
Showing results for 
Search instead for 
Did you mean: 

UART DMA TX FIFO sends only single buffer worth of data

kaspars.laizans
Associate II

Hi,

I'm trying to send data via UART6 TX DMA on STM32F429-Discovery board. All is well, if I send packets of size less than 16 bytes (FIFO buffer size). However, if packet size is larger than 16 bytes, only first 16 are sent and the rest of the data is replaced by 0x00. I.e.

CB 00 01 00 28 00 29 35 32 47 0C 36 34 30 32 15 00 00 00 00

instead of

CB 00 01 00 28 00 29 35 32 47 0C 36 34 30 32 15 16 17 18 89

The initialization code is reasonably straight-forward:

void MX_USART6_UART_Init(void) {
 
	huart6.Instance = USART6;
	huart6.Init.BaudRate = 256000;
	huart6.Init.WordLength = UART_WORDLENGTH_8B;
	huart6.Init.StopBits = UART_STOPBITS_1;
	huart6.Init.Parity = UART_PARITY_NONE;
	huart6.Init.Mode = UART_MODE_TX_RX;
	huart6.Init.HwFlowCtl = UART_HWCONTROL_NONE;
	huart6.Init.OverSampling = UART_OVERSAMPLING_16;
	if (HAL_UART_Init(&huart6) != HAL_OK) {
		_Error_Handler(__FILE__, __LINE__);
	}
 
}
 
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) {
 
	GPIO_InitTypeDef GPIO_InitStruct;
	if (uartHandle->Instance == USART6) {
		__HAL_RCC_DMA2_CLK_ENABLE();
		__HAL_RCC_USART6_CLK_ENABLE();
 
		/**USART6 GPIO Configuration
		 PC6     ------> USART6_TX
		 PC7     ------> USART6_RX
		 */
		GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7;
		GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
		GPIO_InitStruct.Pull = GPIO_PULLUP;
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
		GPIO_InitStruct.Alternate = GPIO_AF8_USART6;
		HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
 
		/* USART6 DMA Init */
		/* USART6_RX Init */
		hdma_usart6_rx.Instance = USARTx_RX_DMA_STREAM;
		hdma_usart6_rx.Init.Channel = USARTx_RX_DMA_CHANNEL;
		hdma_usart6_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
		hdma_usart6_rx.Init.PeriphInc = DMA_PINC_DISABLE;
		hdma_usart6_rx.Init.MemInc = DMA_MINC_ENABLE;
		hdma_usart6_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
		hdma_usart6_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
		hdma_usart6_rx.Init.Mode = DMA_NORMAL;
		hdma_usart6_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
		hdma_usart6_rx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
 
		if (HAL_DMA_Init(&hdma_usart6_rx) != HAL_OK) {
			_Error_Handler(__FILE__, __LINE__);
		}
 
		__HAL_LINKDMA(uartHandle, hdmarx, hdma_usart6_rx);
 
		/* USART6_TX Init */
		hdma_usart6_tx.Instance = USARTx_TX_DMA_STREAM;
		hdma_usart6_tx.Init.Channel = USARTx_TX_DMA_CHANNEL;
		hdma_usart6_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
		hdma_usart6_tx.Init.PeriphInc = DMA_PINC_DISABLE;
		hdma_usart6_tx.Init.MemInc = DMA_MINC_ENABLE;
		hdma_usart6_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
		hdma_usart6_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
		hdma_usart6_tx.Init.Mode = DMA_NORMAL;
		hdma_usart6_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
		hdma_usart6_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
 
		if (HAL_DMA_Init(&hdma_usart6_tx) != HAL_OK) {
			_Error_Handler(__FILE__, __LINE__);
		}
 
		__HAL_LINKDMA(uartHandle, hdmatx, hdma_usart6_tx);
 
		/* NVIC configuration for DMA transfer complete interrupt (USARTx_TX) */
		HAL_NVIC_SetPriority(USARTx_DMA_TX_IRQn, 4, 1);
		HAL_NVIC_EnableIRQ(USARTx_DMA_TX_IRQn);
 
		/* NVIC configuration for DMA transfer complete interrupt (USARTx_RX) */
		HAL_NVIC_SetPriority(USARTx_DMA_RX_IRQn, 3, 2);
		HAL_NVIC_EnableIRQ(USARTx_DMA_RX_IRQn);
 
		/* NVIC configuration for USART TC interrupt */
		HAL_NVIC_SetPriority(USARTx_IRQn, 3, 1);
		HAL_NVIC_EnableIRQ(USARTx_IRQn);
 
		/* Idle line detection interrupt for packets smaller than RX_BUFFER_SIZE */
		__HAL_UART_ENABLE_IT(&huart6, UART_IT_IDLE);
		__HAL_UART_ENABLE_IT(&huart6, UART_IT_RXNE);
		__HAL_UART_CLEAR_IDLEFLAG(&huart6);
	}
}
 
void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle) {
 
	if (uartHandle->Instance == USART6) {
 
		__HAL_RCC_USART1_CLK_DISABLE();
 
		/**USART6 GPIO Configuration
		 PC6     ------> USART6_TX
		 PC7     ------> USART6_RX
		 */
		HAL_GPIO_DeInit(GPIOC, GPIO_PIN_6 | GPIO_PIN_7);
 
		/* USART6 DMA DeInit */
		HAL_DMA_DeInit(uartHandle->hdmarx);
		HAL_DMA_DeInit(uartHandle->hdmatx);
	}
}
 
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart) {
	__asm("nop");
}
 
 
 
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
}
 
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {
	__asm("nop");
}

After assembling the packet, I tried sending it in blocking, followed by DMA mode, using:

	HAL_UART_Transmit(&huart6, packet, len, 100);
	HAL_UART_Transmit_DMA(&huart6, packet, len);

And response is, as always:

CB 00 01 00 28 00 29 35 32 47 0C 36 34 30 32 15 16 17 18 89

CB 00 01 00 28 00 29 35 32 47 0C 36 34 30 32 15 00 00 00 00

I checked the values passed to TransmitDMA(), they seem correct. Even the memory at addresses used is correct.

What is interesting, that if I step into SET_BIT(huart->Instance->CR3, USART_CR3_DMAT); in stm32f4xx_hal_uart.c:919, it transmits just fine. If I break there and just resume, it still loses trailing bits.

4 REPLIES 4
AvaTar
Lead

Not sure what FIFO you talk about - the STM32F4 UART has none.

When using DMA, you configure a specific DMA size - I suppose this happens in __HAL_LINKDMA() (I don't do Cube code).

One trigger of the DMA will send the configured size.

I'm pretty sure you need to break up larger packets yourself, to make them fit the DMA size.

Jack Peacock_2
Senior III

Does your receiver check for framing and overrun errors? DMA pushes data out at the fastest possible bit rate, no gaps between last STOP and next START bit, so no time for receiver to restart. If the receiver clock is off compared to the TX clock, the higher the baud rate the more likely you see framing errors at the end of a packet. That's the problem with async serial at high speeds, not enough idle time between long sequences of bytes for the receiver to recover from clock drift.

Also, I hope you are using something other than RS-232 at 256K bit rate. RS-232 doesn't work reliably when you get past 115Kbaud due to slew rate problems in the signal. The longer the cable the worse it gets.

Jack Peacock

Jack Peacock_2
Senior III

All hardware issues aside, considering it's Cube the most likely explanation is a software bug. I don't use Cube for any commercial products. Too many nasty surprises await the unwary...

Jack Peacock

Thanks for your suggestions.

I tried changing the baudrate to 9600 and I'm getting exactly the same results with about 10cm of dupont wire between the devboard and receiver. I would actually like to push the baud rate into ~10MBaud range for use with RS-485. Simple transmit tests have shown, that I can indeed generate and transmit small packets at maximum rate of 11.25Mbit, but long-time performance remains to be tested.

For my custom board plan to use 24MHz crystal to reduce the clock error multiplication in PLL, hope that's enough.

Just out of curiosity I tried to set DMAT bit in the CR3 register (using debugger) before sending via DMA and it works, although it goes into error processing callback branch, it correctly and consistently sends the expected data until reset. Which leads me to the conclusion mentioned in your other comment, that it is most likely HAL bug somewhere.