我们的世界是一个模拟量和数字量共存的世界,一支笔有没有墨水是数字量,有多少ml墨水又是模拟量。数字和模拟是可以转换的。比如一支笔,从数字的角度看0代表没墨水,1代表有墨水。从模拟的角度来看,0ml代表没墨水,0.1ml到3ml都算有墨水。那么就能这样从模拟量映射到数字量:0ml->0,(0.1~3ml)->1。
以上的例子就是一个最简单的1位ADC了。ADC全名叫Analog-to-Digital Converter,即模拟数字转换器。我之前的回答
ADC0809究竟是输入什么,输出什么?www.zhihu.com有解说过ADC的一种实现结构,大家可以移步去看看。
在单片机应用里,获取到模拟量主要是电压值。很多传感器会把某种物理量通过某种方法变成电压值,然后单片机读取电压值就能感知外界的变化。
例如温度:一个NTC电阻,它的组成材料是对热量敏感的。当外界温度变化的时候,会引起NTC电阻阻值的变化。而通过电阻分压电路,可以把电阻变化变成电压变化。最后被单片机ADC转换成数字量。这个数字换算一下就是温度值了。整个流程就是:环境温度->电阻值->电压值->电压值的数字量->温度值的数字量。
不过STC8单片机并不是所有型号都带ADC,所以就算ECBM库里有ADC库,也要有带ADC的单片机搭配才行。我的另一篇文章有解说STC8型号的资源,有兴趣可以了解一下。
大杰:教你如何分清STC8的型号zhuanlan.zhihu.com我们这次用的平台是ECBM 8051板,基于STC8A8K64SA12单片机,取这个名字是因为它的外形大小和引脚排列和8051的40脚单片机一模一样(板载一个0.42寸OLED屏,可以显示一些东西),并且还带了12位ADC。
12位ADC代表着ADC会把0~Vref的电压转换成0~4095(因为2的12次方=4096,算上0的话从0到4095一共有4096个数)的数字量。ECBM 8051板上将Vref接到了3.3V,因此ADC的量程最大能测到3.3V,最小分辨率为3.3V/4096约等于0.8mV。量程应该很好理解,就是ADC能测的最大电压值,如果被测电压超过了ADC的量程,那么就会损坏ADC。最小分辨率是指ADC能分辨的最小电压。比如同3.3V电压下8位ADC的最小分辨率为12.89mV,而12位ADC的最小分辨率为0.8mV。也就是输入5mV和10mV的时候,对于8位ADC而言都是0x00。而对于12位ADC来说分别是0x0006和0x000C。因此分辨率越高,能读取的电压值就“精细”,也就越有机会靠近电压真实值。计算的公式是这样的:
对于本篇用的硬件,就是:
将ADC转换得到的AD值带入公式就能知道被测电压是多少。
在开始配置ADC之前,先在ecbm_core.h中把单片机的型号设置成STC8A8K64S4A12。
然后使能ADC库,接着打开adc.h文件,进入该文件的可视化界面:
STC8A一共有15个通道,这里只使能ADC的通道0,在STC8A8K64S4A12中对应P1.0脚。如果是STC8G或者STC8C系列的话就不一定是P1.0脚,请注意甄别。
参数方面,分频系数用于调节ADC的采样率,默认为0即可。对齐方式和舍弃低位稍后再说。
可用的函数有:
初始化函数,使用ADC之前都要调用的函数。
ADC读取函数,这个函数用于查询法读取ADC转换后的AD值。如果不舍弃位数的话,该函数返回的是u16型。如果舍弃低位的话,返回的是u8型。
除了查询法外,也可以用中断法。相关函数就是这两个,在主函数中调用adc_read_start,然后执行其他语句···,然后在ADC中断里执行adc_read_it函数读取就行。
同时ADC中断的开关由这两个函数控制:
我们可以读取AD值后,带入上面的公式求出被测电压,不过现在有这个函数可以一步到位了,那就是adc_voltage。该函数只需你提供Vref的实测值和采集通道号,就能返回被测电压值。
于是我们的示例代码这样写:
#include "ecbm_core.h" //加载库函数的头文件。
void main(){ //main函数,必须的。
system_init(); //系统初始化函数,也是必须的。
adc_init(); //初始化ADC,必须的。
while(1){
delay_ms(500); //每500ms采样一次
debug("ADC=%frn",adc_voltage(0,3.3));//将通道0采到的电压值发送到串口调试上。
//Vref实测值为3.3V。
}
}
实物按照这样连线,用杜邦线将基准板的2.5V电压输出连到P1.0脚,将基准板的GND连到单片机的GND。如下图所示。
编译下载之后,打开串口助手,我们能看到如下场景:
都在2.5V附近,看来这效果是非常好的。
然后我们来回顾数据对齐的问题,因为STC8是8位的单片机,它的寄存器也是8位。所以12位的ADC转换结果必须要用两个寄存器才能存的下来。然而两个寄存器一共有16位,那势必会有4位数据的空缺。于是就有:
靠右对齐和靠左对齐两种,命名得很传神,就好比word工具里的靠右对齐和靠左对齐工具一样(微电子大佬谁有兴趣做个居中对齐的ADC?)。
- 靠右对齐的时候,两个寄存器组合在一起,可以直接组装成一个u16型的数据,而且步进值为1。使得读取ADC的方法和读取一个u16变量一样,很方便。
- 靠左对齐的时候,如果你把两个寄存器的值直接组合在一起读的话,是不能直接运算的,因为它的步进值是16。我思来想去,觉得设计的工程师应该是这样考虑的:一个ADC测量电压的时候,总会遇到干扰和杂波。正常情况下这些干扰值不会太大,那么如果直接把低位舍弃掉,就相当于把干扰值舍弃掉了。所以在ECBM库中,如果要用左对齐的话,最好要把左对齐和舍弃低位选项一起用。
如果真的舍弃低位,12位ADC就值剩下8位了。会是什么效果呢?
可以看到虽然不那么容易变动了,但是动一次就要变0.013V。我把他们做成图像放在下面,可以直观的理解“12位舍弃低位的8位ADC”和“原生12位ADC”的区别:
可以看到舍弃了低4位之后,数据平缓的地方变多了,这就是一部分干扰被舍弃掉了。同时也发现8位ADC只要变一下,幅度就会很大,这是因为8位ADC的最小分辨率不够的原因。所以还是推荐大家多用12位吧。
ADC的具体应用实在是太多太多,将会在以后的实例篇和进阶篇提到,敬请期待。
预告:下一篇将会解说模拟和数字的另一个桥梁——CMP。