Stm32之DMA直接内存读取
1.DMA简介:
-
DMA*(Direct Memory Access controller)*:可用于为CPU减负,可以将内存中的数据与外设中的数据进行搬运(无法进行计算操作,单纯的搬运),而在此期间内CPU可以去处理其他事物。
(说白了,就是一个搬运工)
-
特性简介如下:(摘自原子的数据书册)
-
Stm32有两个DMA控制器,DMA1(7个通道),DMA2(5个通道);
-
有三种传输单位宽度可选:字节(8位)、半字(16位)、全字(32位);
-
通道间有四种优先级可调配:很高、高、中等和低;
-
有三种中断/事件标志:DMA 半传输(传输一半了),DMA 传输完成和 DMA 传输出错;
-
有三种传输方向:M2M(内存到内存);M2P(内存到外设);P2M(外设到内存);
-
闪存、SRAM、外设的SRAM、APB1 APB2 和 AHB 外设均可作为访问的源和目标。
-
可编程的数据传输数目:最大为 65536
-
2.DMA的外设对应通道:
DMA每个外设所对应的通道,都是确定好的,默认的优先级及各外设对应的通道如下:
3.相关寄存器:
此处DMA1与DMA2控制的寄存器的地址是分开的,因此只介绍DMA1;DMA2的所有寄存器应该于DMA1类似,均排列在DMA1后(即先全部是DMA1的寄存器,再是DMA2的寄存器);
-
DMA interrupt status register (DMA_ISR):中断标志位:每个通道各4个,共占28位[27:0]
- TEIFx: Transfer Error Interr Flag传输错误标志位
- HIIFx: Half Transfer Interr Flag传输过半标志位
- TCIFx: Transfer Complete Interr Flag传输完成标志位
- GIFx: Global Interr Flag全局中断标志位(只要上述中断有一个发生,该位就置一)
-
DMA interrupt flag clear register (DMA_IFCR):中断标志清除位:一样占28位[27:0];用于与ISR一一对应清除标志;
-
DMA channel x configuration register (DMA_CCRx) (x = 1…7):
各个通道的通道控制寄存器:(每个寄存器占15位[14:0])
-
MEM2MEM[14]:是否使能内存到内存的搬运;
-
PL[13:12]:priority level优先级配置;00-低;01-中;10-高;11-最高;
-
MSIZE[11:10]: Memory size传输内存单位大小;00-8位;01-16位;10-32位;11-保留;
-
PSIZE[9:8]: Peripheral size传输到外设单位大小;00-8位;01-16位;10-32位;11-保留;
-
MINC[7]: Memory increment mode,内存地址是否自增;
-
PINC[6]: Peripheral increment mode,外设地址石否自增;
-
CIRC[5]: Circular mode,是否使能循环传输;
-
DIR[4]: Data transfer direction,传输方向:0-P2M;1-M2P;
-
[3:0] TEIE(Transfer error interrupt enable)和HTIE( Half transfer interrupt enable)和TCIE(Transfer complete interrupt enable)三个中断标志的使能位;
-
EN[0]:该通道的使能位。
-
-
DMA channel x number of data register (DMA_CNDTRx) (x = 1…7):该寄存器的值是通道传输的数据长度,占16位,所以最大值位65535;
-
DMA channel x peripheral address register (DMA_CPARx) (x = 1…7), 32位寄存器,用来存放要传输的外设的地址;
-
DMA channel x memory address register (DMA_CMARx) (x = 1…7),32位寄存器,用来存放要传输的内存的地址;
4.库函数操作:
-
使能DMA的时钟:RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMAx(1/2), ENABLE)
-
初始化要开启的通道参数:
DMA_Init(DMA_Channel_TypeDef DMAy_Channelx, DMA_InitTypeDef DMA_InitStruct)** 要求的参数除了通道几外,还有一个结构体用于设置传输时的具体参数:
typedef struct { uint32_t DMA_PeripheralBaseAddr;//外设基地址 uint32_t DMA_MemoryBaseAddr; //外设基地址 uint32_t DMA_DIR; //传输方向 uint32_t DMA_BufferSize; //数据长度 uint32_t DMA_PeripheralInc; //外设地址是否开启自增 uint32_t DMA_MemoryInc; //内存地址是否开启自增 uint32_t DMA_PeripheralDataSize;//外设数据单位 uint32_t DMA_MemoryDataSize; //内存数据单位 uint32_t DMA_Mode; //单次(Normal)还是循环(Circle) uint32_t DMA_Priority; //优先级 uint32_t DMA_M2M; //是否开启内存传到内存 }DMA_InitTypeDef;
-
对于外设,均要使能该外设:DMA_Cmd(DMA_Channelx, ENABLE);
-
注意:使用DMA传输时,在对应外设中,要将外设中的DMA传输开启;
如: ADC_DMACmd(ADC3,ENABLE);//使能ADC3到DMA的转换
-
两个常用的库函数:
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG) //在 DMA 传输过程中,我们要查询 DMA 传输通道的状态,参数为DMAy_FLAG_TCx;DMAy的x通道 uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx) //是获取当前剩余数据量大小,参数同样是DMAy的通道x
5.关于地址:
-
如果是内存的地址,我们需要使用”&“符号将地址传入即可,
-
如果是数组,则之间将数组名传入即可(而且,数组变量CPU不会优化,会从内存中取值);
-
但如果仅仅是单个变量,需要在声明变量是加上关键字” volatile “以表明,cpu不会优化该变量,每次均会去内存中取值,这样我们读到的值才是DMA每次更新的值
(有点像51中的 code 关键字,将声明的变量存于rom(不可再修改,类似C的consist)中,而默认51是存在寄存器,也就是ram中的)
-
-
对于各种外设的地址,有两种方式获取:
-
一是,通过查表得出外设的基地址,再加上对应寄存器的偏移量即可。
各外设的基地址汇总如下(出自数据手册):
-
二是利用ST封好的库函数中的地址宏进行加减计算;
(第一种方法算出来的地址也建议自定义成宏,提高可读性和代码效率)
在顶层头文件stm32f103x.h中有定义外设的及地址,如下:
#define FLASH_BASE ((uint32_t)0x08000000) /*!< FLASH base address in the alias region */ #define SRAM_BASE ((uint32_t)0x20000000) /*!< SRAM base address in the alias region */ #define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */ #define SRAM_BB_BASE ((uint32_t)0x22000000) /*!< SRAM base address in the bit-band region */ #define PERIPH_BB_BASE ((uint32_t)0x42000000) /*!< Peripheral base address in the bit-band region */ #define FSMC_R_BASE ((uint32_t)0xA0000000) /*!< FSMC registers base address */ /*!< Peripheral memory map */ #define APB1PERIPH_BASE PERIPH_BASE #define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) #define AHBPERIPH_BASE (PERIPH_BASE + 0x20000) #define TIM2_BASE (APB1PERIPH_BASE + 0x0000) #define TIM3_BASE (APB1PERIPH_BASE + 0x0400) #define TIM4_BASE (APB1PERIPH_BASE + 0x0800) #define TIM5_BASE (APB1PERIPH_BASE + 0x0C00) #define TIM6_BASE (APB1PERIPH_BASE + 0x1000) #define TIM7_BASE (APB1PERIPH_BASE + 0x1400) #define TIM12_BASE (APB1PERIPH_BASE + 0x1800) #define TIM13_BASE (APB1PERIPH_BASE + 0x1C00) #define TIM14_BASE (APB1PERIPH_BASE + 0x2000) #define RTC_BASE (APB1PERIPH_BASE + 0x2800) #define WWDG_BASE (APB1PERIPH_BASE + 0x2C00) #define IWDG_BASE (APB1PERIPH_BASE + 0x3000) #define SPI2_BASE (APB1PERIPH_BASE + 0x3800) #define SPI3_BASE (APB1PERIPH_BASE + 0x3C00) #define USART2_BASE (APB1PERIPH_BASE + 0x4400) #define USART3_BASE (APB1PERIPH_BASE + 0x4800) #define UART4_BASE (APB1PERIPH_BASE + 0x4C00) #define UART5_BASE (APB1PERIPH_BASE + 0x5000) #define I2C1_BASE (APB1PERIPH_BASE + 0x5400) #define I2C2_BASE (APB1PERIPH_BASE + 0x5800) #define CAN1_BASE (APB1PERIPH_BASE + 0x6400) #define CAN2_BASE (APB1PERIPH_BASE + 0x6800) #define BKP_BASE (APB1PERIPH_BASE + 0x6C00) #define PWR_BASE (APB1PERIPH_BASE + 0x7000) #define DAC_BASE (APB1PERIPH_BASE + 0x7400) #define CEC_BASE (APB1PERIPH_BASE + 0x7800) #define AFIO_BASE (APB2PERIPH_BASE + 0x0000) #define EXTI_BASE (APB2PERIPH_BASE + 0x0400) #define GPIOA_BASE (APB2PERIPH_BASE + 0x0800) #define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00) #define GPIOC_BASE (APB2PERIPH_BASE + 0x1000) #define GPIOD_BASE (APB2PERIPH_BASE + 0x1400) #define GPIOE_BASE (APB2PERIPH_BASE + 0x1800) #define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00) #define GPIOG_BASE (APB2PERIPH_BASE + 0x2000) #define ADC1_BASE (APB2PERIPH_BASE + 0x2400) #define ADC2_BASE (APB2PERIPH_BASE + 0x2800) #define TIM1_BASE (APB2PERIPH_BASE + 0x2C00) #define SPI1_BASE (APB2PERIPH_BASE + 0x3000) #define TIM8_BASE (APB2PERIPH_BASE + 0x3400) #define USART1_BASE (APB2PERIPH_BASE + 0x3800) #define ADC3_BASE (APB2PERIPH_BASE + 0x3C00) #define TIM15_BASE (APB2PERIPH_BASE + 0x4000) #define TIM16_BASE (APB2PERIPH_BASE + 0x4400) #define TIM17_BASE (APB2PERIPH_BASE + 0x4800) #define TIM9_BASE (APB2PERIPH_BASE + 0x4C00) #define TIM10_BASE (APB2PERIPH_BASE + 0x5000) #define TIM11_BASE (APB2PERIPH_BASE + 0x5400) #define SDIO_BASE (PERIPH_BASE + 0x18000) #define DMA1_BASE (AHBPERIPH_BASE + 0x0000) #define DMA1_Channel1_BASE (AHBPERIPH_BASE + 0x0008) #define DMA1_Channel2_BASE (AHBPERIPH_BASE + 0x001C) #define DMA1_Channel3_BASE (AHBPERIPH_BASE + 0x0030) #define DMA1_Channel4_BASE (AHBPERIPH_BASE + 0x0044) #define DMA1_Channel5_BASE (AHBPERIPH_BASE + 0x0058) #define DMA1_Channel6_BASE (AHBPERIPH_BASE + 0x006C) #define DMA1_Channel7_BASE (AHBPERIPH_BASE + 0x0080) #define DMA2_BASE (AHBPERIPH_BASE + 0x0400) #define DMA2_Channel1_BASE (AHBPERIPH_BASE + 0x0408) #define DMA2_Channel2_BASE (AHBPERIPH_BASE + 0x041C) #define DMA2_Channel3_BASE (AHBPERIPH_BASE + 0x0430) #define DMA2_Channel4_BASE (AHBPERIPH_BASE + 0x0444) #define DMA2_Channel5_BASE (AHBPERIPH_BASE + 0x0458) #define RCC_BASE (AHBPERIPH_BASE + 0x1000) #define CRC_BASE (AHBPERIPH_BASE + 0x3000) #define FLASH_R_BASE (AHBPERIPH_BASE + 0x2000) /*!< Flash registers base address */ #define OB_BASE ((uint32_t)0x1FFFF800) /*!< Flash Option Bytes base address */ #define ETH_BASE (AHBPERIPH_BASE + 0x8000) #define ETH_MAC_BASE (ETH_BASE) #define ETH_MMC_BASE (ETH_BASE + 0x0100) #define ETH_PTP_BASE (ETH_BASE + 0x0700) #define ETH_DMA_BASE (ETH_BASE + 0x1000) #define FSMC_Bank1_R_BASE (FSMC_R_BASE + 0x0000) /*!< FSMC Bank1 registers base address */ #define FSMC_Bank1E_R_BASE (FSMC_R_BASE + 0x0104) /*!< FSMC Bank1E registers base address */ #define FSMC_Bank2_R_BASE (FSMC_R_BASE + 0x0060) /*!< FSMC Bank2 registers base address */ #define FSMC_Bank3_R_BASE (FSMC_R_BASE + 0x0080) /*!< FSMC Bank3 registers base address */ #define FSMC_Bank4_R_BASE (FSMC_R_BASE + 0x00A0) /*!< FSMC Bank4 registers base address */ #define DBGMCU_BASE ((uint32_t)0xE0042000) /*!< Debug MCU registers base address */
使用时加上对应寄存器的偏移量即可:如下:
#define ADC3_DR_Address (ADC3_BASE+0x4C)//DR偏移量0x4c
-