ADC与DMA多通道采集调试问题总结

1、重要理论:
一、为何使用ADC
为了能够使用数字电路处理模拟信号,必须将模拟信号转换成相应的数字信号,方能送入数字系统(例如微型计算机)进行处理,而这一种从模拟信号到数字信号的转换称为模—数转换,或简称为A/D(Analog to Digital)转换[1]。当作为传感器的电路系统产出模拟信号时,AD模块无疑是直观显示传感器功能状态的最佳选择。

STM32F4xx系列的AD最高可配置12位的分辨率,同时可在其规则通道转换期间可产生 DMA 请求来减轻CPU负担。

STM32的AD采集主要区别在于使用的AD数量和AD使用的通道数量,多AD的采集优势在于可以增加传感器的扫描频率,获得更加多的AD采集数据,如在AD通道数量比较紧缺而且对数据量没那么多的情况下,单AD多通道采集无疑是比较好的方法。本次主要讨论的就是单AD多通道采集数据的方法。由于规则通道组只有一个数据寄存器,因此,对于多个规则通道的转换,使用 DMA 非常有帮助。这样可以避免丢失在下一次写入之前还未被读出的 ADC_DR 寄存器中的数据[2]。

二、AD初始化的方式

STM32F429中提供了3个AD可供使用,其中DMA2是直接对ADC提供服务的,DMA2的请求映射分别对应的是:ADC1:数据流0通道0和数据流4通道0,ADC2:数据流2通道1和数据流3通道1,ADC3:数据流0通道2和数据流1通道2,详情可参考CPU手册。

首先需要初始化的是ADC所引出的GPIO引脚,没啥需要注意的,只要没有搞错引脚就行,配置方法可见上一篇(STM32)GPIO库函数使用一览 。

第二步时配置所需要使用的DMA,配置的时候需要注意的几项:1.ADC的外设基址,负责获取ADC的数据;2.读取数据的地址,从这里可以读出DMA的ADC数据,即是设置一个与通道数相应匹配的大小的数组;3.搞清楚DMA的请求映射。

最后就是配置ADC,STM32F4中的AD初始化库函数:ADC_CommonInit()和ADC_Init()配置的分别是通用类型变量和ADC的初始化

typedef struct
{
uint32_t ADC_Mode;
uint32_t ADC_Prescaler;
uint32_t ADC_DMAAccessMode;
uint32_t ADC_TwoSamplingDelay;
}ADC_CommonInitTypeDef;
ADC_CommonInit()中包含的是ADC模式选择,分频,DMA模式配置和采样延迟。

1.ADC模式主要针对于ADC数量的选择,独立模式可选ADC_Mode_Independent。

2.分频可选ADC_Prescaler_Div2/Div4/Div6/Div8。

3.DMA模式,是针对于多ADC中配置的,独立模式选择ADC_DMAAccessMode_Disabled。

4.采样延迟可配置5-20的任意值,ADC_TwoSamplingDelay_10Cycles。

typedef struct
{
uint32_t ADC_Resolution;
FunctionalState ADC_ScanConvMode;
FunctionalState ADC_ContinuousConvMode;
uint32_t ADC_ExternalTrigConvEdge;
uint32_t ADC_ExternalTrigConv;
uint32_t ADC_DataAlign;
uint8_t ADC_NbrOfConversion;
}ADC_InitTypeDef;
ADC_Init()中包含的是ADC分辨率,是否扫描,是否连续转换,触发极性和触发选择,数据对齐和通道数目。

1.分辨率可从12/10/8/6中做选择,ADC_Resolution_12/10/8/6b.

2.在多通道时需要对扫描转换使能,单通道的不需要。

3.连续转换指的是在各个通道读值完毕后继续读值。

4.外部触发的条件有很多,需要根据外设来具体分析

5.数据对齐有两种,左对齐和右对齐,一般是选择右对齐。
6.通道数目为对应配置的通道数。


作者:喝摩卡的被窝君
来源:CSDN
原文:https://blog.csdn.net/qq_40199189/article/details/79473245
版权声明:本文为博主原创文章,转载请附上博文链接!
2、代码重要部分详细标注:
void InitSampleModule(void)
{

initAdcCommon()
RegDma2Stream0IrqHandler(analyzeAdc1Sample, recoverDma2String0);
ConfigDma(DMA2_Stream0, DMA_Channel_0, (u32)&ADC1->DR,(u32)&adc1Sample, ***ADC1_REGULAR_SEQUENCE_LENGTH***);//*DMA通道的缓冲区要和规则通道的通道数对应,否则将出现采集数据错乱的现象*
InitDma2String0Irq();
initAdc1();
InitTimer2Trigger();//Timer2 is as a trigger for ADC1

}
void initAdcCommon(void)
{
ADC_CommonInitTypeDef ADC_CommonInitStructure;

ADC_DeInit();     //ADC复位

ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;//独立模式
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_10Cycles;//两个采样阶段之间的延迟5个时钟(延迟时间尽可能在5-20之间)
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //DMA disable for multi-ADC
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;//预分频4分频。ADCCLK=PCLK2/4=84/4=21Mhz,ADC时钟最好不要超过36Mhz
ADC_CommonInit(&ADC_CommonInitStructure);//初始化

}
void initAdc1(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;

/*Init ADC*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);    
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1时钟

//先初始化ADC1*(模拟输入量的IO口是规则通道的转换数量)*
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;//
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模拟输入
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;//不带上下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;//
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模拟输入
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;//不带上下拉
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;//
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模拟输入
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;//不带上下拉
GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化

ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;//12位模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE;//扫描模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//连续转换
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;//
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
ADC_InitStructure.ADC_NbrOfConversion = ADC1_REGULAR_SEQUENCE_LENGTH;//1个转换在规则序列中 也就是只转换规则序列1
ADC_Init(ADC1, &ADC_InitStructure);//ADC初始化

/* ADC1 regular channelx configuration **************************************/
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_15Cycles);  //Ua
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_15Cycles);  //Ub
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_15Cycles);  //Uc
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_15Cycles);  //Ia
ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5, ADC_SampleTime_15Cycles);  //Ib
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 6, ADC_SampleTime_15Cycles);  //Ic
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 7, ADC_SampleTime_15Cycles);  //AI1
ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 8, ADC_SampleTime_15Cycles);  //AI2
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 9, ADC_SampleTime_3Cycles);  //T1
ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 10, ADC_SampleTime_3Cycles);  //T2
ADC_RegularChannelConfig(ADC1, ADC_Channel_10,11, ADC_SampleTime_3Cycles); //SWA
ADC_RegularChannelConfig(ADC1, ADC_Channel_11,12, ADC_SampleTime_3Cycles); //SWB
ADC_RegularChannelConfig(ADC1, ADC_Channel_12,13, ADC_SampleTime_3Cycles);  //SWC
ADC_RegularChannelConfig(ADC1, ADC_Channel_13,14, ADC_SampleTime_3Cycles);  //T3
ADC_RegularChannelConfig(ADC1, ADC_Channel_14,15, ADC_SampleTime_3Cycles);  //T4
ADC_RegularChannelConfig(ADC1, ADC_Channel_15,16, ADC_SampleTime_3Cycles);  //B4

/* Enable DMA request after last transfer (Single-ADC mode) */
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
ADC_EOCOnEachRegularChannelCmd(ADC1, DISABLE);
/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE);

/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);

}
//DMAx的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_Streamx:DMA数据流,DMA1_Stream07/DMA2_Stream07
//chx:DMA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7
//par:外设地址
//mar:存储器地址
//ndtr:数据传输量
void ConfigDma(DMA_Stream_TypeDef *DMA_Streamx,u32 chx,u32 pAdd,u32 mAdd,u16 dataLen)
{
DMA_InitTypeDef DMA_InitStructure;

if((u32)DMA_Streamx>(u32)DMA2)//得到当前stream是属于DMA2还是DMA1
{
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能

}else
{
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);//DMA1时钟使能
}

DMA_DeInit(DMA_Streamx);

while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}//等待DMA可配置

/* 配置 DMA Stream */
DMA_InitStructure.DMA_Channel = chx;  //通道选择
DMA_InitStructure.DMA_PeripheralBaseAddr = pAdd;//DMA外设地址
DMA_InitStructure.DMA_Memory0BaseAddr = mAdd;//DMA 存储器0地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;//外设到存储器模式
DMA_InitStructure.DMA_BufferSize = dataLen;//数据传输量(缓存数量与通道数量一致)
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外设数据长度:16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//存储器数据长度:16位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;// 使用循环模式
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;//高等优先级
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发单次传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发单次传输
DMA_Init(DMA_Streamx, &DMA_InitStructure);//初始化DMA Stream

DMA_Cmd(DMA_Streamx, ENABLE);					   //开启DMA传输

}

你可能感兴趣的:(调试注意事项)