找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 8128|回复: 4
收起左侧

单片机+PCF8591实现数字电压表的仿真+代码

[复制链接]
ID:460629 发表于 2019-1-3 14:51 | 显示全部楼层 |阅读模式
制作数字电压表(2学时)

一、实验目的:

1. 了解I2C串行总线工作原理及通信协议

2. 了解ADC0804、DAC0832、PCF8591与AT89S51的接口设计

3. 掌握ADC0804、DAC0832、PCF8591的编程方法

4. 能够熟练运用数模转换模块

二、实验要求:

1. 数字电压表:在AT89C52系统中采用PCF8591芯片,测量0-5V范围内的直流电压,并在2位数码管上显示电压值。

三、实验设备:(PROTEUS元件表)


四、实验报告:

1.描述实验过程(重要步骤用屏幕截图表示)。

2.给出在PROTEUS中设计的单片机系统电路图

3.画出程序流程图

4.给出在KEIL中编写的源程序。

5.描述在proteus中仿真运行程序的实验现象

五、实验总结


单片机源程序如下:


  1. /**********************BST-M51实验开发板例程************************
  2. *  平台:BST-M51 + Keil U4 + STC89C52
  3. *  名称:AD串口读取实验
  4. *  公司:深圳市亚博软件开发有限公司      
  5. *  日期:2015-6
  6. *  晶振:11.0592MHZ
  7. ******************************************************************/
  8. #include<reg52.h>    //包含单片机寄存器的头文件
  9. #include <intrins.h>

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

  11. // 变量定义
  12. unsigned char AD_CHANNEL=0;
  13. unsigned char  D[32];
  14. unsigned char code table[10]={0xC0,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};   //共阳极数码管0~9显示的数值表
  15. unsigned int data dis[3]={0x00,0x00,0x00};  //用以计算存储输出电压的3元素数组

  16. sbit scl=P2^0;       //I2C  时钟
  17. sbit sda=P2^1;       //I2C  数据
  18. bit ack;                 /*应答标志位*/
  19. sbit C1=P2^6;//数码管位选
  20. sbit C2=P2^7;//数码管位选
  21. sbit Dp=P2^5;//小数点

  22. unsigned char date;

  23. /*******************************************************************
  24.                      起动总线函数              
  25. 函数原型: void  Start_I2c();
  26. 功能:     启动I2C总线,即发送I2C起始条件.
  27. ********************************************************************/
  28. void Start_I2c()
  29. {
  30.   sda=1;         /*发送起始条件的数据信号*/
  31.   _nop_();
  32.   scl=1;
  33.   _nop_();        /*起始条件建立时间大于4.7us,延时*/
  34.   _nop_();
  35.   _nop_();
  36.   _nop_();
  37.   _nop_();   
  38.   sda=0;         /*发送起始信号*/
  39.   _nop_();        /* 起始条件锁定时间大于4μs*/
  40.   _nop_();
  41.   _nop_();
  42.   _nop_();
  43.   _nop_();      
  44.   scl=0;       /*钳住I2C总线,准备发送或接收数据 */
  45.   _nop_();
  46.   _nop_();
  47. }

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

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

  79. for(i=0;i<8;i++)  /*要传送的数据长度为8位*/
  80.     {
  81.      if((c<<i)&0x80)sda=1;   /*判断发送位*/
  82.        else  sda=0;               
  83.      _nop_();
  84.      scl=1;               /*置时钟线为高,通知被控器开始接收数据位*/
  85.       _nop_();
  86.       _nop_();             /*保证时钟高电平周期大于4μs*/
  87.       _nop_();
  88.       _nop_();
  89.       _nop_();        
  90.      scl=0;
  91.     }

  92.     _nop_();
  93.     _nop_();
  94.     sda=1;                /*8位发送完后释放数据线,准备接收应答位*/
  95.     _nop_();
  96.     _nop_();  
  97.     scl=1;
  98.     _nop_();
  99.     _nop_();
  100.     _nop_();
  101.     if(sda==1)ack=0;   
  102.        else ack=1;        /*判断是否接收到应答信号*/
  103.     scl=0;
  104.     _nop_();
  105.     _nop_();
  106. }

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

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

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

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

  195. /************************************************************
  196. * 函数名                     : PCF8591_RcvByte
  197. * 函数功能                 : 读取一个转换值
  198. * 输入          :
  199. * 输出          : dat
  200. ************************************************************/
  201. unsigned char PCF8591_RcvByte(unsigned char addr)
  202. {
  203.    unsigned char dat;

  204.    Start_I2c();          //启动总线
  205.    I2C_SendByte(addr+1);      //发送器件地址
  206.    if(ack==0)return(0);
  207.    dat=I2C_RcvByte();          //读取数据0

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

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

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

所有资料51hei提供下载:

实验九制作数字电压表.rar (419.46 KB, 下载次数: 185)

评分

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

查看全部评分

回复

使用道具 举报

ID:362692 发表于 2020-1-3 15:10 来自手机 | 显示全部楼层
程序还不错,借鉴的一部分
回复

使用道具 举报

ID:743707 发表于 2020-5-4 16:02 来自手机 | 显示全部楼层
这个图可以有清楚的吗
回复

使用道具 举报

ID:782630 发表于 2020-6-17 22:24 | 显示全部楼层
正在学,感谢分享
回复

使用道具 举报

ID:585455 发表于 2020-9-13 15:33 | 显示全部楼层
正在學習pcf8591;很棒的資料。
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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