UART with DMA

1. Introduction

Direct Memory Access (DMA) is very useful feature in microcontrollers. It allows data transfer between peripherals and memory without CPU intervention. This is very useful in UART communication where data is received and transmitted continuously. In this tutorial, we will learn how to use UART with DMA.

2. CubeMX Configuration

  • Open CubeMX and generate basic code with:

    • microcontroller: stm32f407vgt6 or board: STM32F407VG-DISC1

    • project name: usb_polling

    • Toolchain/IDE: Makefile

  • Move to STM32CubeMX Pinout and Congiguration. From Categories, select Connectivity > USART2. Change mode to Asynchronous mode.

  • Under Configuration > DMA Settings, add USART2_RX and USART2_TX DMA request.

  • Under Configuration > NVIC Settings, enable USART2 global interrupt.

usart2_dma
  • Generate code and open the project.

3. Code to Send and Receive Data using UART Interrupt

  • Navigate to Core > Src and open main.c.

  • Include header file.

    /* USER CODE BEGIN Includes */
    #include <stdio.h>
    /* USER CODE END Includes */
    
  • Overwrite definition of _write as:

    /* USER CODE BEGIN 0 */
    int _write(int file, char *data, int len)
    {
      for (int i = 0; i < len; ++i)
      {
        ITM_SendChar(data[i]);
      }
      return len;
    }
    /* USER CODE END 0 */
    
  • Add global variable to store received data.

    /* USER CODE BEGIN PV */
    char c;
    /* USER
    
  • Add code for sending as well as receiving in main().

    /* USER CODE BEGIN 2 */
    char msg[] = "Hello from controller 1\n";
    uint32_t last_transmit = 0;
    
    HAL_UART_Receive_DMA(&huart2, (uint8_t *)&c, sizeof(c)); // Start receiving
    /* USER CODE END 2 */
    
    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
      if (HAL_GetTick() - last_transmit > 1000)
      {
        HAL_UART_Transmit_DMA(&huart2, (uint8_t *)msg, sizeof(msg));
        last_transmit = HAL_GetTick();
      }
    
      if (!(HAL_UART_GetState(&huart2) & HAL_UART_STATE_BUSY_RX))
      {
       HAL_UART_Receive_DMA(&huart2, (uint8_t *)&c, sizeof(c)); // Start receiving in case of no reception
      }
    /* USER CODE END WHILE */
    
    /* USER CODE BEGIN 3 */
    }
    /* USER CODE END 3 */
    
  • Add callback function for UART RX DMA just above main().

    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
      if (huart->Instance == USART2)
      {
        printf("%c", c);
        HAL_UART_Receive_DMA(&huart2, (uint8_t *)&c, sizeof(c)); // Receive again
      }
    }
    
    /* USER CODE END 0 */
    
  • Build and flash the code to both controllers.

4. Connection

  • Connect TX pin of sender to RX pin of receiver.

  • Connect GND of both controllers together.

References

References are from STM32 HAL Driver documentation.

HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size)

Sends an amount of data in DMA mode.

Notes: - When UART parity is not enabled (PCE = 0) and Word Length is configured to 9 bits (M1-M0 = 01), the sent data is handled as a set of u16. In this case, Size must indicate the number of u16 provided through pData.

Parameters:
  • huart – Pointer to a UART_HandleTypeDef structure containing configuration information for the specified UART module.

  • pData – Pointer to data buffer (contains u8 or u16 data elements).

  • Size – Number of data elements (u8 or u16) to be sent.

Returns:

HAL status.

Return type:

HAL_StatusTypeDef

HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

Receives an amount of data in DMA mode.

Notes: - When UART parity is not enabled (PCE = 0) and Word Length is configured to 9 bits (M1-M0 = 01), the received data is handled as a set of u16. In this case, Size must indicate the number of u16 available through pData. - When the UART parity is enabled (PCE = 1), the received data contains the parity bit.

Parameters:
  • huart – Pointer to a UART_HandleTypeDef structure containing configuration information for the specified UART module.

  • pData – Pointer to data buffer (contains u8 or u16 data elements).

  • Size – Number of data elements (u8 or u16) to be received.

Returns:

HAL status.

Return type:

HAL_StatusTypeDef