找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 36780|回复: 77
收起左侧

51单片机用PCF8591做个简易电压表制作 附程序

  [复制链接]
ID:167599 发表于 2017-3-2 11:31 | 显示全部楼层 |阅读模式
用很简单的方法制作一个51单片机电压表,能测量30V以下的直流电压,没有精确的电压表,无法测定其精度,但是用普通的万用表测试比较之后发现其精度还是不错的,其精度主要是看其供给的参考电压的精度,AD转换的位数,还有分压电阻的精度。但是用于普通的电压测量还是不错的。

0.png 0.png 0.png


可以自行调整分压电阻的大小和改动电压算法参数可以直接适应其他量程的电压测量

1.png

源代码:
  1. #include<reg52.h>    //包含单片机寄存器的头文件
  2. #include <intrins.h>

  3. #define ulong unsigned long
  4. #define uchar unsigned char
  5. #define uint unsigned int
  6. #define LCD1602_PORT P2          //用LED1602_PORT表示P2总线接口
  7. #define  PCF8591 0x90    //PCF8591 地址
  8. #define  NOP()   _nop_()   /* 定义空指令 */
  9. #define  _Nop()  _nop_()   /*定义空指令*/

  10. // 位定义
  11. sbit SCL=P1^0;       //I2C  时钟
  12. sbit SDA=P1^1;       //I2C  数据
  13. sbit LCD1602_RS = P0^5;      //定义1602液晶显示屏的数据/命令选择端,数据/命令(H/L)
  14. sbit LCD1602_RW = P0^6;      //定义1602液晶显示屏的读/写选择端,读/写(H/L)
  15. sbit LCD1602_EN = P0^7;      //定义1602液晶显示屏的使能端
  16. bit ack;                 /*应答标志位*/

  17. // 变量定义
  18. uchar AD_CHANNEL;
  19. ulong xdata  LedOut[8];      //单片机内部存取器
  20. ulong  v,a,ss;
  21. uchar date;

  22. // 函数申明
  23. extern bit ack;        //起动总线函数
  24. extern void Start_I2c();    //结束总线函数  
  25. extern void Stop_I2c();        //应答子函数
  26. extern void Ack_I2c(bit a);        //字节数据发送函数
  27. extern void  SendByte(uchar  c);    //有子地址发送多字节数据函数               
  28. extern bit ISendStr(uchar sla,uchar suba,uchar *s,uchar no) ;    //无子地址发送多字节数据函数   
  29. extern bit ISendStrExt(uchar sla,uchar *s,uchar no);    //无子地址读字节数据函数               
  30. extern uchar RcvByte();

  31. void LCD1602_delay_ms(uint n);
  32. void LCD1602_write_com(uchar com);
  33. void LCD1602_write_data(ulong dat);
  34. void LCD1602_write_word(uchar *s);
  35. void Init_LCD1602();

  36. bit ISendByte(uchar sla,uchar c);
  37. uchar IRcvByte(uchar sla);

  38. //MS延时函数(12M晶振下测试)
  39. void delay_ms(uint n)   
  40. {
  41.     unsigned int  i,j;
  42.     for(i=0;i<n;i++)
  43.         for(j=0;j<123;j++);
  44. }

  45. //写指令
  46. void LCD1602_write_com(uchar com)
  47. {
  48.     LCD1602_RS = 0;         //1602液晶显示屏的数据/命令选择端置0,数据/命令(H/L),此时选择写命令操作
  49.     delay_ms(1);
  50.     LCD1602_EN = 1;         //使能,也就是打开液晶接收数据命令的通道
  51.     LCD1602_PORT = com;     //把命令送给P2口
  52.     delay_ms(1);
  53.     LCD1602_EN = 0;
  54. }

  55. //写数据
  56. void LCD1602_write_data(ulong dat)
  57. {
  58.     LCD1602_RS = 1;         //1602液晶显示屏的数据/命令选择端置0,数据/命令(H/L),此时选择写数据操作
  59.     delay_ms(1);   
  60.     LCD1602_PORT = dat;     //把数据送给P2口
  61.     LCD1602_EN = 1;
  62.     delay_ms(1);
  63.     LCD1602_EN = 0;
  64. }

  65. //连续写字符
  66. void LCD1602_write_word(uchar *s)
  67. {
  68.     while(*s>0)
  69.     {
  70.         LCD1602_write_data(*s);
  71.         s++;
  72.     }
  73. }

  74. //1602初始化函数
  75. void Init_LCD1602()                    
  76. {
  77.     LCD1602_EN = 0;
  78.     LCD1602_RW = 0;                        //设置为写状态
  79.     LCD1602_write_com(0x38);            //显示模式设定
  80.     LCD1602_write_com(0x0c);            //开关显示、光标有无设置、光标闪烁设置
  81.     LCD1602_write_com(0x06);            //写一个字符后指针加一
  82.     LCD1602_write_com(0x01);            //清屏指令
  83. }
  84. /*******************************************************************
  85.                      起动总线函数               
  86. 函数原型: void  Start_I2c();  
  87. 功能:     启动I2C总线,即发送I2C起始条件.  
  88. ********************************************************************/
  89. void Start_I2c()
  90. {
  91.   SDA=1;         /*发送起始条件的数据信号*/
  92.   _Nop();
  93.   SCL=1;
  94.   _Nop();        /*起始条件建立时间大于4.7us,延时*/
  95.   _Nop();
  96.   _Nop();
  97.   _Nop();
  98.   _Nop();   
  99.   SDA=0;         /*发送起始信号*/
  100.   _Nop();        /* 起始条件锁定时间大于4μs*/
  101.   _Nop();
  102.   _Nop();
  103.   _Nop();
  104.   _Nop();      
  105.   SCL=0;       /*钳住I2C总线,准备发送或接收数据 */
  106.   _Nop();
  107.   _Nop();
  108. }

  109. /*******************************************************************
  110.                       结束总线函数               
  111. 函数原型: void  Stop_I2c();  
  112. 功能:     结束I2C总线,即发送I2C结束条件.  
  113. ********************************************************************/
  114. void Stop_I2c()
  115. {
  116.   SDA=0;      /*发送结束条件的数据信号*/
  117.   _Nop();       /*发送结束条件的时钟信号*/
  118.   SCL=1;      /*结束条件建立时间大于4μs*/
  119.   _Nop();
  120.   _Nop();
  121.   _Nop();
  122.   _Nop();
  123.   _Nop();
  124.   SDA=1;      /*发送I2C总线结束信号*/
  125.   _Nop();
  126.   _Nop();
  127.   _Nop();
  128.   _Nop();
  129. }

  130. /*******************************************************************
  131.                  字节数据发送函数               
  132. 函数原型: void  SendByte(UCHAR c);
  133. 功能:     将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
  134.           此状态位进行操作.(不应答或非应答都使ack=0)     
  135.            发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
  136. ********************************************************************/
  137. void  SendByte(uchar  c)
  138. {
  139. uchar  BitCnt;

  140. for(BitCnt=0;BitCnt<8;BitCnt++)  /*要传送的数据长度为8位*/
  141.     {
  142.      if((c<<BitCnt)&0x80)SDA=1;   /*判断发送位*/
  143.        else  SDA=0;               
  144.      _Nop();
  145.      SCL=1;               /*置时钟线为高,通知被控器开始接收数据位*/
  146.       _Nop();
  147.       _Nop();             /*保证时钟高电平周期大于4μs*/
  148.       _Nop();
  149.       _Nop();
  150.       _Nop();         
  151.      SCL=0;
  152.     }
  153.    
  154.     _Nop();
  155.     _Nop();
  156.     SDA=1;                /*8位发送完后释放数据线,准备接收应答位*/
  157.     _Nop();
  158.     _Nop();   
  159.     SCL=1;
  160.     _Nop();
  161.     _Nop();
  162.     _Nop();
  163.     if(SDA==1)ack=0;     
  164.        else ack=1;        /*判断是否接收到应答信号*/
  165.     SCL=0;
  166.     _Nop();
  167.     _Nop();
  168. }

  169. /*******************************************************************
  170.                  字节数据接收函数               
  171. 函数原型: UCHAR  RcvByte();
  172. 功能:        用来接收从器件传来的数据,并判断总线错误(不发应答信号),
  173.           发完后请用应答函数应答从机。  
  174. ********************************************************************/   
  175. uchar   RcvByte()
  176. {
  177.   uchar  retc;
  178.   uchar  BitCnt;
  179.   
  180.   retc=0;
  181.   SDA=1;                     /*置数据线为输入方式*/
  182.   for(BitCnt=0;BitCnt<8;BitCnt++)
  183.       {
  184.         _Nop();           
  185.         SCL=0;                  /*置时钟线为低,准备接收数据位*/
  186.         _Nop();
  187.         _Nop();                 /*时钟低电平周期大于4.7μs*/
  188.         _Nop();
  189.         _Nop();
  190.         _Nop();
  191.         SCL=1;                  /*置时钟线为高使数据线上数据有效*/
  192.         _Nop();
  193.         _Nop();
  194.         retc=retc<<1;
  195.         if(SDA==1)retc=retc+1;  /*读数据位,接收的数据位放入retc中 */
  196.         _Nop();
  197.         _Nop();
  198.       }
  199.   SCL=0;   
  200.   _Nop();
  201.   _Nop();
  202.   return(retc);
  203. }

  204. /********************************************************************
  205.                      应答子函数
  206. 函数原型:  void Ack_I2c(bit a);
  207. 功能:      主控器进行应答信号(可以是应答或非应答信号,由位参数a决定)
  208. ********************************************************************/
  209. void Ack_I2c(bit a)
  210. {
  211.   if(a==0)SDA=0;              /*在此发出应答或非应答信号 */
  212.   else SDA=1;
  213.   _Nop();
  214.   _Nop();
  215.   _Nop();      
  216.   SCL=1;
  217.   _Nop();
  218.   _Nop();                    /*时钟低电平周期大于4μs*/
  219.   _Nop();
  220.   _Nop();
  221.   _Nop();  
  222.   SCL=0;                     /*清时钟线,钳住I2C总线以便继续接收*/
  223.   _Nop();
  224.   _Nop();   
  225. }


  226. /*******************************************************************
  227. ADC发送字节[命令]数据函数               
  228. *******************************************************************/
  229. bit ISendByte(uchar sla,uchar c)
  230. {
  231.    Start_I2c();              //启动总线
  232.    SendByte(sla);            //发送器件地址
  233.    if(ack==0)return(0);
  234.    SendByte(c);              //发送数据
  235.    if(ack==0)return(0);
  236.    Stop_I2c();               //结束总线
  237.    return(1);
  238. }

  239. /*******************************************************************
  240. ADC读字节数据函数               
  241. *******************************************************************/
  242. uchar IRcvByte(uchar sla)
  243. {  
  244.    uchar c;
  245.    Start_I2c();          //启动总线
  246.    SendByte(sla+1);      //发送器件地址
  247.    if(ack==0)return(0);
  248.    c=RcvByte();          //读取数据0
  249.    Ack_I2c(1);           //发送非就答位
  250.    Stop_I2c();           //结束总线
  251.    return(c);
  252. }

  253. //    字符显示函数
  254. void Display()
  255. {
  256.     LCD1602_write_com(0x80);            //指针设置,指向1602液晶第一行第一列
  257.     LCD1602_write_word("Voltage: ");
  258.     LCD1602_write_com(0x80+0x0e);            //指针设置,指向1602液晶第一行第一列
  259.     LCD1602_write_word(" V");
  260. }

  261. //实时电压显示函数
  262. void Display_Voltage()
  263. {
  264.     ss=(v*a);
  265.     ss=((ss*6)/100);
  266.     LCD1602_write_com(0x80+0x09);    //1602液晶屏命令操作,表示起始显示位置为第二行第7列   
  267.     LCD1602_write_data(ss%10000000/1000000 + 0x30);
  268.     LCD1602_write_data(ss%1000000/100000 + 0x30);
  269.     LCD1602_write_data('.');
  270.     LCD1602_write_data(ss%100000/10000 + 0x30);
  271.     LCD1602_write_data(ss%10000/1000 + 0x30);
  272. //    LCD1602_write_data(ss%1000/100 + 0x30);
  273. //    LCD1602_write_data('.');
  274. //    LCD1602_write_data(ss%100/10 + 0x30);
  275. //    LCD1602_write_data(ss%10/1 + 0x30);
  276. }
  277. //******************************************************************/
  278. main()
  279. {  
  280.     Init_LCD1602(); //初始化LCD1602
  281.     Display();
  282.     a=50000000/255;
  283.         while(1)
  284.     {
  285.         ISendByte(PCF8591,0x41);
  286.         v=IRcvByte(PCF8591);      
  287.         Display_Voltage();         
  288.     }      
  289. }

复制代码

评分

参与人数 10黑币 +153 收起 理由
huangyuxin + 15
294 + 4
ZB0717226 + 5 很给力!
jinmei1314 + 5 很给力!
vvv64 + 5 共享资料的黑币奖励!
炫神 + 5 很给力!
自知 + 5 很给力!
qjzyx + 6 赞一个!
darkly5251 + 3 很给力!
admin + 100 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:110866 发表于 2017-5-4 00:15 | 显示全部楼层
仿真怎么没给
回复

使用道具 举报

ID:212280 发表于 2017-6-18 15:35 | 显示全部楼层
非常感谢,正在学习PCF8591的使用
回复

使用道具 举报

ID:145507 发表于 2017-7-28 13:00 | 显示全部楼层
对我帮助很大  学习了
回复

使用道具 举报

ID:239025 发表于 2017-10-12 22:14 | 显示全部楼层
学习了
回复

使用道具 举报

ID:223453 发表于 2017-11-23 20:45 | 显示全部楼层
为什么显示屏显示的都是0啊,都不跳
回复

使用道具 举报

ID:247901 发表于 2017-11-24 19:15 | 显示全部楼层
学习了
回复

使用道具 举报

ID:249545 发表于 2017-11-25 20:55 | 显示全部楼层
老师的电压表相当先进.
能进一步升级吗?  制做液晶玻璃显示屏.51MCU做成OTP COB固定在PCB上,用斑马线连接显示屏.这样成工业品.批量生产.
回复

使用道具 举报

ID:71549 发表于 2017-11-29 18:14 | 显示全部楼层
对我帮助很大  学习了.....
回复

使用道具 举报

ID:257027 发表于 2017-12-4 11:25 | 显示全部楼层
对我帮助很大  学习了
回复

使用道具 举报

ID:241545 发表于 2017-12-13 17:35 | 显示全部楼层
谢谢分享
回复

使用道具 举报

ID:262595 发表于 2017-12-17 11:52 | 显示全部楼层
谢谢分享谢谢啊加两分吧
回复

使用道具 举报

ID:263213 发表于 2017-12-18 18:27 | 显示全部楼层
谢谢,很给力
回复

使用道具 举报

ID:240102 发表于 2017-12-20 19:14 | 显示全部楼层
楼主大大  ,可以写这个 —————— PCF8591 +12864 +89s52测电压0-30V的程序么?! 好想要!
回复

使用道具 举报

ID:264801 发表于 2017-12-21 15:46 | 显示全部楼层
问一下,接线全部按照你的来,为什么显示器不显示电压
回复

使用道具 举报

ID:265343 发表于 2017-12-22 16:01 | 显示全部楼层
很好的帖子。写的很详细!
回复

使用道具 举报

ID:272494 发表于 2018-1-6 19:59 | 显示全部楼层
程序下不了QAQ,,哪位兄弟能给我发一份。。
回复

使用道具 举报

ID:272494 发表于 2018-1-8 14:29 | 显示全部楼层
姚元红 发表于 2017-12-21 15:46
问一下,接线全部按照你的来,为什么显示器不显示电压

我的也是,zenme 回事,请教
回复

使用道具 举报

ID:272625 发表于 2018-1-25 05:05 | 显示全部楼层
好资料,感谢楼主
回复

使用道具 举报

ID:272625 发表于 2018-1-25 21:29 | 显示全部楼层
我也试试看,行不行
回复

使用道具 举报

ID:269211 发表于 2018-1-26 22:05 | 显示全部楼层
非常感谢,正在学习PCF8591的使用
回复

使用道具 举报

ID:285225 发表于 2018-2-25 00:06 | 显示全部楼层
谢谢答主,可以用数码管试试感觉也很方便
回复

使用道具 举报

ID:285225 发表于 2018-2-28 10:58 | 显示全部楼层
非常感谢答主的分享
回复

使用道具 举报

ID:302866 发表于 2018-4-7 21:50 | 显示全部楼层
老王个 发表于 2018-1-8 14:29
我的也是,zenme 回事,请教

我的也)是  你的弄好了么
回复

使用道具 举报

ID:303099 发表于 2018-4-9 17:20 | 显示全部楼层
学习了~感谢楼主的分享
回复

使用道具 举报

ID:305873 发表于 2018-4-11 15:04 | 显示全部楼层
学习了,非常感谢答主的分享
回复

使用道具 举报

ID:308601 发表于 2018-4-15 15:28 | 显示全部楼层
学习了,感谢分享
回复

使用道具 举报

ID:316572 发表于 2018-5-4 20:13 | 显示全部楼层

学习了,感谢分享!!!学习了,感谢分享!!!!
回复

使用道具 举报

ID:146874 发表于 2018-5-7 16:04 | 显示全部楼层
感谢楼主分享,很给力
回复

使用道具 举报

ID:146874 发表于 2018-5-7 16:05 | 显示全部楼层
谢谢分享
回复

使用道具 举报

ID:146874 发表于 2018-5-7 16:05 | 显示全部楼层
谢谢分享,正在学习这方面的资料
回复

使用道具 举报

ID:324272 发表于 2018-5-7 16:15 | 显示全部楼层
很厉害啊
回复

使用道具 举报

ID:316171 发表于 2018-5-8 20:50 | 显示全部楼层
左下的元件有谁告诉一下名称是什么么
回复

使用道具 举报

ID:316572 发表于 2018-5-9 21:14 | 显示全部楼层
楼主可以仔细解释一下位定义的意思吗,不太明白这几条代码啊
回复

使用道具 举报

ID:240994 发表于 2018-5-11 21:53 | 显示全部楼层
感谢楼主
回复

使用道具 举报

ID:48413 发表于 2018-5-15 08:00 | 显示全部楼层
不错值的学习
回复

使用道具 举报

ID:318584 发表于 2018-5-15 13:39 | 显示全部楼层
你好 我按照这个原理图焊接了50k与250k的电阻 显示误差有些大  2.48v我测了2.7v 3.26v测了3.5v 4.96 测了5.05v 请问如何使用算法调整精度
回复

使用道具 举报

ID:277463 发表于 2018-5-21 21:10 | 显示全部楼层
estbon 发表于 2017-6-18 15:35
**** 作者被禁止或删除 内容自动屏蔽 ****

感谢楼主
回复

使用道具 举报

ID:281991 发表于 2018-5-23 20:16 | 显示全部楼层
感谢楼主。
回复

使用道具 举报

ID:353778 发表于 2018-6-19 16:28 | 显示全部楼层

感谢楼主。
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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