找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 10715|回复: 7
打印 上一主题 下一主题
收起左侧

51单片机仿真PCF8591数字电压表(程序带详细注释)电压0-5V

  [复制链接]
跳转到指定楼层
楼主
在AT89C52系统中采用PCF8591芯片,测量0-5V范围内的直流电压,并在2位数码管上显示电压值。
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)


注释很全的单片机源程序如下:
  1. #include<reg52.h>    //包含单片机寄存器的头文件
  2. #include <intrins.h>

  3. #define  AddWr 0x90    //PCF8591 地址

  4. // 变量定义
  5. unsigned char AD_CHANNEL=0;
  6. unsigned char  D[32];

  7. sbit scl=P2^0;       //I2C  时钟
  8. sbit sda=P2^1;       //I2C  数据
  9. bit ack;                 /*应答标志位*/

  10. unsigned char date;
  11. sbit C1=P2^6;//数码管位选
  12. sbit C2=P2^7;//数码管位选
  13. sbit Dp=P2^5;//小数点
  14. table[10]={0x3f,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};   //共阳极数码管0~9显示的数值表
  15. unsigned int data dis[3]={0x00,0x00,0x00};  //用以计算存储输出电压的3元素数组
  16. /*******************************************************************
  17.                      起动总线函数               
  18. 函数原型: void  Start_I2c();  
  19. 功能:     启动I2C总线,即发送I2C起始条件.  
  20. ********************************************************************/
  21. void Start_I2c()
  22. {
  23.   sda=1;         /*发送起始条件的数据信号*/
  24.   _nop_();
  25.   scl=1;
  26.   _nop_();        /*起始条件建立时间大于4.7us,延时*/
  27.   _nop_();
  28.   _nop_();
  29.   _nop_();
  30.   _nop_();   
  31.   sda=0;         /*发送起始信号*/
  32.   _nop_();        /* 起始条件锁定时间大于4μs*/
  33.   _nop_();
  34.   _nop_();
  35.   _nop_();
  36.   _nop_();      
  37.   scl=0;       /*钳住I2C总线,准备发送或接收数据 */
  38.   _nop_();
  39.   _nop_();
  40. }

  41. /*******************************************************************
  42.                       结束总线函数               
  43. 函数原型: void  Stop_I2c();  
  44. 功能:     结束I2C总线,即发送I2C结束条件.  
  45. ********************************************************************/
  46. void Stop_I2c()
  47. {
  48.   sda=0;      /*发送结束条件的数据信号*/
  49.   _nop_();       /*发送结束条件的时钟信号*/
  50.   scl=1;      /*结束条件建立时间大于4μs*/
  51.   _nop_();
  52.   _nop_();
  53.   _nop_();
  54.   _nop_();
  55.   _nop_();
  56.   sda=1;      /*发送I2C总线结束信号*/
  57.   _nop_();
  58.   _nop_();
  59.   _nop_();
  60.   _nop_();
  61. }

  62. /*******************************************************************
  63.                  字节数据发送函数               
  64. 函数原型: void  I2C_SendByte(UCHAR c);
  65. 功能:     将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
  66.           此状态位进行操作.(不应答或非应答都使ack=0)     
  67.            发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
  68. ********************************************************************/
  69. void  I2C_SendByte(unsigned char  c)
  70. {
  71. unsigned char  i;

  72. for(i=0;i<8;i++)  /*要传送的数据长度为8位*/
  73.     {
  74.      if((c<<i)&0x80)sda=1;   /*判断发送位*/
  75.        else  sda=0;               
  76.      _nop_();
  77.      scl=1;               /*置时钟线为高,通知被控器开始接收数据位*/
  78.       _nop_();
  79.       _nop_();             /*保证时钟高电平周期大于4μs*/
  80.       _nop_();
  81.       _nop_();
  82.       _nop_();         
  83.      scl=0;
  84.     }
  85.    
  86.     _nop_();
  87.     _nop_();
  88.     sda=1;                /*8位发送完后释放数据线,准备接收应答位*/
  89.     _nop_();
  90.     _nop_();   
  91.     scl=1;
  92.     _nop_();
  93.     _nop_();
  94.     _nop_();
  95.     if(sda==1)ack=0;     
  96.        else ack=1;        /*判断是否接收到应答信号*/
  97.     scl=0;
  98.     _nop_();
  99.     _nop_();
  100. }

  101. /*******************************************************************
  102.                  字节数据接收函数               
  103. 函数原型: UCHAR  I2C_RcvByte();
  104. 功能:        用来接收从器件传来的数据,并判断总线错误(不发应答信号),
  105.           发完后请用应答函数应答从机。  
  106. ********************************************************************/   
  107. unsigned char   I2C_RcvByte()
  108. {
  109.   unsigned char  retc=0,i;
  110.   sda=1;                     /*置数据线为输入方式*/
  111.   for(i=0;i<8;i++)
  112.       {
  113.         _nop_();           
  114.         scl=0;                  /*置时钟线为低,准备接收数据位*/
  115.         _nop_();
  116.         _nop_();                 /*时钟低电平周期大于4.7μs*/
  117.         _nop_();
  118.         _nop_();
  119.         _nop_();
  120.         scl=1;                  /*置时钟线为高使数据线上数据有效*/
  121.         _nop_();
  122.         _nop_();
  123.         retc=retc<<1;
  124.         if(sda==1)retc=retc+1;  /*读数据位,接收的数据位放入retc中 */
  125.         _nop_();
  126.         _nop_();
  127.       }
  128.   scl=0;   
  129.   _nop_();
  130.   _nop_();
  131.   return(retc);
  132. }

  133. /********************************************************************
  134.                      应答子函数
  135. 函数原型:  void Ack_I2c(bit a);
  136. 功能:      主控器进行应答信号(可以是应答或非应答信号,由位参数a决定)
  137. ********************************************************************/
  138. void Ack_I2c(bit a)
  139. {  
  140.   if(a==0)sda=0;              /*在此发出应答或非应答信号 */
  141.   else sda=1;                                  /*0为发出应答,1为非应答信号 */
  142.   _nop_();
  143.   _nop_();
  144.   _nop_();      
  145.   scl=1;
  146.   _nop_();
  147.   _nop_();                    /*时钟低电平周期大于4μs*/
  148.   _nop_();
  149.   _nop_();
  150.   _nop_();  
  151.   scl=0;                     /*清时钟线,住I2C总线以便继续接收*/
  152.   _nop_();
  153.   _nop_();   
  154. }

  155. /************************************************************
  156. * 函数名        : Pcf8591_DaConversion
  157. * 函数功能      : PCF8591的输出端输出模拟量
  158. * 输入          : addr(器件地址),channel(转换通道),value(转换的数值)
  159. * 输出                 : 无
  160. ******************* *****************************************/
  161. bit Pcf8591_DaConversion(unsigned char addr,unsigned char channel,  unsigned char Val)
  162. {
  163.    Start_I2c();              //启动总线
  164.    I2C_SendByte(addr);            //发送器件地址
  165.    if(ack==0)return(0);
  166.    I2C_SendByte(0x40|channel);              //发送控制字节
  167.    if(ack==0)return(0);
  168.    I2C_SendByte(Val);            //发送DAC的数值  
  169.    if(ack==0)return(0);
  170.    Stop_I2c();               //结束总线
  171.    return(1);
  172. }

  173. /************************************************************
  174. * 函数名        : Pcf8591_SendByte
  175. * 函数功能                : 写入一个控制命令
  176. * 输入          : addr(器件地址),channel(转换通道)
  177. * 输出                 : 无
  178. ************************************************************/
  179. bit PCF8591_SendByte(unsigned char addr,unsigned char channel)
  180. {
  181.    Start_I2c();              //启动总线
  182.    I2C_SendByte(addr);            //发送器件地址
  183.    if(ack==0)return(0);
  184.    I2C_SendByte(0x40|channel);              //发送控制字节
  185.    if(ack==0)return(0);
  186.    Stop_I2c();               //结束总线
  187.    return(1);
  188. }

  189. /************************************************************
  190. * 函数名               : PCF8591_RcvByte
  191. * 函数功能           : 读取一个转换值
  192. * 输入          :
  193. * 输出          : dat
  194. ************************************************************/
  195. unsigned char PCF8591_RcvByte(unsigned char addr)
  196. {  
  197.    unsigned char dat;

  198.    Start_I2c();          //启动总线
  199.    I2C_SendByte(addr+1);      //发送器件地址
  200.    if(ack==0)return(0);
  201.    dat=I2C_RcvByte();          //读取数据0

  202.    Ack_I2c(1);           //发送非应答信号
  203.    Stop_I2c();           //结束总线
  204.    return(dat);
  205. }
  206. /*------------------------------------------------
  207.                  串口初始化函数
  208. ------------------------------------------------*/
  209. void init_com(void)
  210. {
  211. EA=1;        //开总中断
  212. ES=1;        //允许串口中断
  213. ET1=1;
  214. TMOD=0x22;   //定时器T1,在方式2中断产生波特率
  215. PCON=0x00;   //SMOD=0
  216. SCON=0x50;   // 方式1 由定时器控制
  217. TH1=0xfd;    //波特率设置为9600
  218. TL1=0xfd;
  219. TR1=1;       //开定时器T1运行控制位


  220. }
  221. /*------------------------------------------------
  222.                   延时函数
  223. ------------------------------------------------*/
  224. void delay(unsigned char i)
  225. {
  226.   unsigned char j,k;
  227.   for(j=i;j>0;j--)
  228.     for(k=125;k>0;k--);
  229. }
  230. /*------------------------------------------------
  231. 把读取值转换成一个一个的字符,给串口显示
  232. ------------------------------------------------*/
  233. void To_ascii(unsigned char num)
  234. {       
  235.          SBUF=num/100+'0';                             
  236.          delay(200);                  
  237.          SBUF=num/10%10+'0';                          
  238.          delay(200);       
  239.          SBUF=num%10+'0';
  240.          delay(200);
  241. }
  242. /*------------------------------------------------
  243.                     主函数
  244. ------------------------------------------------*/
  245. main()
  246. {  

  247.         init_com();
  248.         while(1)
  249.         {
  250.          /********以下AD-DA处理*************/  
  251.          PCF8591_SendByte(AddWr,0);         //启动转换
  252.          D[0]=PCF8591_RcvByte(AddWr);  //读转换完的数字信号,ADC0 模数转换1      光敏电阻       
  253.          /********以下将AD的值通过串口发送出去*************/
  254.          dis[1]=D[0]/51;   //每刻度值为5/256V,所以电压值为输出的8位数字值*5/256V,计算输出电压的整数值
  255.         dis[2]=D[0]%51;   //dis[2]位中间暂存数据位
  256.         dis[2]=dis[2]*10;   
  257.         dis[0]=dis[2]/51;    //计算输出电压的小数值       
  258.                
  259.     C1=1;                                                
  260.         Dp=0;                //打开小数点
  261.         P0=table[dis[0]];    //显示整数部分及小数点
  262.                              
  263.         C1=0;
  264.         delay(10);   
  265.                        
  266.         C2=1;                //打开第二位数码管
  267.         Dp=1;                //关闭小数点
  268.         P0=table[dis[1]];     //显示小数部分
  269.         C2=0;               
  270.         // delay(200);       
  271.         // To_ascii(D[0]);
  272.          /*SBUF='\n';
  273.          //delay(200);
  274.          if(RI)
  275.         {
  276.                 date=SBUF;    //单片机接受
  277.                 SBUF=date;    //单片机发送
  278.                 RI=0;
  279.         }*/         
  280.    }
  281. }
复制代码

所有资料51hei提供下载:
数字电压表.zip (197.63 KB, 下载次数: 278)


评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏5 分享淘帖 顶1 踩
回复

使用道具 举报

沙发
ID:489559 发表于 2019-3-14 19:16 | 只看该作者
从串口中输入的数据是电压值吗
回复

使用道具 举报

板凳
ID:404722 发表于 2019-5-19 17:41 | 只看该作者
shawang 发表于 2019-3-14 19:16
从串口中输入的数据是电压值吗

这里没有使用串口,可以通过串口把数据发送出去
回复

使用道具 举报

地板
ID:712432 发表于 2020-3-20 17:49 | 只看该作者
楼主就是测量不是有范围吗。超过范围降压设计以后怎么保证输出值是降压前的。还有测量出的是有效值吗?
回复

使用道具 举报

5#
ID:909477 发表于 2021-5-16 10:35 | 只看该作者
请问要显示小数点后两位该怎样处理
回复

使用道具 举报

6#
ID:992042 发表于 2021-12-18 21:03 | 只看该作者
楼主设计的太好了,学习到了很多。
回复

使用道具 举报

7#
ID:1031469 发表于 2022-6-2 13:55 | 只看该作者
P1是什么器件在protus里怎么找的呀
回复

使用道具 举报

8#
ID:236933 发表于 2024-4-5 11:31 | 只看该作者
代码编译之后,用这个仿真运行数码管无显示
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表