32f4怎样同时采集两个adc_ECBM系列教程8:数字单片机也能感受模拟世界——ADC

我们的世界是一个模拟量和数字量共存的世界,一支笔有没有墨水是数字量,有多少ml墨水又是模拟量。数字和模拟是可以转换的。比如一支笔,从数字的角度看0代表没墨水,1代表有墨水。从模拟的角度来看,0ml代表没墨水,0.1ml到3ml都算有墨水。那么就能这样从模拟量映射到数字量:0ml->0,(0.1~3ml)->1。

以上的例子就是一个最简单的1位ADC了。ADC全名叫Analog-to-Digital Converter,即模拟数字转换器。我之前的回答

ADC0809究竟是输入什么,输出什么?​www.zhihu.com
434d1fcbccc61bf930b1afdf30244015.png

有解说过ADC的一种实现结构,大家可以移步去看看。

在单片机应用里,获取到模拟量主要是电压值。很多传感器会把某种物理量通过某种方法变成电压值,然后单片机读取电压值就能感知外界的变化。

例如温度:一个NTC电阻,它的组成材料是对热量敏感的。当外界温度变化的时候,会引起NTC电阻阻值的变化。而通过电阻分压电路,可以把电阻变化变成电压变化。最后被单片机ADC转换成数字量。这个数字换算一下就是温度值了。整个流程就是:环境温度->电阻值->电压值->电压值的数字量->温度值的数字量。

不过STC8单片机并不是所有型号都带ADC,所以就算ECBM库里有ADC库,也要有带ADC的单片机搭配才行。我的另一篇文章有解说STC8型号的资源,有兴趣可以了解一下。

大杰:教你如何分清STC8的型号​zhuanlan.zhihu.com

我们这次用的平台是ECBM 8051板,基于STC8A8K64SA12单片机,取这个名字是因为它的外形大小和引脚排列和8051的40脚单片机一模一样(板载一个0.42寸OLED屏,可以显示一些东西),并且还带了12位ADC。

5c07e587300bf92c9dd3cc492dcc6758.png

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值带入公式就能知道被测电压是多少。


cdaab85df5afed5d07d28934ffbebb4c.png

在开始配置ADC之前,先在ecbm_core.h中把单片机的型号设置成STC8A8K64S4A12。

beb46b2a76a98f4faf0a232281cf6bd5.png

然后使能ADC库,接着打开adc.h文件,进入该文件的可视化界面:

d71cc352749adc2aaa5a41ae46905c5d.png

STC8A一共有15个通道,这里只使能ADC的通道0,在STC8A8K64S4A12中对应P1.0脚。如果是STC8G或者STC8C系列的话就不一定是P1.0脚,请注意甄别。

dc65d53e9bbec5192bb3c0759d2b0f30.png

参数方面,分频系数用于调节ADC的采样率,默认为0即可。对齐方式和舍弃低位稍后再说。

可用的函数有:

e7066459bd7017cc319e48668e4a19b1.png

初始化函数,使用ADC之前都要调用的函数。

9dcd709a536b3aa483425e2cb6e2abc1.png

ADC读取函数,这个函数用于查询法读取ADC转换后的AD值。如果不舍弃位数的话,该函数返回的是u16型。如果舍弃低位的话,返回的是u8型。

d945f48d153ee61378e13492f9d6c62d.png

除了查询法外,也可以用中断法。相关函数就是这两个,在主函数中调用adc_read_start,然后执行其他语句···,然后在ADC中断里执行adc_read_it函数读取就行。

同时ADC中断的开关由这两个函数控制:

2fdde3cab83b3f354aa4c6e6f3df9208.png

我们可以读取AD值后,带入上面的公式求出被测电压,不过现在有这个函数可以一步到位了,那就是adc_voltage。该函数只需你提供Vref的实测值和采集通道号,就能返回被测电压值。

f77005a5e15f84dc48bd59f2738d09b7.png

于是我们的示例代码这样写:

#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。如下图所示。

d5a0a06f5ce0d6be6e3deaeac0127faa.png

编译下载之后,打开串口助手,我们能看到如下场景:

ddb2604d28ceb39a766ad8b447ff96f6.png

都在2.5V附近,看来这效果是非常好的。


然后我们来回顾数据对齐的问题,因为STC8是8位的单片机,它的寄存器也是8位。所以12位的ADC转换结果必须要用两个寄存器才能存的下来。然而两个寄存器一共有16位,那势必会有4位数据的空缺。于是就有:

a0c132bfb604b6adbe5bc54ccb76bff6.png

靠右对齐和靠左对齐两种,命名得很传神,就好比word工具里的靠右对齐和靠左对齐工具一样(微电子大佬谁有兴趣做个居中对齐的ADC?)。

  • 靠右对齐的时候,两个寄存器组合在一起,可以直接组装成一个u16型的数据,而且步进值为1。使得读取ADC的方法和读取一个u16变量一样,很方便。
  • 靠左对齐的时候,如果你把两个寄存器的值直接组合在一起读的话,是不能直接运算的,因为它的步进值是16。我思来想去,觉得设计的工程师应该是这样考虑的:一个ADC测量电压的时候,总会遇到干扰和杂波。正常情况下这些干扰值不会太大,那么如果直接把低位舍弃掉,就相当于把干扰值舍弃掉了。所以在ECBM库中,如果要用左对齐的话,最好要把左对齐和舍弃低位选项一起用。

如果真的舍弃低位,12位ADC就值剩下8位了。会是什么效果呢?

718108bc15a8950bd173ab120911ab69.png

可以看到虽然不那么容易变动了,但是动一次就要变0.013V。我把他们做成图像放在下面,可以直观的理解“12位舍弃低位的8位ADC”和“原生12位ADC”的区别:

a58c09068027259d23b7390e30fa159c.png

可以看到舍弃了低4位之后,数据平缓的地方变多了,这就是一部分干扰被舍弃掉了。同时也发现8位ADC只要变一下,幅度就会很大,这是因为8位ADC的最小分辨率不够的原因。所以还是推荐大家多用12位吧。


ADC的具体应用实在是太多太多,将会在以后的实例篇和进阶篇提到,敬请期待。

预告:下一篇将会解说模拟和数字的另一个桥梁——CMP。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值