4
votes

I'm trying to get UART transmit working over DMA on an stm32f405. This part of my application is designed to send out text strings as a command line interface. I have the RX part of the UART working with DMA fine (using 1 byte circular DMA to handle anything that comes in) but the TX side is proving a little more tricky.

I'm able to send out strings of data using: HAL_UART_Transmit_DMA(&handle, pData[], strlen(pData)) provided there is a delay between consecutive calls of the function. As soon as my program decides to send two strings one after another, the new data pointer is ignored.

By using while(HAL_UART_Transmit_DMA(...) != HAL_OK){} I'm able to have the program wait the required time and send out consecutive strings.

This works for a while (few tens of transfers), then gets stuck due to HAL_NOT_OK.

For reference, my DMA settings are: DMA2 stream 7, channel 4, mem to periph, periph inc disabled, mem inc enabled, mem and periph align byte, normal mode (not circular), low priority, fifo disabled.

UART set to 9600 baud, 8 bit word, 1 stop bit, no parity, no hw control, 16 oversampling.

I'm trying to figure out if using FIFO will help me out here, though I'm not totally sure if i understand it yet. If i wanted to send out a single byte of data, could i do it with FIFO? is there a 1 word min limit here?

I have set up a tx complete callback that im not currently using. I did wonder if there are any flags I'd need to clear during this interrupt but not sure..

Any help appreciated!

3

3 Answers

3
votes

I have set up a tx complete callback that im not currently using. I did wonder if >there are any flags I'd need to clear during this interrupt but not sure.

You SHOULD wait for the tx complete callback before using HAL_UART_Transmit_DMA again. Something like:

bool txDoneFlag = false;
HAL_UART_Transmit_DMA(...)
while(!txDoneFlag);
txDoneFlag = false;
...
...


void HAL_UART_TxCpltCallback(){
    txDoneFlag = true;
}
1
votes

HammerFet, I believe you will need to use circular mode (e.g. DMA_Mode_Circular). Give that a try.

Also, please review the very short section 1.1.8 in the STM32F4 Application Note, which states the following:

Normal mode: once the DMA_SxNDTR register reaches zero, the stream is disabled (the EN bit in the DMA_SxCR register is then equal to 0).

An example of DMA config setup as follows can be seen below:

volatile char Buffer[] = "first uart test with stm32f4\r\n";

void DMA_Configuration(void)
{

  DMA_InitTypeDef  DMA_InitStructure;

  DMA_DeInit(DMA1_Stream2);

  DMA_InitStructure.DMA_Channel = DMA_Channel_4;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; // Receive
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)Buffer;
  DMA_InitStructure.DMA_BufferSize = (uint16_t)sizeof(Buffer);
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&UART4->DR;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

  DMA_Init(DMA1_Stream2, &DMA_InitStructure);

  /* Enable the USART Rx DMA request */
  USART_DMACmd(UART4, USART_DMAReq_Rx, ENABLE);

  /* Enable DMA Stream Half Transfer and Transfer Complete interrupt */
  DMA_ITConfig(DMA1_Stream2, DMA_IT_TC | DMA_IT_HT , ENABLE);

  /* Enable the DMA RX Stream */
  DMA_Cmd(DMA1_Stream2, ENABLE);

}
1
votes

FIFO for the transmit is useful if you are transmitting at high baud rates (>1MBaud). What happens is that the processor can't feed the UART fast enough and then a buffer underflow related error can occur. You can also change the priority of the DMA channel feeding the UART.

Circular mode is not that useful for TX, as it will endlessly retransmit the data in the buffer, unless you've updated it in time. Though I find circular buffers very useful for receiving data.

I have also had issues where if the number of bytes transmitted was small (<10Bytes) that the DMA transmission would fail after a while. Using the IT function worked without issue. I was never able find the reason. Thus, for messages < 10 bytes, I use the IT transmit function and for messages >= 10 bytes, I use the DMA transmit function.