d3b7 发表于 2016-2-9 17:35:52

【参赛】省电的“星光"LED时钟

本帖最后由 d3b7 于 2016-2-9 17:55 编辑

    我曾经有个烂尾工程 旷日持久的制作开始了——家用时钟,快5年过去了,仍旧没有投入实用。
    但卧室确实缺少一个夜里能轻松看清的时钟,于是正儿八经再重做这个项目吧。
    PCB就在厂家做了,当年用感光膜只能做单面工艺,还费时间。改版后,LED仍然在正面,单片机和升压电路就在背面了。这样一块板子搞定。


    上面这个图只带了两个数字的显示,一个钟需要两套显示板,就复制一份好了。控制电路只需要一份公用,所以左右两边的单片机部分只需要装一个。那么为什么我的PCB还布上两份同样的控制呢?这样可以分割成两块用,做99秒倒计时器啥的。



      这个电路不复杂,就是用三只超高亮度的LED来组成数码显示的一个笔段,用4片74HC164作为串-并转换和锁存,构成显示驱动。单片机ATMEGA48作为主控,SPI输出串行显示驱动信号,平时也就半秒种唤醒一次,刷新显示;其余时间单片机都在休眠状态,几乎没有功耗。而用于LED发光的显示耗电情况,取决于LED的性能和电流。

       PCB元旦后收到。



       焊接正面的LED,74HC164等,主要是LED手工焊接很消耗时间……



       焊接MCU主控,调试程序



       基本的程序(完成了走时和时钟调节)#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <stdlib.h>
#include <string.h>

/* Current time */
uint8_t hh=12, mm=0, ss=0;
char half=0;

/* Clock parameters */
uint8_t flag=0;
uint8_t mode;

static char brt1, brt2; // button release time

ISR (TIMER2_COMPA_vect)
{
    if(half)
    {
      half=0;
//      PORTD |= _BV(5);
      ss++;
      if(ss>59)
      {
            ss=0;
            mm++;
            if(mm>59)
            {
                mm=0;
                hh++;
                if(hh>23)
                  hh=0;
            }
      }
    }
    else
    {
      half=1;
//      PORTD &= ~_BV(5);
    }
    if(brt1>-16)
      brt1-=16;
    if(brt2>-16)
      brt2-=16;
}

ISR (PCINT0_vect)
{
}

uint8_t getbutton()
{
    static char bs1, bs2;   // button state
    static char bh1, bh2;   // button hold time
    char button=PINB;
    char tick=TCNT2;
    char state=0;
   
    if(!bs1)
    {
      if(button&1)    // button 1 release
      {
            bs1=1;
            brt1=tick;
            bh1=0;
      }
      else
      {
            if(tick==0)
                bh1++;
      }
      if(bh1>5)
            state|=4;
    }
    else
    {
      if(!(button&1)) // button 1 press
      {
            bs1=0;
            if(tick-brt1>3) // valid key
                state|=1;
      }
    }

    if(!bs2)
    {
      if(button&2)    // button 2 release
      {
            bs2=1;
            brt2=tick;
      }   
      else
      {
            if(tick==0)
                bh2++;
      }
      if(bh2>5)
            state|=8;

    }
    else
    {
      if(!(button&2)) // button 2 press
      {
            bs2=0;
            if(tick-brt2>3) // valid key
                state|=2;
      }
    }
    return state;
}

const char digdisp=
{
    0x02,   //0 xxxx xx.h
    0x7a,   //1 x... .x.h
    0x90,   //2 .xx. xxxh
    0x30,   //3 xx.. xxxh
    0x68,   //4 x..x .xxh
    0x24,   //5 xx.x x.xh
    0x04,   //6 xxxx x.xh
    0x72,   //7 x... xx.h
    0x00,   //8 xxxx xxxh
    0x20,   //9 xx.x xxxh
    0xfc,   // '-' symbol
    0xfe    // blank
/*0x40,   //A x.xx xxxh
    0x0c,   //b xxxx ..xh
    0x86,   //C .xxx x..h
    0x18,   //d xxx. .xxh
    0x84,   //E .xxx x.xh
    0xc4    //F ..xx x.xh*/
};

inline void nop(void)
{
    __asm__ volatile("nop");
}

#define HHMM 8
#define MMSS 9


void display()
{   
    uint8_t l,h;
    uint8_t colon;
    uint8_t b4,b3,b2,b1;

    if(flag & 0x01)
      colon=0;
    else
      colon=1;
    if(flag & 0x02)
    {
      switch(mode)
      {
            case HHMM:
                h=(mm*26)>>8;
                l=mm-10*h;
                break;
            case MMSS:
                h=(ss*26)>>8;
                l=ss-10*h;
                break;
            default:
                h=10;
                l=10;
                break;
      }
    }
    else
    {
      h=11;
      l=11;
    }
    b4=digdisp;
    b3=digdisp|colon;
    if(flag & 0x04)
    {
      switch(mode)
      {
            case HHMM:
                if(hh<10)
                {
                  h=11;
                  l=hh;
                }
                else
                {
                  h=(hh*26)>>8;
                  l=hh-10*h;
                }
                break;
            case MMSS:
                h=(mm*26)>>8;
                l=mm-10*h;
                break;
            default:
                h=10;
                l=10;
                break;
      }
    }
    else
    {
      h=11;
      l=11;
    }
    b2=digdisp;
    b1=digdisp;

    SPDR=b4;
    while(!(SPSR & _BV(SPIF)));
    SPDR=b3|colon;
    while(!(SPSR & _BV(SPIF)));
    SPDR=b2;
    while(!(SPSR & _BV(SPIF)));
    SPDR=b1;
    while(!(SPSR & _BV(SPIF)));
}

int main()
{
    int i;
    // set sysclk prescaler
    CLKPR=_BV(CLKPCE);
    CLKPR=0;    // no div
    nop();
    nop();
    nop();
    nop();
    nop();
   
   
    // configure Timer 2      
    ASSR=_BV(AS2);// asynchronous clock osc
    TCCR2A=_BV(WGM21);// Timer2 CTC mode, TOP as OCRA
    OCR2A=15;
    TCCR2B=_BV(CS22)|_BV(CS21)|_BV(CS20);   // enable, clk/1024
    TIFR2=_BV(OCF2A);   // clear flag
    TIMSK2=_BV(OCIE2A); // enable interrupt
    sei();
   
    // configure SPI
    DDRB=_BV(2)|_BV(3)|_BV(5);// SPI output pin, SS must be output
    SPSR=_BV(SPI2X);
    SPCR=_BV(SPE)|_BV(MSTR)|_BV(CPHA);// Fastest SPI
   
    set_sleep_mode(SLEEP_MODE_IDLE);
   
    DDRD=_BV(5)|_BV(2); //PD5: Status (BLUE), PD5: DC-DC EN
    PORTD=_BV(2);   // DC-DC on
    PORTB |= _BV(0)|_BV(1); // button 1, 2
   
    flag=7;
    display();
    sleep_mode();   // wait until RTC oscillator is stable
    set_sleep_mode(SLEEP_MODE_PWR_SAVE);

    PCICR=_BV(PCIE0);   // Pin-change interrupt 0
    PCMSK0=_BV(PCINT0)|_BV(PCINT1);
    PCIFR=_BV(PCIF0);
   
    mode=HHMM;
    for(;;)
    {
      static char phalf=3;
      char b;
      char show=0;
      static char adjust=0;
      char update=0;

      if(phalf!=half)
      {
            show=1;
            phalf=half;
      }

      b=getbutton();
      if(adjust==0)
      {
            if(b&2)
            {
                if(mode==HHMM)
                  mode=MMSS;
                else
                  mode=HHMM;
                show=1;
            }
            else
            {
                if((b&12)==4)   // button 1 Hold
                {
                  set_sleep_mode(SLEEP_MODE_IDLE);    // change back later
                  if(mode==HHMM)
                        adjust=1;
                  else
                        adjust=3;
                  show=1;
                }
            }
      }
      else
      {
            switch(adjust)
            {
                case 1:   // adjust Minute
                if(b&2)
                {
                  mm++;
                  if(mm>59)
                        mm=0;
                  update=1;
                }
                else
                  if(b&1)
                  {
                        adjust=2;
                        show=1;
                  }
                break;
                case 2:   // adjust Hour
                if(b&2)
                {
                  hh++;
                  if(hh>23)
                        hh=0;
                  update=1;
                }
                else
                  if(b&1)
                  {
                        adjust=0;
                        set_sleep_mode(SLEEP_MODE_PWR_SAVE);
                        show=1;

                  }
                break;
                case 3:   // adjust Second
                if(b&2)
                {
                  ss=0;
                  TCNT2=0;
                  half=0;
                  update=1;
                }
                else
                  if(b&1)
                  {
                        adjust=0;
                        set_sleep_mode(SLEEP_MODE_PWR_SAVE);
                        show=1;
                  }
                break;
            }
            if(update)
                show=1;
      }
      
               
      if(mode==HHMM && half==0)
      {
            flag |= 0x01;
      }
      else
      {
            flag &= ~0x01;
      }
      
      if(half==0 || update)
      {
            flag |= 0x06;
      }
      else
      {
            if(adjust==1 || adjust==3)
                flag &= ~0x02;
            if(adjust==2)
                flag &= ~0x04;
      }
      
      if(show)
            display();

      sleep_mode();

    }
   

}
相对于传统的LED钟来说,这个是相当省电:1节3.2V的磷酸铁锂电池供电时,总电流大约1mA~1.1mA之间


       当然,这个电流下不可能觉得亮,强光下就很难看出来了。台灯下勉强能识别。


      做这个钟我是给卧室用的,暗光环境下足够亮了,关了灯就觉得很亮了。


       其实我还设计了亚克力的面板,只是没有做绿色亚克力的,所以颜色不搭,暂时没安装。

补充内容 (2016-2-16 20:56):
又做了一个蓝色LED的,加上亚克力面板。
6101171

bluegrass 发表于 2016-2-9 17:45:58

不错不错,比我的大很多,可以考虑加入授时系统哦,有单片机就比较简单了。

正直电子 发表于 2016-2-9 19:42:29

牛!比我用数码管做的好看,而且经济

gd4424 发表于 2016-2-9 20:27:10

佩服动手能力强,我一直都想做一个可编程不熟悉:lol:lol:lol

d3b7 发表于 2016-2-16 20:55:14

本帖最后由 d3b7 于 2016-2-16 20:58 编辑

今天又焊了一块,使用蓝色LED。 同等电流下蓝色的亮度不及如翠绿色的。

liuruizhou2 发表于 2016-2-16 21:23:10

szshly 发表于 2016-2-17 08:08:03

白天不能用吗?

ckc 发表于 2016-2-17 12:03:51

很漂亮!
亮度有些差异,美中不足。
总电流这么小?才1mA多点?

d3b7 发表于 2016-2-17 15:04:43

szshly 发表于 2016-2-17 08:08 static/image/common/back.gif
白天不能用吗?

LED电流需要根据环境光线强度设定(可以加光敏元件自动调节)
白天室内很亮的话,需要把电流调大,就不那么省电了。

d3b7 发表于 2016-2-17 15:09:33

ckc 发表于 2016-2-17 12:03 static/image/common/back.gif
很漂亮!
亮度有些差异,美中不足。
总电流这么小?才1mA多点?

亮度和颜色不均匀是因为LED的离散性,如果买来先筛选一下的话可以做得好些。
没错,总电流1mA多晚上足够亮了。翠绿色LED效率很高,每只LED的电流数十微安级就够了。

huarana 发表于 2016-2-17 17:36:59

我也一直有这个想法。


不过要加入授时 哦 ,不加授时的话要用 ds3231这类高精度的

lxa000 发表于 2016-3-6 21:40:36

d3b7 发表于 2016-2-17 15:09 static/image/common/back.gif
亮度和颜色不均匀是因为LED的离散性,如果买来先筛选一下的话可以做得好些。
没错,总电流1mA多晚上足够 ...

还是用数码管效果好

HYLG 发表于 2016-3-18 22:40:10

我做的是白光的可以当照明用的.每段6个LED.
PCB画好了还没做板.等有空了做出来也来晒一下.

xinglong1115 发表于 2016-3-23 08:49:42

做的不错,以前也做过不少LED/LCD的电子时钟,但是精度是个不好解决的问题,都成了摆设

LI192721 发表于 2016-3-25 20:23:41

楼主,可以卖了,我们需要
页: [1]
查看完整版本: 【参赛】省电的“星光"LED时钟