目录
一、DMA的作用
- DMA,全称Direct Memory Access,即直接存储器访问。
- DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。
- 当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。
- DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。
二、DMA的框图
- DMA是挂在AHB总线上的外设
- STM32F103C8T6 DMA资源:DMA1(7个通道)
- 提供在外设和存储器之间或者存储器和存储器之间的高速数据传输
三、DMA数据转运基本结构
- 外设和存储器、存储器之间可以进行数据传输,参数包括:起始地址、数据宽度、地址是否自增、传输方向。
- 传输计数器:存储传输次数,没传输一次数据,计数器减一,减到0,传输停止。
- 自动重装器:可自动重装传输计数器的数值。
- M2M:决定是软件触发还是硬件触发。
- 开关控制:控制DMA的开启和关闭。
四、实现SRAM数组之间的数据转运
- 开启DMA时钟。
- 初始化DMA。包括:起始站点的地址、传输的数据宽度、地址是否自增;目标站点的地址、传输的数据宽度、地址是否自增;传输方向;传输计数器的设置;是否自动重装计数器;触发方式和优先级的选择。
- 选择DMA通道。
- 使能DMA。
程序如下:
#include "stm32f10x.h" // Device header
void MyDMA_Init(uint32_t ADDR_A,uint32_t ADDR_B,uint16_t size)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
DMA_InitTypeDef DMA_InitStruct;
DMA_InitStruct.DMA_PeripheralBaseAddr=ADDR_A;//外设站点(起始站点)
DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;//数据宽度
DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Enable;//是否自增
DMA_InitStruct.DMA_MemoryBaseAddr=ADDR_B;//存储器站点(目的点站)
DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;//数据宽度
DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable;//是否自增
DMA_InitStruct.DMA_BufferSize=size;//传输次数
DMA_InitStruct.DMA_DIR=DMA_DIR_PeripheralSRC;//从外设站点到存储器站点转运
DMA_InitStruct.DMA_M2M=DMA_M2M_Enable;//软件触发
DMA_InitStruct.DMA_Mode=DMA_Mode_Normal;//传输计数器不自动重装
DMA_InitStruct.DMA_Priority=DMA_Priority_Medium;//优先级
DMA_Init(DMA1_Channel1,&DMA_InitStruct);
DMA_Cmd(DMA1_Channel1,ENABLE);//使能DMA
}
五、ADC转换和DMA转运数据
- 选择ADC转换模式:连续转换,扫描模式。
- 初始化ADC。
- 初始化DMA。
- 使能DMA。
- 设置DMA触发方式为:ADC1硬件触发。(由附图二可知,ADC1能发送DMA请求,ADC2不能)
- 使能ADC。
- ADC校准。
- 启动AD转换
程序如下:
#include "stm32f10x.h" // Device header
uint16_t AD_value[4];
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AIN;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
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_InitStruct;
ADC_InitStruct.ADC_ContinuousConvMode=ENABLE;//单次转换
ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right;//数据右对齐
ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//无外部触发源
ADC_InitStruct.ADC_Mode=ADC_Mode_Independent;//ADC1和ADC2独立
ADC_InitStruct.ADC_NbrOfChannel=4;//通道数目
ADC_InitStruct.ADC_ScanConvMode=ENABLE;//扫描模式
ADC_Init(ADC1,&ADC_InitStruct);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
DMA_InitTypeDef DMA_InitStruct;
DMA_InitStruct.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR;//外设站点(起始站点)
DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;//数据宽度
DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//是否自增
DMA_InitStruct.DMA_MemoryBaseAddr=(uint32_t)AD_value;//存储器站点(目的点站)
DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;//数据宽度
DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable;//是否自增
DMA_InitStruct.DMA_BufferSize=4;//传输次数
DMA_InitStruct.DMA_DIR=DMA_DIR_PeripheralSRC;//从外设站点到存储器站点转运
DMA_InitStruct.DMA_M2M=DMA_M2M_Disable;//硬件触发
DMA_InitStruct.DMA_Mode=DMA_Mode_Circular;//传输计数器自动重装
DMA_InitStruct.DMA_Priority=DMA_Priority_Medium;//优先级
DMA_Init(DMA1_Channel1,&DMA_InitStruct);
DMA_Cmd(DMA1_Channel1,ENABLE);//使能DMA
ADC_DMACmd(ADC1,ENABLE);//ADC硬件触发DMA
ADC_Cmd(ADC1,ENABLE);
//校准
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)==SET);
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1)==SET);
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//开始转换
}
六、附图
6.1 附图1:存储器映像
类型 | 起始地址 | 存储器 | 用途 |
ROM | 0x0800 0000 | 程序存储器Flash | 存储C语言编译后的程序代码 |
0x1FFF F000 | 系统存储器 | 存储BootLoader,用于串口下载 | |
0x1FFF F800 | 选项字节 | 存储一些独立于程序代码的配置参数 | |
RAM | 0x2000 0000 | 运行内存SRAM | 存储运行过程中的临时变量 |
0x4000 0000 | 外设寄存器 | 存储各个外设的配置参数 | |
0xE000 0000 | 内核外设寄存器 | 存储内核各个外设的配置参数 |
6.2 附图2:DMA1硬件触发请求映像
6.3 数据宽度与对齐
- 数据宽度: 源端宽度与目标宽度相等时,全部转运。
- 源端宽度<目标宽度时,目标地址数据高位补0。
- 源端宽度>目标宽度时,目标地址数据舍去高位。