ADC即模拟数字转换器,通常用于将外部的模拟量信号转换为数字信号。STM32的ADC是12位逐次逼近型的模拟数字转换器,最大可以计数到4095,有18个通道,16个外部通道和2个内部通道。
ADC框图
ADC的功能框图可以分为七个部分:
①输入电压:
ADC输入范围为:VREF- ≤ VIN ≤ VREF+。由VREF-、 VREF+ 、VDDA 、VSSA、这四个外部引脚决定。
我们在设计原理图的时候一般把VSSA和VREF-接地, 把VREF+和VDDA 接3V3,得到ADC的输入电压范围为:0~3.3V。 在64脚以下的CPU中,没有VREF-和VREF+这两个引脚,ADC电压输入范围直接由VDDA和VSSA决定。
②输入通道:STM32每个ADC有18个通道,16个外部通道和2个内部通道,具体通道和IO口的匹配关系如下表:
③通道分类:外部通道中又分为规则通道和注入通道,规则通道最多16个,注入通道最多4个。
规则通道:规则通道就是普通的通道,通常使用的都是规则通道。
注入通道:注入通道有点像中断,可以插入到规则通道组中,如果规则通道组转换过程中触发了注入通道,注入通道会打断规则通道的转换,所有注入通道转换完后规则通道才能继续转换。
④触发源:ADC触发转换的方式有两种,给ADC_CR2寄存器的ADON位写1或者通过外部事件触发,外部事件包括定时器触发,EXTI触发等。
⑤转换时间:ADC输入时钟ADC_CLK由PCLK2经过分频产生,最大是14M,分频因子由RCC时钟配置寄存器RCC_CFGR的位15:14 ADCPRE[1:0]设置, 可以是2/4/6/8分频,注意这里没有1分频。一般我们设置PCLK2=HCLK=72M。
ADC使用若干个ADC_CLK周期对输入的电压进行采样, 采样的周期数可通过ADC 采样时间寄存器ADC_SMPR1和ADC_SMPR2中的SMP[2:0]位设置, ADC_SMPR2控制的是通道0~9,ADC_SMPR1控制的是通道10~17。每个通道可以分别用不同的时间采样。其中采样周期最小是1.5个, 即如果我们要达到最快的采样,那么应该设置采样周期为1.5个周期,这里说的周期就是1/ADC_CLK。
ADC的转换时间公式为:Tconv = 采样时间 + 12.5个周期。当ADCLK = 14MHZ (最高), 采样时间设置为1.5周期(最快),那么总的转换时间(最短)Tconv = 1.5周期 + 12.5周期 = 14周期 = 1us,一般我们设置PCLK2=72M,经过ADC预分频器能分频到最大的时钟只能是12M(6分频),采样周期设置为1.5个周期, 算出最短的转换时间为1.17us,这个才是最常用的。
⑥数据寄存器:ADC 规则组数据寄存器 ADC_DR 是一个 32 位的寄存器,独立模式时只使用到该寄存器低 16 位保存 ADC1/2/3 的规则转换数据。因为 ADC 的精度是 12 位的,ADC_DR 寄存器无论高 16 位还是低 16,存放数据的位宽都是 16 位的,所以需要选择数据对齐方式是左对齐还是右对齐。规则组最多有 16 个输入通道,而 ADC 规则数据寄存器只有一个,如果使用多通道转换,那么这些通道的数据也会存放在 DR 里面,按照规则组的顺序,上一个通道转换的数据,会被下一个通道转换的数据覆盖掉,所以当通道转换完成后要及时把数据取走。比较常用的方法是使用 DMA 模式。当规则组的通道转换结束时,就会产生 DMA 请求,这样就可以及时把转换的数据搬运到用户指定的目的地址存放。注意:只有 ADC1 和 ADC3 可以产生 DMA 请求,而由 ADC2 转换的数据可以通过双 ADC 模式,利用 ADC1 的 DMA 功能传输。
⑦中断:ADC有三种中断,规则组转换结束中断,注入组转换结束中断和模拟看门狗中断。当输入模拟电压低于低阈值或高于高阈值时会触发模拟看门狗中断。
扫描模式和连续模式的区别
①扫描模式(Scan Mode):在扫描模式下,ADC可以配置为对多个输入通道进行连续转换。ADC将按照通道号的顺序对设置的多个通道进行转换,并将结果按顺序保存在对应的数据寄存器中。可以通过使用EOC(End of Conversion)标志或DMA传输等方式来获取每个通道的转换结果。
②连续模式(Continuous Conversion Mode):在连续转换模式下,ADC在完成一次转换后自动开始下一次转换,无需外部触发。这种模式适用于需要持续采样的应用场景,如数据采集。
具体到单通道和多通道的情况:
①单通道:在单通道的情况下,扫描模式失能,因为只有一个通道需要进行转换。如果配置为单次模式使能,则该通道转换一次后停止转换;如果配置为连续模式使能,则该通道会不断进行连续的转换。
②多通道:在多通道的情况下,必须使用扫描模式。如果配置为单次模式使能且扫描模式使能,则多个通道会按照配置的顺序依次转换一次后停止转换;如果配置为连续模式使能且扫描模式使能,则多个通道会按照配置的顺序依次转换一次后接着进行下一次转换,实现不断连续的转换。
综上所述,扫描模式只在多通道的条件下有效,使得各个通道按照配置循序依次转换。而单次模式无论在单通道还是多通道下只对这些或者这个通道进行一次转换,连续模式无论是在单通道还是多通道下都对这些或者这个通道不断进行连续的转换。
ADC多通道采集(DMA读取)实验
①使能ADC、DMA和ADC通道IO时钟
②设置ADC分频因子为6分频,72/6=12M
③配置ADC通道IO为模拟输入模式
④复位ADC
⑤配置ADC工作模式
⑥配置ADC规则通道转换顺序
⑦使能ADC,使能复位校准,等待校准技术,开启ADC校准,等待校准结束,使能ADC软件触发
⑧DMA通道复位
⑨DMA配置
⑩使能DMA,使能ADC的DMA
volatile uint16_t ADC_ConvertedValue[3]={0,0,0}; //存放ADC数据的缓冲区
//初始化ADC
//这里我们仅以规则通道为例
//我们默认将开启通道0~3
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE );//使能ADC1通道时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能DMA时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
//PA1为ADC通道1 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOA, &GPIO_InitStructure);
//PA2为ADC通道2 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOA, &GPIO_InitStructure);
//PA3为ADC通道3 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:工作在独立模式,只用到一个ADC
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //扫描模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //模数转换工作在连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 3; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
//配置ADC规则通道转换顺序
ADC_RegularChannelConfig(ADC1,ADC_Channel_1,1,ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1,ADC_Channel_2,2,ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1,ADC_Channel_3,3,ADC_SampleTime_239Cycles5);
ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
ADC_ResetCalibration(ADC1); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
//DMA初始化
DMA_DeInit(DMA1_Channel1);//DMA通道复位
DMA_InitStructure.DMA_BufferSize = 3;//三路通道,缓冲区为3
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //数据传输方向由外设到内存
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//没用到内存到内存传输
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ADC_ConvertedValue;//分配内存地址
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//设置内存数据宽度为半字
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;//内存地址进行增加,因为需要进行对数据的存储
DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;//设置传输模式位连续不断的循环模式
DMA_InitStructure.DMA_PeripheralBaseAddr=( u32 ) ( & ( ADC1->DR ) );//DMA的外设基地址
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;//设置外设数据字长
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设的基地址是否进行增加,否
DMA_InitStructure.DMA_Priority=DMA_Priority_High;//设置DMA的转换的优先级为高优先级
DMA_Init(DMA1_Channel1,&DMA_InitStructure);
DMA_Cmd( DMA1_Channel1 , ENABLE);// 使能 DMA 通道
ADC_DMACmd(ADC1,ENABLE);//使能ADC1的DMA
// ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
}