找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

  [复制链接]
跳转到指定楼层
#
用很简单的方法制作一个51单片机电压表,能测量30V以下的直流电压,没有精确的电压表,无法测定其精度,但是用普通的万用表测试比较之后发现其精度还是不错的,其精度主要是看其供给的参考电压的精度,AD转换的位数,还有分压电阻的精度。但是用于普通的电压测量还是不错的。




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



源代码:
  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 共享资料的黑币奖励!

查看全部评分

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

使用道具 举报

77#
ID:314651 发表于 2025-8-25 10:15 | 只看该作者
ADC转化扩展量程的话,主要是在分压电阻那里分压。与电阻的精度干系很大。建议使用10K以内,1K以上精度1%的电阻。
回复

使用道具 举报

76#
ID:1142899 发表于 2025-1-11 18:48 | 只看该作者
学习用一下!
非常感谢!
回复

使用道具 举报

75#
ID:1087836 发表于 2023-9-27 16:27 | 只看该作者
请问楼主输入部分再做一个输入保护可以吗,防止ADC芯片被过压烧坏
回复

使用道具 举报

74#
ID:1031469 发表于 2022-6-1 16:43 | 只看该作者
owenliang 发表于 2018-2-25 00:06
谢谢答主,可以用数码管试试感觉也很方便

有案例吗分享一下
回复

使用道具 举报

73#
ID:1018006 发表于 2022-4-13 09:59 | 只看该作者
做挺好!!!!
回复

使用道具 举报

72#
ID:984325 发表于 2021-11-24 18:11 | 只看该作者
帮大忙了 I2C学的我太乱了
回复

使用道具 举报

71#
ID:72238 发表于 2020-7-27 20:05 | 只看该作者
非常感谢,正在学习PCF8591的使用
回复

使用道具 举报

70#
ID:800180 发表于 2020-7-9 15:23 | 只看该作者
GUOXUAN 发表于 2017-11-25 20:55
老师的电压表相当先进.
能进一步升级吗?  制做液晶玻璃显示屏.51MCU做成OTP COB固定在PCB上,用斑马线连接 ...

只测直流电压,为何不做指针表
回复

使用道具 举报

69#
ID:799680 发表于 2020-7-8 16:45 | 只看该作者
没仿真吗
回复

使用道具 举报

68#
ID:789624 发表于 2020-6-25 17:32 | 只看该作者
大佬太厉害了!!!!
回复

使用道具 举报

67#
ID:586915 发表于 2020-5-6 22:18 | 只看该作者
真想要一个
回复

使用道具 举报

66#
ID:677031 发表于 2019-12-30 12:49 | 只看该作者
已经打不开了
回复

使用道具 举报

65#
ID:554547 发表于 2019-12-15 14:16 | 只看该作者
很实用,电路也很简单,要是能给出图中AD转化芯片的型号就更好了
回复

使用道具 举报

64#
ID:507222 发表于 2019-11-16 16:00 | 只看该作者
link996 发表于 2018-5-15 13:39
你好 我按照这个原理图焊接了50k与250k的电阻 显示误差有些大  2.48v我测了2.7v 3.26v测了3.5v 4.96 测了5. ...

电路怎么接的线,为什么我的没有现象
回复

使用道具 举报

63#
ID:632444 发表于 2019-11-13 23:24 | 只看该作者
先收藏了,用到时在来回看,太感谢了。
回复

使用道具 举报

62#
ID:587535 发表于 2019-11-12 23:42 来自触屏版 | 只看该作者
学习了
回复

使用道具 举报

61#
ID:583423 发表于 2019-9-28 20:53 | 只看该作者
ss=(v*a);ss=((ss*6)/100);
为什么ss在被v*a赋值后,还要被((ss*6)/100)赋值。
回复

使用道具 举报

60#
ID:582635 发表于 2019-8-9 13:02 | 只看该作者
显示不了啊
回复

使用道具 举报

59#
ID:590636 发表于 2019-7-26 09:48 | 只看该作者
太厉害了,横有用
回复

使用道具 举报

58#
ID:552473 发表于 2019-6-24 14:37 | 只看该作者
确实很不错啊
回复

使用道具 举报

57#
ID:518516 发表于 2019-5-23 00:19 | 只看该作者
很棒的
回复

使用道具 举报

56#
ID:540545 发表于 2019-5-17 11:31 | 只看该作者

问一下,接线全部按照你的来,为什么显示器不显示电压
回复

使用道具 举报

55#
ID:493013 发表于 2019-3-18 12:49 | 只看该作者
正在学习pcf8591中
回复

使用道具 举报

54#
ID:396560 发表于 2019-2-20 10:45 | 只看该作者
楼主好人一生平安。
回复

使用道具 举报

53#
ID:317383 发表于 2019-1-5 14:46 | 只看该作者
做的不错,推一下
回复

使用道具 举报

52#
ID:222949 发表于 2019-1-4 00:14 | 只看该作者
谢谢分享,学习中
回复

使用道具 举报

51#
ID:50522 发表于 2018-12-25 11:41 来自触屏版 | 只看该作者
a=50000000是什么意思
回复

使用道具 举报

50#
ID:416593 发表于 2018-11-12 13:50 | 只看该作者
我按照仿真搭好电路后,源代码烧进去后,为什么什么都没显示
回复

使用道具 举报

49#
ID:311157 发表于 2018-11-7 19:45 | 只看该作者
自己做LCD的板子的时候老是对不准显示位置,来看看
回复

使用道具 举报

48#
ID:421094 发表于 2018-11-5 15:19 | 只看该作者
学习学习
回复

使用道具 举报

47#
ID:107690 发表于 2018-11-4 15:21 | 只看该作者
谢谢分享 刚好需要用到
回复

使用道具 举报

46#
ID:399179 发表于 2018-10-2 08:30 来自触屏版 | 只看该作者
这个不简单啊!
回复

使用道具 举报

45#
ID:69408 发表于 2018-10-1 20:38 | 只看该作者
下载学习学习,谢谢楼主啦!!!
回复

使用道具 举报

44#
ID:380389 发表于 2018-8-8 06:43 来自触屏版 | 只看该作者
好资料,收藏
回复

使用道具 举报

43#
ID:375880 发表于 2018-8-6 11:19 来自触屏版 | 只看该作者
急需此代码
回复

使用道具 举报

42#
ID:367531 发表于 2018-7-23 02:35 | 只看该作者
ss的计算公式与和AIN0口相连的两个电阻的阻止有关系吗?
回复

使用道具 举报

41#
ID:359573 发表于 2018-6-26 18:32 | 只看该作者
很好用,很给力!
回复

使用道具 举报

40#
ID:292119 发表于 2018-6-26 10:37 | 只看该作者
学习用一下,做一个低电压报警电路
回复

使用道具 举报

39#
ID:353778 发表于 2018-6-19 16:28 | 只看该作者

感谢楼主。
回复

使用道具 举报

38#
ID:281991 发表于 2018-5-23 20:16 | 只看该作者
感谢楼主。
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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