amoBBS 阿莫电子论坛

 找回密码
 注册
搜索
bottom↓
查看: 506|回复: 18

用不带AD的STC15W204S比较器测量电池电压,感觉还行

[复制链接]
发表于 2019-8-18 18:14:33 | 显示全部楼层 |阅读模式
本帖最后由 XTXB 于 2019-8-19 10:55 编辑

入坑多年,还没玩过比较器,加上坛友们一直心心念念着带AD的8脚STC,闲来无事,用不带AD的STC15W204S做了个板子测量电池电压,100mv无压力,感觉够用了。
占用资源:比较器+定时器+2个端口。
端口: CMP+        P55
        P54通过1.5M电阻给1uf电容充电,P55比较电容电压。串口1输出。其他管脚接LED-,低电平点亮。
功能:测量单片机供电电压,将充电时间值adc_val通过串口发送出,用10个LED光柱显示3.3V-4.2V电压。
原理:
        P54输出高电位,通过1M电阻给1u电容充电,P55检测电容电压,与内部BGV Vbgv比较,达到翻转时,记下充电时间T,
        T与电源电压V相关:T=—R*C*ln((V-Vbgv)/V)
        单位:T 秒;R 欧姆;C 法拉;V 伏特;ln 自然对数
        如果R=1.5M C=1uf Vbgv=1.2V V=4.2V T=-(1.5*1000000)*(1/1000000)ln((4.2-1.2)/4.2)=0.504(秒)
        实际充电时间比这个要小,因为在短时间内电容不可能完全放完所有电荷,本试验中根据实际测量的时间标定所需分度值。
步骤:
        1 DC_IN为低电平,将电容的电放掉。
        2 延时2秒。
        3 DC_IN为高电平,充电时间计数器adc_val_cnt复位,在定时器0中开始计数。
        4 比较翻转,记录adc_val_cnt值到adc_val。
        5 将计数值adc_val通过串口发送出,用10个LED光柱显示3.3V-4.2V电压。
        6 开始下一次测量。
调整大小 IMG_20190818_172432.jpg
IMG_20190818_171647.jpg
IMG_20190818_171452.jpg
调整大小 IMG_20190818_171624.jpg


  1. #include "STC15Fxxxx.H"
  2. #define CMPEN   0X80          //比较器使能
  3. #define CMPIF   0X40          //比较器中断标志
  4. #define PIE     0X20          //比较器上升沿中断使能
  5. #define NIE     0X10          //比较器下降沿中断使能
  6. #define PIS     0X08          //比较器正极选择位
  7. #define NIS     0X04          //比较器负极选择位
  8. #define CMPOE   0X02          //比较器结果输出控制位
  9. #define CMPRES  0X01          //比较器比较结果标志位
  10. #define INVCMPO 0X80          //比机器反向输出控制位
  11. #define DISFLT  0X01          //比较器输出端0.1us滤波控制位
  12. #define LCDTY   0X3F          //比价器输出去抖时间
  13. #define OFF 1
  14. #define ON 0
  15. #define DC_IN P54  //被测量电压,这里直接测量单片机的工作电压。
  16. u16 adc_val=0;   //电容充电到比较电压所需时间
  17. u16 adc_val_cnt=0;  //充电时间计数器
  18. u16  dida_cnt=0;//测量周期计数器
  19. bit B_TX1_Busy; //发送忙标志
  20. /********************* 十六进制转ASCII函数 *************************/
  21. unsigned char HEX2ASCII(unsigned char dat)
  22. {
  23.         dat &= 0x0f;
  24.         if (dat <= 9)
  25.                 return (dat + '0'); //数字0~9
  26.         return (dat - 10 + 'A'); //字母A~F
  27. }
  28. //
  29. /*****************向串口发送字节***********************/
  30. void SendData_Uart1(unsigned char dat)
  31. {
  32.         B_TX1_Busy = 1;
  33.         SBUF =dat;
  34.         while(B_TX1_Busy);             //等待发送完成
  35. }
  36. //
  37. /*****************定时器0初始化**********************/
  38. void time0_Int(void)
  39. {
  40.         //1毫秒@11.0592MHz
  41.         EA=0;
  42.         AUXR &= 0x7F;                //定时器时钟12T模式
  43.         TMOD &= 0xF0;               
  44.         TL0 = 0x66;                //设置定时初值
  45.         TH0 = 0xFC;                //设置定时初值
  46.         TF0 = 0;               
  47.         TR0 = 1;                //定时器0开始计时
  48.         ET0 = 1;    //使能定时器0中断
  49.         EA=1;
  50. }
  51. //
  52. /**************串口1波特率初始化***************************/
  53. void Uart1_Sys_Init(void)               
  54. {
  55.                 //9600bps@11.0592MHz 串口1
  56.                 SCON = 0x50;                //8位数据,可变波特率
  57.                 AUXR |= 0x01;                //串口1选择定时器2为波特率发生器
  58.                 AUXR &= 0xFB;                //定时器2时钟为Fosc/12,即12T
  59.                 T2L = 0xE8;                //设定定时初值
  60.                 T2H = 0xFF;                //设定定时初值
  61.                 AUXR |= 0x10;                //启动定时器2
  62.                 ES = 1;     //串行中断 允许控制位
  63.                 REN = 1;    //串口1允许接收
  64.           EA = 1;
  65. }
  66. //
  67. /*****************16位HEX转字符通过串口1发送*********************/
  68. void print_string(u16 dat)
  69. {
  70.                         //SendData_Uart1(HEX2ASCII(dat/1000)); //千位
  71.                         SendData_Uart1(HEX2ASCII(dat%1000/100)); //百位
  72.                         SendData_Uart1(HEX2ASCII(dat%100/10)); //十位
  73.                         SendData_Uart1(HEX2ASCII(dat%10)); //个位
  74.                         SendData_Uart1(0x0d);
  75.                         SendData_Uart1(0x0a);       
  76. }
  77. //
  78. void main(void)
  79. {
  80.         static bit CMP_Flag=0;
  81.         //端口设置
  82.         P3M1=B0000_0000;   //00上拉准双向口/10浮空输入  
  83.         P3M0=B0000_0000;   //11开漏输出/01推挽输出
  84.         P5M1=B0000_0000;      //00上拉准双向口/10浮空输入
  85.         P5M0=B0000_0000;      //11开漏输出/01推挽输出       
  86.         DC_IN=0;       
  87.         Uart1_Sys_Init();
  88.         time0_Int();
  89.                 CMPCR1=0;             //比较器寄存器1初始化
  90.                 CMPCR2=0;             //比较器寄存器2初始化
  91.                 CMPCR1&=~PIS;                  //=0选择P55脚为比较器的正极输入端
  92.                 CMPCR1&=~NIS;         //=0选择内部BandGap电压BGV作为比较器负极输入源
  93.                 CMPCR1&=~CMPOE;                  //禁止比较器的结果输出
  94.                 CMPCR2&=~INVCMPO;          //比机器结果正常输出
  95.                 CMPCR2|=DISFLT;          //不使能比较器输出0.1us滤波
  96.                 CMPCR2&=~LCDTY;       //比较器结果不去抖 直接输出
  97.                 CMPCR1|=CMPEN;        //使能比较器
  98.         while (1)
  99.         {
  100.                         if(dida_cnt==10)
  101.                         {
  102.                                 DC_IN=0;                //电容放电
  103.                         }               
  104.                         if(dida_cnt==2000)
  105.                         {
  106.                                 DC_IN=1;                //2秒后给电容充电
  107.                                 adc_val_cnt=0;  //充电时间计数器复位
  108.                                 CMP_Flag=1;               
  109.                         }       
  110.                         if(dida_cnt>3000)
  111.                         {
  112.                                 dida_cnt=0;
  113.                                 print_string(adc_val);
  114.                         }
  115.                        
  116.                         if(dida_cnt>2000)
  117.                         {               
  118.                                  if(CMPCR1&CMPRES)        //比较器输出高电平
  119.                                  {
  120.                                          if(CMP_Flag==1)
  121.                                          {
  122.                                                         CMP_Flag=0;                                         
  123.                                                         adc_val=adc_val_cnt;
  124.                                          }
  125.                                  }       
  126.                         }
  127.                 //根据实测标定,还没有精调
  128.                 if(adc_val<290)        P12=ON;else P12=OFF;                //4.2V
  129.                 if(adc_val<302)        P13=ON;else P13=OFF;                //4.1V
  130.                 if(adc_val<312)        P14=ON;else P14=OFF;                //4.0V
  131.                 if(adc_val<335)        P15=ON;else P15=OFF;                //3.9V
  132.                 if(adc_val<348)        P11=ON;else P11=OFF;                //3.8V               
  133.                 if(adc_val<365)        P10=ON;else P10=OFF;                //3.7V
  134.                 if(adc_val<405)        P37=ON;else P37=OFF;                //3.6V       
  135.                 if(adc_val<428)        P36=ON;else P36=OFF;                //3.5V
  136.                 if(adc_val<443)        P33=ON;else P33=OFF;                //3.4V       
  137.                 if(adc_val<478)        P32=ON;else P32=OFF;                //3.3V
  138.         }
  139. }
  140. //定时0中断
  141. void Timer_Isr (void) interrupt 1
  142. {
  143.   adc_val_cnt++;//充电时间计数
  144.   dida_cnt++;//测量周期计数器
  145. }
  146. //
  147. void UART1_Interrupt(void) interrupt 4
  148. {               
  149.         if(RI)       
  150.         {
  151.                 TI=0;
  152.         }
  153.         if(TI)
  154.         {
  155.                 TI = 0;
  156.                 B_TX1_Busy = 0;
  157.         }
  158. }
  159. //
复制代码
发表于 2019-8-18 19:08:15 | 显示全部楼层
感谢分享玩到极致的做法
发表于 2019-8-18 19:47:34 | 显示全部楼层
鸡你太美 贝贝
发表于 2019-8-18 20:13:19 | 显示全部楼层
这个电容和电阻精度要求很高吧?另外不同温度下还能这么准吗?
发表于 2019-8-18 22:27:00 来自手机 | 显示全部楼层
费劲         
发表于 2019-8-18 23:04:59 来自手机 | 显示全部楼层
用cbb类的薄膜电容,回很准确
发表于 2019-8-18 23:09:57 | 显示全部楼层
至少多了不少电阻和PCB面积吧,感觉要是带一路8位的AD就在一些简易的应用中就够了。
发表于 2019-8-18 23:16:03 | 显示全部楼层
单片机都发展到现在了,还至于要这么玩吗?
发表于 2019-8-19 07:44:02 | 显示全部楼层
Stc 以前一直在推广用比较器当AD用,他们说这样省成本,论坛里面还有其他的推广的帖子。
发表于 2019-8-19 07:45:22 | 显示全部楼层
这样做到6位ADC   就是64级吗?
 楼主| 发表于 2019-8-19 08:05:35 | 显示全部楼层
本帖最后由 XTXB 于 2019-8-19 08:30 编辑
工程师030 发表于 2019-8-18 20:13
这个电容和电阻精度要求很高吧?另外不同温度下还能这么准吗?


我用的就是普通电阻电容,刚才用烙铁烫了一下估计有60多度吧,读数没有变化,可能100mv的精度本来就是粗线条,比较扛糙吧
 楼主| 发表于 2019-8-19 08:28:48 | 显示全部楼层
本帖最后由 XTXB 于 2019-8-19 09:22 编辑
linghu886 发表于 2019-8-19 07:45
这样做到6位ADC   就是64级吗?


个人理解,这种AD测量方式理论上没有级别限制,缺点就是转换速度慢,非线性,需要标定。
发表于 2019-8-19 09:40:26 | 显示全部楼层
XTXB 发表于 2019-8-19 08:28
个人理解,这种AD测量方式理论上没有级别限制,缺点就是转换速度慢,非线性,需要标定。 ...

高手学习了,请问这种芯片有多便宜呢?
 楼主| 发表于 2019-8-19 10:47:11 | 显示全部楼层
本帖最后由 XTXB 于 2019-8-19 10:51 编辑
工程师030 发表于 2019-8-19 09:40
高手学习了,请问这种芯片有多便宜呢?


见笑了,我就是个业余菜鸟,玩玩而已。
360截图-10946808.jpg
发表于 2019-8-19 15:07:47 | 显示全部楼层
STC有例程,用比较器做ADC,电荷平衡方式,电容的精度不影响ADC精度,比如那个电容可以用104,也可以用224,程序不需要任何改动,不影响精度。
发表于 2019-8-19 15:16:05 | 显示全部楼层
学习了   学习了    价格和STM8s003 差不多      STC 的好处就是价格比较稳定   ST 鬼知道那天又闹腾
发表于 2019-9-5 11:41:20 | 显示全部楼层
XTXB 发表于 2019-8-19 10:47
见笑了,我就是个业余菜鸟,玩玩而已。

你好!!方便留个联系方式吗???有个问题想请教下
发表于 2019-9-5 11:51:51 来自手机 | 显示全部楼层
我也正在想做一个采集电压和电流的东西
友情提示:标题不合格、重复发帖,将会被封锁ID。详情请参考:论坛通告:封锁ID、获得注册邀请码、恢复被封ID、投诉必读
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|阿莫电子论坛(原ourAVR/ourDEV) ( 公安备案:44190002001997(交互式论坛) 工信部备案:粤ICP备09047143号 )

GMT+8, 2019-9-15 18:57

阿莫电子论坛, 原"中国电子开发网"

© 2004-2018 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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