一般与ADC模数转换配合使用,ADC将不断接收到的模拟数据转化为数字量存储在寄存器中。使用规则组进行AD转换时,由于其数据寄存器只有一个,同时扫描多个数据通道时,会形造成数据覆盖。这时候可以使用DMA来搬运数据,每一次AD转换完成后及时将数据搬运到其他地方,避免数据被覆盖。
DMA运行的条件:
DMA使能
有触发源
传输计数器计数值大于0
外设寄存器:仅表示需要搬运数据的某个寄存器,可以是外设的寄存器也可以是系统内的某个存储器。
任意通道都可以使用软件触发
而硬件触发时不同的外设的DMA通道不一致
硬件触发DMA时应该主动开启该外设的DMA功能。
如: ADC_DMACmd(ADC1,ENABLE); //开启ADC的DMA功能
ADC扫描模式下,每转换完成一次数据,将数据写入到ADC_DR后,发出触发信号。DMA随即将ADC_DR位置的数据搬运到存储器。然后ADC再不断转换写入数据,不断触发DMA去搬运数据,从而实现多路ADC及其数据的存储。
单路DMA:
#include "stm32f10x.h" // Device header
uint16_t MyDMA_Size; //全局变量,存储传输次数
/**
* 函 数:DMA初始化
* 参 数:uint32_t AddrA 需要转运的数据首地址
* 参 数:uint32_t AddrA 转运后数据存储的地址
* 参 数:uint16_t Size 转运的数据个数
* 返 回 值:
*/
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB ,uint16_t Size)
{
MyDMA_Size=Size;
/*开启时钟*/
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
/*配置DMA*/
DMA_InitTypeDef DMA_InitStructrue; //定义结构体
DMA_InitStructrue.DMA_PeripheralBaseAddr= AddrA; //外设基地址,即需要转运的数据首地址
DMA_InitStructrue.DMA_PeripheralInc= DMA_PeripheralInc_Enable; //地址是否自增
DMA_InitStructrue.DMA_PeripheralDataSize= DMA_PeripheralDataSize_Byte; //传出多少宽度的数据
DMA_InitStructrue.DMA_MemoryBaseAddr= AddrB; //转运后数据存储的地址
DMA_InitStructrue.DMA_MemoryDataSize= DMA_PeripheralDataSize_Byte; //写入多少宽度的数据
DMA_InitStructrue.DMA_MemoryInc= DMA_MemoryInc_Enable; //地址是否自增
DMA_InitStructrue.DMA_DIR= DMA_DIR_PeripheralSRC; //数据传输方向
DMA_InitStructrue.DMA_BufferSize= Size; //传输数据的次数
DMA_InitStructrue.DMA_M2M= DMA_M2M_Enable; //存储器到存储器:软件出发 OR 外设到存储器:硬件触发
DMA_InitStructrue.DMA_Mode= DMA_Mode_Normal; //传输模式 Normal:计数器不重装 OR Circular:计数器重装 不能与软件出发同时使用
DMA_InitStructrue.DMA_Priority= DMA_Priority_Medium; //传输优先级
DMA_Init(DMA1_Channel1,&DMA_InitStructrue); // 读取结构体,完成初始化
DMA_Cmd(DMA1_Channel1,DISABLE); //是否开启DMA转运,这里先不开启
}
/**
* 函 数:数据转运,转运数据数为MyDMA_Size
* 参 数:无
* 返 回 值:
*/
void MyDMA_Transfer(void)
{
DMA_Cmd(DMA1_Channel1,DISABLE); //失能,修改计数器值时DMA不能处于使能状态
DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size); //设置计数器的值
DMA_Cmd(DMA1_Channel1,ENABLE); //使能,开始转运数据
while(DMA_GetFlagStatus(DMA1_FLAG_TC1)==RESET); //等待数据转运完成
DMA_ClearFlag(DMA1_FLAG_TC1); //清除标志位
}
多路DMA:
使用ADC扫描模式+DMA时:
将ADC配置成扫描模式
DMA配置成循环模式
给ADC一次触发信号,硬件就会不断得进行数据的转换和转换完成后的数据搬运。
#include "stm32f10x.h" // Device header
uint16_t AD_Value[4]={0,0,0,0};
/**
* 函 数:AD初始化
* 参 数:无
* 返 回 值:无
*/
void AD_Init(void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd (RCC_APB2Periph_ADC1, ENABLE); //打开ADC1的时钟
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA, ENABLE); //打开GPIOA的时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); //开启DMA1的时钟
/*设置ADC时钟分频*/
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //ADC时钟为系统六分频
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 |GPIO_Pin_1 |GPIO_Pin_2 |GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure); //将PA0/1/2/3配置成模拟输入模式
/*规则组通道配置*/
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初始化*/
ADC_InitTypeDef ADC_InitStructrue; //定义结构体
ADC_InitStructrue.ADC_Mode= ADC_Mode_Independent; //模式,选择独立模式,仅使用单个ADC
ADC_InitStructrue.ADC_DataAlign= ADC_DataAlign_Right; //数据对齐,右对齐
ADC_InitStructrue.ADC_ExternalTrigConv= ADC_ExternalTrigConv_None; //外部触发,仅由软件触发
ADC_InitStructrue.ADC_ContinuousConvMode= ENABLE; //连续转换,使能,软件触发一次后会连续转换
ADC_InitStructrue.ADC_NbrOfChannel= 4 ; //使用的通道数
ADC_InitStructrue.ADC_ScanConvMode= ENABLE; //扫描模式,使能,转换多个序列
ADC_Init(ADC1,&ADC_InitStructrue); //读入结构体
/*配置DMA*/
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_M2M= DMA_M2M_Disable; //存储器到存储器:EN:软件触发 DIS:外设到存储器:硬件触发
DMA_InitStructure.DMA_Mode= DMA_Mode_Circular; //传输模式 Normal:计数器不重装 OR Circular:计数器重装 不能与软件出发同时使用
DMA_InitStructure.DMA_Priority= DMA_Priority_Medium; //传输优先级
DMA_Init(DMA1_Channel1,&DMA_InitStructure); //读取结构体,完成初始化
DMA_Cmd(DMA1_Channel1,ENABLE); //是否开启DMA转运,这里先不开启
ADC_DMACmd(ADC1,ENABLE); //开启ADC的DMA功能
ADC_Cmd(ADC1,ENABLE); //启动ADC
/*ADC校准*/
ADC_ResetCalibration(ADC1); //固定流程,内部有电路自动执行
while(ADC_GetResetCalibrationStatus(ADC1)==SET);
ADC_StartCalibration(ADC1);
while( ADC_GetCalibrationStatus(ADC1)==SET);
ADC_SoftwareStartConvCmd(ADC1,ENABLE); //触发信号,ADC转换一次
}