The first time I came into contact with DMA was when I was learning ARM9 bare board programming at school. It has been almost 2 years since then. Now let's take a look at the DMA of the STM32 platform. Similarly, with the support of the standard peripheral library, the DMA programming of STM32 is very simple, but since it is a study, it is better to spend some time to understand the relevant concepts and principles of DMA.
1. Introduction to DMA
DMA is the abbreviation of Direct Memory Access, which means direct memory access. DMA is one of the peripherals of STM32 microcontrollers, and its main function is to move data. Moving data through DMA does not require direct CPU control, nor does it require the preservation and restoration of the scene like the interrupt processing method. While transmitting data, the CPU can do other things.
Data transfer without using DMA:
Data transfer using DMA:
DMA data transfer supports from peripherals to memory, memory to peripherals, and memory to memory (the memory mentioned here can be SRAM or FLASH). The DMA controller includes DMA1 controller and DMA2 controller, which have 7 and 5 channels for data transfer respectively. Each channel is dedicated to managing the memory access requests from one or more peripherals, and an arbitrator is used to coordinate the priority of each peripheral for DMA transfer requests. Note that DMA2 only exists in large-capacity or interconnected STM32 microcontrollers.
2. DMA functional block diagram
2.1 STM32 peripherals' requests and channels for DMA
The request and channel correspond to the numbers 1 and 2 in the figure: If the STM32 peripheral wants to transfer data through DMA, it must first send a DMA request to the DMA controller. After receiving the DMA request from the peripheral, the controller will send a response signal to the peripheral. After the peripheral responds and the DMA controller receives the response from the peripheral, the DMA starts the transmission until the transmission is completed.
Why do we need to send requests, respond, and receive responses? As can be seen from the blue box in the figure, DMA transmission and CPU share the system bus. The premise for starting DMA transmission is that the system bus is idle. In other words, the CPU does not occupy the system bus. Therefore, the above response mechanisms are required before starting DMA transmission. The bottom layer is that the DMA controller and the CPU are coordinating for the system bus. DMA1 has 7 channels and DMA2 has 5 channels. Different peripheral requests must be sent to the DMA controller through the corresponding DMA channels. Transmitting different peripheral requests to the corresponding channels is what we set in software programming.
DMA1 open channels and corresponding requests:
DMA2 open channels and corresponding requests:
Although each channel can receive requests from multiple peripherals, it can only receive one at a time.
2.2 Arbitrator
The arbiter corresponds to the number 3 in the figure: When DMA requests occur on multiple channels of the DMA controller, the arbiter is required to manage the order of response processing. The arbiter manages DMA requests through software and hardware: Software refers to the code we write, which is set in the DMA_CCRx (x refers to the channel number) register, with 4 levels, very high (DMA_Priority_VeryHigh), high (DMA_Priority_High), medium (DMA_Priority_Medium) and low (DMA_Priority_Low). Hardware means that if two or more DMA channel requests have the same priority, their response order depends on the channel number, and the one with a lower number has a higher priority. In the STM32 with DMA2, the DMA1 controller has a higher response priority than DMA2.
2.3 Configuring the DMA Controller
Configuring the DMA controller is nothing more than the following registers:
As mentioned earlier, the DMA data transfer mechanism does not require the participation of the CPU, but for the DMA controller to work properly and data to be transmitted correctly, three necessary conditions are required: source address, destination address and data size. For data to be transmitted in batches, the data size condition also includes the size and unit of each transmission.
(1) Source address and destination address
DMA has three data transfer directions: from peripheral to memory, from memory to peripheral, and from memory to memory. BIT[4]DIR of DMA_CCR is used to configure the data transfer direction:
a value of 0 indicates from peripheral to memory, and a value of 1 indicates from memory to peripheral. The peripheral address is configured in the DMA_CPAR register, and the memory address is configured in the DMA_CMAR register.
(2) The size and unit of the transmitted data.
Taking the serial port sending data to the computer as an example (memory->peripheral direction), the development board software can send a large amount of data to the computer at one time. The specific amount is configured in DMA_CNDTR:
The lower 16 bits of DMA_CNDTR are valid, and a maximum of 65535 data can be transmitted at one time.
To transmit data correctly, the data width of the source and target storage must be consistent. The serial port data register is 8 bits, that is, the value of BIT[9:8]PSIZE of the
peripheral data width setting register DMA_CCRx is 0: The value of BIT[11:10]MSIZE of the memory data width setting register DMA_CCRx is also 0: When
DMA transmits data, it is also necessary to set the incremental mode of the data sending pointer on the source address and the data storage pointer on the destination address. When the development board serial port sends data to the computer, assuming that there is a lot of data to be sent, the data sending pointer on the memory (source address) needs to be increased by 1 each time it is sent, but the serial port data register does not need to be increased because there is only one register. The data on the data register is cleared after being transmitted to the computer (even if it is not cleared, it does not matter if the data is directly overwritten). The address pointer increment mode of the peripheral is configured by PINC of DMA_CCRx, and the address pointer of the memory is configured by MINC.
(3) Transmission end
DMA interrupt status register DMA_ISR can set each DMA channel to generate corresponding flags when the transmission is halfway through, the transmission is completed, and the transmission error
occurs. In DMA_CCRx bits 1, 2, and 3, interrupts can be generated when the transmission is halfway through, the transmission is completed, and the transmission error occurs:
In addition, bit 0 is used to enable DMA transmission. The
transmission is divided into two modes: one-time transmission and cyclic transmission. One-time transmission means that the transmission stops after one transmission. If you want to transmit again, you need to turn off the DMA enable and reconfigure it before you can continue the transmission. Cyclic transmission means that after one transmission is completed, the configuration of the first transmission is restored to the cyclic transmission, and so on. The setting bit is CIRC in the DMA_CCRx register.
3. DMA function module description structure
In the consistent style of the standard library, the DMA_InitTypeDef initialization structure is defined in the stm32f10x_dma.h file, and the DMA_Init() function is defined in stm32f10x_dma.c.
typedef struct
{
uint32_t DMA_PeripheralBaseAddr; //Peripheral address
uint32_t DMA_MemoryBaseAddr; //Memory address
uint32_t DMA_DIR; //Transmission direction
uint32_t DMA_BufferSize; //The amount of data transferred
uint32_t DMA_PeripheralInc; //Peripheral address increment mode
uint32_t DMA_MemoryInc; //Memory address increment mode
uint32_t DMA_PeripheralDataSize; //Peripheral data width
uint32_t DMA_MemoryDataSize; //Memory data width
uint32_t DMA_Mode; //Mode selection
uint32_t DMA_Priority; //Channel priority
uint32_t DMA_M2M; //Memory to memory mode
}DMA_InitTypeDef;
(1)DMA_PeripheralBaseAddr: Peripheral address. If it is memory-to-memory mode, this member is set to the address of one of the memories, otherwise it is set to the address of the peripheral.
(2)DMA_MemoryBaseAddr: Memory address. It is generally set to the first address of the container (array) that stores data in the program.
(3)DMA_DIR: Transfer direction. It can be set to peripheral to memory or memory to peripheral. Note that there is no memory-to-memory option here. When using memory-to-memory, you only need to use one of the memories as a peripheral.
(4)DMA_BufferSize: Set the number of data to be transferred. (5)DMA_PeripheralInc: Peripheral address increment mode. If the value is DMA_PeripheralInc_Enable, the automatic increment function of the peripheral address is enabled. Generally, peripherals have only one data register, so this bit is not enabled.
(6)DMA_MemoryInc: If configured as DMA_MemoryInc_Enable, the automatic increment function of the memory address is enabled. Generally, memories are customized by us and multiple data are stored in the area, so this bit is generally enabled.
(7)DMA_MemoryDataSize: Peripheral data width, optional 8 bits (byte), 16 bits (half word), 32 bits (word)
(8)DMA_MemoryDataSize: Memory data width, optional 8 bits (byte), 16 bits (half word), 32 bits (word)
(9)DMA_Mode: Transfer mode selection, one-time transfer or cyclic transfer
(10)DMA_Priority: Channel priority setting, optional very high, high, medium, low
(11)DMA_M2M: Memory to memory mode
4. Common programming functions
4.1 DMA clock enable
4.2 Initialize DMA function module description structure
Function prototype: void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
DMAy_Channelx specifies which DMA channel, and DMA_InitStruct is the description structure parsed above
Example of use: DMA_Init(DMAy_Channel1, &DMA_InitStruct);
4.3 Enable peripheral DMA transmission
Take starting the DMA sending function as an example:
Function prototype: void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState
NewState)
Example of use:
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
DMA transfers to peripherals require corresponding settings, but not to memory. For memory to memory, the DMA_M2M member in the DMA_InitTypeDef structure needs to be enabled.
4.4 Enable DMA channel and start DMA transmission
Function prototype: void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);
Example of use: DMA_Cmd(DMAy_Channel1, ENABLE);
After enabling, the DMA controller starts working and starts data transfer under DMA control at the appropriate time (CPU does not occupy the bus).
4.5 Query DMA transfer status
During DMA transmission, we can query the status of the transmission channel through the function:
Function prototype: FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)
Suppose you want to query whether DMA channel 4 transfer is completed:
DMA_GetFlagStatus(DMA1_FLAG_TC4);
A return value of RESET indicates that the transfer has not been completed, while a return value of SET indicates that the transfer is complete.
Function to get the current remaining data size:
Function prototype: uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
Example: Get the amount of data that has not been transferred on DMA channel 4:
DMA_GetCurrDataCounter(DMAy_Channel4);
5. Programming Practice
5.1 DMA Transfer – Memory to Memory Mode
The hardware platform is the Zhengdian Atom MiniSTM32, which has two LEDs on board: red and green.
The program function is to copy the built-in FLASH data of STM32 to the built-in SRAM: define a const static variable as the source data, use DMA transfer to copy the source data to the target address, and compare whether the source data and the target data are the same. If they are the same, the green LED light will be on, otherwise the red LED light will be on.
The core of DMA programming lies in
(1) Enable DMA clock
(2) Configure DMA initialization structure parameters
(3) Enable DMA and start data transfer
(4) Wait for data transmission to complete
The project structure is:
BSP_LED.c implements configuration of LED pins:
#include
void LED_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitTypeStu;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD, ENABLE);
//³õʼ»¯PA8ÍÆÍìÊä³ö
GPIO_InitTypeStu.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitTypeStu.GPIO_Pin = GPIO_Pin_8;
GPIO_InitTypeStu.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitTypeStu);
GREEN_LED_OFF;
GPIO_InitTypeStu.GPIO_Pin = GPIO_Pin_2;
GPIO_Init(GPIOD, &GPIO_InitTypeStu);
RED_LED_OFF;
}
BSP_USART.c implements the configuration of USART1 function:
#include "BSP_USART.h"
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
};
FILE __stdout;
_sys_exit(int x)
{
x = x;
}
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40) == RESET);
USART1->DR = (u8)ch;
return ch;
}
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStu;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStu.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStu.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStu.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStu.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStu);
}
void USART_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStu;
USART_InitTypeDef USART_InitStu;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
GPIO_InitStu.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStu.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStu.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStu);
GPIO_InitStu.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStu.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIO_InitStu);
USART_InitStu.USART_BaudRate = 115200;
USART_InitStu.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStu.USART_Parity = USART_Parity_No;
USART_InitStu.USART_StopBits = USART_StopBits_1;
USART_InitStu.USART_WordLength = USART_WordLength_8b;
USART_InitStu.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART1, &USART_InitStu);
NVIC_Configuration();
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE);
}
void USART_SendChar(USART_TypeDef* pUSARTx, uint8_t c)
{
USART_SendData(pUSARTx, c);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
void USART_SendString(USART_TypeDef* pUSARTx, char* str)
{
uint32_t n = 0;
while (*(str + n) != '\0')
{
USART_SendChar(pUSARTx, *(str + n));
n++;
}
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
The above has been written in the previous related articles. Let’s look at BSP_LED.c to implement the configuration of DMA function:
#include
void DMA_Configuration(const uint32_t *SrcBuf, uint32_t *destBuf, uint32_t BUF_SZ)
{
DMA_InitTypeDef DMA_InitTypeStu;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitTypeStu.DMA_BufferSize = BUF_SZ;
DMA_InitTypeStu.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitTypeStu.DMA_M2M = DMA_M2M_Enable;
DMA_InitTypeStu.DMA_MemoryBaseAddr = (uint32_t)SrcBuf;
DMA_InitTypeStu.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitTypeStu.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitTypeStu.DMA_Mode = DMA_Mode_Normal;
DMA_InitTypeStu.DMA_PeripheralBaseAddr = (uint32_t)destBuf;
DMA_InitTypeStu.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitTypeStu.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
DMA_InitTypeStu.DMA_Priority = DMA_Priority_VeryHigh;
DMA_Init(DMA1_Channel1, &DMA_InitTypeStu);
DMA_Cmd(DMA1_Channel1, ENABLE);
}
uint8_t BufCmp(const uint32_t* pSrc, uint32_t* pDest, uint32_t len)
{
int i;
for (i = 0; i < len; i++)
{
if (*(pSrc + i) != *(pDest + i))
return 1;
}
return 0;
}
main() function:
#include
#include
#include
#define BUF_SZ 32
const uint32_t SrcBuf[BUF_SZ] = {0x12, 0x23, 0x45, 0x86, 0x45, 0x63, 0x89, 0x87,
0x22, 0x23, 0x26, 0x27, 0x28, 0x31, 0x33, 0x86,
0x12, 0x23, 0x45, 0x86, 0x45, 0x63, 0x89, 0x87,
0x22, 0x23, 0x26, 0x27, 0x28, 0x31, 0x33, 0x86};
uint32_t destBuf[BUF_SZ];
void delay_time(void)
{
int i, j;
for (i = 0; i < 500; i++)
for (j = 0; j < 6000; j++);
}
int main(void)
{
int i;
LED_Configuration();
USART_Configuration();
DMA_Configuration(SrcBuf, destBuf, BUF_SZ);
delay_time();
#if 0
for (i = 0; i < BUF_SZ; i++)
{
printf("SrcBuf[%d] = %u\r\n", i, SrcBuf[i]);
}
printf("\r\n");
for (i = 0; i < BUF_SZ; i++)
{
printf("destBuf[%d] = %u\r\n", i, destBuf[i]);
}
#endif
#if 1
if (BufCmp(SrcBuf, destBuf, BUF_SZ) == 0)
{
GREEN_LED_ON;
RED_LED_OFF;
}
else
{
GREEN_LED_OFF;
RED_LED_ON;
}
#endif
while (1);
return 0;
}
5.2 Memory to Serial Port (Peripheral) Mode
//BSP_DMA.c
void DMA_Configuration(const uint32_t *SrcBuf, uint32_t *destBuf, uint32_t BUF_SZ)
{
DMA_InitTypeDef DMA_InitTypeStu;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitTypeStu.DMA_BufferSize = BUF_SZ;
DMA_InitTypeStu.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitTypeStu.DMA_M2M = DMA_M2M_Disable;
DMA_InitTypeStu.DMA_MemoryBaseAddr = (uint32_t)SrcBuf;
DMA_InitTypeStu.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitTypeStu.DMA_MemoryInc = DMA_MemoryInc_Enable;
//DMA_InitTypeStu.DMA_Mode = DMA_Mode_Normal;
DMA_InitTypeStu.DMA_Mode = DMA_Mode_Circular; //Circular sending
DMA_InitTypeStu.DMA_PeripheralBaseAddr = (uint32_t)destBuf; //(uint32_t)(USART1_BASE + 0x04);
DMA_InitTypeStu.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitTypeStu.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitTypeStu.DMA_Priority = DMA_Priority_VeryHigh;
DMA_Init(DMA1_Channel4, &DMA_InitTypeStu);
DMA_Cmd(DMA1_Channel4, ENABLE);
}
//main.c
int main(void)
{
LED_Configuration();
USART_Configuration();
DMA_Configuration((const uint32_t*)SrcBuf, (uint32_t*)(&USART1->DR), BUF_SZ); //(uint32_t*)(USART1_BASE + 0x04)
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
while(1)
{
GREEN_LED_ON;
RED_LED_OFF;
delay_time();
GREEN_LED_OFF;
RED_LED_ON;
delay_time();
}
return 0;
}
Under DMA control, the data on SRAM is continuously sent to the DR register of USART1. The CPU does not need to participate in this process, so the LED flashes all the time.
DMA transfer is actually very simple, I have said it in a long-winded way. The key lies in the understanding of the concept, the use of library functions is still the key, and register operations are basically not needed.
Previous article:Difference between bootloader in stm32 system memory and bootloader in flash
Next article:Address mapping in stm32 library
- Popular Resources
- Popular amplifiers
- Virtualization Technology Practice Guide - High-efficiency and low-cost solutions for small and medium-sized enterprises (Wang Chunhai)
- usb_host_device_code
- Transplantation of real-time operating system RT-ThreadSmart on STM32MP1
- Design of switching power supply with adjustable output voltage based on STM32
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- IIC Protocol Analysis
- Award Ceremony | How do you purchase or select electronic components?
- MSP430 MCU Development Record (13)
- Current sensing crosstalk issue?
- Electronic pill box reminder solution
- The first analog I2C program learned by MSP430
- SD Card 2.0 Protocol.pdf
- How to set the bin file generated by ccs6.2 for tms320f28069 chip?
- Millimeter wave 5G costs dropped by 52%
- 28335 memory space