EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册  
 
x
 
首先思考一个问题,我们的世界是数字的还是模拟的?      当然是模拟的了,所有的量都是在一定范围内连续变化的。我们为了能够更加方便的描述这些量,对它们进行了数字化。而数字量就不一样了,它是分立的的几个值。       举个例子,我们形容一个人的身高,模拟的说法是一米七到一米七五之间,数字的说法就是一米七三。 接下来说AD转换器,它的出现也是为了让我们能更方便、更直接的描述电压的高低。AD转换器,英文全称为Analog-to-Digital Converter,是模拟量到数字量的一个转换过程,主要用于电压的采集。它的出现就如同有了一把尺子,很容易就能量出电压的高低。 在电子设备中,经常要检测各种模拟量:温度、压力、速度、流量、重力加速度等等,这些模拟量都被相应的传感器转换为电压信号,我们只需要测量电压的高低,就能得到相应参数。 AD的主要参数有哪些? 1、AD的位数:表明这个AD共有2^n个刻度,8位AD,输出的刻度是0~255. 2、分辨率:就是AD能够分辨的最小的模拟量变化,假设5.10V的系统用8位的AD采样,那么它能分辨的最小电压就是5.10/255=0.02V。 3、INL:Interger NONliner 积分非线性度,表示了ADC器件在所有的数值点上对应的模拟值,和真实值之间误差最大的那一点的误差值。也就是,输出数值偏离线性最大的距离。单位是LSB(即最低位所表示的量)。比如12位ADC:TLC2543,INL值为1LSB。那么,如果基准4.095V,测某电压得的转换结果是1000,那么,真实电压值可能分布在0.999~1.001V之间。 4、DNL:Differencial NonLiner-差分非线性度,理论上说,模数器件相邻量个数据之间,模拟量的差值都是一样的。就相一把疏密均匀的尺子。但实际并不如此。一把分辨率1毫米的尺子,相邻两刻度之间也不可能都是1毫米整。那么,ADC相邻两刻度之间最大的差异就叫差分非线性值(Differencial NonLiner)。DNL值如果大于1,那么这个ADC甚至不能保证是单调的,输入电压增大,在某个点数值反而会减小。这种现象在SAR(逐位比较)型ADC中很常见。 5、基准源:有内部基准源、外部基准源等等。 6、转换速率:也就是转换周期的倒数,转换周期就是完成一次AD转换所需的时间。  
3 a* Z* d% ~/ e$ L; ?9 z% y, @* [今天要用到的器件是PCF8591,为什么选它?太多的开发板上用它做演示了,而且还是IIC总线通信的。既学习了AD采样,又学习了IIC总线。 先上应用电路:  
  q1 F4 e" R3 V9 _# n3 r7 H      如上图所示,PCF8591的9脚和10脚,一个是数据线SDA,一个是时钟线SCL。分别接到 单片机的P2.0 , P2.1上面。 为什么选这两个引脚?因为51单片机上没有IIC总线接口,需要用普通的IO模拟,所以它随便选了两个IO接上就行。 VREF是什么?基准电压,也是它能测量的最大电压。 如何控制?今天先不说IIC总线,只说控制流程。 看器件手册可以知道: 
 
 
$ r# ]7 K$ R: F: l* A7 F) j5 B分四步: 1、发送地址字节,选择该器件。 2、发送控制字节,选择相应通道。               // 3、重新发送地址字节,选择该器件。 4、接收目标通道的数据。 4 _5 }3 [8 k; c( h+ x 
这次的程序流程是:AD采样,串口发送,循环执行。 下面是AD采样源代码:  
2 S) L9 T+ G) d9 l8 B. K  s------------------------------------------------------------------------------------- 4 X3 J+ l1 j) H$ Y2 S 
下面介绍PCF8591的DA输出: 
# Y1 a5 o0 w8 Y0 |- H* o, X) y% a+ h- g 
       忽然发现,已经写到AD/DA这里来了。严格来说,已经不是51单片机的内容了,而是周边应用电路的一些东西。这些东西涉及的知识面比较广,什么都有可能提到。       关于AD/DA,或者其它设备,我的学习思路是先模仿,再深究。       因为无论是课本也好,器件手册也好,大部分讲的都是原理或者寄存器,起到的是一个工具书的作用,类似于语文课上用的字典。但是这就出现了一个问题,很多人想通过看课本或者看器件手册的方式来掌握这些设备。       这个思路有问题吗?没有问题吗?       还记得我刚才说的话么,它们就类似于语文课上用的字典,但是,有谁是通过看字典学会说话的!!!       我们都是通过模仿别人学会说话的,遇到不认识的字才去查字典!但是很多人或者很多学校都把这两件事的顺序搞反了。       记得之前我在英飞凌官网进行芯片选型,网页都翻烂了,找不到合适的。因为英飞凌不是我家开的,我不能保证每次都能顺利的找到我想要的东西。       但是,我同事参加了一次电子展,在展会上遇到了英飞凌的展台,然后问他们,他们一听我们的需求,马上找出一堆能满足我们要求的芯片。       这就是思路的问题!       扯远了,说回到DA控制。 DA转换(Digital to Analog),是将数字量变成模拟量的一个过程。AD与DA刚好是相反的两个过程,AD是把模拟信号变成单片机可识别的数字信号;DA是把单片机可识别的数字信号变成连续变化的模拟量。这两种功能的应用范围都非常广泛!       主要参数如下,具体什么意思就不讲了,大家可以百度一下。(因为我编不出来了...) 1)分辩率(Resolution) 2) 转换速率(Conversion Rate) 3)量化误差 (Quantizing Error) 4)偏移误差(Offset Error) 5)满刻度误差(Full Scale Error) 6)线性度(Linearity) 其他指标还有:绝对精度(Absolute Accuracy) ,相对精度(Relative Accuracy),微分非线性,单调性和无错码,总谐波失真(Total H ARMonic Distotortion缩写THD)和积分非线性。 看到这么多参数,是不是很晕? 搞了这些年电子,感触最深的有一点是:无论做什么,先求有,再求好! 不要总想一口吃个胖子,没那么多天才。参数是很多,但是没要求你一下子全都记住,甚至你可以只记一两个。先把大致的应用流程跑一遍,跑下来,你才对这个设备有一个整体的概念,然后针对你的要求,比对相应的参数,进行修改、调试。 哪怕是在工作中,也不一定会考虑全部的参数。例如转换时间,我到现在也没认真看PIC内部的AD采样转换时间有多久,因为有些设备对实时性要求很低,速度慢一些也没事。 然后是控制流程,认真看器件手册的,或者看了昨天日志的,都知道是怎样一个流程: 第一步:写器件地址; 第二步:写控制位。 第三步:写入数据。 4 E: |& K  ^4 X) M' | 
好了,上程序。通过DA输出渐变电压控制LED,形成呼吸灯的效果。里面有个警告: *** WARNING L16: UNCALLED SEGMENT, IGNORED FOR OVERLAY PROCESS       大家可以研究下,如何消除警告。 程序源码如下: - /**********************51单片机学习例程************************
 - *  平台: Keil U34 + STC89C52RD
 - *  名称:IIC协议 PCF8591ADDA转换        ,此程序通过IIC协议对DAAD芯片操作, 并输出模拟量,用LED亮度渐变指示
 - *  编写:eda365
 - *  晶振:11.0592MHZ
 - ******************************************************************/
 -  #include<reg52.h>    //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
 -  #include <intrins.h> //包含NOP空指令函数_nop_();
 -  #define AddWr 0x90   //写数据地址
 -  #define AddRd 0x91   //读数据地址
 -  sbit RST=P2^4;   //关掉时钟芯片输出
 -  sbit Sda=P2^0;      //定义总线连接端口
 -  sbit Scl=P2^1;
 -  sbit Fm=P2^3;          //FM
 -  sbit dula=P2^6;
 -  sbit wela=P2^7;
 - // bit ADFlag;          //定义AD采样标志位
 - unsigned char code Datatab[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//7段数共阴码管段码表
 - data unsigned char  Display[8];//定义临时存放数码管数值
 - /*------------------------------------------------
 -                     延时程序
 - ------------------------------------------------*/
 -  void mDelay(unsigned char j)
 -  {
 -   unsigned int i;
 -   for(;j>0;j--)
 -      {
 -           for(i=0;i<125;i++)
 -              {;}
 -           }
 -   }
 - /*------------------------------------------------
 -                     初始化定时器1
 - ------------------------------------------------*/
 - void Init_Timer1(void)
 - {
 -  TMOD |= 0x10;
 -  TH1=0xff;                              /* Init value */
 -  TL1=0x00;
 -  //PT1=1;                   /* 优先级    */
 -  EA=1;                      /* interupt enable */
 -  ET1=1;                     /* enable timer1 interrupt */
 -  TR1=1;
 - }
 - /*------------------------------------------------
 -                     启动IIC总线
 - ------------------------------------------------*/
 -   void Start(void)
 -   {
 -    Sda=1;
 -    _nop_();
 -    Scl=1;
 -    _nop_();
 -    Sda=0;
 -    _nop_();
 -    Scl=0;
 -   }
 - /*------------------------------------------------
 -                     停止IIC总线
 - ------------------------------------------------*/
 -   void Stop(void)
 -   {
 -    Sda=0;
 -    _nop_();
 -    Scl=1;
 -    _nop_();
 -    Sda=1;
 -    _nop_();
 -    Scl=0;
 -    }
 - /*------------------------------------------------
 -                    应答IIC总线
 - ------------------------------------------------*/
 -    void Ack(void)
 -    {
 -     Sda=0;
 -         _nop_();
 -         Scl=1;
 -         _nop_();
 -         Scl=0;
 -         _nop_();
 -         }
 - /*------------------------------------------------
 -               发送一个字节
 - ------------------------------------------------*/
 -          void Send(unsigned char Data)
 -          {
 -           unsigned char BitCounter=8;
 -           unsigned char temp;
 -           do
 -             {
 -                  temp=Data;
 -                  Scl=0;
 -                  _nop_();
 -                  if((temp&0x80)==0x80)
 -                     Sda=1;
 -                  else
 -                     Sda=0;
 -                         Scl=1;
 -                         temp=Data<<1;
 -                         Data=temp;
 -                         BitCounter--;
 -                   }
 -           while(BitCounter);
 -               Scl=0;
 -           }
 - /*------------------------------------------------
 -                     写入DA数模转换值
 - ------------------------------------------------*/
 -           void DAC(unsigned char Data)
 -           {
 -                    Start();
 -                    Send(AddWr); //写入芯片地址
 -                    Ack();
 -                    Send(0x40);  //写入控制位,使能DAC输出
 -                    Ack();
 -                    Send(Data);  //写数据
 -                    Ack();
 -                    Stop();
 -            }
 -         void fmg(void)//fm关
 -         {
 -         Fm=1;        //                关 fm
 -         }
 -                  void cmg(void)//数码管锁存函数                   关时钟DS1302
 -         {
 -         dula=1;
 -         P0=0x00;
 -         dula=0;
 -         wela=1;
 -         P0=0x00;
 -         wela=0;
 -         RST=0;                //                关时钟DS1302
 -         }
 - /*------------------------------------------------
 -                    主程序
 - ------------------------------------------------*/
 -         void main()
 -         {
 -          unsigned char num;                   //DA数模输出变量
 -    Init_Timer1();
 -          cmg();//数码管锁存
 -          fmg();
 -          while(1)
 -            {
 -        DAC(num);       //DA输出,可以用LED模拟电压变化
 -                    num++;          //累加,到256后溢出变为0,往复循环。显示在LED上亮度逐渐变化
 -                    mDelay(20);     //延时用于清晰看出变化
 -            }
 -         }
 
9 a- A7 q1 e! _, a% o& l$ y 
  
! Y% K0 [2 X0 U6 W) ^- y2 J 
( \) W  C4 d3 w5 Y0-0' R6 A4 e( t; u! m& N" P/ s 
 
9 @4 k2 f$ T+ |) |# X. j# M5 i |