这个月一直忙于准备考试,已经考完一半科目了,偷闲写了这篇文章。因为还没考完试,估计还得咕一段时间了。
模拟量转换为数字量的过程称为A/D模数转换,完成这一转换过程的器件称为ADC(Analog-to-Digital Converter)。
在STM32中,有三个ADC:ADC1、ADC2、ADC3,其分辨率为12位,每个ADC通道具有18个通道,其中外部通道有16个。
我们分为七个部分讲解:
输入电压一般由参考电压VREF-
、VREF+
和VDDA(模拟电压源)
和VSSA(模拟地)
提供,具体的输入电压范围为VREF- ≤ VIN ≤ VREF+
。
在一般的电路设计中,我们会将VSSA
和VREF-
接地(GND),将VDDA
和VREF+
接高电平(即3.3V),如下图电路原理图所示。因此,ADC的输入电压范围为0 ~ 3.3V
。
我们可以根据数字量来算出模拟量:首先 ADC 分辨率为12位,电压范围为 0~3.3V,则最小精度为 3.3 − 0 2 12 \frac{3.3-0}{2^{12}} 2123.3−0,假设数字量为X,则最终模拟量 = 3.3 − 0 2 12 ⋅ X \frac{3.3-0}{2^{12}}\cdot X 2123.3−0⋅X。
当然,ADC可以测量范围为 0 ~ 3.3V 的电压,但如果超出这个范围的电压该如何测量呢?举一个简单的例子:如果我们想扩大测量范围至 -10 ~ +10V,可以将电路设计成如下:
根据KCL(基尔霍夫定律:节点流入电流之和等于流出电流之和)方程可得出:
V I N − V O U T R 2 + 3.3 V − V O U T R 1 = V O U T R 3 \frac{VIN-VOUT}{R2}+\frac{3.3V-VOUT}{R1}=\frac{VOUT}{R3} R2VIN−VOUT+R13.3V−VOUT=R3VOUT
已知 R1、R2 和 R3,就能根据这个方程得出 VIN 和 VOUT 的关系式。
STM32 为三个 ADC 分配了 18 个通道,如下表所示:
这是 STM32F103ZET6 的通道引脚分配,不同型号的芯片有不同的分配。在这里,对于 ADC1 和 ADC2,通道16和17均为内部通道;对于 ADC3,通道9和通道14~17均为内部通道。注意有部分 IO 引脚被重复占用。
STM32 的 ADC 输入通道分为两类:规则通道和注入通道。ADC 可以对一组指定的通道,按照既定的顺序,逐个转换这组通道,转换结束后,再从头循环。这些指定的通道组被称为规则通道组(规则组),组内的通道被称为规则转换通道(规则通道)。但在实际情况中,有可能需要临时中断规则组的转换,对某些通道进行转换,这些需要中断的规则组就称为注入通道组(注入组),组内的通道被称为注入转换通道(注入通道)。
规则组最多可以设置 16 个通道。
我们可通过规则序列寄存器SQRx(x=1, 2, 3)
来设置规则通道的优先顺序。规则序列寄存器一共有三个,除了寄存器SQR1
的第23位至20位(SQL[3:0]
)用做配置转换通道的个数外,其他都是每 5 位一组配置第 n 个转换的通道。如下表所示:
例如,配置寄存器SQR1
的SQL[3:0]
为3,表明有 3 个规则通道,则可在寄存器SQR3
的SQ1[4:0]
、SQ2[4:0]
和SQ3[4:0]
中分别配置为11、5、7,表明通道5、7和11都是规则通道,而且转换顺序是先通道11、再通道5、最后通道7。
注入通道和规则通道有所不同的是,注入通道与中断程序类似,在规则通道进行转换时“强行”插入进行转换。因此,注入也有插入的意思,插入和规则是相对的,注入通道只有在规则通道存在的时候才会出现。
我们可通过注入序列寄存器JQSR
来设置注入通道的优先顺序。注入序列寄存器只有一个,第21和20位两位(JL[1:0]
)用来设置转换通道的个数,其余都是每 5 位一组配置第 n 个转换通道。如下表所示:
因为与规则通道序列寄存器类似,因此就不举例了。但要注意一点:寄存器JSQR
和寄存器SQR
的转换顺序是不一样的。JSQR
的第 4 个转换通道其实是第一次进行转换的,第 3 个转换通道是第二次进行转换的,正好是反过来的,以此类推。
关于规则组和注入组的例子:系统需要采集温度,但又要适时监控湿度,那么湿度采集的转换通道可以放在注入组中,通过某种触发源启动转换。一旦启动注入通道转换,规则通道转换被暂停,然后等待注入通道转换完成后,规则组在进行转换。
是否启动 ADC 由寄存器ADC_CR2
的ADON
位控制,写入1则表示打开或启动模拟至数字转换器的电压。
启动转换器后,由寄存器ADC_CR2
的SWSTART
位和JSWSTART
位分别控制规则通道的转换和注入通道的转换,写入1则表示转换使能允许。
寄存器ADC_CR2
的EXTSEL
位和EXTTRIG
位控制规则通道的触发源,触发源来自TIM1_CHx(x=1, 2, 3)
、TIM2_CH2
、TIM4_CH4
和TIM3_TRGO
。EXTSEL
位用于选择使用哪个触发源,EXTTRIG
位用于使能触发允许或禁止。
类似地,寄存器ADC_CR2
的JEXTSEL
位和JEXTTRIG
位控制注入通道的触发源,触发源来自TIM1_CH4
、TIM2_CH1
、TIM3_CH4
、TIM1_TRGO
、TIM2_TRGO
和TIM4_TRGO
。JEXTSEL
位用于选择使用哪个触发源,JEXTTRIG
位用于使能触发允许或禁止。
注意,以上描述的是 ADC1 和 ADC2 的触发源,ADC3 与前两者有区别,具体可参考手册内容。
GPIO 外部中断的 EXTI11 和 EXTI15 也可作为触发源。
转换时间公式为:T(conv) = 采样时间 + 12.5个周期
。
转换速度由 ADC 模拟时钟(ADC_CLK)决定,其最大频率为 14MHz。 ADC_CLK 由 PCLK2 提供。之前在讲解时钟树的时候已经提及过,PCLK2 在经过 APB2 预分频器后最大频率可达到 72MHz。因为 ADC_CLK 规定最大频率为 14MHz,因此, 时钟信号经过 的 ADC 预分频器的分频因子最小为 6,所以实际的最大频率为72MHz / 6 = 12MHz
。
那么在哪里可以配置 ADC 预分频因子呢?注意,并不在 ADC 相关的寄存器中配置,而是在与 RCC 相关的寄存器进行配置。在寄存器RCC_CFGR
的ADCPRE[1:0]
可以配置 ADC 预分频因子。
ADC 需要若干个 ADC_CLK 周期才能完成对输入模拟量的采样,因此采样时间可由 n
个 ADC_CLK 周期来表示,即采样的周期数。我们可以通过两个采样时间寄存器ADC_SMPRx(x=1, 2)
来配置(如下表所示),位SMPx[2:0] (x=0-17)
用来配置采样的周期。其中寄存器ADC_SMPR2
控制的是通道 0~9,寄存器ADC_SMPR1
控制的是通道 10~17。这意味着每个通道都可以采用不同的采样周期。
如上表所示,最快的采样周期为 1.5 个 ADC_CLK 周期,最慢的采样周期为 239.5 个 ADC_CLK 周期。而一个周期的时间一般为1 / 12MHz
,所以最短的转换时间为:T(conv) = 采样时间 + 12.5个周期 = 1.5 + 12.5 = 14 个周期 = 14 *(1 / 12us) = 1.17us
。
ADC将转换好的数据存放到数据寄存器中,规则组的数据存放到寄存器ADC_DR
中,注入组的数据则存放到寄存器ADC_JDRx(x=1, 2, 3, 4)
中。
对于独立模式,即只使用一个 ADC 的情况(即只使用 ADC1 或 ADC2 或 ADC3)下,由于 ADC 分辨率为 12 位,所以只使用寄存器的低16位中的12位,高16位就不使用了。 这12位数据可以左对齐,也可以右对齐,这是由寄存器ADC_CR2
的ALIGN
来决定。
对于双ADC模式(ADC1和ADC2同时使用,注意ADC3是不能用于双ADC模式的),ADC2 的数据存放在寄存器的高16位,而 ADC1 的数据存放在寄存器的低16位。
由于数据规则寄存器只有一个,因此在使用多通道采集的时候寄存器的数据必然会被覆盖,一般的解决办法是采用DMA模式(ADC1和ADC3可用,2不可用),将转换结果转移进 DMA 中。
和数据规则寄存器一样,由于 ADC 分辨率为 12 位,所以只使用寄存器的低16位中的12位,高16位就不使用了。 这12位数据可以左对齐,也可以右对齐,这是由寄存器ADC_CR2
的ALIGN
来决定。
由于数据注入寄存器有 4 个,因此就不存在多通道采集时所产生的数据覆盖问题了。
ADC能产生三种中断,这三种中断对应的标志事件是:转换结束(EOC)、注入转换结束(JEOC)、模拟看门狗事件(AWID),所对应的中断使能位是EOCIE、JEOCIE、AWIDIE。
这里说明一下模拟看门狗的作用:在某些情况下我们希望模拟电压达到一定阈值后就产生一次中断,可以用模拟看门狗来监测模拟电压是否达到这个阈值,如果是,那么就会发出一次中断请求。在 ADC看门狗高阈值寄存器(ADC_HTR) 和 ADC看门狗低阈值寄存器(ADC_LRT) 可以设置这个阈值电压,两者均为低12位有效。
typedef struct
{
uint32_t ADC_Mode; // ADC工作模式
FunctionalState ADC_ScanConvMode; // ADC扫描(多通道)或单次(单通道)模式
FunctionalState ADC_ContinuousConvMode; // ADC单次转换或连续转换
uint32_t ADC_ExternalTrigConv; // ADC触发源选择
uint32_t ADC_DataAlign; // ADC数据寄存器对齐格式
uint8_t ADC_NbrOfChannel; // ADC采集通道数
}ADC_InitTypeDef;
在寄存器ADC_CR1
的DUALMOD[3:0]
位可以配置ADC工作模式,下面来简要介绍每种模式(除了独立模式外,剩余的模式都属于双重模式,只能搭配ADC1/2):
ADC_Mode_Independent
):ADC1/2/3 单独使用其中一个。ADC_Mode_RegSimult
):ADC1 和 ADC2 同时转换一个规则通道组,其中 ADC1 为主,ADC2 为从,ADC1 转换的结果放在ADC_DR
低16位,ADC2 转换的结果放在ADC_DR
的高16位。ADC_Mode_InjecSimult
):ADC1 和 ADC2 同时转换一个注入通道组,其中 ADC1 为主,ADC2 为从,ADC1 转换的结果放在各自的ADC_JDR
。ADC_Mode_FastInterl
):ADC1 和 ADC2 交替采集一个规则通道组(通常为一个规则通道),当 ADC2 触发后,ADC1 需要等待 7 个 ADCCLK 之后才能触发。ADC_Mode_SlowInterl
):ADC1 和 ADC2 交替采集一个规则通道组(只能为一个规则通道),当 ADC2 触发后,ADC1 需要等待 14 个 ADCCLK 之后才能触发。ADC_Mode_AlterTrig
):ADC1 和 ADC2 轮流采集注入通道组,当 ADC1 采集完所有通道后,ADC2 再采集自己的通道,如此循环。ADC_Mode_RegInjecSimult
):规则组同步转换被中断,以启动注入组的同步转换。ADC_Mode_RegSimult_AlterTrig
):规则组同步转换被中断,以启动注入组的交替触发转换。ADC_Mode_InjecSimult_FastInterl
):快速交叉转换被中断,同步注入启动。ADC_Mode_InjecSimult_SlowInterl
):慢速交叉转换被中断,同步注入启动。其中,同步规则模式和快速交叉模式比较常用。
在寄存器ADC_CR1
的SCAN
位进行配置。
实际是一个使能位:选择单通道扫描(ENABLE)还是多通道扫描(DISABLE)。
在寄存器ADC_CR2
的CONV
位进行配置。
实际是一个使能位:对通道选择采集一次(DISABLE)还是不停地重复采集(ENABLE)。
寄存器ADC_CR2
的EXTSEL
位和EXTTRIG
位控制规则通道的触发源,寄存器ADC_CR2
的JEXTSEL
位和JEXTTRIG
位控制注入通道的触发源。
可参考功能框图讲解的第五节触发源。
由寄存器ADC_CR2
的ALIGN
来配置数据的左对齐或右对齐。
由寄存器ADC_SQR1
的L[3:0]
位或寄存器ADC_JSQR
的JL[1:0]
位进行配置。
以下为ADC常用的库函数:
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState); // 软件触发
void ADC_ExternalTrigInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState); // 外部引脚触发
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);
ADC未完待续~