The AVR controlled clock

The AVR controlled clock:

A friend of mine needed a clock into his amplifier as an extra function. So I made him one. It consists only of one AVR processor and one BQ-M512RD (or compatible, with common anodes) LED display. I tried to keep the whole clock as simple as possible, so anyone can build one…

avr_clock1

Here’s the schematic (click for a bigger image):

sch_hodiny_avr

And the photo of the device:

 #include <avr/io.h>
 #include <avr/interrupt.h>
 #include <avr/wdt.h>
 #include <stdio.h>

 #define _s_A 2
 #define _s_B 0
 #define _s_C 6
 #define _s_D 4
 #define _s_E 3
 #define _s_F 1
 #define _s_G 7
 #define _s_dot 5

 const unsigned char segs[] =
 {
  _BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F), //0
  _BV(_s_B) | _BV(_s_C), //1
  _BV(_s_A) | _BV(_s_B) | _BV(_s_D) | _BV(_s_E) | _BV(_s_G), //2
  _BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_D) | _BV(_s_G), //3
  _BV(_s_B) | _BV(_s_C) | _BV(_s_F) | _BV(_s_G), //4
  _BV(_s_A) | _BV(_s_C) | _BV(_s_D) | _BV(_s_F) | _BV(_s_G), //5
  _BV(_s_A) | _BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G), //6
  _BV(_s_A) | _BV(_s_B) | _BV(_s_C), //7
  _BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G),//8
  _BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_F) | _BV(_s_G),//9

  _BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G), //A
  _BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G), //B
  _BV(_s_A) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F), //C
  _BV(_s_B) | _BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_G), //D
  _BV(_s_A) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G), //E
  _BV(_s_A) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G) //F
 };

 #define _ms(n) (17*n)

 void wait(unsigned int a) //basic wait
 { 
  volatile unsigned int b,c;
  for(b=0;b!= a; b++)for(c=0;c!= 50;c++);
  return;
 }

 volatile unsigned char prescale=0;
 volatile unsigned char sec=0;
 volatile unsigned char min_1=0;
 volatile unsigned char min_10=0;
 volatile unsigned char hour_1=0;
 volatile unsigned char hour_10=0;
 volatile unsigned char show_t=0;

 ISR(TIMER1_OVF_vect)
 {
  if(++prescale == 225){prescale = 0;sec++;};
  if(sec>59){min_1++;sec=0;};
  if(min_1>9){min_1=0;min_10++;};
  if(min_10>5){min_10=0;hour_1++;};
  if(hour_1>9){hour_1=0;hour_10++;};
  if(hour_10>1 && hour_1>3){hour_1=0;hour_10=0;};

  if(++show_t==4) show_t=0;
  switch(show_t)
  {
   case 0: //show minutes
    PORTC = 0x04;
    PORTD = (~segs[min_1]);
   break;
   case 1: //show 10 minutes
    PORTC = 0x08;
    PORTD = (~segs[min_10]);
   break;
   case 2: //show hours
    PORTC = 0x10;
    PORTD = (~segs[hour_1]) & ~_BV(_s_dot);
   break;
   case 3: //show 10hours
    PORTC = 0x20;
    PORTD = (~segs[hour_10]);
   break;
   default:
    show_t = 0;
   break;
  } 
  return; 
 }

 #define B1() (bit_is_clear(PINB,3))
 #define B2() (bit_is_clear(PINB,4))
 #define B_WAIT 300

 #define nop() asm volatile ("nop;")

 int main(void)
 {

  TIMSK = 0x04;
  TCCR1B = 0x01;

  DDRD = 0xFF;
  DDRC = 0x3F;
  DDRB = 0x00;
  PORTB = 0xFF;

  sei();
  while(1)
  {
   if(B1())
   {
    wait(_ms(B_WAIT));
    min_1++;
    sec=0;
   }
   if(B2())
   {
    wait(_ms(B_WAIT));
    hour_1++;
    sec=0;
   }
  }
 }

Sorry for the lack of comments. It was a really simple project, so I didn’t need em. Enjoy!

Here is the project folder (source, hexfile) and the schematic in Eagle format:

Other implementations

Several people have made this clock or based their designs on my design. Most of them have not shared, but some have.

Radek Stybnar:

avr_clock_radek

OK1KVK implementation:

OLYMPUS DIGITAL CAMERA

MEKWEB also made a clock based on this design, though with a few improvements.

 

This entry was posted in Projects, Simple MCU stuff and tagged . Bookmark the permalink.

24 Responses to The AVR controlled clock

  1. reza ruad says:

    thanks a lot . very nice project .

  2. svits says:

    i could not understand the meaning of
    const unsigned char segs[] =
    {
    _BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F), //0
    _BV(_s_B) | _BV(_s_C), //1
    _BV(_s_A) | _BV(_s_B) | _BV(_s_D) | _BV(_s_E) | _BV(_s_G), //2
    _BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_D) | _BV(_s_G), //3
    _BV(_s_B) | _BV(_s_C) | _BV(_s_F) | _BV(_s_G), //4
    _BV(_s_A) | _BV(_s_C) | _BV(_s_D) | _BV(_s_F) | _BV(_s_G), //5
    _BV(_s_A) | _BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G), //6
    _BV(_s_A) | _BV(_s_B) | _BV(_s_C), //7
    _BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G),//8
    _BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_F) | _BV(_s_G),//9

    _BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G), //A
    _BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G), //B
    _BV(_s_A) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F), //C
    _BV(_s_B) | _BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_G), //D
    _BV(_s_A) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G), //E
    _BV(_s_A) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G) //F
    }; this part what it means…….

    • daqq says:

      Well, this part is the table that tells which pins should be turned on for individual characters. For instance, imagine the 4 character – in it the segments B, C, F and G should be turned on – an indeed, on the line:

      _BV(_s_B) | _BV(_s_C) | _BV(_s_F) | _BV(_s_G), //4

      they indeed are. So segs[4] gives you the value for the port for the number 4.

  3. vivek jain says:

    what is the meaning of _BV in this code & where it is defined.
    what is the meaning of #define _ms(n) (17*n) .
    why you use prescaler value as 225 & what is the meaning of this statement if(++prescale == 225){prescale = 0;sec++;}.
    i know the meaning of volatile in c but what is nop here (#define nop() asm volatile )(“nop;”)
    what is the meaning of #define B_WAIT 300?????????????

    • daqq says:

      The _BV is an AVR-GCC macro – it gives you the value of a bit – say, _BV(7) will give you 128. You should read the documentation.
      225 – because 14745600 Hz (crystal frequency) / 65536 = 225 Hz – so I get an interrupt with a frequency of 225 Hz.
      NOP – No OPeration – an ASM instruction for waiting. When volatile is used like that, it tells the optimization system to not optimize it away. You should read the documentation.
      The B_WAIT – well, look at the rest of the code – it’s the time the system waits after a button press.

  4. sakshi says:

    on which software you compile it for genrating the hex code.
    as you provide directly if i burn it into my controller than it work or not.
    or anything extra i want to do……..

    • daqq says:

      I used Atmel Studio. If you burn the provided hex file into your controller you also have to set the fuses properly.

    • daqq says:

      By the way, if you build this clock, please send me a photo of it, so I can publish it and/or link to your site.

  5. Mirek says:

    Zdravím. Potřeboval bych poradit, jak na DDRB nastavit, aby na PB1 až PB3 se mi po každých 4 hodinách změnil stav. Ovládám s nimi tranzistory PNP pro velký displej a taak potřebuji, aby na nich byl stav 110, za 4 hodiny 101, za další 4 hodiny 011 a tak stále dokola. PORTB mám nastavený na 0b11111111; . Nejsem odborník v programování ale začátečník. Proto budu moc rád za pomoc. Předem mockrát děkuji za ochotu a pomoc. S pozdravem: Mirek

    • daqq says:

      Ahoj, neviem velmi naco by to bolo uzitocne, ale da sa to.

      DDRB je register, ktory urcuje smer dat – jednotka na prislusnom mieste ti nastavi prislusny pin do VYSTUPU. Takze ak chces, aby to boli furd vystupy, tak DDRB nastav konstantne na 0xFF (0b11111111, inak, pozri si hexadecimalnu notaciu). Prestavujes potom PORTB register na rozne hodnoty. K tomu kam a ako, bez odskusania ti mozem poradit len skusit:

      Pod:if(hour_10>1 && hour_1>3){hour_1=0;hour_10=0;};
      pridat
      PORTB = (PORTB & 0xF1) | (HourLookupTable[hour_10*10 + hour_1] & 0x0E);

      a pod
      volatile unsigned char show_t=0;

      pridat
      const unsigned char HourLookupTable[] = {0x0C, 0x0C, 0x0C, 0x0C, 0x0A, 0x0A, 0x0A, 0x0A, 0x06, 0x06, 0x06, 0x06, 0x0C, 0x0C, 0x0C, 0x0C, 0x0A, 0x0A, 0x0A, 0x0A, 0x06, 0x06, 0x06, 0x06};

      Neni to elegantne riesenie, ale malo by to fungovat. Neviem to ale odskusat.

      Inak, ked dokoncite vase zariadenie, prosim poslite mi fotku, hodim ju sem a dam tam link na vasu stranku.

      • Mirek says:

        Tak jsem vše přidal do programu a teď mi červená barva slabonce bliká asi tak rychlostí 5Hz a přibližně za minutu jasně blikne zelená a celé se to opakuje. Můžu ti to poslat celé do mejlu abych tě nemátl? Poslal bych ti schéma i program jak teď je upravený. Dík.

  6. deardailyvn says:

    Sorry, I am not technician, i work in financial field. I like electronic. I used your pcb and atmega8a-pu and Crystals 16MHz, it does not light segment B, and F. And time is so fast. How do I have to change this code for running, not change your pcb ? Please. My english is not good.
    Thanks

    • daqq says:

      Hi,

      The proper crystal frequency ( 14.745600 MHz ) is important as this defines how fast the clock goes. As to your problem with the B and F segment, I don’t know. Did you use exactly the same display?

      David

      • deardailyvn says:

        i checked B and F segment, they still light. I don’t know why they don’t light. Maybe, because of my ic atmega8a-pu, but i think atmega8-16pu same to atmega8a-pu.

  7. Michal says:

    Jak mám nastavit pojistky prosím ?

  8. Ivan says:

    Can you please provide me an alternative for the oscillator circuit?
    I can’t manage to fund the 14.75MHz Xtal and 12pF in SMD package

  9. Mek says:

    Hello, I like this clock for its simplicity, and the code is also pretty simple. However, I don’t have the very specific crystal that is used but have a plethora of other crystals. In an older discussion comment you explained why prescaler value is 225 – it’s because 14745600 Hz (crystal frequency) / 65536 = 225 Hz. I have many 12MHz crystals but 12000000 / 65536 is not a whole number, I guess it’s not possible to use such crystal. But I have also found a crystal of 24.576 MHz and this divides nicely: 24576000 / 65536 = 375. So my question is – will this clock run with the 24.576 MHz crystal if I change prescaler from 225 to 375 in source code?

    • Mek says:

      Hm, now I found out that max frequency of Atmega8 is 16 MHz so it will not work with my 24.576 MHz crystal. None of my other crystals divide OK with 65536 so I guess I am out of options here. Anyway, in the datasheet I saw that it is possible to connect 32.768 kHz low frequency crystal directly (and I have some of these) on XTAL1/XTAL2. I guess that would require some changes in code but is it even possible?

      • daqq says:

        Hello Mek,

        If you change the prescallers to any pretty much any other value you can use any value – there are simple ways to implement it to run from, say, a 11059200Hz crystal, or a But that would require the modification of the code to use a different TIMER – the TIMER1 overflows at 65536. If I remember correctly, TIMER0 overflows at 256. Or you could use the output compare, where you could trigger and reset at any arbitrary count, which would allow you to use even very silly frequencies.

        I did this project a long time ago, so it’s far from optimally implemented.

        Thank you for your interest in this project.

        • Mek says:

          Hey, thank you for your answer. Yesterday I studied a bit (well, I am a bit new to AVR) and found out that CTC mode with interrupt is exactly what I need. I can set up a 12 MHz crystal with a timer and a prescale value, and calculate required comparison value for timer.
          I am going to try it with blinking LED first to be sure I got the timer right. When I manage to build a clock with display, I will make a post with description and pictures on my web page 🙂

          To anyone wanting to learn about timers, this is an excellent guide which got me on the track: http://www.avrfreaks.net/forum/tut-c-newbies-guide-avr-timers

          • daqq says:

            🙂 Yup, that’s what I meant. Though I’d advise using something different for time measurement – an oscilloscope or frequency counter would be preferable to determine the actual frequency, since you can make mistakes that have a hardly noticeable effect on the actual time.

          • Mek says:

            I got it working. My clock is a little different but I learnt very much from the clock on this page.
            Everything is available on my web page: http://mekweb.eu/?lang=en&q=download-details&file=75
            and I also made a video. Hopefully it will help someone 🙂

          • daqq says:

            Nice project, I’ll link to it from the original post.

Leave a Reply

Your email address will not be published. Required fields are marked *