找回密码
 立即注册

QQ登录

只需一步,快速开始

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

STC15F104W单片机利用RC充放电使用IO口测量外部电压

[复制链接]
ID:245767 发表于 2018-12-21 15:00 | 显示全部楼层 |阅读模式
宏晶科技STC15F系列单片机
RC充放电实现检测外部电压

对于没有ADC的MCU,而又要测量外部的一个电压时,使用RC充放电的方式是比较容易实现并且低成本的方法。
STC15F系列是1T的MCU,其IO口有OPEN-DRAIN模式,此模式可以很容易用一个IO口配合一个定时器实现RC充放电来测量外部未知电压。如果没有空余的定时器,也可以使用指令循环的方式实现。本例使用定时器。
本范例使用P3.2(INT0)来做RC测量,电路和波形示意图如下:
操作流程:
  • 初始化程序将P3.2设置成OPEN-DRAIN模式, 并将P3.2输出0给电容放电。INT0设置成上升沿中断。Timer 0设置成16位自动重装定时器模式,时钟源为12T,允许中断。
  • 测量时,先清Timer 0TH0TL0,然后将P3.2输出1开始对电容充电,接着设置TR0 = 1来启动Timer 0,然后在INT0中断里设置TR0 = 0来停止计数,并将P3.2输出0对电容放电。读出TH0TL0的值就是RC充电时间。
由于MCU工作在5V时,IO口读到“1”的门限电压大约为2V,所以要求输入的电压高于2V,本例的测试数据从4~12.4V,测试结果参考后面的附录1。
假设输入电压为Ux,IO口门限电压为2V,则RC充电时间为:
                            T =  - R * C * ln ( 1 – 2 / Ux )
按图示参数,当输入为10V时,RC时间大约为446uS,附录1中实测为447uS。
由于RC时间跟R和C有关,而R的温漂一般较小,但普通电容的温漂较大,所以要使用温漂小并且漏电也小的电容。
由充电公式或曲线图可知,Ux和RC值的关系是非线性的,所以实际项目使用时,要根据自己的实际电路做一些标定,这样可以得到比较准确的值。
本方法适用于对测量精度要求不是很高的场合。

附录1:测量结果和曲线

输入电压(V)
Timer 0读数
时间uS(18.432MHZ)
4
2100
1367
4.2
1966
1280
4.4
1846
1202
4.6
1740
1133
4.8
1646
1072
5
1560
1016
5.2
1487
968
5.4
1416
922
5.6
1354
882
5.8
1297
844
6
1245
811
6.2
1196
779
6.4
1150
749
6.6
1110
723
6.8
1070
697
7
1033
673
7.2
1000
651
7.4
968
630
7.6
938
611
7.8
910
592
8
884
576
8.2
860
560
8.4
836
544
8.6
814
530
8.8
793
516
9
772
503
9.2
754
491
9.4
736
479
9.6
718
467
9.8
702
457
10
686
447
10.2
671
437
10.4
656
427
10.6
642
418
10.8
629
410
11
616
401
11.2
604
393
11.4
592
385
11.6
581
378
11.8
570
371
12
560
365
12.2
550
358
12.4
540
352
附录2C语言程序
  1. /*

  2. 功能描述: 使用STC15F系列C版本做的RC测量电压的例子.

  3. */

  4. #include "reg51.h"

  5. #define MAIN_Fosc                            22118400L              //定义主时钟

  6. #define              uchar              unsigned char
  7. #define uint              unsigned int

  8. sfr AUXR = 0x8e;    //Auxiliary register
  9. sfr P3M1  = 0xB1;              //P3M1.N,P3M0.N               =00--->Standard,              01--->push-pull
  10. sfr P3M0  = 0xB2;              //                                                                      =10--->pure input,              11--->open drain

  11. sbit              P_TXD1  = P3^1;              //定义模拟串口发送脚,打印信息用
  12. sbit    P_RC = P3^2;              //RC port
  13. uchar              SampleCnt;                            //发送结果的采样间隔计数
  14. uchar              LineCnt;                            //每行显示结果计数
  15. bit                            B_Over;                                          //超时标志
  16. bit                            B_ADC_OK;                            //检测完成标志
  17. uint              adc;                                          //RC做的ADC值

  18. void              RC_start(void);
  19. void              Tx1Send(uchar dat);
  20. void               InitTimer(void);
  21. void              delay_ms(unsigned char ms);

  22. ///////////////////////////////////////////////////////////

  23. void main(void)
  24. {

  25.               InitTimer();                            //初始化Timer

  26.     P3M1 |=  1 << 2;         //P3.2 config as Open-Drain
  27.               P3M0 |=  1 << 2;
  28.               P_RC = 0;               //Clear RC port to 0
  29. //    TMOD |= 0x00;           //T0 as 16 bits timer, auto reload

  30.     while (1)
  31.     {
  32.         delay_ms(5);   //放电时间
  33.                            
  34.                             B_ADC_OK = 0;                            //清除ADC结束标志
  35.                             B_Over = 0;                                          //清除超量程标志
  36.         RC_start();           //RC charge-decharge
  37.                             while(!B_ADC_OK && !B_Over)              ;              //等待ADC结束或超时
  38.                             if(B_ADC_OK)
  39.                             {
  40.                                           if(++SampleCnt >= 100)              //1秒钟发一个结果给串口
  41.                                           {
  42.                                                         SampleCnt = 0;
  43.                                                         Tx1Send(adc / 10000 + '0');           //send to PC from the UART
  44.                                                         Tx1Send(adc % 10000 / 1000 + '0');
  45.                                                         Tx1Send(adc % 1000 / 100 + '0');
  46.                                                         Tx1Send(adc % 100 / 10 + '0');
  47.                                                         Tx1Send(adc % 10 + '0');
  48.                                                         Tx1Send(' ');
  49.                                                         Tx1Send(' ');
  50.                                                         if(++LineCnt >= 10)                            //10个结果后换行
  51.                                                         {
  52.                                                                       LineCnt = 0;
  53.                                                                       Tx1Send(0x0d);   //send CR
  54.                                                                       Tx1Send(0x0a);
  55.                                                         }
  56.                                           }
  57.                             }
  58.     }
  59. }


  60. ///////////////////////////////////////////////////////////

  61. //============================================================
  62. // 函数: void  delay_ms(unsigned char ms)
  63. // 描述: 延时函数。
  64. // 参数: ms,要延时的ms数.
  65. // 返回: none.
  66. // 版本: VER1.0
  67. // 日期: 2010-12-15
  68. // 备注:
  69. //============================================================
  70. void  delay_ms(unsigned char ms)
  71. {
  72.      unsigned int i;
  73.               do
  74.               {
  75.                             i = MAIN_Fosc / 14000L;              //1T
  76.                             while(--i)              ;   //13T per loop
  77.      }while(--ms);
  78. }


  79. /**************** RC启动函数 ******************************/
  80. void   RC_start(void)
  81. {
  82.                         //使用Timer 0 计时
  83.     TH0 = 0;            //clear Timer 0
  84.     TL0 = 0;
  85.               B_Over = 0;                                          //Clear Over flag
  86.     P_RC = 1;           //RC charge
  87.     TR0 = 1;            //enable Timer 0
  88.               IE0 = 0;                                          //Clear INT0 flag
  89.               EX0 = 1;                            //INT0 Enable
  90.               IT0 = 0;                            //INT0 上升,下降沿中断            
  91. }

  92. /********************* INT0中断函数 *************************/
  93. void INT0_int (void) interrupt 0                            //
  94. {
  95.     if(INT0 && !B_Over)                            //上升沿中断,无超时
  96.               {
  97.                             TR0 = 0;            //deable Timer 0
  98.                             P_RC = 0;           //decharge
  99.                             adc = TH0;            //read the RC time
  100.                             adc = (adc << 8) + TL0;
  101.                             B_ADC_OK = 1;                            //标志ADC结束
  102.               }
  103. }


  104. /**************** Timer初始化函数 ******************************/
  105. void InitTimer(void)
  106. {
  107.               TMOD = 0;                            //for STC15Fxxx系列              Timer0 as 16bit reload timer.
  108.               TH0 = 0;
  109.               TL0 = 0;
  110.               ET0 = 1;                            //允许Timer0中断
  111.               TR0 = 0;
  112.               EA  = 1;                            //允许总中断
  113. }

  114. /********************** Timer0中断函数************************/
  115. void timer0 (void) interrupt 1
  116. {
  117.               TR0 = 0;              //超量程关闭
  118.               B_Over = 1;              //标志超量程
  119. }


  120. /********************** 模拟串口相关函数************************/

  121. void              BitTime(void)              //位时间函数
  122. {
  123.               uint i;
  124.               i = ((MAIN_Fosc / 100) * 104) / 130000L - 1;                            //根据主时钟来计算位时间
  125.               while(--i);
  126. }

  127. //模拟串口发送
  128. void              Tx1Send(uchar dat)                            //9600,N,8,1                            发送一个字节
  129. {
  130.               uchar              i;
  131.               EA = 0;
  132.               P_TXD1 = 0;
  133.               BitTime();
  134.               for(i=0; i<8; i++)
  135.               {
  136.                             if(dat & 1)                            P_TXD1 = 1;
  137.                             else                                          P_TXD1 = 0;
  138.                             dat >>= 1;
  139.                             BitTime();
  140.               }
  141.               P_TXD1 = 1;
  142.               EA = 1;
  143.               BitTime();
  144.               BitTime();
  145. }
复制代码

完整的Word格式文档51黑下载地址:
STC15F104W利用RC充放电使用IO口测量外部电压.DOC (99.5 KB, 下载次数: 40)


评分

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

查看全部评分

回复

使用道具 举报

ID:424598 发表于 2018-12-21 18:31 | 显示全部楼层
有实物图吗?
回复

使用道具 举报

ID:67274 发表于 2020-4-2 19:56 | 显示全部楼层
本帖最后由 a461624201 于 2020-4-2 20:18 编辑

图为5v电压,然后取最大位的时间数,判定为5V.循环指令,一个nop,就是一个指令时间,1us/次,对于STC15F104,

1.png
回复

使用道具 举报

ID:67274 发表于 2020-4-2 20:14 | 显示全部楼层
这个代码,连接1637后,就会死机, 贴上不使用定时器和中断,循环指令RC测量代码,
while(1){                dianrong_jiao = 0;               
                           delay_ms(5);

                   sj=0          ;       
                 
                    
             dianrong_jiao = 1;           
       
                while(dianrong_jiao==0){          _nop_(); sj++;   }
回复

使用道具 举报

ID:67274 发表于 2020-4-2 20:27 | 显示全部楼层
其实,无论是定时器中断还是循环nop,另一方面,测量瓷片电容,还是能粗略判断电容的容量的,有种简单电容表的感觉。
回复

使用道具 举报

ID:67274 发表于 2020-4-3 00:08 | 显示全部楼层
不用STC15F104中断的,可以接TM1637数码管的while程序。  
while (1)
    { P_RC= 0;

        Delay1us(5);
                B_ADC_OK= 0;                //清除ADC结束标志
                                  

                TH0 = 0;            //clear Timer 0
        TL0 = 0;
             B_Over= 0;                        //Clear Over flag
   
            P_RC= 1;           //RC charge
        TR0 = 1;  
                ET0 = 1;                //允许Timer0中断
            EA  = 1;                //允许总中断
       while( P_RC==0 ){ };
       
           if( B_Over==0){
                   TR0 = 0;            //关闭定时器
                 ET0 = 0;                 
            EA  = 0;                 
            
             adc = TH0;            //取数据
                adc = (adc << 8) + TL0;
                B_ADC_OK= 1;         //标志ADC结束       
                 }
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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