当我第一次学习51单片机的AD采集时,网上找到的资源参差不齐,当然最终还是理解了工作原理,每一行代码是什么意思,但是当我过段时间想要再次梳理一遍的时候我发现我并没有过系统的整理,导致我想要再系统的学习就会略显吃力。
所以我准备以我自己的理解的尽量的让这个文章通俗易懂,无论您是第一次接触这方面的小白也好,或是这方面的技术大牛仅仅是想再回想一下AD采集里的某一个细节,都可以在这篇文章中有所收获,这也是我写这篇文章的初衷了。
可能在第一次学习到ADC对这方面或多或少的有些蒙,那么让我们先从字面意思来理解一下吧:ADC(Analog-to-Digital Converter)是用于将模拟形式的连续信号转换为数字形式的离散信号的一类设备。
让我们总结一下:
在仪器检测系统中,常常需要将检测到的连续变化的模拟量如:温度、压力、流量、速度等转换为离散的数字量,才能进行计算处理。
这些模拟量通过传感器转换为电信号后,就需要通过一定的处理变成数字量,实现模拟量到数字量转换的设备,我们通常称为ADC,也叫做A/D。
看完之后您可能会想知道这ADC它的工作原理是如何实现的,或者是我该如何快速的使用它呢?先别着急让我们继续看下去!
提示:如果您仅仅是想要知道我该如何使用ADC,那么可以直接跳过这一段。
在这一段中我想把我对ADC工作原理的理解系统的展示出来,它或许会让您对ADC工作原理能有一个更清晰的认知,如果真是这样也达到了我的目的所在了。
采集数据其实并不算是ADC的工作,ADC的工作是将采集到的模拟信号转换为数字信号,这我就不过多赘述了。
采集数据需要用到相应的传感器,那什么是传感器呢?
传感器就是能够检测相应的环境因素来输出相应的数据。
其实传感器有很多种,返回的有模拟量也有的返回数据量,而我们所要用到的当然就是返回模拟量的传感器啦!
传感器通过采集到的环境值来返回一个电压信号时,这时候就需要我们ADC来将返回过来的电压信号转变为我们可以运算的数据了。
这段内容是为下面的数据转换做个基础
下面来说一个例子:
一个温度传感器在0℃的时候所对应产生2.5V的电压,那么单片机是无法直接知道此时的电压是2.5v的,单片机本身只能知道电平是高还是低。因此这个2.5V的电压就需要经过ADC转换为数字量,如果是用8位分辨率的ADC、参考电压为5V,那么转换结果就是127,也就是0x7F,这样的话,单片机就可以判断这个温度是否过高或者过低,就能进行运算和控制了。
表明这个该AD采集共有2n个刻度,也就是说如果一个8位的AD,它最终输出的数值是在0~255之间的值。
分辨率是AD采集数据输出数值的单位,例如5.1V的参考电压,系统采用8位的AD,那么分辨率就是:5.1/255 = 0.02V。
它表示了ADC器件在所有的数值上对应的模拟值,和真实值之间误差最大的那一点的误差值。单位:LSB 比如:1LSB,测得数字值是100,对应电压是2v,实际电压可能是1.98V~2.02V。
它表示了ADC相邻两刻度之间最大的差异,比如DNL= 2LSB,测量A点电压度数是100,那么代表了2V,B点电压是200,代表4V,表面看他们相差4-2=2V,实际相差大概是1.96V~2.04V。
是指完成一次从模拟转换到数字的所需要的时间的倒数。也就是一秒内可以转换多少次。
因为本人平常用的都是STC12C5A60S2的单片机,那么这里就拿该单片机举例
下面是STC12C5A60AD/S2系列单片机的A/D转换介绍
STC12C5A60AD/S2系列带A/D转换的单片机的A/D转换口在P1 口(P1.7~P1.0),有8路10 位高速A/D转换器,速度可达到250KHz(25万次/秒)。8路电压输入型A/D,可做滁度检测、电池电压检测、按键扫描、频谱检测等。上电复位后P1口为弱上拉型I/0口,用户可以通过软件设置将8路中的任何一路设置为A/D转换,不需作为A/D使用的口可继续作为I/0口使用。
下面是在单片机中所用到的寄存器,了解了ADC相关的寄存器能够极大的帮助我们学会如何使用单片机的ADC。
在单片机采集信号处理数据的原理的图中我们已经看见了ADC_CONTR这个寄存器的结构了,下面就让我们看看它到底是什么意思!
- 建议进入空闲模式和掉电模式前,将ADC电源关闭,可降低功耗。
- 启动A/D转换前一定要确认A/D电源已打开,A/D转换结束后关闭A/D电源可降低功耗,也可不关闭。
- 初次打开内部A/D转换模拟电源,需适当延时,等内部模拟电源稳定后,再启动A/D转换。
SPEED1 | SPEED0 | ADC时钟周期 |
---|---|---|
0 | 0 | 90 |
0 | 1 | 180 |
1 | 0 | 360 |
1 | 1 | 540 |
转换标志位:每次当AD转换结束完成后,这个位就会自动置"1",需要手动将这一位重新置"0"。
转换启动位:每当手动将其置"1"后,AD转换开始,当AD转换结束后这个位就会自动置"0"。
CHS2 | CHS1 | CHS0 | ADC输入通道 |
---|---|---|---|
0 | 0 | 0 | ADC0(P1^0) |
0 | 0 | 1 | ADC1(P1^1) |
0 | 1 | 0 | ADC2(P1^2) |
0 | 1 | 1 | ADC3(P1^3) |
1 | 0 | 0 | ADC4(P1^4) |
1 | 0 | 1 | ADC5(P1^5) |
1 | 1 | 0 | ADC6(P1^6) |
1 | 1 | 1 | ADC7(P1^7) |
P1ADF寄存器:就是专门的选择ADC转换的引脚
注:ADC_CONTR的后三位(CSH0、CSH1、CSH2)也是选择AD转换引脚的,所以在配置的时候这两个寄存器得相互对应起来才行
比如我想让P1^7口作为ADC口,那么让P1ADF中的P1^7置为1,其他位置为0即可。
例:假如我想配置P17为AD输入,那就让ADC_CONTR = 7就行了
因为十进制的7正好对应二进制的0111;
而P1ADF寄存器就简单的用P1ADF = (0x01 << 7);这样就行了。
这个要结合下一个ADC结果寄存器一同理解,其实很简单,就是改变了一下存储数值的方式。
该寄存器用来存放ADC转换后的结果值,有十位八位之分。
该寄存器用来控制单片机的各种中断。
在此只介绍与ADC相关的寄存位。
EA的作用是使中断允许形成多级控制。即各中断源首先受EA控制;其次还受各中断源自己的中断允许控制位控制。
EADC的作用就是用来控制AD转换的开启与停止。
这个用来设置单片机端口的IO状态,不仅仅是P1口其他口同理
P1M1[7:0] | P1M0[7:0] | I/O 口模式 |
---|---|---|
0 | 0 | 准双向口(传统8051 IO口模式,弱上拉),灌电流可达20mA,拉电流为270uA,由于制造误差,实际为270uA~150uA |
0 | 1 | 推挽输出(强上拉输出,可达20mA,要加限流电阻) |
1 | 0 | 高阻输入(电流既不能流入也不能流出) |
1 | 1 | 开漏(Open Drain),内部上拉电阻断开。开漏模式既可读外部状态也可对外输出(高电平或低电平)。如要正确读外部状态或需要对外输出高电平,需外加上拉电阻,否则读不到外部状态,也对外输不出高电平。 |
当然如果您是第一次听,那就泛泛的了解一下就行了。并不会影响我们对后面的理解。
看完之后您可能已经大概的了解ADC它的工作原理是如何实现的,那么你可能就要想我应该如何快速的使用它呢?下面就让我们看下它的代码是怎么写的吧!
提示:下面代码会分为:初始化ADC函数的代码理解、采集ADC函数的代码理解、读入数据的代码理解;三部分组成。
注:本代码为8位ADC,并且初始化采集口为P1^0~P1^2三个采集口。
代码如下:
/* ADC初始化函数*/
void Init_ADC()
{
//开启了P1.0,P1.1,P1.2 三路ADC,0000 0111->0x07
P1 = P1M0 = P1M1 = 0x07; // 0x07 ==> 0000 0111
//清除以前结果
ADC_RES = 0;
//ADC_POWEER:为宏定义(#define ADC_POWEER 0x80)及 1000 0000
//ADC_SPEEDLL:为宏定义(#define ADC_SPEEDLL 0X00)用来控制单片机ADC的转换速度
ADC_CONTR = ADC_POWER | ADC_SPEEDLL;
//加上延时函数 ADC通电和延迟
Delay(2);
}
代码如下:
/*返回值类型为char类型 其中uchar是typedef unsigned char uchar*/
uchar GetADCResult(uchar ch)
{
//ADC_POWER:为宏定义(#define ADC_POWEER 0x80)及 1000 0000
//ADC_SPEEDLL:为宏定义(#define ADC_SPEEDLL 0X00)用来控制单片机ADC的转换速度
//ch:为输入的形参,表示进行P1中哪一个端口的ADC 范围在(0~2【与初始化几个端口有关】)
//ADC_START:为宏定义(#defien ADC_START 0X08)转换启动位:
// 每当手动将其置"1"后,AD转换开始,当AD转换结束后这个位就会自动置"0"。
ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ch | ADC_START;//开启ADC,采集P1^ch引脚的值
_nop_();
_nop_();
_nop_();
_nop_(); //给予四个时钟周期的延迟
//ADC_FLAG:为宏定义(#define ADC_FLAG 0X10)
while (!(ADC_CONTR & ADC_FLAG)); //检测是否ADC完成
ADC_CONTR &= ~ADC_FLAG; //转换标志位:手动将其置0
//ADC_RES为ADC转换结果寄存器,存储转换后的结果
return ADC_RES; //将转换后的结果输出
}
代码如下:
#include
void main(){
char ad_Data0,ad_Data1,ad_Data2;
EA = 0;// 关闭总中断
Init_ADC();//ADC初始化
EA = 1;//开启总中断
while(1){
ad0 = GetADCResult(0);//采集P1^0的AD值
ad1 = GetADCResult(1);//采集P1^1的AD值
ad2 = GetADCResult(2);//采集P1^2的AD值
Delay(100);//延时缓冲
}
}
本文章对单片机内部ADC使用的知识进行了相应的梳理,如果有任何疑问的地方欢迎在文章下面留言,我都会一一回复的,希望本篇文章能有或多或少的帮助到您~