Detailed explanation of STM32 DMA usage

Publisher:EuphoricVoyageLatest update time:2017-01-18 Source: eefocusKeywords:STM32 Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

The DMA part I used is relatively simple. Of course, maybe this is a new thing, and I can't use its complex functions for the time being. The following is a question and answer format to express my thoughts.

What is DMA used for?

       Direct memory access is used to provide high-speed data transfer between peripherals and memory or between memory and memory. Data can be moved quickly through DMA without CPU intervention. This saves CPU resources for other operations.

How many DMA resources are there?

       There are two DMA controllers, DMA1 has 7 channels and DMA2 has 5 channels.

Where is the data sent from and to?

       Peripheral to SRAM (I2C/UART etc. get data and send it to SRAM);

       Between the two regions of SRAM;

       Peripheral to peripheral (ADC reads data and sends it to TIM1 to control it to generate different PWM duty cycles);

       SRAM to peripherals (pre-stored data in SRAM is sent to DAC to generate various waveforms);

       ...There are some things that are still unclear.

How much data can DMA transfer?

       The traditional concept of DMA is to transfer large amounts of data, but I understand that in STM32, its concept has been expanded, and perhaps more often speed is the focus of its application. The data can range from 1 to 65535.

Direct Memory Access (DMA) is a memory access technology in computer science. It allows certain hardware subsystems (computer peripherals) inside a computer to read and write system memory independently without bypassing the CPU. Under the same CPU load, DMA is a fast data transfer method. It allows hardware devices of different speeds to communicate without relying on a large number of interrupt requests from the CPU. [From Wikipedia]

Nowadays, more and more microcontrollers use DMA technology to provide high-speed data transfer between peripherals and memory or between memories. When the CPU initializes the transfer action, the transfer action itself is implemented and completed by the DMA controller. STM32 has a DMA controller with 7 channels, each of which is dedicated to managing one or more peripherals' requests for memory access, and an arbitrator to coordinate the priority of each DMA request.

The DMA controller and the Cortex-M3 core share the system data bus to perform direct memory data transfers. When the CPU and DMA access the same target (RAM or peripherals) at the same time, the DMA request may stop the CPU from accessing the system bus for several cycles, and the bus arbiter performs round-robin scheduling to ensure that the CPU can get at least half of the system bus (memory or peripherals) bandwidth.

After an event occurs, the peripheral sends a request signal to the DMA controller. The DMA controller processes the request according to the priority of the channel. When the DMA controller starts to access the peripheral, the DMA controller immediately sends an acknowledge signal to the peripheral. When receiving the acknowledge signal from the DMA controller, the peripheral immediately releases its request. Once the peripheral releases the request, the DMA controller also cancels the acknowledge signal. If more requests occur, the peripheral can start the next processing.

In summary, each DMA transfer consists of 3 operations:

1. Perform a load operation from a peripheral data register or from a memory location at the address specified by the DMA_CMARx register.

2. Store data into the peripheral data register or into the memory unit at the address specified by the DMA_CMARx register.

3. Perform a decrement of the DMA_CNDTRx register. This register contains the number of outstanding operations.

1

The arbiter initiates peripheral/memory access based on the priority of the channel request. The priority is divided into two levels: software (4 levels: highest, high, medium, low) and hardware (channels with lower numbers have higher priority than channels with higher numbers).

Interrupts can be generated when DMA transfer is halfway through, transfer is complete, and transfer errors occur.

Different DMA interrupts in STM32 (transfer complete, half transfer, transfer complete) are connected to NVIC via "wired OR" and need to be judged in the interrupt routine.

Before configuring DMA, do not forget to enable the DMA clock in the RCC settings. The DMA controller of STM32 is hung on the AHB bus.

DMA has a total of 7 channels, and the DMA mapping relationship of each channel is as follows:

2

The events of the peripherals are connected to the corresponding DMA channels. Each channel can be triggered by software to realize DMA data transfer within the memory (M2M mode)

Tips: In library 2.0, the parameter of the function RCC_AHBPeriphClockCmd is changed from "RCC_AHBPeriph_DMA" to "RCC_AHBPeriph_DMA1" (if it is a DMA1 controller).

The DMA transfer flags (CHTIFx, CTCIFx, CGIFx) are set to "1" by hardware, but need to be cleared by software and cleared in the interrupt service routine. When CGIFx (global interrupt flag) is cleared, CHTIFx and CTCIFx are also cleared.

 

Process: How to enable DMA? First of all, as we all know, initialization is required before any device is enabled. To initialize the module, you must first understand the corresponding structure and function of the module in order to set it correctly. Since DMA is relatively complex, I will only talk about the basic structure and common functions of DMA, which are provided by ST in the library function.

1. The following code is a standard DMA setting. Of course, it can be tailored according to the actual situation in actual application:

DMA_DeInit(DMA_Channel1);

The above sentence is to configure the channel for DMA. According to the information provided by ST, the DMA in STM3210Fx contains 7 channels (CH1~CH7), which means that 7 "bridges" can be provided for peripherals or memory (please allow me to use the word bridge, I think it is easier to understand, haha, don't "throw bricks"!);

DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;

The DMA_InitStructure in the above statement is a DMA structure, which is declared in the library. Of course, it must be defined before use; DMA_PeripheralBaseAddr is a data member in the structure, giving DMA a starting address, just like a buffer starting address. The data flow is: peripheral register à DMA_PeripheralBaseAdd à memory variable space (or flash data space, etc.), ADC1_DR_Address is an address variable I defined;

DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ADC_ConvertedValue;

The above sentence is obviously the address of the variable in Memory that DMA wants to connect to. ADC_ConvertedValue is a variable I defined in memory myself.

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

The above sentence is to set the DMA transmission direction. As I said before, DMA can be bidirectional or unidirectional. Here it is set to unidirectional transmission. If bidirectional transmission is required, change DMA_DIR_PeripheralSRC to DMA_DIR_PeripheralDST.

DMA_InitStructure.DMA_BufferSize = 2;

The above sentence sets the length of the DMA buffer during transmission. The starting address of the buffer has been defined before: ADC1_DR_Address. For security and reliability, it is generally necessary to define a storage area for the buffer. There are three types of units for this parameter: Byte, HalfWord, and Word. I set 2 half-words (see the settings below); 1 half-word occupies 16 bits in a 32-bit MCU.

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

The above sentence sets the peripheral increment mode of DMA. If the channel (CHx) selected by DMA has multiple peripherals connected, you need to use the peripheral increment mode: DMA_PeripheralInc_Enable; in my example, DMA only establishes a connection with ADC1, so I choose DMA_PeripheralInc_Disable

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

The above sentence sets the memory increment mode of DMA. When DMA accesses multiple memory parameters, DMA_MemoryInc_Enable needs to be used. When DMA only accesses one memory parameter, it can be set to: DMA_MemoryInc_Disable.

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;

The above sentence is to set the data length of each operation when DMA accesses. There are three types of data length, which have been mentioned before and will not be described here.

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

The same as above. No further explanation is given here.

DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

The above sentence sets the DMA transfer mode: continuous loop mode. If you only want to access it once and then not access it (or ask it in reverse according to the instruction operation, that is, access it when you want it to access, and stop it when you don’t want it to access), you can set it to the general mode: DMA_Mode_Normal

DMA_InitStructure.DMA_Priority = DMA_Priority_High;

The above sentence is to set the DMA priority level: it can be divided into 4 levels: VeryHigh, High, Medium, Low.

DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

The above sentence is to set the variables in the two memories of DMA to access each other.

DMA_Init(DMA_Channel1,&DMA_InitStructure);

The previous ones are settings for the DMA structure members. Now we will initialize the entire DMA module so that the DMA members are consistent with the above parameters.

/*DMA Enable*/

DMA_Cmd(DMA_Channel1,ENABLE);

Hahaha! I don’t think I’ll elaborate on this sentence, everyone will understand it at a glance.

At this point, the entire DMA is finally set up, but how is the DMA channel connected to the peripherals? Haha, this is also the thing I wanted to know the most at the beginning, don't worry! Let me have a cup of tea~~~~~~hahaha!

To establish an effective connection between DMA and peripherals, this is not the business of DMA itself, but the business of each peripheral. Each peripheral has a xxx_DMACmd(XXXx,Enable) function. If you want to establish an effective connection between DMA and ADC, use ADC_DMACmd(ADC1,Enable); (Here I enabled the ADC1 module in ADC).

 

A simple example transfer a word data buffer from FLASH memory to embedded SRAM memory.
In the V3.1.2 library location
STM32F10x_StdPeriph_Lib_V3.1.2\Project\STM32F10x_StdPeriph_Examples\DMA\FLASH_RAM

/* DMA1 channel6 configuration */
DMA_DeInit(DMA1_Channel6);
  //peripheral base address
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SRC_Const_Buffer;
  //memory base address   
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DST_Buffer;
  //data transfer direction Peripheral is source               
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
//buffer size Number of data to be transferred (0 up to 65535). Number of data transfers     
DMA_InitStructure.DMA_BufferSize = BufferSize;
   // the Peripheral address register is incremented       
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
  //the memory address register is incremented
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
//the Peripheral data width       
DMA_ InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; 
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
//the DMAy Channelx will be used in memory-to-memory transfer
//DMA channel operation can be performed without peripheral request, this operation is memory to memory mode.
DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;   
DMA_Init(DMA1_Channel6, &DMA_InitStructure);

/* Enable DMA1 Channel6 Transfer Complete interrupt */ DMA_ITConfig (
DMA1_Channel6, DMA_IT_TC, ENABLE);


/* Enable DMA1 Channel6 transfer */
DMA_Cmd(DMA1_Channel6, ENABLE); ===
... ) function, if DMA establishes a valid connection with ADC, use ADC_DMACmd (ADC1, Enable); (here I enabled the ADC1 module in ADC). /* DMA1 channel1 configuration ----------------------------------------------*/ DMA_DeInit(DMA1_Channel1); DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&AD_Value;    //u16 AD_Value[2]; It should be OK without &. The array name represents the address DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = 2; //############## Changed DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //############### Changed DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;





















DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);

/* Enable DMA1 channel 1 */
DMA_Cmd(DMA1_Channel1, ENABLE);

/* ADC1 configuration ------ ----------------------------------------*/
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None
; = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 2; //############## Changed
ADC_Init(ADC1, &ADC_InitStructure);
//Internal temperature sensor Add this sentence 
/* Enable the temperature sensor and vref internal channel */
ADC_TempSensorVrefintCmd(ENABLE);
//############### changed

//################ Channel 10 (potentiometer )
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_13Cycles5);
//###### Internal temperature sensor Channel 16 ###################
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 2, ADC_SampleTime_55Cycles5);

  /* Enable ADC1 DMA */Enable ADC1's DMA request image
  ADC_DMACmd(ADC1, ENABLE);

/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);

/* Enable ADC1 reset calibaration register */ //Be sure to calibrate
ADC_ResetCalibration(ADC1) before use ;
/* Check the end of ADC1 reset calibration register */
while(ADC_GetResetCalibrationStatus(ADC1));

/* Start ADC1 calibaration */
ADC_StartCalibration(ADC1);
/* Check the end of ADC1 calibration */
while(ADC_GetCalibrationStatus(ADC1));

/* Start ADC1 Software Conversion */ 
ADC_SoftwareStartConvCmd(ADC1, ENABLE);


Keywords:STM32 Reference address:Detailed explanation of STM32 DMA usage

Previous article:Create STM32L-DISCOVERY Project based on MDK-ARM
Next article:ARM learning notes 2 LED lighting program design

Recommended ReadingLatest update time:2024-11-15 13:43

STM32-GPIO Study Notes
     STM32F103RB has 4 IO ports (A~D), each IO port is controlled by 7 registers, they are:         Port configuration register (32 bits, two in total, CRL and CRH) Data register (32 bits, two in total, IDR and ODR) Set/Reset Register (32 bits, one, BSRR) Reset register (16 bits, one, BRR) Latch register (32 b
[Microcontroller]
STM32's built-in SPI implements reading and writing of external FLASH (W25Q128)
Experimental function: Use the KEY1 button to control the writing of W25Q128, and use another button KEY0 to control the reading of W25Q128. Hardware circuit: Software Configuration: //The following is the initialization code of the SPI module, configured as host mode   //SPI port initialization //This is the in
[Microcontroller]
The difference between STM32 push-pull output and open-drain output
When the port is configured as output:  Open-drain mode: When outputting 0, N-MOS is turned on, P-MOS is not activated, and output is 0.  When outputting 1, N-MOS is high impedance, P-MOS is not activated, and output is 1 (external pull-up circuit is required); this mode can use the port as a bidirectional IO.  Push-
[Microcontroller]
STM32 RTC and standby mode
1. Background Recently I have used low power mode - standby mode and RTC wake-up, so I reorganized RTC and sleep mode - standby mode. 1.1 What is standby mode? Standby mode: Based on the CortexTM-M4F deep sleep mode, where the voltage regulator is disabled, so the 1.2V domain is powered down - the PLL, HSI oscilla
[Microcontroller]
STM32 RTC and standby mode
STM32 IAP design example (I)
The project requires the development of a handheld device to upgrade the product software. Current products all use STM32, so it is very convenient to use the IAP function of STM32 to upgrade the software online. The overall requirement is that the host Master sends data to the slave Slave through the CAN interface. A
[Microcontroller]
STM32 IAP design example (I)
Using STM32's TIMER for external counting
Use the input signal of the ETR pin as the counting clock. This example uses Timer 2, whose ETR input pin is PA1. The working mode of this pin is input. Input mode, Timer working mode is slave mode; in addition, use PC6 to output an analog square wave clock signal. When testing, short-circuit PC6 and PA1. (Yo
[Microcontroller]
The power-on program does not run due to NVIC_Init in STM32
Recently, I encountered a strange problem when running multitasking on STM32 using ucosii. I used 4 serial ports. As the program grew larger, it did not run after power-on. I used an LED flashing to indicate the running, and there was no problem using STLINK for debugging, but it did not work after powering on again,
[Microcontroller]
STM32 serial port configuration (interrupt mode)
The serial port interrupt configuration of STM32 is also very simple. The first step is to configure the UART GPIO port The first step is to configure the UART GPIO port /************************************************ * Name: UART1_GPIO_Configuration * Deion: Configures the uart1 GPIO ports. * Input : No
[Microcontroller]
Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号