ADC转换原理:逐次逼近型ADC

stm32的ADC转换电路是一种逐次逼近型的ADC,采用二分法估测模拟电压。原理图:
逐次逼近型ADC 以上是一个八位的逐次逼近型ADC逻辑图,输入通道有8个,通过地址锁存器打开对应的通道使模拟电压进入比较器。这里的DAC具有参考电压ref,一般也是取供电电压VCC,0——VCC也是输入电压的范围,起始时逐次逼近(SAR)寄存器最高位为1,其余为0。

DAC根据SAR寄存器产生电压,例如初始最高位为1,其余为0,即是VCC/2,如果输入模拟电压大于参考电压,则高位置1,将第二位置1,仍然高于置1、反之置0,以此类推,SAR为8位,经过8次比较即得出该模拟电压的数字编码。

stm32的ADC逻辑设计

基本逻辑 stm32使用的SAR是12位的,意味着其电压分辨率是4096,输出结果0——4095,输入电压范围0——3.3V,最多18个输入通道,其中16个是外部引脚输入,2个是内部信号源,分别来自内部校准电压Vrefint和温度传感器。

这里的规则组、注入组是为了同时接收多通道ADC,无需重复接收ADC请求,只要将ADC引脚需求提交,系统会按顺序转换各个通道的模拟电压。规则组一般用于常规的ADC转换,支持软件触发、定时器、外部中断触发等,而注入组一般用于优先级较高的事件。规则组最多支持16个通道,但只有一个16位寄存器存放结果,因此常常需要DMA支持及时移走数据。而注入组最多支持4个通道,而且独享4个结果寄存器,不存在数据覆盖的问题。

ADC驱动时钟来自ADCCLK,规定最大只能是14MHz,因此72MHz主频最多只能选择6分频和8分频(不能使用2和4分频)。

ADC中断 ADC转换完成,规则组会产生中断EOC、注入组产生中断JEOC,代表数据转换完成,可以读走。此外还可以设置模拟看门狗事件,如果启动模拟看门狗,会监控对应寄存器上下限阈值,一旦超出阈值则触发AWD中断,配置NVIC完成相应的处理。

最后是关于触发源: 触发源 触发信号来自定时器中断、定时器的TRGO中断、外部引脚中断和软件模拟中断等,TRGO中断也来自定时器,和定时器中断不同它无需经过CPU,可直连定时器实现级联定时、或者主模式触发ADC。

参数配置相关

ADC规则组扫描模式

ADC规则组+DMA是普遍需求,规则组两个参数,两两组合配置四种模式:

  • 单次转换/连续转换:单次转换下只触发一次,下一次触发条件来临才会再转换;连续转换模式则是触发条件来临一次,ADC会持续对通道进行转换,产生EOC信号又继续转换,用户只需要在需要时读走寄存器数据内容即可。

  • 扫描模式/非扫描模式:扫描模式就是按规则组登记的全部通道依次进行转换,而非扫描模式则始终只会转换第一个登记的通道。

左对齐与右对齐

存放规则组、注入组ADC结果的均为16位寄存器,但是SAR本身只是12位寄存器,意味着结果是0——4095,因此存在高位填充还是低位填充问题,左对齐是右侧填0,右对齐是左侧填0;

ADC采集代码(无DMA)

ADC软件触发、监测PA1的模拟电压:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//初始化
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); //ADC1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //gpioA

RCC_ADCCLKConfig(RCC_PCLK2_Div6); //ADC时钟6分频

//GPIOA模拟输入
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);

//ADC资源、通道、规则组序号(第1)、采样保持时间:41.5个时钟周期
ADC_RegularChannelConfig(ADC1,ADC_Channel_1,1, ADC_SampleTime_41Cycles5);

ADC_InitTypeDef ADC_InitStruct;
ADC_InitStruct.ADC_ContinuousConvMode=DISABLE; //非扫描模式
ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right; //右对齐
ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None; //触发源:软件触发(非外部事件)
ADC_InitStruct.ADC_Mode=ADC_Mode_Independent; //独立模式/双ADC模式(少用)
ADC_InitStruct.ADC_NbrOfChannel=1; //仅扫描模式:指明规则组需要转换的通道数目
ADC_InitStruct.ADC_ScanConvMode=DISABLE; //单次转换
ADC_Init(ADC1,&ADC_InitStruct);

ADC_Cmd(ADC1,ENABLE); //使能ADC

//ADC校准:固定步骤的4部曲
ADC_ResetCalibration(ADC1); //复位校准寄存器
while(ADC_GetResetCalibrationStatus(ADC1)==SET); //等待复位校准完成
ADC_StartCalibration(ADC1); //启动校准,消除ADC电路偏差
while(ADC_GetCalibrationStatus(ADC1)==SET); //等待完成
}

uint16_t AD_Turn(void)
{
ADC_SoftwareStartConvCmd(ADC1,ENABLE); //软件触发
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET); //等待EOC中断来临
return ADC_GetConversionValue(ADC1); //返回结果寄存器(EOC标志位会随之清除)
}
AD_Turn放在循环内执行,相当于多次进行软件触发,不断读取ADC结果即可实时获取电压的范围。

多通道模式下则只能放到DMA一节了。