数码之家

 找回密码
 立即注册
搜索
查看: 10478|回复: 53

[C51] 做个锂电供电的低功耗WIFI数码管时钟

[复制链接]
发表于 2019-9-4 11:27:05 | 显示全部楼层 |阅读模式

爱科技、爱创意、爱折腾、爱极致,我们都是技术控

您需要 登录 才可以下载或查看,没有账号?立即注册

x
2年前用8266做过WIFI自动对时时钟,直接用nodemcu/lua控制595,用起来还不错,但手摸总是温温的,接个电流表大概是70ma左右,这种功耗肯定没法接电池了,一直拖着个5V电源线用着。前些天从抽屉底找到一块1000ma的手机电池,手机不知道去哪里了,于是想想怎么可以把电池用上,干掉数码管时钟的电源线,中间折腾就不说了,最后做成的时钟正常运行时是大概1.2ma,估计充满电能用1个月左右,目前稳定跑了2周,目测还行。

IMG_20190904_090134.jpg

上面是时钟,下面是电池。中间两根排针直接输出大概4.0v(锂电是3.7-4.2,测试3.7v时 8266还能工作,再低也许会有问题,该充电了)为电路板供电,加了个tp4056充电带保护小板(网上2-3块钱到处都是)埋在热熔胶里面,旁边留出usb充电口。原本打算打印个像样的外壳,一直没时间弄,目前直接插着用,起码功能是达到了。
原理比较简单,tm1650控制数码管,stc51单片机处于掉电状态,每秒唤醒一次刷新tm1650,主要是为了中间的两个点闪烁。但51本身掉电时定时器误差很大,每分钟从ds1302读取一次时间校正,也保证分钟的显示基本正确。当然ds1302的误差也不小,所以通过51控制esp8266,每天对时两次,校正ds1302的时间。这样基本上误差也就是1-2秒,对于一个以显示分钟的时钟来说并没什么问题。
至于耗电,主要就是数码管和tm1650和stc15每秒的刷新,万用表实测1.2-1.3ma左右,esp8266对时耗电倒是比较高,大概有80-100ma,但一般10秒内就完成,信号好时3-5秒,然后自己去深度睡眠了,影响不大。

发个电路图

d1.png

电路里面除了电源接口排针,其他的按键还有排针都是为了刷stc51和8266,都是焊好了再调程序,而且这样改程序也方便。

代码附后,tm1650和ds1302驱动都是网上抄回来改的,因为类似的代码太多搞不清来源了,如果有人觉得不妥可以留言沟通。
电路设计和主程序妥妥是自己写的,引用请写出处。

楼下更新代码:




补充内容 (2019-9-30 15:15):
经过近一个月的使用,1块1000ma的手机锂电支持运行时间是20天,到时会因为电压太低锂电保护板截止而关闭,直接充电就好。

打赏

参与人数 3家元 +122 收起 理由
家睦 + 100
飞向狙沙 + 16 謝謝分享
xiaoqi233 + 6

查看全部打赏

发表于 2019-9-4 12:24:58 | 显示全部楼层
这电路不重要 ,,代码重要
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-9-4 12:32:05 | 显示全部楼层
本帖最后由 spirndai 于 2019-9-5 10:49 编辑

贴上8266的代码,其中和51通信用了nodemcu 的 ow(one-wire)模块,但没按标准方法使用(ow那根线没接上拉电阻,用标准ow方法可能有问题,中断的延迟和冲突也不好处理),51那边有相应的接收代码,用来做标准ow接收应该是可以的。


  1. IOPIN = 2            
  2. ow.setup(IOPIN)
  3. gpio.mode(IOPIN,gpio.OUTPUT);
  4. gpio.write(IOPIN,gpio.HIGH)

  5. function initall()
  6.         local sta_config=wifi.sta.getconfig(true)
  7.         if sta_config==nil or sta_config.ssid==nil or sta_config.ssid=="" then
  8.                 print("init1...")
  9.                 wifi.sta.autoconnect(0)
  10.                 wifi.setmode(wifi.STATION,false)
  11.                 station_cfg={}
  12.                 station_cfg.ssid="ap 名称"
  13.                 station_cfg.pwd="ap 密码"
  14.                 station_cfg.save=true
  15.                 station_cfg.auto=false
  16.                 wifi.sta.config(station_cfg)
  17.                 wifi.sta.connect(function(a,b,c) print("conn0 ok") end)
  18.         else
  19.                 print("init2...",sta_config.ssid)
  20.                 wifi.setmode(wifi.STATION,false)
  21.                 wifi.sta.connect(function(a,b,c) print("conn1 ok") end)
  22.         end

  23. end
  24. initall()
  25. conn_wifi = tmr.create()

  26. local function  num2bcd( dec)
  27.         return (dec%10)+(dec/10)*16
  28. end
  29. function out_bytes()


  30.         gpio.write(IOPIN,gpio.HIGH);
  31.         gpio.write(IOPIN,gpio.LOW);
  32.         tmr.delay(2000000); -- 2s

  33.         local tm = rtctime.epoch2cal(rtctime.get()+8*3600)
  34.         b = {tm["sec"],tm["min"],tm["hour"],tm["day"],tm["mon"],tm["wday"],tm["year"]-2000};
  35.         csum = 0
  36.         for pos=1,#b do
  37.                 b[pos]=num2bcd(b[pos])
  38.                 csum = bit.bxor(csum,b[pos])
  39.         end
  40.         gpio.write(IOPIN,gpio.HIGH);
  41.         for pos=1,#b do
  42.                 ow.write(IOPIN,b[pos],1);
  43.         end
  44.         ow.write(IOPIN,csum,1);
  45.         print("output ok");
  46.         gpio.mode(IOPIN,gpio.OUTPUT);
  47.         gpio.write(IOPIN,gpio.HIGH)
  48. end
  49. function output_time()

  50.         local time_start = rtctime.get()+8*3600
  51.         print(time_start);
  52.         if (time_start>1566159285) then
  53.                 out_bytes();
  54.         else
  55.                 print("time error")
  56.         end
  57.         node.dsleep(0)
  58. end

  59. count=0
  60. conn_wifi:register(1000,tmr.ALARM_AUTO, function()
  61.         count= count + 1
  62.         if count>30 then
  63.                 conn_wifi:stop()
  64.                 print("fail")
  65.                 return
  66.         end
  67.         print("syncing ",wifi.sta.status())
  68.         if wifi.sta.status()~=wifi.STA_GOTIP then
  69.                 local rr =    wifi.sta.status()
  70.                 if rr== wifi.STA_CONNECTING then
  71.                         print("connect...")
  72.                 elseif rr == wifi.STA_WRONGPWD then
  73.                         print("wrong pass")
  74.                 elseif rr== wifi.STA_APNOTFOUND then
  75.                         print("ap not found")
  76.                 elseif rr== wifi.STA_FAIL then
  77.                         print("sta fail")
  78.                 end
  79.                 if rr~= wifi.STA_CONNECTING then
  80.                         wifi.sta.connect(function(a,b,c) print("conn1 ok") end)
  81.                 end
  82.                 return
  83.         end
  84.         conn_wifi:stop()
  85.         sntp.sync(nil,
  86.                 function(a,b,c,d)
  87.                         print("sync ok")
  88.                         output_time()
  89.                 end,
  90.                 function(a,b)
  91.                         conn_wifi:start()
  92.                 end
  93.         )

  94. end)

  95. conn_wifi:start()
复制代码
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-9-4 12:39:21 | 显示全部楼层
本帖最后由 spirndai 于 2019-9-4 12:58 编辑

51主控代码

  1. #include "stc15.h"
  2. /*
  3. #include "C:\Keil\C51\INC\STC\STC15.H"
  4. #include "intrins.h"

  5. */
  6. #include "tm1650.h"
  7. #include "ds1302.h"


  8. #define ESP12_EN P31
  9. #define ESP12 P35


  10. uchar dec2bcd(uchar dec){
  11.         return (dec%10)+(dec/10)*16;
  12. }
  13. uchar   bcd2dec(uchar num){
  14.         return ((num/16 * 10) + (num % 16));
  15. }

  16. int fixtime = 1750 ;
  17. void  powerdown()
  18. {
  19.         WKTCL = fixtime&0xff;           //about 500us for 1
  20.         WKTCH = 0x80|(fixtime>>8);           //最高位必须为1,0x80,即使能掉电唤醒控制位为1
  21.         PCON = 0x02;            //进入掉电模式
  22.         _nop_();               
  23.         _nop_();

  24. }




  25. uchar int3_happend = 0 ;
  26. uchar time_rev_buf[8] ;

  27. void handle_ow(){
  28.         uchar tmp = 0;
  29.         uchar i;

  30.         int3_happend = 0 ;

  31.         while (ESP12==0 && ++tmp<0xff)  ;
  32.         if (tmp<150) goto endint;
  33.         while (ESP12==0) ;
  34.         for (i=0;i<64;i++){
  35.                 while (ESP12==1) ;
  36.                 tmp=39; while (--tmp) ; //15us
  37.                 tmp = i>>3  ;
  38.                 time_rev_buf[tmp] >>= 1;
  39.                 if (ESP12==1) time_rev_buf[tmp] |= 0x80 ;
  40.                 else while (ESP12==0) ;
  41.         }
  42.         tmp = 0 ;
  43.         for (i=0;i<8;i++) tmp = tmp ^ time_rev_buf[i] ;
  44.         if (tmp==0){
  45.                 for (i=0;i<7;i++) ds1302_timebuf[i] = time_rev_buf[i] ;
  46.                 write_ds1302_time();
  47.         }

  48. endint:
  49.         ESP12_EN = 0 ;
  50. }


  51. void main(){


  52.         int tmp ;
  53.         uchar point ;

  54.         P3M0 = 0x02 ; // P31控制esp12,电流强一些也许比较好
  55.         P3M1 = 0 ;
  56.         get_ds1302_time();



  57.         ESP12_EN = 0 ;      
  58.         while (ds1302_timebuf[4]==0){
  59.                 powerdown();
  60.                 ESP12_EN = 1 ;
  61.                 if (ESP12==0) handle_ow();
  62.         }


  63.         while(1)
  64.         {


  65.                 if (((ds1302_timebuf[2]&0xf)==5) && ds1302_timebuf[1]==0x59 && ds1302_timebuf[0]<5){
  66.                         ESP12_EN = 1 ;
  67.                 }
  68.                 if (ds1302_timebuf[0]>0x55 ){
  69.                         ESP12_EN = 0 ;
  70.                 }
  71.                 if (ESP12==0) handle_ow();
  72.                 powerdown();
  73.                 if (ESP12==0) handle_ow();



  74.                 tmp = bcd2dec(ds1302_timebuf[0]);
  75.                 if (++tmp>=60){
  76.                         get_ds1302_time();
  77.                         tmp = bcd2dec(ds1302_timebuf[0]);
  78.                         if (tmp>50) fixtime += 20;
  79.                         else if (tmp>1) fixtime -= 10;
  80.                         else if (tmp==1) fixtime-=3;
  81.                         if (fixtime>2000 || fixtime<1500) fixtime=1750;
  82.                 }else{
  83.                         ds1302_timebuf[0] = dec2bcd(tmp);
  84.                 }

  85.                 if (ESP12==0) handle_ow();

  86.                         point = ds1302_timebuf[0]&1;
  87.                         point = point << 7 ;
  88.                         tmp = ds1302_timebuf[2];
  89.                         if (tmp<=0x8)
  90.                                 LED_print(0x48,16,0); // level 3 bright
  91.                         else
  92.                                 LED_print(0x48,18,0); // level 7 bright
  93.                         if (tmp>=0x10)
  94.                                 LED_print (0x68, tmp>>4,0);
  95.                         else
  96.                                 LED_print (0x68, 17,0); // close it
  97.                         LED_print (0x6a, tmp&0xf,point);
  98.                         tmp = ds1302_timebuf[1];
  99.                         LED_print (0x6c, tmp>>4,point);
  100.                         LED_print (0x6e, tmp&0xf,0);
  101.            


  102.         }
  103. }
复制代码

ds1302驱动
  1. #include "stc15.h"
  2. #include "ds1302.h"

  3. unsigned char ds1302_addr[7]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};   //写的地址
  4. unsigned char  ds1302_timebuf[7]={0,0,0,0,0,0,0}; //秒分时 日月周年

  5. void writeByte(uchar dat){
  6.         uchar i;
  7.         for(i=0;i<8;i++) //传送8位地址,往哪里写
  8.         {
  9.                 DS_CLK=0; //再置为低,以便实现上升沿
  10.                 _nop_(); DS_IO=dat&0x01; //先传送最低位
  11.                 dat>>=1; //向右移位,把8位数据依次写入
  12.                 DS_CLK=1; //数据在上升沿时,写入数据
  13.                 _nop_();
  14.         }

  15. }
  16. void write(uchar addr,uchar dat) {
  17.         DS_CLK=0; _nop_();
  18.         DS_EN=0; _nop_(); DS_EN=1; //拉高,准备写地址读数据
  19.         _nop_(); _nop_();
  20.         writeByte(addr);
  21.         writeByte(dat);
  22.         DS_EN=0; //复位
  23. }
  24. uchar readByte(){
  25.         uchar i,dat=0;
  26.         for(i=0;i<8;i++) //读出8位数据
  27.         {
  28.                 DS_CLK=1; //置为高,以便实现下降沿
  29.                 _nop_();
  30.                 dat>>=1; //向右移位,把8位数据依次读出
  31.                 DS_CLK=0; //读数据时,下降沿有效,这时可以读取数据
  32.                 if(DS_IO==1) dat=dat|0x80;
  33.                 _nop_();
  34.         }
  35.         return dat ;
  36. }
  37. uchar read(uchar addr) {
  38.         uchar dat ;
  39.         DS_CLK=0; _nop_();
  40.         DS_EN=0; _nop_(); DS_EN=1; //拉高,准备写地址读数据
  41.         _nop_(); _nop_();
  42.         writeByte(addr);

  43.         dat = readByte();
  44.         DS_EN=0; //复位
  45.         _nop_();
  46.         DS_CLK = 1; _nop_();  DS_IO = 0; _nop_();  DS_IO = 1; _nop_();
  47.         return dat; //返回读到的数据
  48. }
  49. void write_ds1302_time(){
  50.         char n;
  51.         write(0x8e,0);
  52.         for(n=1;n<7;n++){
  53.                 write (ds1302_addr[n],ds1302_timebuf[n]);
  54.         }
  55.         write (ds1302_addr[0],ds1302_timebuf[0]);
  56.         write(0x8e,0x80);
  57. }


  58. void get_ds1302_time(){
  59.         uchar n;
  60.         for(n=0;n<7;n++)
  61.                 ds1302_timebuf[n]=read(ds1302_addr[n]+1);   //读取分秒时日月周年
  62.         ds1302_timebuf[0] = ds1302_timebuf[0] & 0x7f ;
  63. }

复制代码

tm1650驱动
  1. #include "stc15.h"
  2. /*
  3. #include "C:\Keil\C51\INC\STC\STC15.H"
  4. #include "intrins.h"
  5. */
  6. #include "tm1650.h"

  7. #define  Pt_TM1650_SDAM    P3M1
  8. #define  Pt_TM1650_SDA     P34
  9. #define  Pt_TM1650_SCL     P30
  10. #define  DIO     P34

  11. #define  SDA_TM1650_IN     Pt_TM1650_SDAM=0x10  //
  12. #define  SDA_TM1650_OUT    Pt_TM1650_SDAM=0  //

  13. #define  DIO_H         Pt_TM1650_SDA=1   //
  14. #define  DIO_L         Pt_TM1650_SDA=0   //
  15. #define  CLK_H         Pt_TM1650_SCL=1   //
  16. #define  CLK_L         Pt_TM1650_SCL=0   //
  17. #define  W5NOP          _nop_();_nop_();_nop_();_nop_();_nop_();
  18. #define uint8 unsigned char

  19. void Delay_us(unsigned char k)          //@11.0592MHz
  20. {

  21.         _nop_();
  22.         k <<= 1;
  23.         while (--k);
  24. }

  25. void I2CStart(void)//开始信号
  26. {
  27.         CLK_H;
  28.         DIO_H;
  29.         Delay_us(5);
  30.         DIO_L;

  31. }

  32. void I2Cask(void) {
  33.         uint8 timeout = 1;
  34.         CLK_H;
  35.         Delay_us(5);
  36.         CLK_L;
  37.         while((DIO)&&(timeout<=100))
  38.         {
  39.                   timeout++;
  40.         }
  41.         Delay_us(5);
  42.         CLK_L;
  43. }

  44. void I2CStop(void) //停止信号
  45. {
  46.         CLK_H;
  47.         DIO_L;
  48.         Delay_us(5);
  49.         DIO_H;
  50. }

  51. void I2CWrByte(uint8 oneByte) {
  52.         uint8 i;
  53.         CLK_L;
  54.         Delay_us(1);
  55.         for(i=0;i<8;i++)
  56.         {
  57.                 oneByte = oneByte<<1;
  58.                 DIO = CY;
  59.                 CLK_L;
  60.                 Delay_us(5);
  61.                 CLK_H;
  62.                 Delay_us(5);
  63.                 CLK_L;
  64.         }
  65. }
  66. unsigned char LED_0F[] =
  67. {
  68.         //0   1     2     3     4     5     6     7     8     9     A     b     C     d     E     F     -
  69.         0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,                0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x31,0,0x71
  70. };

  71. void LED_print(uint8 Address, uint8 dat,uint8 p) //数码管显示
  72. {
  73.         //写显存必须从高地址开始写
  74.         I2CStart();
  75.         I2CWrByte(Address); //第一个显存地址
  76.         I2Cask();
  77.         I2CWrByte(LED_0F[dat] | p);
  78.         I2Cask();
  79.         I2CStop();
  80. }
复制代码
回复 支持 反对

使用道具 举报

发表于 2019-9-5 08:54:40 | 显示全部楼层
高手,这是高手,软硬兼施。
回复 支持 1 反对 0

使用道具 举报

发表于 2019-9-5 11:13:39 | 显示全部楼层
8266固件用啥的呢。。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-9-5 12:13:08 | 显示全部楼层
本帖最后由 spirndai 于 2019-9-5 13:34 编辑
jpdd521 发表于 2019-9-5 11:13
8266固件用啥的呢。。

https://nodemcu-build.com/  这里定制的固件。除了缺省的模块,要加上sntp bit ow rtctime 这几个
回复 支持 反对

使用道具 举报

发表于 2019-9-5 17:00:26 | 显示全部楼层
8266本身就是单片机,还带wifi功能,接TM1650和DS1302没任何问题,何况自己也有RTC,加上网络校时,所以51和ds1302都可以取消了。8266也有低功耗,也可以需要时才开wifi,另外用arduino编程非常简单,比51爽多了。
回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2019-9-5 17:41:52 | 显示全部楼层
本帖最后由 spirndai 于 2019-9-5 18:28 编辑
kindzhon 发表于 2019-9-5 17:00
8266本身就是单片机,还带wifi功能,接TM1650和DS1302没任何问题,何况自己也有RTC,加上网络校时,所以51 ...

8266不开wifi也要10-20ma,而且8266 深度睡眠是通过重启唤醒的,中间时钟丢失。所以8266要保留时钟最多就是关wifi,保持大概10-20ma来运行。而实际测试中,即使关掉wifi,也有60-80ma的电流,非要重启有一次才能真正降下来。当然你可以保留ds1302,定期从ds1302读取数据从而避开8266 深度睡眠时钟丢失的问题,这样相当于用8266取代了51。但8266跑起来至少也要十几ma的电流,而51大概2-3ma(我试过不掉电直接循环),所以最后还是加了个51。

这个钟核心关键就是要省电,后面如果还要改进
1、是把tm1650去掉,换上595,做基于笔画而不是字母的扫描,这样可以保证同一时间只有一个笔画而不是一个字母被点亮。但换来的代价就是51会有更多的运算时间,运气不好也许会耗电更多。
2、加个555,用pwm点亮数码管,可以在晚上进一步降低亮度,白天也可以适当的调整

暂时来说没想到继续用数码管的减功耗方案了。

回复 支持 1 反对 0

使用道具 举报

发表于 2019-9-5 17:56:36 | 显示全部楼层
单片机高手。
回复 支持 反对

使用道具 举报

发表于 2019-9-5 23:35:11 | 显示全部楼层
对我等小白来说,还是直接买个成品简单省事,哈哈哈哈
回复 支持 反对

使用道具 举报

发表于 2019-9-6 11:25:37 | 显示全部楼层
长亮吗?才1.2ma,关闭对时就很实用了。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-9-6 11:43:28 | 显示全部楼层
ssn2 发表于 2019-9-6 11:25
长亮吗?才1.2ma,关闭对时就很实用了。

是长亮的,就是照片里面的亮度
回复 支持 反对

使用道具 举报

发表于 2019-9-6 13:34:55 | 显示全部楼层
spirndai 发表于 2019-9-5 17:41
8266不开wifi也要10-20ma,而且8266 深度睡眠是通过重启唤醒的,中间时钟丢失。所以8266要保留时钟最多就 ...

tm1650本身就有8级亮度控制。
回复 支持 反对

使用道具 举报

发表于 2019-9-6 13:41:23 | 显示全部楼层
spirndai 发表于 2019-9-5 17:41
8266不开wifi也要10-20ma,而且8266 深度睡眠是通过重启唤醒的,中间时钟丢失。所以8266要保留时钟最多就 ...

http://www.jxtobo.com/11168.html,rtc是不丢失的。还light sleep。就2MA。你仔细看看。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-9-6 14:12:51 | 显示全部楼层
kindzhon 发表于 2019-9-6 13:41
http://www.jxtobo.com/11168.html,rtc是不丢失的。还light sleep。就2MA。你仔细看看。 ...

d2.png https://www.espressif.com/sites/default/files/9b-esp8266-low_power_solutions_en_0.pdf

官方文档里面写得清清楚楚,lightsleep 和 deepsleep 里面 system clock是off的,当然就是时钟丢失。modemsleep电流15ma,也就是为了保持系统时钟运行最小的电流。rtc保持不等于时钟保持,而且8266 deep sleep的rtc是低精度的,就是为了发个中断,误差非常的大。

回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-9-6 14:17:18 | 显示全部楼层
kindzhon 发表于 2019-9-6 13:34
tm1650本身就有8级亮度控制。

我代码里面就根据时间设置了亮度3和亮度7,完全没觉得有什么差异。
回复 支持 反对

使用道具 举报

发表于 2019-9-6 22:08:07 | 显示全部楼层
spirndai 发表于 2019-9-6 14:17
我代码里面就根据时间设置了亮度3和亮度7,完全没觉得有什么差异。

我的有,0到10区别不小。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-9-6 22:49:37 | 显示全部楼层
kindzhon 发表于 2019-9-6 22:08
我的有,0到10区别不小。

11.png
tm1650只有1-8级亮度,没有0-10……
回复 支持 反对

使用道具 举报

发表于 2019-9-7 01:20:12 | 显示全部楼层
高手啊,代码看不懂,看来要学习的东西太多了
回复 支持 反对

使用道具 举报

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

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2024-4-27 12:31 , Processed in 0.202800 second(s), 15 queries , Redis On.

Powered by Discuz!

© 2006-2023 smzj.net

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