简介:
AD转换包括采样阶段和转换阶段。在采样阶段对通道数据进行采集,在转换阶段只是将采到的数据转换为数字量输出。ADC为12位,故转换的数字量范围为0-4095
我们为什么要使用双ADC呢?
相较于单ADC模式他有什么好处呢?下面就让我来浅浅解答一下:
首先ADC独立模式是指单个ADC模块独立地进行模数转换,即每个ADC模块都有自己的采样和转换控制器,可以独立地进行模数转换。这种模式适用于需要单独处理每个模拟信号的应用场景。
而双ADC同步模式是指两个ADC模块同时进行模数转换,即两个ADC模块共享同一个采样和转换控制器,可以同时对两个模拟信号进行采样和转换。这种模式适用于需要同时处理两个模拟信号的应用场景,例如音频信号的采集和处理。
现在假设我们要采集两路马达的电流值,对电流值的稳定性有一定要求。最初使用DMA+一个ADC的两个通道,采集的数据老是错位且数据变化波动大。而当我转换成双ADC+DMA模式时,使用两个ADC采集,就基本解决了以上问题。
当然双重 ADC 模式 较独立模式一个最大的优势就是提高了采样率,弥补了单个 ADC 采样不够快的缺点。
下面我会列举一点双ADC同步规则模式配置中容易混淆的知识点:
注意只有ADC1、ADC3可以使能DMA位,而ADC2没有。这里我们使用ADC1作为主ADC,ADC2作为从ADC。我们只需要使能ADC1的DMA即可。
在双ADC模式里,为了在主数据寄存器上读取从转换数据,必须使能DMA位,即使不使用DMA。
在ADCx配置中应注意:
1、 数据格式右对齐、选择ADC规则同步模式(ADC_Mode_RegSimult)、开启连续转换扫描模式、使用软件触发ADC转换。
2、不要忘记配置ADC规则组通道,注意对应的ADC的通道选择。
3、在ADCx的初始化函数中我们应配置: 不用外部触发转换,而是软件开启。
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
但由于两个ADC为同步规则触发,要想达到同步,我们可以将从ADC开启通道的外部触发模式,
//使能外部触发转换ADC2
ADC_ExternalTrigConvCmd(ADC2,ENABLE);
这样当主ADC的被触发时,从ADC也会被同时开启。
4、ADC1为主,占ADC1->DR的低十六位,ADC2为从,占ADC1->DR的高16位
5、这里只开启ADC1的DMA即可。
6、必须先使能ADC,再进行ADC的校准。
7、两个ADC的通道的采样时间需要一致。
在DMA配置中应注意:
1、外设基地址为ADC1数据寄存器的地址即 (uint32_t)(&(ADC1->DR)) 这里我们设置 外设为源地址。ADC1数据寄存器只有一个 故地址不自增。
2、存储器基地址为我们定义的存放数据的变量的地址 。若要接受的数据有多个,则可以设置存储器地址自增。
3、每个ADC是12位转换结果,但是我们采用的是双ADC。而在双ADC模式中,ADC1和ADC2转换的数据都是存在ADC1中(也就是主ADC中),他们分别占主ADC1->DR的低十六位、高十六位。
4、最后软件触发ADC1开启转换,则相当于开启了双ADC同步规则模式。
基本流程分析:
1、ADC模拟输入管脚初始化 (模拟输入模式)
2、ADC1工作参数初始化、ADC2工作参数初始化(注意开启时钟)
3、DMA配置、ADC1校准、ADC2校准x
4、使能ADCx,开软件触发ADC1转换。
部分代码分享:
void ADC_DMA_Init(void)
{
ADC_InitTypeDef ADC_InitStruct;
DMA_InitTypeDef DMA_InitStruct;
//开启外设时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2,ENABLE);
//配置ADCCLK
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//将输入时钟(72MHz)6分频
/************************ADC1配置************************************/
ADC_InitStruct.ADC_ContinuousConvMode=ENABLE; //连续转换
ADC_InitStruct.ADC_ScanConvMode=ENABLE; //扫描
ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right;
ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
ADC_InitStruct.ADC_Mode=ADC_Mode_RegSimult; //同步规则
ADC_InitStruct.ADC_NbrOfChannel=NOFCHANEL; //AD通道
ADC_Init(ADC1,&ADC_InitStruct);
//AD规则组通道配置
ADC_RegularChannelConfig(ADC1,ADC_Channel_1,1,ADC_SampleTime_55Cycles5);
//AD到DMA使能
ADC_DMACmd(ADC1,ENABLE);
/************************ADC2配置************************************/
ADC_InitStruct.ADC_ContinuousConvMode=ENABLE; //连续转换
ADC_InitStruct.ADC_ScanConvMode=ENABLE; //扫描
ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right;
ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
ADC_InitStruct.ADC_Mode=ADC_Mode_RegSimult; //同步规则
ADC_InitStruct.ADC_NbrOfChannel=NOFCHANEL; //AD通道
ADC_Init(ADC2,&ADC_InitStruct);
//AD规则组通道配置
ADC_RegularChannelConfig(ADC2,ADC_Channel_4,1,ADC_SampleTime_55Cycles5);
//使能外部触发转换ADC2
ADC_ExternalTrigConvCmd(ADC2,ENABLE);
/************************DMA配置************************************/
DMA_InitStruct.DMA_BufferSize=1; //缓冲区大小,应等于数据目的地大小
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_InitStruct.DMA_MemoryBaseAddr=(uint32_t)ADC_ConvertedValue; //一个32位变量接收数据
DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_Word; //DMA转运的是32位
DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable; //存储器地址自增
DMA_InitStruct.DMA_PeripheralBaseAddr=(uint32_t)(&(ADC1->DR)); //主ADC的数据寄存器
DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Word;
DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
DMA_Init(DMA1_Channel1,&DMA_InitStruct); //ADC的DMA请求映像通道为DMA1的通道1
DMA_Cmd(DMA1_Channel1,ENABLE);
/************************必须先使能ADC,再进行ADC的校准************************************/
ADC_Cmd(ADC1,ENABLE);
ADC_Cmd(ADC2,ENABLE);
/************************ADC1校准************************************/
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)==SET);
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1)==SET);
/************************ADC2校准************************************/
ADC_ResetCalibration(ADC2);
while(ADC_GetResetCalibrationStatus(ADC2)==SET);
ADC_StartCalibration(ADC2);
while(ADC_GetCalibrationStatus(ADC2)==SET);
/************************************************************/
//软件先不开启ADC
ADC_SoftwareStartConvCmd(ADC1,DISABLE);
}
有问题欢迎指正哦~