本文笔者学习江科大stm32过程中强化学习效果所写,同时希望对大家有所帮助,若有错误恳请指正,谢谢!!!!
DMA(Direct Memory Access)直接存储器存取
功能:DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源。
12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)(注意支持软件触发和特定的硬件触发)
存储器映像地址分配
DMA框图介绍
1.总线矩阵左边的是主动单元右边的是被动单元(即右边的被左边的控制),其中DCode总线是专门访问FLASH的
2.DMA里面有7个独立的通道(各个通道可以分别设置他们的源地址和目标地址),由仲裁器决定哪一个通道使用DMA总线 (注意:如果DMA和CPU访问的目标相同,DMA会暂停CPU的访问,但会保留一半的总线带宽,使CPU能正常的工作)
3.AHB从设备可以完成DMA的相应配置,从AHB总线可以看出DMA既是主动单元又是被动单元
4.DMA请求连接了可以触发DMA的硬件触发源
注意:FLASH是ROM的一种,是只读存储器,DMA总线只能对他进行读取不能进行写入,寄存器中有些是只读的有些是可读可写的,但是数据寄存器是可以进行读写的。
DMA基本结构图介绍
1.DMA两大工作站:即外设寄存器和存储器站点
转运的方向:外设到存储器,存储器到外设,存储器到存储器(例如FLASH到SRAM或者SRAM到SRAM)
注意:FLASH是只读的不能进行FLASH到FLASH,SRAM到FLASH的转运操作
2.转运参数的配置(以外设和存储器为例)
外设的起始地址和存储器的起始地址:决定数据从哪来到哪去(注意此处的外设和存储器仅为代号,我们可以在外设写存储器地址,也可以在存储器写外设地址都是可以的)
数据宽度:指定一次转运要按多大的数据宽度来进行(可选字节,字,半字分别对应8位,16位,32位)
地址是否自增:即转运一次结束后源地址和目标地址是否需要自增
3.传输计数器:指定转运次数,每转运一次传输计数器就会-1,当它减到0以后DMA就不会进行数据转运,之前自增的地址就会回到起始地址的位置
4.自动重装器:即当传输计数器减到0以后,就会给它自动恢复初始值
5.触发源:分为硬件触发和软件触发,触发源的选择由M2M(Memory to Memory)数据位控制
软件触发:以最快的速度执行DMA转运,实现传输计数器的尽快清0(注意不能和循环模式使用,否则转运停不下来),一般用于存储器到存储器的转运
硬件触发:触发源可以选择ADC,串口,定时器等等,一般是和外设有关的转运,转运需要一定的时机。
6.开关控制:给DMA_Cmd()函数使能以后DMA就准备就绪可以开始转运了
DMA转运条件
1.开关控制,即DMA_Cmd()函数使能
2.传输计数器必须大于0
3.触发源有触发信号
当计数器为0且没有自动重装时,我们需要修改传输计数器的值就要先使开关控制失能,然后修改计数器的值后再使能开关控制
DMA请求(以DMA1为例)
该图中EN是数据选择器的开关控制位,M2M是软件触发的控制位,当它为1时是软件触发。硬件触发每一个通道对应的请求信号是不同的 。具体选择哪一个触发源是由对应的外设是否开启DMA转运来控制的,由类似于ADC_Cmd的函数决定。
数据宽度与对齐
1.当数据宽度一致时,按位进行读写
2.源端宽度小于目标宽度时,在目标地址写入高位进行扩展后的数据
3. 源端宽度大于目标宽度时,在目标地址保留舍弃高位后的数据
示例
1.数据转运+DMA
示例代码
#include "stm32f10x.h" // Device header
uint16_t MyDMA_Size;
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size)
{
MyDMA_Size=Size;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr=AddrA;//外设站点的起始地址
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;//外设站点的数据宽度
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Enable;//外设站点是否自增
DMA_InitStructure.DMA_MemoryBaseAddr=AddrB;//存储器站点的起始地址
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;//存储器站点的数据宽度
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;//存储器站点是否自增
DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC;//传输方向
DMA_InitStructure.DMA_BufferSize=Size;//传输大小,这个参数给多少就传输几次
DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;//传输模式
DMA_InitStructure.DMA_M2M=DMA_M2M_Enable;//选择硬件触发还是软件触发
DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;//优先级
DMA_Init(DMA1_Channel1,&DMA_InitStructure);
DMA_Cmd(DMA1_Channel1,DISABLE);
}
void MyDMA_Transfer(void)
{
DMA_Cmd(DMA1_Channel1,DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size);
DMA_Cmd(DMA1_Channel1,ENABLE);
while(DMA_GetFlagStatus(DMA1_FLAG_TC1)==RESET);
DMA_ClearFlag(DMA1_FLAG_TC1);
}
2.ADC扫描+DMA
示例代码
#include "stm32f10x.h" // Device header
uint16_t AD_Value[4];
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72MHZ/6=12MHZ
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_55Cycles5);
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode=ENABLE;
ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;
ADC_InitStructure.ADC_NbrOfChannel=4;
ADC_InitStructure.ADC_ScanConvMode=ENABLE;
ADC_Init(ADC1,&ADC_InitStructure);
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR;//外设站点的起始地址
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;//外设站点的数据宽度
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设站点是否自增
DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)AD_Value;//存储器站点的起始地址
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;//存储器站点的数据宽度
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;//存储器站点是否自增
DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC;//传输方向
DMA_InitStructure.DMA_BufferSize=4;//传输大小,这个参数给多少就传输几次
DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;//传输模式
DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;//选择硬件触发还是软件触发
DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;//优先级
DMA_Init(DMA1_Channel1,&DMA_InitStructure);
DMA_Cmd(DMA1_Channel1,ENABLE);
ADC_DMACmd(ADC1,ENABLE);
ADC_Cmd(ADC1,ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)==SET);
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1)==SET);
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
}