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
- Popular Resources
- Popular amplifiers
- Learn ARM development(16)
- Learn ARM development(17)
- Learn ARM development(18)
- Embedded system debugging simulation tool
- A small question that has been bothering me recently has finally been solved~~
- Learn ARM development (1)
- Learn ARM development (2)
- Learn ARM development (4)
- Learn ARM development (6)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- CGD and Qorvo to jointly revolutionize motor control solutions
- CGD and Qorvo to jointly revolutionize motor control solutions
- Keysight Technologies FieldFox handheld analyzer with VDI spread spectrum module to achieve millimeter wave analysis function
- Infineon's PASCO2V15 XENSIV PAS CO2 5V Sensor Now Available at Mouser for Accurate CO2 Level Measurement
- Advanced gameplay, Harting takes your PCB board connection to a new level!
- Advanced gameplay, Harting takes your PCB board connection to a new level!
- A new chapter in Great Wall Motors R&D: solid-state battery technology leads the future
- Naxin Micro provides full-scenario GaN driver IC solutions
- Interpreting Huawei’s new solid-state battery patent, will it challenge CATL in 2030?
- Are pure electric/plug-in hybrid vehicles going crazy? A Chinese company has launched the world's first -40℃ dischargeable hybrid battery that is not afraid of cold
- Revealed! PPO, the "hard core" material in the 5G era
- [Atria AT32WB415 Series Bluetooth BLE 5.0 MCU Review] 1.0 Unboxing and evaluation and setting up the operating environment
- Briefly describe the application of embedded logic analyzer in FPGA testing.zip
- Share MSP430F5529 library functions
- One year after being sanctioned, the U.S. Department of Commerce suddenly allowed U.S. companies to work with Huawei to develop 5G network standards
- Is there any microcontroller burning tool that supports Android?
- Key points for installing pressure transmitter
- How to Use X-MWBLOCKS
- Prize-winning live broadcast: A wireless connection solution for both near and far distances. You are invited to watch at 10:00 am on March 25 (Thursday)!
- 48V to 400V Isolated Bidirectional DC/DC Converter UPS Reference Design