找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3324|回复: 5
收起左侧

分享一个用单片机比较器做电压表的程序

  [复制链接]
ID:624769 发表于 2021-10-6 19:37 | 显示全部楼层 |阅读模式
本程序用STC15W201S单片机。16引脚共14IO,  共连接 一个 3位 8段共阴数码管,两个按键,以及680K+10K电阻分压连接比较器测量外部电压。
其中,P10~P15 + P36,P37 分别通过1K电阻连接数码管 段选 ,P31~P33 直接接数码管共阴位选,P5.4,P5.5 操纵比较器测量电压,P3.0, 接两个按键,分别连接P3.1 P3.2 共阴位选引脚。可以说用光单片机所有IO.

电压比较通过TO定时器 实现,T2定时器负责按键检测和数码管显示。按键主要是为了校正阻值的误差所造成的电压失准。校正后自动保存Eeprom. 初始数值已经存在随压缩包的bin文件中,烧录文件时,应通过Eeprom页一起烧入单片机。

单片机源程序如下:
  1. /****************************
  2. 本示例在Keil开发环境下请选择STC15系列芯片型号进行编译

  3. 本例程MCU的工作频率为24MHz,  工作电压3~5V均可.

  4. 使用MCU自带的比较器进行ADC转换, 并通过串口输出结果.

  5. 比较器配置为P5.5做高阻正输入, 接输入分压电阻, 比较器的负输入接到内部1.25V参考电压. P5.4配置为开漏输出, 做反馈来平衡电荷.

  6. 本例程使用680K+10K分压电阻接P5.5输出, 用定时器0产生20us中断查询比较器的状态, 从P3.6输出反馈来做电荷平衡.

  7. 最高输入电压 = 1.25 * (1 + 680 / 10) = 85 V.  程序显示1.25~84V, 低于1.25V返回000, 高于84V返回FFF.

  8. 例: 比较一次的时间间隔为10us, 量程为33200, 则做1次ADC的时间为33ms. 比较器的响应时间越短, 则完成ADC就越快.

  9. 由于要求每次比较时间间隔都要相等,所以用C编程最好在定时器中断里进行, 定时器设置为自动重装, 高优先级中断, 其它中断均低优先级.

  10. 用汇编的话, 保证比较输出电平处理的时间要相等.



  11.                                          680K
  12.                             /| P5.5         ___
  13.                            /+|-----------o-|___|- ------- Vin
  14.                       ----<  | P5.4 ___  |
  15.                            \-|-----|___|-o
  16.                             \|      10K  |
  17.                                          |
  18.                                         ---
  19.                                         ---  104
  20.                                          |  
  21.                                          |
  22.                                         ===
  23.                                         GND

  24. ******************************/
  25. #include        "MAIN.H"
  26. #include        "EEPROM.C"
  27. /*************        本地常量声明        **************/
  28. #define                BandGap                122        //1246 mv
  29. #define                ADC_SCALE        *((unsigned short volatile data *)0x18)
  30. #define                ADC_Reference        *((unsigned long volatile data *)0x18)
  31. #define                ADC_Multiple        *((unsigned short volatile data *)0x1C)
  32. #define                BGP                *((unsigned short volatile data *)0x1E)
  33. //#define                adc_value        *((unsigned short volatile data *)0x38)
  34. #define                Value_H                *((unsigned char volatile data *)0x20)
  35. #define                Value_L                *((unsigned char volatile data *)0x21)
  36. //u8 volatile data        adc_temp[2] _at_ 0x30;
  37. //#define                BandGap                *((unsigned short volatile data *)0x30)
  38. //#define                ADC_Multiple        *((unsigned short volatile data *)0x32)
  39. //#define                ADC_SCALE        *((unsigned short volatile data *)0x34)
  40. //#define                ADC_Reference        *((unsigned long volatile data *)0x34)
  41. u8 volatile data        adc_temp[8] _at_ 0x18;
  42. u16 volatile data        adc_value _at_ 0x20;
  43. //u8 volatile data        Value_H _at_ 0x38;
  44. //u8 volatile data        Value_L _at_ 0x39;
  45. #define                CMPRES                (CMPCR1 & 0x01)
  46. //u8 code Led_Lib[]        = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
  47. u8 code Led_Count[]        = {0xf7,0xfb,0xfd};

  48. /*************        本地变量声明        **************/
  49. sbit P_ADC        = P5^4;                //P3.6 比较器转IO输出端
  50. sbit Press        = P3^0;                //P3.0 按键检测IO
  51. u8        data        BandGap_addr, adc_addr, Key, Long_Press, Disp_Count, T2_Count;
  52. u8        data        Led_Temp[4], Led_Buf[3], Key_Press[3];
  53. u16        data        adc;                        //ADC中间值, 用户层不可见
  54. u16        data        adc_duty;                //ADC计数周期, 用户层不可见
  55. //u16        data        adc_value;                //ADC值, 用户层使用
  56. bit        Show_Mode, Edit_Mode, adc_ok;                        //ADC结束标志, 为1则adc_value的值可用. 此标志给用户层查询,并且清0

  57. /*************        本地函数声明        **************/
  58. void Get_ADCSCALE()
  59. {
  60.         adc_temp[0] = 0xff;
  61.         while(adc_temp[0] == 0xff)
  62.         {
  63.                 adc_temp[0] = Read_EepromH(--adc_addr);
  64.         }
  65.         adc_temp[1] = Read_EepromL(adc_addr);
  66. }

  67. void Save_ADCSCALE()
  68. {
  69.         if(++adc_addr == 0x00)        Erase_Eeprom();
  70.         Write_EepromH(adc_addr, adc_temp[0]);
  71.         Write_EepromL(adc_addr, adc_temp[1]);
  72. }

  73. /*
  74. void Get_ADCSCALE()
  75. {
  76.         adc_temp[0]        = 0xff;
  77.         BandGap_addr        = 0x80;
  78.         while(adc_temp[0] == 0xff)
  79.         {
  80.                 adc_temp[0] = Read_EepromH(--BandGap_addr);
  81.         }
  82.         adc_temp[1] = Read_EepromH(BandGap_addr | 0x80);
  83.         adc_temp[2] = Read_EepromL(BandGap_addr);
  84.         adc_temp[3] = Read_EepromL(BandGap_addr | 0x80);
  85. }

  86. void Save_ADCSCALE()
  87. {
  88.         if(++BandGap_addr == 0x80)
  89.         {
  90.                 BandGap_addr        = 0x00;
  91.                 Erase_Eeprom();
  92.         }
  93.         Write_EepromH(BandGap_addr, adc_temp[0]);
  94.         Write_EepromH((BandGap_addr | 0x80), adc_temp[1]);
  95.         Write_EepromL(BandGap_addr, adc_temp[2]);
  96.         Write_EepromL((BandGap_addr | 0x80), adc_temp[3]);
  97. }

  98. void Reference_ADC()
  99. {
  100.         ADC_Reference = BandGap        * ADC_Multiple * 2621;
  101. }
  102. */

  103. void main(void)
  104. {
  105.         IAP_CONTR = ENABLE_IAP;
  106.         Get_ADCSCALE();
  107. //        Reference_ADC();        
  108.         P1Mode        = DDPP_PPPP;        //P1.5~P1.0  设置为强推挽输出(Led段码a~f)
  109.         P3Mode        = PPDD_OOOD;        //P3.7~P3.6  设置为强推挽输出(Led段码g、dp)  P3.3~P3.1  设置为开漏输出(Led位码0~2)  P3.0  设置为准双向(按键检测)
  110.         P5Mode        = DDHO_DDDD;        //P5.5  设置为高阻输入(adc正极输入,电压检测)  P5.4  设置为开漏输出(adc电荷平衡)

  111.         TH0 = Reload_for_Timer0 / 256;        //重装值  中断率50KHZ, 中断周期20us
  112.         TL0 = Reload_for_Timer0 % 256;

  113.         T2H = Reload_for_Timer2 / 256;
  114.         T2L = Reload_for_Timer2 % 256;   //设置波特率重装值

  115.                                 //        7        6        5        4        3        2        1        0
  116. //        IAP_CONTR=B_1000_0001;        //        IAPEN        SWBS        SWRST        CMD_FAL        -        WT2        WT1        WT0
  117. //        IAP_CMD        = B_0000_0001;        //        -        -        -        -        -        -        MS1        MS0        // IAP_TRIG = 5a a5
  118.         CMPCR1        = B_1000_0000;        //        CMPEN        CMPIF        PIE        NIE        PIS        NIS        CMPOE        CMPRES
  119.         CMPCR2        = B_0000_0000;        //        INVCMPO        DISFLT        LCDTY[5:0]

  120. //        SCON        = B_0101_0000;        //        SM0/FE        SM1        SM2        REN        TB8        RB8        TI        RI
  121.         TMOD        = B_0000_0000;        //        GATE        C/T        M1        M0        GATE        C/T        M1        M0
  122.         TCON        = B_0001_0000;        //        TF1        TR1        TF0        TR0        IE1        IT1        IE0        IT0
  123.         AUXR        = B_1001_0000;        //        T0x12        T1x12        UARTxM6        T2R        T2_C/T        T2x12        EXTRAM        S1ST2
  124.         IE2        = B_0000_0100;        //        -        ET4        ET3        ES4        ES3        ET2        ESPI        ES2
  125.         IP        = B_0000_0010;        //        PPCA        PLVD        PADC        PS        PT1        PX1        PT0        PX0
  126.         IE        = B_1000_0010;        //        EA        ELVD        EADC        ES        ET1        EX1        ET0        EX0

  127.         while (1)
  128.         {
  129.                 if(Show_Mode)
  130.                 {
  131.                         Led_Buf[0]        = Led_Lib(adc_temp[0] >> 4);
  132.                         Led_Buf[1]        = Led_Lib(adc_temp[0] & 0x0f);
  133.                         Led_Buf[2]        = Led_Lib(adc_temp[1] >> 4);
  134.                 }
  135.                 else
  136.                 {
  137.                         if(adc_ok)                //等待ADC结束
  138.                         {
  139.                                 
  140.                                 Led_Temp[1]        = Hex2BCD(Value_H);
  141.                                 Led_Temp[0]        = Led_Temp[1] >> 4;
  142.                                 Led_Temp[1]        |= 0xf0;
  143.                                 Led_Temp[3]        = Hex2BCD(Value_L);
  144.                                 Led_Temp[2]        = Led_Temp[3] >> 4;
  145.                                 Led_Temp[3]        &= 0x0f;
  146.                                 if(adc_value > 999)
  147.                                 {
  148.                                         if(adc_value > 8400)
  149.                                         {
  150.                                                 Led_Buf[0]        = 0x71;
  151.                                                 Led_Buf[1]        = 0x71;
  152.                                                 Led_Buf[2]        = 0x71;
  153.                                         }
  154.                                         else
  155.                                         {
  156.                                                 Led_Buf[0]        = Led_Lib(Led_Temp[0]);
  157.                                                 Led_Buf[1]        = Led_Lib(Led_Temp[1]);
  158.                                                 Led_Buf[2]        = Led_Lib(Led_Temp[2]);
  159.                                         }
  160.                                 }
  161.                                 else
  162.                                 {
  163.                                         if(adc_value > 125)
  164.                                         {
  165.                                                 Led_Buf[0]        = Led_Lib(Led_Temp[1]);
  166.                                                 Led_Buf[1]        = Led_Lib(Led_Temp[2]);
  167.                                                 Led_Buf[2]        = Led_Lib(Led_Temp[3]);
  168.                                         }
  169.                                         else
  170.                                         {
  171.                                                 Led_Buf[0]        = 0x3f;
  172.                                                 Led_Buf[1]        = 0x3f;
  173.                                                 Led_Buf[2]        = 0x3f;
  174.                                         }
  175.                                 }
  176.                                 if(Edit_Mode)        Led_Buf[2] |= 0x80;
  177.                                 adc_ok = 0;                                //清除ADC已结束标志
  178.                         }
  179.                 }
  180.                 if(Key != 0x00)
  181.                 {
  182.                         switch(Key)
  183.                         {
  184.                                 case 0x01:
  185.                                         if(Edit_Mode)        ADC_SCALE -= 16;
  186.                                         if(Show_Mode)        ADC_Multiple -= 1;
  187.                                 break;
  188.                                 case 0x02:
  189.                                         if(Edit_Mode)        ADC_SCALE += 16;
  190.                                         if(Show_Mode)        ADC_Multiple += 1;
  191.                                 break;
  192.                                 case 0x11:
  193.                                         if(!Show_Mode)
  194.                                         {
  195.                                                 if(Edit_Mode)
  196.                                                 {
  197.                                                         Save_ADCSCALE();
  198.                                                         Edit_Mode = 0;
  199.                                                 }
  200.                                                 else        Edit_Mode = 1;
  201.                                         }
  202.                                 break;
  203.                                 case 0x12:
  204.                                         if(!Edit_Mode)        Show_Mode        = ~Show_Mode;
  205.                                 break;
  206.                                 default:
  207.                                 break;
  208.                         }
  209.                         Key = 0x00;
  210.                 }
  211.         }
  212. }

  213. //====================        Timer0        中断函数 (电压检测)        ===============
  214. void Timer0_Routine()        interrupt 1        using 1
  215. {
  216.         if(CMPRES)
  217.         {
  218.                 P_ADC        = 0;
  219.                 adc++;                        //如比较结果为1,则adc计数加1
  220.         }
  221.         else        P_ADC        = 1;
  222.         if(--adc_duty == 0)                        //ADC周期-1, 到0则ADC结束
  223.         {
  224. ……………………

  225. …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
51hei.png
所有代码51hei附件下载:
STC15W201S简易电压表(开漏).7z (508.42 KB, 下载次数: 52)

评分

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

查看全部评分

回复

使用道具 举报

ID:1108581 发表于 2024-1-9 22:48 | 显示全部楼层
大佬牛逼,能写这么多代码,学习学习
回复

使用道具 举报

ID:1064915 发表于 2024-1-12 08:43 | 显示全部楼层
STC15W201S系列单片机引脚图:
20180929112536110.png

回复

使用道具 举报

ID:1064915 发表于 2024-1-12 16:10 | 显示全部楼层
142634ioq09p9avh9p7xyp.png.thumb.png
MCU的工作频率为22.1184MHz.
使用MCU自带的比较器进行ADC转换, 并通过串口输出结果. 用定时器0产生10us中断查询比较器的状态.
使用比较器做ADC, 原理图如下.
做ADC的原理是基于电荷平衡的计数式ADC.
电压从Vin输入, 通过100K+104滤波, 进入比较器的P5.5正输入端, 经过比较器的比较, 将结果输出到P1.5再通过100K+104滤波后送比较器P5.4负输入端,跟输入电压平衡.
设置两个变量: 计数周期(量程)adc_duty 和 比较结果高电平的计数值 adc, adc严格比例于输入电压.
ADC的基准就是P1.5的高电平. 如果高电平准确,比较器的放大倍数足够大,则ADC结果会很准确.
当比较结果为高电平,则P1.5输出1, 并且adc+1.
当比较结果为低电平,则P1.5输出0.
每一次比较都判断计数周期是否完成,完成则adc里的值就是ADC结果.
电荷平衡计数式ADC的性能类似数字万用表用的双积分ADC, 当计数周期为20ms的倍数时,具有很强的抗工频干扰能力,很好的线性和精度.
原理可以参考ADD3501(3 1/2位数字万用表)或ADD3701(3 3/4位数字万用表), 也可以参考AD7740 VFC电路.
例: 比较一次的时间间隔为10us, 量程为10000, 则做1次ADC的时间为100ms. 比较器的响应时间越短, 则完成ADC就越快.
由于要求每次比较时间间隔都要相等,所以用C编程最好在定时器中断里进行, 定时器设置为自动重装, 高优先级中断, 其它中断均低优先级.
用汇编的话, 保证比较输出电平处理的时间要相等.
回复

使用道具 举报

ID:1109333 发表于 2024-1-15 20:51 | 显示全部楼层
看似简单,其实很复杂!
回复

使用道具 举报

ID:200118 发表于 2024-1-19 16:25 | 显示全部楼层
分享电路图就更好
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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