找回密码
 立即注册

QQ登录

只需一步,快速开始

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

单片机+ADC0832 如何修正AD显示值并判断电压是否符合要求?附程序

[复制链接]
回帖奖励 50 黑币 回复本帖可获得 10 黑币奖励! 每人限 1 次
跳转到指定楼层
楼主
这个程序也是在本论坛上找的,在使用ADC0832模块读取电压值时,发现转换的电压值同电压表显示的有偏差,现在想通过修正使两者基本一致,即不同的电压范围加上相应的数字,条件如下:
1.AD值在0.2以下时不加;
2.大于0.2,小于1.0时加0.05;
3.大于1.0,小于3.0时加0.1
4.大于3.0,小于4.0时加0.13
5.大于4.0时,加0.16

还有一个需求:
电压值在0.2以下或1.5-3.0范围时点亮P3.0,其它的值均点亮P3.1。

感谢各位老师。

以下是目前使用的单片机程序:

  1. //-----------------------------------------------------------------
  2. //  说明: 调整VR1时,ADC0832将模拟电压转换为数字电压显示在1602液晶
  3. //        屏第0行,液晶屏第1行同时以进程条方式显示当前电压大小.
  4. //
  5. //-----------------------------------------------------------------
  6. #include <reg51.h>
  7. #include <intrins.h>
  8. #include <string.h>
  9. #define uchar  unsigned char
  10. #define uint unsigned int
  11. //ADC0832引脚定义
  12. sbit CS   =  P1^0;                //片选线(电路中固定连接低电平)
  13. sbit CLK  =  P1^1;                //时钟线
  14. sbit DIO  =  P1^2;                //数据线
  15. //LCD1602端口定义
  16. sbit RS = P2^5;                        //寄存器选择
  17. sbit RW = P2^6;                        //读写控制
  18. sbit E  = P2^7;                        //使能控制
  19. #define LCD_PORT  P0        //液晶端口
  20. uchar Disp_Buff1[] = " VOLTAGE:  0.00V";        //数字电压显示缓冲
  21. uchar Disp_Buff2[16];                                                //进程条图形显示缓冲
  22. #define delay4us() { _nop_();_nop_();_nop_();_nop_();}
  23. //-----------------------------------------------------------------
  24. // 延时函数
  25. //-----------------------------------------------------------------
  26. void delay_ms(uint x)
  27. {
  28.         uchar t; while(x--) for(t = 0; t < 120; t++);
  29. }

  30. //-----------------------------------------------------------------
  31. // 读LCD忙状态
  32. //-----------------------------------------------------------------
  33. bit Read_LCD_Busy_Flag()
  34. {
  35.         uchar result;
  36.         LCD_PORT=0xff;                        //液晶屏端口电平拉高
  37.         RS=0;RW=1;                            //选择命令寄存器,准备读
  38.         E=1;delay4us(); result=P0; E=0;       //下降沿读命令寄存器
  39.         return (result&0x80)? 1 : 0;          //返回忙状态
  40. }

  41. //-----------------------------------------------------------------
  42. // 写LCD命令
  43. //-----------------------------------------------------------------
  44. void Write_LCD_Command(uchar cmd)        
  45. {
  46.         while(Read_LCD_Busy_Flag());           //液晶屏忙等待
  47.         RS=0;RW=0;                             //选择命令寄存器,准备写
  48.         E=0; _nop_();_nop_();LCD_PORT=cmd;     //数据送到液晶屏端口
  49.         delay4us();E=1;delay4us();E=0;         //写入后禁止液晶屏
  50. }

  51. //-----------------------------------------------------------------
  52. // 写LCD数据
  53. //-----------------------------------------------------------------
  54. void Write_LCD_Data(uchar dat)
  55. {
  56.         while(Read_LCD_Busy_Flag());           //液晶屏忙等待
  57.         RS=1;RW=0;                             //选择数据寄存器,准备写
  58.         E=0; LCD_PORT=dat; delay4us();         //数据送到液晶屏端口
  59.         E=1;delay4us(); E=0;                   //写入后禁止液晶屏
  60. }

  61. //-----------------------------------------------------------------
  62. // 在LCD指定行/列位置显示字符串
  63. //-----------------------------------------------------------------
  64. void LCD_Show_String(uchar r,uchar c, char *s)
  65. {
  66.         uchar i=0;
  67.         uchar code DDRAM[]={0x80,0xc0};              //液晶屏上下两行的DDRAM首地址   
  68.         Write_LCD_Command(DDRAM[r] | c);             //设置显示起始位置
  69.         while(s[i] && i<16) Write_LCD_Data(s[i++]);  //输出显示字符
  70. }

  71. //-----------------------------------------------------------------
  72. // LCD初始化
  73. //-----------------------------------------------------------------
  74. void LCD_Initialise()
  75. {
  76.         Write_LCD_Command(0x38); delay_ms(1);
  77.         Write_LCD_Command(0x0C); delay_ms(1);
  78.         Write_LCD_Command(0x06); delay_ms(1);
  79.         Write_LCD_Command(0x01); delay_ms(1);
  80. }

  81. //-----------------------------------------------------------------
  82. // 获取AD转换结果 ( 0通道 )
  83. //-----------------------------------------------------------------
  84. uchar Get_AD_Result()
  85. {
  86.         uchar i,dat1=0,dat2=0;
  87.         //使能ADC0832,时钟线初始置低电平
  88.         CS=0; CLK=0;
  89.         //第1个时钟脉冲上升沿之前,通过DIO选择模式
  90.         DIO=1;        _nop_();_nop_();
  91.         CLK=1;        _nop_();_nop_();
  92.         //第2个时钟脉冲上升沿之前,通过DIO选择模式
  93.         //设DI=1选择单端(SGL) 设DI=0 选择分差(DIF)
  94.         //下面设置的是单端模式
  95.         CLK=0; DIO=1;  _nop_();_nop_();
  96.         CLK=1;         _nop_();_nop_();
  97.         //第2个时钟脉冲上升沿之前,设DI=0/1,分别对应选择CH0/CH1
  98.         CLK=0; DIO=0;  _nop_();_nop_();
  99.         CLK=1;         _nop_();_nop_();
  100.         //第3个时钟脉冲下升沿之后置DI=1,释放数据线,准备接收(设置原理可参考第一章端口说明)
  101.         //P1端口读取数据时需要先写1,否则总线将出现逻辑冲突(黄色方块闪烁)
  102.         CLK=0; DIO=1;  _nop_();_nop_();
  103.         //根据时序图可知,在正式读取转换数据之前,此时的D0将固定输出0
  104.         //P3=(uchar)DIO;
  105.         //第4~11,共8个下降沿读数据(MSB-LSB)
  106.         for(i=0;i<8;i++)
  107.         {
  108.                 CLK=1;        _nop_();_nop_();
  109.                 CLK=0;        _nop_();_nop_();
  110.                 dat1<<=1;
  111.                 if(DIO)
  112.                         dat1=dat1+0x01;
  113.         }
  114.         //第11~18,共8个下降沿读数据(LSB——MSB)
  115.         for(i=0;i<8;i++)
  116.         {
  117.                 dat2>>=1;
  118.                 if(DIO)
  119.                         dat2=dat2+0x80;
  120.                 CLK=1;       _nop_();_nop_();
  121.                 CLK=0;       _nop_();_nop_();
  122.         }
  123.         CS=1; //禁止ADC0832
  124.         //如果MSB——LSB和LSB——MSB读取的结果相同则返回读取的结果,否则返回0
  125.         if(dat1==dat2)
  126.                 return dat1;
  127.         return 0x00;
  128. }

  129. //-----------------------------------------------------------------
  130. // 主程序
  131. //-----------------------------------------------------------------
  132. void main()
  133. {
  134.         uchar  AD; uint        d;                //注意d的类型为uint
  135.         LCD_Initialise();                        //初始化LCD
  136.         while(1)
  137.         {        
  138.                 AD=Get_AD_Result();    //获取A/D转换值
  139.                 //根据四舍五入法计算电压值(放大100倍以便分解)
  140.                 d=AD*500.0*2/511.0;
  141.                 //根据舍尾取整数计算电压值(放大100倍以便分解)
  142.                 //将d = AD*500.0/256;
  143.                 //将AD转换后得到数据分解为三个数位
  144.                 Disp_Buff1[11] = d / 100     +'0';
  145.                 Disp_Buff1[13] = d / 10 %10  +'0';
  146.                 Disp_Buff1[14] = d % 10      +'0';
  147.                 LCD_Show_String(0,0,Disp_Buff1);
  148.                 //将模数值折算为实心方块及空心方框字符个数,控制进程条图形显示
  149.         }
  150. }
复制代码

51hei图片_20240120173631.jpg (220.27 KB, 下载次数: 43)

电压值

电压值

ADC0832模数转换与LCD显示.rar

87.87 KB, 下载次数: 2

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

使用道具 举报

沙发
ID:883242 发表于 2024-1-20 21:40 | 只看该作者
你的做法相当的不好,因为临界点会出现数值跳动,太假了。

你应该用最小二乘法把校准数据拟合成二次曲线。
回复

使用道具 举报

板凳
ID:893997 发表于 2024-1-22 15:17 | 只看该作者
你这样简单,但是结果可信度不高,最好还是用高精度万用表测一下全范围数据,然后求偏差曲线。
回复

使用道具 举报

地板
ID:1045846 发表于 2024-1-22 20:33 | 只看该作者
感谢两位老师的建议。
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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