DMA method
1. Introduction to DMA
Direct memory access (DMA) is used in order to provide high-speed data transfer between
peripherals and memory and between memory and memory. Data can be quickly moved by
DMA without any CPU action. This keeps CPU resources free for other operations.
The DMA controller combines a powerful dual AHB master bus architecture with
independent FIFO to optimize the bandwidth of the system, based on a complex bus matrix
architecture.
The two DMA controllers have 16 streams in total (8 for each controller), each dedicated to
managing memory access requests from one or more peripherals. Each stream can have
up to 8 channels (requests) in total. And each has an arbiter for handling the priority
between DMA requests.
2. DMA main features
The main DMA features are:
● Dual AHB master bus architecture, one dedicated to memory accesses and one
dedicated to peripheral accesses
● AHB slave programming interface supporting only 32-bit accesses
● 8 streams for each DMA controller, up to 8 channels (requests) per stream
● Four separate 32 first-in, first-out memory buffers (FIFOs) per stream, that can be used
in FIFO mode or direct mode:
– FIFO mode: with threshold level software selectable between 1/4, 1/2 or 3/4 of the
FIFO size
– Direct mode
Each DMA request immediately initiates a transfer from/to the memory. When it is
configured in direct mode (FIFO disabled), to transfer data in memory-to-peripheral mode, the DMA preloads only one data from the memory to the internal FIFO to ensure an immediate data transfer as soon as a DMA request is triggered
by a peripheral.
● Each stream can be configured by hardware to be:
– a regular channel that supports peripheral-to-memory, memory-to-peripheral and
memory-to-memory transfers
– a double buffer channel that also supports double buffering on the memory side
● Each of the 8 streams are connected to dedicated hardware DMA channels (requests)
● Priorities between DMA stream requests are software-programmable (4 levels
consisting of very high, high, medium, low) or hardware in case of equality (request 0
has priority over request 1, etc.)
● Each stream also supports software trigger for memory-to-memory transfers (only
available for the DMA2 controller)
● Each stream request can be selected among up to 8 possible channel requests. This
selection is software-configurable and allo ws several peripherals to initiate DMA
requests
● The number of data items to be transferred can be managed either by the DMA
controller or by the peripheral:
– DMA flow controller: the number of data items to be transferred is software-programmable from 1 to 65535
– Peripheral flow controller: the number of data items to be transferred is unknown
and controlled by the source or the destination peripheral that signals the end of
the transfer by hardware
● Independent source and destination transfer width (byte, half-word, word): when the
data widths of the source and destination are not equal, the DMA automatically
packs/unpacks the necessary transfers to optimize the bandwidth. This feature is only
available in FIFO mode
● Incrementing or nonincrementing addressing for source and destination
● Supports incremental burst transfers of 4, 8 or 16 beats. The size of the burst is
software-configurable, usually equal to half the FIFO size of the peripheral
● Each stream supports circular buffer management
● 5 event flags (DMA Half Transfer, DMA Transfer complete, DMA Transfer Error, DMA
FIFO Error, Direct Mode Error) logically ORed together in a single interrupt request for
each stream
3.DMA settings and uart input and output
There are two ways of input and output of uart DMA, one is polling mode and the other is interrupt mode.
(1) Polling method
The first is DMA initialization. This is an example of DMA Polling initialization.
/* Private functions ---------------------------------------------------------*/
/**
* @brief Main program
* @param None
* @retval None
*/
int main(void)
{
/* Initialize DMA and usart, gpio related clock*/
RCC_Configuration();
...
/* Configure the DMA */
DMA_Configuration(); //DMA configuration
...
/*USART initialization*/
USART_InitStructure.USART_BaudRate = 230400;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
/* Configure USARTy */
USART_Init(USARTy, &USART_InitStructure);
....
/* Enable USARTy DMA Rx and TX request
Here, set the CR3 register to support DMA input and output*/
USART_DMACmd(USARTy, USART_DMAReq_Rx | USART_DMAReq_Tx, ENABLE);
...
/* Enable the corresponding input and output DMA channel*/
/* Enable USARTy TX DMA1 Channel */
DMA_Cmd(USARTy_Tx_DMA_Channel, ENABLE);
/* Enable USARTy RX DMA1 Channel */
DMA_Cmd(USARTy_Rx_DMA_Channel, ENABLE);
...
/* Enable the USARTy */
USART_Cmd(USARTy, ENABLE);
/*After this sentence is finished, the TxBuffer1 data set in the following DMA_Configuration() function should be
To be output, you can wait for the sending to be completed as follows, and then wait for the input and output buffers to be compared after receiving*/
/* Wait until USARTy TX DMA1 Channel Transfer Complete */
while (DMA_GetFlagStatus(USARTy_Tx_DMA_FLAG) == RESET)
{
}
/* Wait until USARTy RX DMA1 Channel Transfer Complete */
while (DMA_GetFlagStatus(USARTy_Rx_DMA_FLAG) == RESET)
{
}
TransferStatus1 = Buffercmp(TxBuffer2, RxBuffer1, TxBufferSize2);
//CNTR indicates the size of the data to be transmitted each time. If it is a cyclic method, it will stop after one transmission. If you want to continue the transmission next time, you need to reset CNTR. See the following example
}
void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
/* USARTy TX DMA1 Channel (triggered by USARTy Tx event) Config */
DMA_DeInit(USARTy_Tx_DMA_Channel);
DMA_InitStructure.DMA_PeripheralBaseAddr = USARTy_DR_Base;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)TxBuffer1;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //The memory data is put into the usart dr register through DMA
DMA_InitStructure.DMA_BufferSize = TxBufferSize1;
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_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(USARTy_Tx_DMA_Channel, &DMA_InitStructure);
/* USARTy RX DMA1 Channel (triggered by USARTy Rx event) Config */
DMA_DeInit(USARTy_Rx_DMA_Channel);
DMA_InitStructure.DMA_PeripheralBaseAddr = USARTy_DR_Base;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)RxBuffer1;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = TxBufferSize2;
DMA_Init(USARTy_Rx_DMA_Channel, &DMA_InitStructure);
}
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
{
uint32_t tmpreg = 0;
/* Check the parameters */
assert_param(IS_DMA_ALL_PERIPH(DMAy_Channelx));
assert_param(IS_DMA_DIR(DMA_InitStruct->DMA_DIR));
assert_param(IS_DMA_BUFFER_SIZE(DMA_InitStruct->DMA_BufferSize));
assert_param(IS_DMA_PERIPHERAL_INC_STATE(DMA_InitStruct->DMA_PeripheralInc));
assert_param(IS_DMA_MEMORY_INC_STATE(DMA_InitStruct->DMA_MemoryInc));
assert_param(IS_DMA_PERIPHERAL_DATA_SIZE(DMA_InitStruct->DMA_PeripheralDataSize));
assert_param(IS_DMA_MEMORY_DATA_SIZE(DMA_InitStruct->DMA_MemoryDataSize));
assert_param(IS_DMA_MODE(DMA_InitStruct->DMA_Mode));
assert_param(IS_DMA_PRIORITY(DMA_InitStruct->DMA_Priority));
assert_param(IS_DMA_M2M_STATE(DMA_InitStruct->DMA_M2M));
/*--------------------------- DMAy Channelx CCR Configuration -----------------*/
/* Get the DMAy_Channelx CCR value */
tmpreg = DMAy_Channelx->CCR;
/* Clear MEM2MEM, PL, MSIZE, PSIZE, MINC, PINC, CIRC and DIR bits */
tmpreg &= CCR_CLEAR_Mask;
/* Configure DMAy Channelx: data transfer, data size, priority level and mode */
/* Set DIR bit according to DMA_DIR value */
/* Set CIRC bit according to DMA_Mode value */
/* Set PINC bit according to DMA_PeripheralInc value */
/* Set MINC bit according to DMA_MemoryInc value */
/* Set PSIZE bits according to DMA_PeripheralDataSize value */
/* Set MSIZE bits according to DMA_MemoryDataSize value */
/* Set PL bits according to DMA_Priority value */
/* Set the MEM2MEM bit according to DMA_M2M value */
tmpreg |= DMA_InitStruct->DMA_DIR | DMA_InitStruct->DMA_Mode |
DMA_InitStruct->DMA_PeripheralInc | DMA_InitStruct->DMA_MemoryInc |
DMA_InitStruct->DMA_PeripheralDataSize | DMA_InitStruct->DMA_MemoryDataSize |
DMA_InitStruct->DMA_Priority | DMA_InitStruct->DMA_M2M;
/* Write to DMAy Channelx CCR */
DMAy_Channelx->CCR = tmpreg;
/*--------------------------- DMAy Channelx CNDTR Configuration ---------------*/
/* Write to DMAy Channelx CNDTR */
DMAy_Channelx->CNDTR = DMA_InitStruct->DMA_BufferSize;
/*--------------------------- DMAy Channelx CPAR Configuration ----------------*/
/* Write to DMAy Channelx CPAR */
DMAy_Channelx->CPAR = DMA_InitStruct->DMA_PeripheralBaseAddr;
/*--------------------------- DMAy Channelx CMAR Configuration ----------------*/
/* Write to DMAy Channelx CMAR */
DMAy_Channelx->CMAR = DMA_InitStruct->DMA_MemoryBaseAddr;
}
When the channel is configured in non-circular mode, no DMA operation will be generated after the transfer is completed (that is, the transfer count becomes 0). To start a new DMA transfer, it is necessary to rewrite the transfer count in the DMA_CNDTRx register while the DMA channel is closed.
example:
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
{
uint32_t tmpreg = 0;
/* Check the parameters */
assert_param(IS_DMA_ALL_PERIPH(DMAy_Channelx));
assert_param(IS_DMA_DIR(DMA_InitStruct->DMA_DIR));
assert_param(IS_DMA_BUFFER_SIZE(DMA_InitStruct->DMA_BufferSize));
assert_param(IS_DMA_PERIPHERAL_INC_STATE(DMA_InitStruct->DMA_PeripheralInc));
assert_param(IS_DMA_MEMORY_INC_STATE(DMA_InitStruct->DMA_MemoryInc));
assert_param(IS_DMA_PERIPHERAL_DATA_SIZE(DMA_InitStruct->DMA_PeripheralDataSize));
assert_param(IS_DMA_MEMORY_DATA_SIZE(DMA_InitStruct->DMA_MemoryDataSize));
assert_param(IS_DMA_MODE(DMA_InitStruct->DMA_Mode));
assert_param(IS_DMA_PRIORITY(DMA_InitStruct->DMA_Priority));
assert_param(IS_DMA_M2M_STATE(DMA_InitStruct->DMA_M2M));
/*--------------------------- DMAy Channelx CCR Configuration -----------------*/
/* Get the DMAy_Channelx CCR value */
tmpreg = DMAy_Channelx->CCR;
/* Clear MEM2MEM, PL, MSIZE, PSIZE, MINC, PINC, CIRC and DIR bits */
tmpreg &= CCR_CLEAR_Mask;
/* Configure DMAy Channelx: data transfer, data size, priority level and mode */
/* Set DIR bit according to DMA_DIR value */
/* Set CIRC bit according to DMA_Mode value */
/* Set PINC bit according to DMA_PeripheralInc value */
/* Set MINC bit according to DMA_MemoryInc value */
/* Set PSIZE bits according to DMA_PeripheralDataSize value */
/* Set MSIZE bits according to DMA_MemoryDataSize value */
/* Set PL bits according to DMA_Priority value */
/* Set the MEM2MEM bit according to DMA_M2M value */
tmpreg |= DMA_InitStruct->DMA_DIR | DMA_InitStruct->DMA_Mode |
DMA_InitStruct->DMA_PeripheralInc | DMA_InitStruct->DMA_MemoryInc |
DMA_InitStruct->DMA_PeripheralDataSize | DMA_InitStruct->DMA_MemoryDataSize |
DMA_InitStruct->DMA_Priority | DMA_InitStruct->DMA_M2M;
/* Write to DMAy Channelx CCR */
DMAy_Channelx->CCR = tmpreg;
/*--------------------------- DMAy Channelx CNDTR Configuration ---------------*/
/* Write to DMAy Channelx CNDTR */
DMAy_Channelx->CNDTR = DMA_InitStruct->DMA_BufferSize;
/*--------------------------- DMAy Channelx CPAR Configuration ----------------*/
/* Write to DMAy Channelx CPAR */
DMAy_Channelx->CPAR = DMA_InitStruct->DMA_PeripheralBaseAddr;
/*--------------------------- DMAy Channelx CMAR Configuration ----------------*/
/* Write to DMAy Channelx CMAR */
DMAy_Channelx->CMAR = DMA_InitStruct->DMA_MemoryBaseAddr;
}
(2) Interrupt mode
Next, let's talk about how to generate an interrupt after the DMA transfer is completed. When half of the data is transferred, the half transfer flag (HTIF) is set to 1, and when the half transfer interrupt enable bit (HTIE) is set, an interrupt request will be generated. After the data transfer is completed, the transfer completion flag (TCIF) is set to 1, and when the transfer completion interrupt enable bit (TCIE) is set, an interrupt request will be generated.
There is a TCIE (Transfer completion interrupt enable) bit in the CCR register of DMA
This bit is set and cleared by software.
0: Disable TC interrupt
1: Enable TC interrupt
So in order to use DMA interrupts, we need the following code:
[cpp] view plaincopy
DMA1_Channel7->CCR |= DMA_IT_TC; //Transfer complete interrupt enable
In addition, whether the interrupt can be responded to depends on NVIC, so the following code is also necessary:
[cpp] view plaincopy
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel7_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 5;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
Here is a simple example program. The uCOS semaphore is also used in the example program. The interrupt processing function notifies Task through the semaphore that the DMA transfer is completed:
#include "stm32f10x.h"
#include "uart.h"
#include "led.h"
#include "COMMRTOS.H"
#include "ucos_ii.h"
#define TASK_STK_SIZE 128
OS_STK TaskStartStk[TASK_STK_SIZE];
void USART2_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);
/* Configure USART Tx as alternate function push-pull */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
/* Configure USART Rx as input floating */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART2, &USART_InitStructure);
USART_Cmd(USART2, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void UART2_TX_DMA_Init(uint8_t *p_str, uint16_t cnt)
{
// DMA_InitTypeDef DMA_InitStructure;
// DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &(USART2->DR);
// DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) str;
// DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
// DMA_InitStructure.DMA_BufferSize = 14;
// 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_Normal;
// DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
// DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// DMA_Init(DMA1_Channel7, &DMA_InitStructure);
DMA1_Channel7->CPAR = (uint32_t) &(USART2->DR);
DMA1_Channel7->CMAR = (uint32_t) p_str;
DMA1_Channel7->CNDTR = cnt;
DMA1_Channel7->CCR = DMA_DIR_PeripheralDST | DMA_Priority_Low |
DMA_Mode_Normal | DMA_PeripheralInc_Disable |
DMA_MemoryInc_Enable | DMA_PeripheralDataSize_Byte |
DMA_MemoryDataSize_Byte | DMA_M2M_Disable;
}
OS_EVENT *UART2_DMA_TX_Sem;
uint8_t str[] = "Hello World!!!";
void TaskStart(void *pdata)
{
unsigned char err;
SysTick_Config(SystemCoreClock/10);
USART2_Heat();
USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);
UART2_TX_DMA_Init(str);
UART2_DMA_TX_Sem = OSSemCreate(1);
for(;;)
{
LED_Spark();
OSSemPend(UART2_DMA_TX_Sem, 0, &err);
//DMA_Cmd(DMA1_Channel7, DISABLE);
DMA1_Channel7->CCR &= (uint16_t)(~DMA_CCR1_EN);
//DMA_Init(DMA1_Channel7, &DMA_InitStructure);
DMA1_Channel7->CNDTR = 14;
//DMA_Cmd(DMA1_Channel7, ENABLE);
DMA1_Channel7->CCR |= DMA_CCR1_EN;
//USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);
OSTimeDly(10);
}
}
int main(void)
{
SystemInit();
LED_Heat();
OSInit();
OSTaskCreate(TaskStart, (void *)0, &(TaskStartStk[TASK_STK_SIZE-1]), 1);
OSStart();
for(;;)
{
}
}
void DMA1_Channel7_IRQHandler(void)
{
OS_CPU_SR cpu_sr;
OS_ENTER_CRITICAL(); /* Tell uC/OS-II that we are starting an ISR */
OSIntNesting++;
OS_EXIT_CRITICAL();
OSSemPost(UART2_DMA_TX_Sem);
//UART_PutChar(USART2, '+');
DMA1->IFCR = DMA1_FLAG_TC7;
OSIntExit();
}
Previous article:STM32 DMA buffersize meaning and setting
Next article:Understanding and using DMA of STM32
Recommended ReadingLatest update time:2024-11-23 16:39
- Popular Resources
- Popular amplifiers
- Microcomputer Principles and Interface Technology 3rd Edition (Zhou Mingde, Zhang Xiaoxia, Lan Fangpeng)
- Automotive Electronics S32K Series Microcontrollers: Based on ARM Cortex-M4F Core
- LPC1700 Chapter 31 General Purpose DMA
- ARM Cortex-M4+Wi-Fi MCU Application Guide (Embedded Technology and Application Series) (Guo Shujun)
- Naxin Micro and Xinxian jointly launched the NS800RT series of real-time control MCUs
- How to learn embedded systems based on ARM platform
- Summary of jffs2_scan_eraseblock issues
- Application of SPCOMM Control in Serial Communication of Delphi7.0
- Using TComm component to realize serial communication in Delphi environment
- Bar chart code for embedded development practices
- Embedded Development Learning (10)
- Embedded Development Learning (8)
- Embedded Development Learning (6)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Intel promotes AI with multi-dimensional efforts in technology, application, and ecology
- ChinaJoy Qualcomm Snapdragon Theme Pavilion takes you to experience the new changes in digital entertainment in the 5G era
- Infineon's latest generation IGBT technology platform enables precise control of speed and position
- Two test methods for LED lighting life
- Don't Let Lightning Induced Surges Scare You
- Application of brushless motor controller ML4425/4426
- Easy identification of LED power supply quality
- World's first integrated photovoltaic solar system completed in Israel
- Sliding window mean filter for avr microcontroller AD conversion
- What does call mean in the detailed explanation of ABB robot programming instructions?
- STMicroelectronics discloses its 2027-2028 financial model and path to achieve its 2030 goals
- 2024 China Automotive Charging and Battery Swapping Ecosystem Conference held in Taiyuan
- State-owned enterprises team up to invest in solid-state battery giant
- The evolution of electronic and electrical architecture is accelerating
- The first! National Automotive Chip Quality Inspection Center established
- BYD releases self-developed automotive chip using 4nm process, with a running score of up to 1.15 million
- GEODNET launches GEO-PULSE, a car GPS navigation device
- Should Chinese car companies develop their own high-computing chips?
- Infineon and Siemens combine embedded automotive software platform with microcontrollers to provide the necessary functions for next-generation SDVs
- Continental launches invisible biometric sensor display to monitor passengers' vital signs
- SM1623 VFD drive controller
- Going public is not important; the business model is important
- General principles of PCB design [Share]
- Newbie help! About MSP430G2553 control RC522 RF card reader/writer program problem
- Discussion on Broadband Access Authentication Platform
- Design and implementation of the S value measurement system of the main cone of automobile based on PLC
- STMicroelectronics' STM32MP1 is coming. What do you think of this chip?
- The highest bit of the byte of the serial port receiving data of GD32F350 is found to be invalid
- Component pad design, everything you don’t understand is here!
- Adjustment of basic idle speed of electronically controlled vehicles 1