单片机是一个典型的数字系统,数字系统只能呢个对输入的数字信号进行处理,其输出信号也是数字的。但工业或者生活中的很多量都是模拟量,这些模拟量可以通过传感器变成与之对应的电压、电流等模拟量。为了实现数字系统对这些电模拟量的测量,运算和控制,就需要一个模拟量和数字量之间的相互转化的过程。
A/D和D/A的基本概念
A/D是模拟量到数字量的转换,依靠的是模数转换器(Analog to Digital Converter),简称ADC。D/A是数字量到模拟量的转换,依靠的是数模转换器(Digital to Analog Converter),简称DAC。它们的道理是完全一样的,只是转换方向不同,因此我们讲解过程主要以A/D为例来讲解。
什么是模拟量?就是指变量在一定范围内连续变化的量,总之,任何两个数字之间都有无限个中间值,所以称之为连续变化的量,也就是模拟量。ADC就是起到把连续的信号用离散的数字表达出来的作用
A/D的主要指标
我们在选取和使用A/D的时候,依靠什么指标很重要。由于AD的种类很多,分为积分型,逐次逼近型,并行/串行比较型,Σ-Δ型等多种类型。同时指标也比较多,并且有的指标还有轻微差别。
1.ADC的位数
一个n位的ADC表示这个ADC共有2的n次方个刻度。8位ADC,输出的是从0-255的256个数字,也就是2的8次方的一个数据刻度。
2.基准源
基准源也叫基准电压,是ADC的1个重要标准,要想把输入ADC的信号测量准确,那么基准源首先要准,基准源的偏差会导致转换结果的偏差比如一根米尺,总长度本应该是1米,假定这根米尺被火烤了一下,实际变成了1.2米,再用这根米尺测物体长度的话自然就有了较大的偏差。假如我们的基准源应该是5.10V,但是实际上提供的却是4.5V,这样误把4.5V当成了5.10V来处理的话,偏差也会比较大。
3.分辨率
分辨率是数字量变化的一个最小刻度时,模拟信号的变化量,定义为满刻度量程与2n-1的比值。假定5.10V的电压系统,使用8位的ADC进行测量,那么相当于0-255一共256个刻度把5.10V划分成255份,那么分辨率就是5.10/255=0.02V.
INL(积分非线性度)和DNL(差分非线性度)
一般容易混淆两个概念就是分辨率和精度,认为分辨率越高,则精度越高,而实际上,两者并没有必然的联系。分辨率是用来描述刻度划分的,而精度是用来描述准确程度。同样一根米尺,刻度数相同,分辨率就相当,但是精度却可以相差很大,ADC精度关系重大的两个指标是INL(Integral NonLiner)和DNL(Differencial NonLiner)。
INL指的是ADC器件在所有的数值上对应的模拟值,和真实值之间的误差最大的哪一个点的误差值,是ADC最重要的一个精度标准,单位是LSB。LSB是最低有效位的意思,那么实际上对应的就是ADC的分辨率。一个基准为5.10V的8位ADC,它的分辨率就是0.02V,用它去测量一个电压信号,得到的实际结果是100,就是表示它测到的电压值是100*0.02=2V,假定它的INL是1LSB,就是表示这个电压信号真实的准确值是在1.98V——2.02之间的,按理想情况对应的数字因该是99-101,测量误差是一个最低的有效位,即1LBS。
DNL表示的是ADC相邻两个刻度之间最大的差异,单位也是LSB。一把分辨率是1毫米的尺子,相邻的刻度之间并不都刚好是一毫米,而总是会存在或大或小的误差。同理,一个ADC的两个刻度线之间也不总是准确的等于分辨率,也是存在误差,这个就是DNL。一个基准为5.10V的8位ADC,假定DNL是0.5LSB,那么当它的转换结果从100增加到101时,理想情况下实际电压应该增加0.02V,但DNL为0.5LSB的情况下实际电压为0.01——0.03V之间,值得一提的是DNL并非一定小于1LSB,很多时候它会等于或者大于1LSB,这在相当一定程度上刻度紊乱,当实际电压保持不变时,ADC得出的结果可能会在几个数值之间跳动,很大程度上由于这个原因(但并不完全是,因为还有很多干扰)。
5.转换速率
转换速率,是指ADC每秒能进行采样转换的最大次数,单位是sps(或s/s、sa/s,即samples per second),它与ADC完成一次从模拟到数字的转换所需要的时间互为倒数关系。ADC的种类比较多,其中积分型的ADC转换时间是毫秒级的,属于低速ADC;逐次逼近型ADC转换时间是微妙级的,属于中速ADC;并行/串行的ADC的转换时间可达到纳秒级,属于高速ADC。
ADC的这几个主要指标大家先熟悉一下,对于其它的,作为一个入门级别的选手来说,先不着急深入理解。以后使用过程中遇到了,再查找相关资料深入学习,当前重点是在头脑中建立一个ADC的基本概念。
PCF8591
PCF8591是一个单电源低功耗的8位CMOS数据采集器件,具有4路模拟输入,1路模拟输出和一个串行I2C总线接口用来与单片机通信。引脚1、2、3、4是4路模拟输入,引脚5、6、7是I2C总线的硬件地址,8脚是数字地GND,9脚和10脚是I2C总线的SDA和SCL。12脚是时钟选择引脚,如果接高电平表示用外部时钟输入,接低电平则用内部时钟,我们这套电路用的是内部时钟,因此12脚直接接GND,同时11脚悬空。13脚是模拟地AGND,在实际开发中,如果有比较复杂的模拟电路,那么AGND部分在布局布线上要特别处理,而且和GND的连接也有多种方式,这里大家先了解即可。在我们板子上没有复杂的模拟部分电路,所以我们把AGND和GND接到一起。14脚是基准源,15脚是DAC的模拟输出,16脚是供电电源VCC。
PCF8591的ADC是逐次逼近型的,转换速率算是中速,但是它的速度瓶颈在I2C通信上。由于I2C通信速度较慢,所以最终的PCF8591的转换速度,直接取决于I2C的通信速率。由于I2C速度的限制,所以PCF8591得算是个低速的AD和DA的集成,主要应用在一些转换速度要求不高,希望成本较低的场合,比如电池供电设备,测量电池的供电电压,电压低于某一个值,报警提示更换电池等类似场合。
软件编程
PCF8591的通信接口是I2C,那么编程肯定是要符合这个协议的。单片机对PCF8591进行初始化,一共发送三个字节即可。第一个字节,和EEPROM类似,是器件地址字节,其中7位代表地址,1位代表读写方向。地址高4位固定是0b1001,低三位是A2,A1,A0,这三位我们电路上都接了GND,因此也就是0b000,发送到PCF8591的第二个字节将被存储在控制寄存器,用于控制PCF8591的功能。其中第3位和第7位是固定的0,另外6位各自有各自的作用。
控制字节的第6位是DA使能位,这一位置1表示DA输出引脚使能,会产生模拟电压输出功能。第4位和第5位可以实现把PCF8591的4路模拟输入配置成单端模式和差分模式,单端模式和差分模式的区别,我们在17.5节有介绍,这里大家只需要知道这两位是配置AD输入方式的控制位即可
控制字节的第2位是自动增量控制位,自动增量的意思就是,比如我们一共有4个通道,当我们全部使用的时候,读完了通道0,下一次再读,会自动进入通道1进行读取,不需要我们指定下一个通道,由于A/D每次读到的数据,都是上一次的转换结果,所以同学们在使用自动增量功能的时候,要特别注意,当前读到的是上一个通道的值。为了保持程序的通用性,我们的代码没有使用这个功能,直接做了一个通用的程序。
控制字节的第0位和第1位就是通道选择位了,00、01、10、11代表了从0到3的一共4个通道选择。
发送给PCF8591的第三个字节D/A数据寄存器,表示D/A模拟输出的电压值。D/A模拟我们一会介绍,大家知道这个字节的作用即可。我们如果仅仅使用A/D功能的话,就可以不发送第三个字节。
/* 读取当前的ADC转换值,chn-ADC通道号0~3 */
unsigned char GetADCValue(unsigned char chn)
{
unsigned char val;
I2CStart();
if (!I2CWrite(0x48<<1)) //寻址PCF8591,如未应答,则停止操作并返回0
{
I2CStop();
return 0;
}
I2CWrite(0x40|chn); //写入控制字节,选择转换通道
I2CStart();
I2CWrite((0x48<<1)|0x01); //寻址PCF8591,指定后续为读操作
I2CReadACK(); //先空读一个字节,提供采样转换时间
val = I2CReadNAK(); //读取刚刚转换完的值
I2CStop();
return val;
}
DAC的输出
此处输入代码/* 设置DAC输出值,val-设定值 */
void SetDACOut(unsigned char val)
{
I2CStart();
if (!I2CWrite(0x48<<1)) //寻址PCF8591,如未应答,则停止操作并返回
{
I2CStop();
return;
}
I2CWrite(0x40); //写入控制字节
I2CWrite(val); //写入DA值
I2CStop();
}