1
\$\begingroup\$

I'm working on firmware for an STM32F103 which is communicating over RS232 at 9600 baud. I need to transmit and receive the data by using UART_DMA method. For transmitting the data, the DMA register has some bits that should be enabled:

enter image description here

For receiving data it doesn't have any bits to enable in the DMA register. Can anyone guide me on how to transmit and receive data with the UART using DMA?

\$\endgroup\$
4
  • 2
    \$\begingroup\$ Use the HAL to do it. If you don't want to use the HAL, use the HAL source code as reference. \$\endgroup\$
    – Justme
    Dec 19, 2022 at 8:37
  • 5
    \$\begingroup\$ I think these bit names would be for the DMA transfer not for the UART transmission. \$\endgroup\$
    – Lundin
    Dec 19, 2022 at 8:48
  • 1
    \$\begingroup\$ Cortex M have low latency interrupts, it may or may not pay to use DMA for Rx especially at 9600 baud. DMA is dumb, it can't flag end of line, packet etc like an interrupt can. \$\endgroup\$ Dec 19, 2022 at 12:53
  • \$\begingroup\$ @ErikFriesen That is right but the interrupt cannot either. And if the point is to use DMA then it does not matter. \$\endgroup\$
    – Justme
    Dec 19, 2022 at 14:50

1 Answer 1

1
\$\begingroup\$

Relevant HAL functions are HAL_UART_Transmit_DMA and HAL_UART_Receive_DMA. You can check the code in Drivers/STM32F0xx_HAL_Driver/Src/stm32f1xx_hal_uart.c.

In UART_Start_Receive_DMA you can see that a call to HAL_DMA_Start_IT which enables relevant DMA Interrupts, and also several other bits related to UART errors and starting the DMA.

Personnaly, I have implemented a double buffer for transmission which seems to work for development purposes.

There are two buffers in uartTxBuffers. currentIdx indicates the buffer that is available for filling. This is where the data is appended to. The code then tries to start the DMA. If that succeeds then it switches buffers, if not it sticks with the current buffer.

/**
 * Send data to serial link
 *
 * @param file For future use, to redirect according to ID
 * @param ptr Pointer to data
 * @param len Length of data to send
 * @return
 */
int serialWrite(int file, const char *ptr, uint8_t len) {
  // Append to current buffer
  if (len != 0) {
    if (currentIdx + len <= UART_TX_BUFFER_SIZE) {
      memcpy((void *)uartTxBuffers[currentTxBuffer] + currentIdx, ptr, len);  // NOLINT
      currentIdx += len;
    } else {
      return 0;
    }
  }
  if (currentIdx != 0) {
    // DMA is not busy, send current buffer.
    if (HAL_OK == HAL_UART_Transmit_DMA(&huart2, uartTxBuffers[currentTxBuffer], currentIdx)) {
      currentTxBuffer = !currentTxBuffer;
      currentIdx = 0;
    }
  }
  return len;
}

These two callbacks regarding transmission make sure that the next buffer is sent when the transfer is complete, the first one may only be needed in case of a circular buffer - not checked.

// Probably unneeded
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *UartHandle) {
  /* Set transmission flag: transfer complete*/
  // UartReady = SET;
  serialWrite(0, NULL, 0);  // Try sending next buffer if any.
}

void UART_DMATransmitCplt(UART_HandleTypeDef *UartHandle) {
  serialWrite(0, NULL, 0);  // Try sending next buffer if any.
}

I have not implemented Rx yet, but I have the following placeholders.

void serialRxInit() {
  HAL_UART_Receive_DMA(&huart2, usart_rx_dma_buffer, sizeof (usart_rx_dma_buffer));
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle) {
  /* Set reception flag: transfer complete*/
  // UartReady = SET;
  __NOP();
}

void HAL_UART_ErrorCallback(UART_HandleTypeDef *UartHandle) {
  Error_Handler();
}

There is some kind of "end of transmission" detection in reception by setting the ReceptionType to HAL_UART_RECEPTION_TOIDLE

The HAL code is helpful to know which interrupts are important.

When using the HAL code, note that USE_HAL_UART_REGISTER_CALLBACKS determines how you set the callbacks - either through weak functions or members of the UART_HandleTypeDef structure. My code above uses the weak functions.

\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.