一、DMA简介
- DMA(Direct Memory Access)直接存储器存取
- DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源
- 12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)
- 每个通道都支持软件触发(存储器和存储器)和特定的硬件触发(外设和存储器)
- STM32F103C8T6 DMA资源:DMA1(7个通道)
二、存储器地址

三、DMA框图

四 、DMA基本结构

五、两种转运模式
六、代码部分
cpp
void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx);//恢复缺损配置
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);//初始化
void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct);//结构体初始化
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);//使能
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);//中断输出使能
//设置当前数据寄存器
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber);
//获取当前数据寄存器,返回传输寄存器的值
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);//获取标志位状态
void DMA_ClearFlag(uint32_t DMAy_FLAG);//清除标志位
ITStatus DMA_GetITStatus(uint32_t DMAy_IT);//获取中断状态
void DMA_ClearITPendingBit(uint32_t DMAy_IT);//清除中断挂起位
数据转运整体代码:
cpp
#include "stm32f10x.h" // Device header
//DMA转运的3个条件:
//1:传输计数器大于0
//2:触发源有触发信号(软件触发,所以一直都有触发信号)
//3:DMA使能
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是AHB总线设备
DMA_InitTypeDef DMA_InitStruct;
DMA_InitStruct.DMA_PeripheralBaseAddr = AddrA; //外设站点的起始地址,写一个32位的地址
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
//外设站点的数据宽度,这里是以字节的方式传输
// DMA_PeripheralDataSize_Byte 字节,就是uint8_t
// DMA_PeripheralDataSize_HalfWord 半字,就是uint16_t
// DMA_PeripheralDataSize_Word 字,就是uint32_t
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Enable; //外设站点的是否自增
DMA_InitStruct.DMA_MemoryBaseAddr = AddrB; //存储器站点的起始地址
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //存储器站点的数据宽度
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器站点的是否自增,选择自增。
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; //传输方向
// DMA_DIR_PeripheralDST 外设站点作为DST,destination,目的地,传输方向是存储器站点到外设站点
// DMA_DIR_PeripheralSRC 外设站点作为 SRC,source,源头,外设站点到存储器站点的传输方向
DMA_InitStruct.DMA_BufferSize = Size; //缓冲区大小,也就是传输计数器,传输次数
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; //传输模式,是否使用自动重装
DMA_InitStruct.DMA_M2M = DMA_M2M_Enable;
//选择是否是存储器到存储器,也就是选择硬件触发还是软件触发,这里选择软件触发
DMA_InitStruct.DMA_Priority = DMA_Priority_Medium; //优先级,按照参数要求,给一个优先级
DMA_Init(DMA1_Channel1,&DMA_InitStruct);
DMA_Cmd(DMA1_Channel1,DISABLE);
}
void MyDMA_Transfer(void) //DMA传输函数
{
//需要给传输计数器赋值,需要先给DMA失能!!!!!!!!!!
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);
//DMA1_FLAG_GL1 全局标志位
//DMA1_FLAG_TC1 转运完成标志位
//DMA1_FLAG_HT1 转运过半标志位
//DMA1_FLAG_TE1 转运错位标志位
}
AD多通道加DMA转运代码:
cpp
#include "stm32f10x.h" // Device header
uint16_t AD_Value[4];
void AD_init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//DMA是AHB总线设备
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
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; //选择50mhz
GPIO_Init(GPIOA, &GPIO_InitStruct);
//配置序列里的通道((1:ADCX,2:指定的通道,3:序列几的位置,4:指定通道的采样时间))
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_Mode = ADC_Mode_Independent;//ADC转换模式,这里选择独立模式,ADC1和ADC2各自转换
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;//左对齐还是右对齐
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
//外部触发转换选择,触发控制的触发源选择,这里的参数是外部触发none,也就是不使用外部触发,使用内部触发
ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;//连续转换模式,非扫描模式
ADC_InitStruct.ADC_ScanConvMode = ENABLE; //扫描转换模式,单次转换
ADC_InitStruct.ADC_NbrOfChannel = 4; //通道数目,
ADC_Init(ADC1,&ADC_InitStruct);
//中断和看门狗如果有需要可以在下面配置//
//*******************//
//****************DMA配置***************//
DMA_InitTypeDef DMA_InitStruct;
DMA_InitStruct.DMA_PeripheralBaseAddr =(uint32_t)&ADC1->DR; //外设站点的起始地址,写一个32位的地址
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
//外设站点的数据宽度,这里是以字节的方式传输
// DMA_PeripheralDataSize_Byte 字节,就是uint8_t
// DMA_PeripheralDataSize_HalfWord 半字,就是uint16_t
// DMA_PeripheralDataSize_Word 字,就是uint32_t
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设站点的是否自增
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)AD_Value; //存储器站点的起始地址
DMA_InitStruct.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord; //存储器站点的数据宽度
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器站点的是否自增,选择自增。
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; //传输方向
// DMA_DIR_PeripheralDST 外设站点作为DST,destination,目的地,传输方向是存储器站点到外设站点
// DMA_DIR_PeripheralSRC 外设站点作为 SRC,source,源头,外设站点到存储器站点的传输方向
DMA_InitStruct.DMA_BufferSize = 4; //缓冲区大小,也就是传输计数器,传输次数
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; //传输模式,是否使用自动重装
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
//选择是否是存储器到存储器,也就是选择硬件触发还是软件触发,这里选择软件触发
DMA_InitStruct.DMA_Priority = DMA_Mode_Circular; //优先级,按照参数要求,给一个优先级
DMA_Init(DMA1_Channel1,&DMA_InitStruct);
ADC_DMACmd(ADC1,ENABLE);
DMA_Cmd(DMA1_Channel1,ENABLE);
//*****************************************//
ADC_Cmd(ADC1,ENABLE);
//***********对ADC进行校准*****************//
ADC_ResetCalibration(ADC1);//复位校准
while (ADC_GetResetCalibrationStatus(ADC1) == SET);//返回复位校准的状态,一直循环等它硬件清0.说明复位校准完成
ADC_StartCalibration(ADC1);//启动校准,内部电路自动进行校准
while (ADC_GetCalibrationStatus(ADC1) == SET);//获取校准状态,等待校准是否完成
ADC_SoftwareStartConvCmd(ADC1,ENABLE); //软件触发转换的函数,ADC开始转换。
}