Do UART Serial Ports Need DMA for Transmission?

DMA is a commonly used feature in embedded real-time task processing.
Using DMA for UART data packet transmission can significantly reduce CPU processing time, preventing excessive waste of CPU resources, especially when transmitting a large number of data packets (such as high-frequency command transmissions).

Brief Introduction to DMA

DMA: Direct Memory Access. In simple terms, it allows direct data interaction between RAM and other devices (peripherals) without CPU intervention.

DMA allows hardware devices operating at different speeds to communicate without relying on a heavy interrupt load on the CPU. Otherwise, the CPU would need to copy each piece of data from the source to a buffer and then write it back to a new location, during which the CPU cannot perform other tasks.

Advantages of DMA

The role of DMA in a system is akin to that of an employee in a company, with the CPU being the company owner.

The owner wants to send a package to Beijing and only needs to give a command to arrange for an employee to handle all the details, such as filling out the tracking number, logistics, and delivery. Finally, once the package is received, the employee notifies the owner.
Returning to UART data transmission, the same principle applies. The CPU only needs to perform a simple operation (“assign a task”) to hand over a data packet to DMA for direct transmission, and once the transmission is complete, it receives a transmission completion interrupt to notify the CPU.
At this point, most people should understand that while the owner can personally drive or fly to deliver the package, it would consume a lot of the owner’s time.
Similarly, if we use UART for transmission, the CPU will continuously arbitrate the transmission results, consuming significant CPU resources.
In RTOS, especially when there are many tasks to handle, using DMA for UART transmission brings great convenience. This is particularly prominent in bare-metal operations.

Example Code: DMA Transmission Configuration

This article will briefly explain the configuration using the STM32F4 MCU and the standard peripheral library.

1. USART Configuration

USART (COM) macro definitions:

/* COMM communication */#define COMM_COM                  USART2#define COMM_COM_CLK              RCC_APB1Periph_USART2#define COMM_COM_TX_GPIO_CLK      RCC_AHB1Periph_GPIOD     //UART TX#define COMM_COM_TX_PIN           GPIO_Pin_5#define COMM_COM_TX_GPIO_PORT     GPIOD#define COMM_COM_TX_SOURCE        GPIO_PinSource5#define COMM_COM_TX_AF            GPIO_AF_USART2#define COMM_COM_RX_GPIO_CLK      RCC_AHB1Periph_GPIOD     //UART RX#define COMM_COM_RX_PIN           GPIO_Pin_6#define COMM_COM_RX_GPIO_PORT     GPIOD#define COMM_COM_RX_SOURCE        GPIO_PinSource6#define COMM_COM_RX_AF            GPIO_AF_USART2#define COMM_COM_IRQn             USART2_IRQn#define COMM_COM_Priority         9                        //Priority#define COMM_COM_BaudRate         115200                   //Baud rate#define COMM_COM_IRQHandler       USART2_IRQHandler        //Interrupt function interface (see stm32f4xx_it.c)

USART Configuration:

/************************************************Function Name: USART_COMM_ConfigurationFunction: Communication port configurationParameters: NoneReturn Value: NoneAuthor: strongerHuang*************************************************/void USART_COMM_Configuration(void){  GPIO_InitTypeDef  GPIO_InitStructure;  USART_InitTypeDef USART_InitStructure;  NVIC_InitTypeDef  NVIC_InitStructure;  /* Clock configuration */  RCC_AHB1PeriphClockCmd(COMM_COM_TX_GPIO_CLK | COMM_COM_RX_GPIO_CLK, ENABLE);    if((USART1 == COMM_COM) || (USART6 == COMM_COM))    RCC_APB2PeriphClockCmd(COMM_COM_CLK, ENABLE);  else    RCC_APB1PeriphClockCmd(COMM_COM_CLK, ENABLE);  /* Multiplexing configuration */  GPIO_PinAFConfig(COMM_COM_TX_GPIO_PORT, COMM_COM_TX_SOURCE, COMM_COM_TX_AF);  GPIO_PinAFConfig(COMM_COM_RX_GPIO_PORT, COMM_COM_RX_SOURCE, COMM_COM_RX_AF);  /* Pin configuration */  GPIO_InitStructure.GPIO_Pin = COMM_COM_TX_PIN;                     //USART Tx  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                       //Multiplexing mode  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  GPIO_Init(COMM_COM_TX_GPIO_PORT, &GPIO_InitStructure);  GPIO_InitStructure.GPIO_Pin = COMM_COM_RX_PIN;                     //USART Rx  GPIO_Init(COMM_COM_RX_GPIO_PORT, &GPIO_InitStructure);  /* NVIC configuration */  NVIC_InitStructure.NVIC_IRQChannel = COMM_COM_IRQn;  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = COMM_COM_Priority;  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  NVIC_Init(&NVIC_InitStructure);  /* USART configuration */  USART_InitStructure.USART_BaudRate = COMM_COM_BaudRate;            //Baud rate  USART_InitStructure.USART_WordLength = USART_WordLength_8b;        //Data bits  USART_InitStructure.USART_StopBits = USART_StopBits_1;             //Stop bits  USART_InitStructure.USART_Parity = USART_Parity_No ;               //Parity  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;    //Receive and transmit function  USART_Init(COMM_COM, &USART_InitStructure);  USART_ClearFlag(COMM_COM, USART_FLAG_RXNE | USART_FLAG_TC);  USART_ITConfig(COMM_COM, USART_IT_RXNE, ENABLE);                   //Enable receive interrupt  USART_DMACmd(COMM_COM, USART_DMAReq_Tx, ENABLE);                   //Enable DMA  USART_Cmd(COMM_COM, ENABLE);                                       //Enable USART}

2. DMA Configuration

DMA macro definitions:
/* COMM_DMA */#define COMM_DR_ADDRESS           ((uint32_t)USART2 + 0x04)#define COMM_DMA                  DMA1#define COMM_DMA_CLK              RCC_AHB1Periph_DMA1#define COMM_TX_DMA_CHANNEL       DMA_Channel_4#define COMM_TX_DMA_STREAM        DMA1_Stream6#define COMM_TX_DMA_FLAG_TCIF     DMA_FLAG_TCIF6#define COMM_TX_DMA_IRQn          DMA1_Stream6_IRQn#define COMM_TX_DMA_Priority      8                        //Priority#define COMM_TX_DMA_IRQHandler    DMA1_Stream6_IRQHandler  //Interrupt function interface (see stm32f4xx_it.c)#define COMM_TX_DMA_IT_TCIF       DMA_IT_TCIF6
DMA Configuration:
/************************************************Function Name: USART_COMM_DMA_ConfigurationFunction: Communication port DMA configurationParameters: NoneReturn Value: NoneAuthor: strongerHuang*************************************************/void USART_COMM_DMA_Configuration(void){  DMA_InitTypeDef DMA_InitStructure;  NVIC_InitTypeDef NVIC_InitStructure;  /* Enable clock */  RCC_AHB1PeriphClockCmd(COMM_DMA_CLK, ENABLE);  /* NVIC configuration */  NVIC_InitStructure.NVIC_IRQChannel = COMM_TX_DMA_IRQn;  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = COMM_TX_DMA_Priority;  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  NVIC_Init(&NVIC_InitStructure);  /* DMA configuration */  DMA_DeInit(COMM_TX_DMA_STREAM);  DMA_InitStructure.DMA_Channel = COMM_TX_DMA_CHANNEL;               //DMA channel  DMA_InitStructure.DMA_PeripheralBaseAddr = COMM_DR_ADDRESS;        //Peripheral address  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)0;               //Memory address (to be passed in)  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;            //Transfer direction  DMA_InitStructure.DMA_BufferSize = 0;                              //Transfer length (to be passed in)  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;   //Peripheral increment  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;            //Memory increment  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;    //Data width  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                      //Normal mode  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;              //Priority  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;  DMA_Init(COMM_TX_DMA_STREAM, &DMA_InitStructure);  DMA_ClearFlag(COMM_TX_DMA_STREAM, COMM_TX_DMA_FLAG_TCIF);  DMA_ITConfig(COMM_TX_DMA_STREAM, DMA_IT_TC, ENABLE);               //Enable DMA transfer complete interrupt  DMA_Cmd(COMM_TX_DMA_STREAM, DISABLE);                              //Initialize disable}
3. Sending UART Data Packets via DMA
DMA send function:
/************************************************Function Name: COMM_SendBufByDMAFunction: Send data via communication port through DMAParameters: Buf ------ Data (address)            Length --- Data length (bytes)Return Value: NoneAuthor: strongerHuang*************************************************/void COMM_SendBufByDMA(uint8_t *Buf, uint16_t Length){  DMA_Cmd(COMM_TX_DMA_STREAM, DISABLE);                              //Disable DMA  //Memory address  DMA_MemoryTargetConfig(COMM_TX_DMA_STREAM, (uint32_t)Buf, DMA_Memory_0);  DMA_SetCurrDataCounter(COMM_TX_DMA_STREAM, Length);                //Set DMA transfer length  DMA_Cmd(COMM_TX_DMA_STREAM, ENABLE);                               //Enable DMA}

Careful readers will notice that this send function is quite simple. Of course, this example uses the STM32F4 chip, but other chips are similar, and the principles are alike. The HAL library can also accomplish this.

Regarding the DMA transmission complete interrupt, depending on the actual situation, if using RTOS, the data transmission is generally a task that the OS waits (detects) for the transmission completion signal (i.e., DMA transmission complete interrupt).

Source: strongerHuang

Kind Reminder:

Due to recent changes in the WeChat public platform push rules, many readers have reported not seeing updated articles in a timely manner. According to the latest rules, it is recommended to click on “Recommended Reading, Share, Favorite,” etc., to become a regular reader.

Recommended Reading:

  • “Chip Olympics” paper included, China ranks first in the world

  • All employees dismissed! Another electronic giant in Shenzhen announces closure

  • A Tesla in Shanghai allegedly caught fire, causing 12 luxury cars including Rolls Royce to be burned, with losses up to 50 million!

Please click 【View】 to give the author a thumbs up

Do UART Serial Ports Need DMA for Transmission?

Leave a Comment