Few ordinary 8-bit or 16-bit microcontrollers include DMA controllers, so many embedded programmers may not be familiar with DMA. Simply put, direct memory access (DMA) is used to provide high-speed data transfer between peripherals and memory or between memory and memory. Since there is no need for CPU intervention, data can be moved quickly through DMA, which saves CPU resources for other operations.
The STM32F10x has two DMA controllers with a total of 12 channels (DMA1 has 7 channels and DMA2 has 5 channels), each of which is dedicated to managing memory access requests from one or more peripherals. There is also an arbitrator to coordinate the priority of each DMA request.
According to the STM32 reference manual: "The DMA controller and the Cortex™-M3 core share the system data bus and perform direct memory data transfers. When the CPU and DMA access the same target (RAM or peripherals) at the same time, the DMA request will suspend the CPU from accessing the system bus for several cycles, and the bus arbitrator performs a circular schedule to ensure that the CPU can get at least half of the system bus (memory or peripheral) bandwidth." So we don't have to worry about the DMA controller monopolizing bus resources. The CPU can always get general bus time.
Next, we will take the data transmission of USART2 as an example to introduce DMA. First, from Figure 22 of the STM32 reference manual, we can see that the transmission function of USART2 can use the 7th channel of DMA1.
The setup work of using DMA transfer can be roughly divided into 6 steps:
1. Set the address of the peripheral register in the DMA1_CPAR7 register. When a peripheral data transfer request occurs, this address will be the source or destination of the data transfer.
2. Set the address of the data memory in the DMA1_CMAR7 register. When a peripheral data transfer request occurs, the transferred data will be read from or written to this address.
3. Set the amount of data to be transferred in the DMA1_CNDTR7 register. This value is decremented after each data transfer.
4. Set the priority of the channel in the PL[1:0] bits of the DMA1_CCR7 register.
5. Set the direction of data transfer, circular mode, incremental mode of peripherals and memory, data width of peripherals and memory, interruption when half of the transfer is generated, or interruption when the transfer is completed in the DMA1_CCR7 register.
6. Set the ENABLE bit in the DMA1_CCR7 register to enable the channel.
The code corresponding to step 1 is:
- DMA1_Channel7->CPAR = (uint32_t) &(USART2->DR);
The code corresponding to step 2 is as follows, where p_str is a pointer pointing to the first address of the data to be transferred:
- DMA1_Channel7->CMAR = (uint32_t) p_str;
- DMA1_Channel7->CNDTR = cnt;
- DMA1_Channel7->CCR |= DMA_Priority_Low;
- DMA1_Channel7->CCR |= DMA_DIR_PeripheralDST |
- DMA_Mode_Normal |
- DMA_PeripheralInc_Disable |
- DMA_MemoryInc_Enable |
- DMA_PeripheralDataSize_Byte |
- DMA_MemoryDataSize_Byte |
- DMA_M2M_Disable;
- DMA1_Channel7->CCR |= DMA_CCR1_EN;
Actually, there should be 2 more steps before these 6 steps. First, before setting up DMA, you need to turn on the DMA clock:
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
- USARTx->CR3 |= USART_DMAReq_Tx;
Or use the following function:
- USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);
Once a DMA channel is started, it can respond to DMA requests from peripherals connected to the channel. How to start the next DMA transfer after completing a DMA transfer? This problem has troubled me for several days. Finally, I found the following sentence in the STM32 reference manual:
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.
Here is a simple example program:
- 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);
- }
- 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;
- }
- uint8_t str[] = "Hello World!!!";
- void TaskStart(void *pdata)
- {
- SysTick_Config(SystemCoreClock/10);
- USART2_Init();
- UART2_TX_DMA_Init(str, 14);
- for(;;)
- {
- LED_Spark();
- //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_Init();
- OSInit();
- OSTaskCreate(TaskStart, (void *)0, &(TaskStartStk[TASK_STK_SIZE-1]), 1);
- OSStart();
- for(;;)
- {
- }
- }
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:
- DMA1_Channel7->CCR |= DMA_IT_TC; //Transfer complete interrupt enable
- 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_Init();
- 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_Init();
- 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:STM32F10x Study Notes 9 (Solving the problem of JLink being unable to download programs)
Next article:STM32F10x study notes 7 (USART realizes serial communication 3)
Recommended ReadingLatest update time:2024-11-15 14:08
- Popular Resources
- Popular amplifiers
- Dual Radar: A Dual 4D Radar Multimodal Dataset for Autonomous Driving
- Semantic Segmentation for Autonomous Driving: Model Evaluation, Dataset Generation, Viewpoint Comparison, and Real-time Performance
- Monocular semantic map localization for autonomous vehicles
- Microcomputer Principles and Interface Technology 3rd Edition (Zhou Mingde, Zhang Xiaoxia, Lan Fangpeng)
- 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
- Do you know all the various motors commonly used in automotive electronics?
- What are the functions of the Internet of Vehicles? What are the uses and benefits of the Internet of Vehicles?
- Power Inverter - A critical safety system for electric vehicles
- Analysis of the information security mechanism of AUTOSAR, the automotive embedded software framework
- Brief Analysis of Automotive Ethernet Test Content and Test Methods
- How haptic technology can enhance driving safety
- Let’s talk about the “Three Musketeers” of radar in autonomous driving
- Why software-defined vehicles transform cars from tools into living spaces
- How Lucid is overtaking Tesla with smaller motors
- Wi-Fi 8 specification is on the way: 2.4/5/6GHz triple-band operation
- I need help from an expert to look at the program and tell me how to modify it so that the buzzer can sound three times and then pause for 0.5 seconds before sounding again.
- MSP430G2755 Main Memory Bootloader UART Porting Guide
- McAsp multi-channel understanding
- Please help me look at this oscillator circuit
- About NCP1236 automatic recovery function test after removing short circuit and overload test
- Zigbee IoT module market: Differences between LTE standard Cat.1 and nbiot wireless communication modules
- ADS Installation Issues
- A Brief Discussion on FPGA Resource Evaluation
- [Atria Development Board AT32F421 Review] - Experience Sharing
- MCU newbie asks for help regarding data transfer instructions