Thursday, November 13, 2008

Making a Digital Clock (Updated)

My fist Microcontroller project, PIC Digital Clock, is shown below.
Prototype digital clock on Breadbord

I have added time setting feature to make it to be a usable clock. The updated version is shown below:
7-Segment Digital Clock

More features will be added in the future.
There is no fast or slow time setting as in normal/simple digital clocks. In this clock, each digit of the clock display can be set one by one via 2 setting buttons.
Features
1. Bright Led 7-Segment display without Multiplexing
2. Display: Hour, Minute, Second
3. Set time via 2 buttons
4. Set time Digit by Digit
5. Use internal oscillator of PIC16F627a or PIC16F628
6. Use 32.768KHz crystal for better clock accuracy


Setting Time:
1. The clock shows 12:34:56 when power the clock on. The first digit of hour (it's number 1 in this case) will be blinking to notify that the time is not correct and need to be set.
2. Press SET button to count up the digit.
3. Press MODE button when the digit is the correct time. The next digit will be blinking.
4. Repeat step 2 and 3 for setting minute and second.
5. Make sure that pressing MODE button for setting the second digit of second at the correct time.


Shecmatic of the Digital Clock
Schematic of a Digital Clock using PIC16F627a or PIC16F628 and Led 7-Segment with showing second

Example of a PCB design of the Digital Clock
PCB of a Digital Clock using PIC16F627a or PIC16F628 and Led 7-Segment with showing second

The firmware ( source code in C ) written in MikroC
//6 digit  clock
//Using timer1 16bit counter interrupt
// PIC16F627A or PIC16F628
// Internal Clock 4MHz
// PUNKKY@gmail.com
#define MODE PORTB.F4
#define SET PORTB.F5
#define Sec_port_l PORTA.F6
#define Sec_port_h PORTA.F4
#define Min_port_l PORTA.F3
#define Min_port_h PORTA.F2
#define Hr_port_l PORTA.F1
#define Hr_port_h PORTA.F0
#define Blink PORTA.F7
#define HTMR1 0x80
#define LTMR1 0x00
typedef unsigned short uns8;
uns8 i;
uns8 hr_h;
uns8 hr_l;
uns8 min_h;
uns8 min_l;
uns8 sec_h;
uns8 sec_l;
uns8 tick;
uns8 myTimer;
uns8 setting_time;
void setup ();
void set_time ();
void show_time ();
void display (uns8 digit);
void blink_digit (uns8 digit);
void check_bt ();

//void check_bt(); //chech button
void interrupt ()
{
        PIR1.TMR1IF = 0;
        // clears TMR1IF
        TMR1H = HTMR1;
        tick = 1;
        Blink = 1;
        sec_l ++;
        if(sec_l>9){
            sec_l = 0;
            sec_h++;
        }
        if(sec_h>5){
            sec_h=0;
            min_l++;
        }
        if(min_l>9){
            min_l = 0;
            min_h++;
        }
        if(min_h>5){
            min_h = 0;
            hr_l++;
        }
        if(hr_l>9){
            hr_l = 0;
            hr_h++;
        }
        if(hr_h >2){
            hr_h = 0;
        }
        if(hr_h >=2 && hr_l>3){
           hr_h = 0;
           hr_l = 0;
        }
}
void main ()
{
        setup ();
        
        //Set time
        hr_h = 1;
        hr_l = 2;
        min_h = 3;
        min_l = 4;
        sec_h = 5;
        sec_l = 6;
        show_time ();
        setting_time = 1;
        set_time();
        while (1)
        {
                //blink_digit();
                if (tick)
                {
                        tick = 0;
                        show_time ();
                        Delay_ms (300);
                        Blink = 0;
                }
                check_bt ();
        }
}
void setup ()
{
        tick = 0;
//Digital output on PORTA
        CMCON = 0x07;
        //Input buttons + external clock
        TRISB = 0xB0;

        PORTB = 0x00;
        TRISA = 0x00;
        PORTA = 0x00;
        //Internal Clock 4MHz
        PCON.OSCF = 1;
        // Prescaler 1:1   external clock
        T1CON = 0x0F;

        PIE1.TMR1IE = 0;  // disable interupt to stop the clock

        INTCON = 0xC0;
        // Set GIE, PEIE
        TMR1L = LTMR1;
        TMR1H = HTMR1;
        // TMR1 starts at 0x0BDC = 3036 to make TMR1 counts to 62500 and
        // overclows in every 0.1 sec
        // Math: 1/500000*8*62500 = 0.1
        // 1/5000000 : time for 20MHz crystal (internal clock will be 20/4 = 5MHz)
        // 8: prescaler
        // 62500: TMR1 counts to 62500
        // Counting number of overflows to 10 will get 1 sec.

}

void show_time ()
{
        display (1);
        display (2);
        display (3);
        display (4);
        display (5);
        display (6);
}
void display (uns8 digit)
{
        switch (digit)
        {
                case 1 :
                PORTB = hr_h;
                Hr_port_h = 1;
                Hr_port_h = 0;
                break;
                case 2 :
                PORTB = hr_l;
                Hr_port_l = 1;
                Hr_port_l = 0;
                break;
                case 3 :
                PORTB = min_h;
                Min_port_h = 1;
                Min_port_h = 0;
                break;
                case 4 :
                PORTB = min_l;
                Min_port_l = 1;
                Min_port_l = 0;
                break;
                case 5 :
                PORTB = sec_h;
                Sec_port_h = 1;
                Sec_port_h = 0;
                break;
                case 6 :
                PORTB = sec_l;
                Sec_port_l = 1;
                Sec_port_l = 0;
                break;
        }
}
void blink_digit (uns8 digit)
{
        switch (digit)
        {
                case 1 :
                PORTB = 0xFF;
                Hr_port_h = 1;
                Hr_port_h = 0;
                Delay_ms (100);
                display (1);
                Delay_ms (100);
                break;
                case 2 :
                PORTB = 0xFF;
                Hr_port_l = 1;
                Hr_port_l = 0;
                Delay_ms (100);
                display (2);
                Delay_ms (100);
                break;
                case 3 :
                PORTB = 0xFF;
                Min_port_h = 1;
                Min_port_h = 0;
                Delay_ms (100);
                display (3);
                Delay_ms (100);
                break;
                case 4 :
                PORTB = 0xFF;
                Min_port_l = 1;
                Min_port_l = 0;
                Delay_ms (100);
                display (4);
                Delay_ms (100);
                break;
                case 5 :
                PORTB = 0xFF;
                Sec_port_h = 1;
                Sec_port_h = 0;
                Delay_ms (100);
                display (5);
                Delay_ms (100);
                break;
                case 6 :
                PORTB = 0xFF;
                Sec_port_l = 1;
                Sec_port_l = 0;
                Delay_ms (100);
                display (6);
                Delay_ms (100);
                break;
        }
}
void set_time ()
{

        i = 1;
        while (setting_time)
        {
                blink_digit (i);
                while (SET == 0)
                {
                        Delay_ms (5);
                        switch (i)
                        {
                                case 1 :
                                hr_h ++;
                                if (hr_h > 2)
                                {
                                        hr_h = 0;
                                }
                                break;
                                case 2 :
                                hr_l ++;
                                if (hr_l > 9)
                                {
                                        hr_l = 0;
                                }
                                if (hr_h >= 2 && hr_l > 3)
                                {
                                        hr_l = 0;
                                }
                                break;
                                case 3 :
                                min_h ++;
                                if (min_h > 5)
                                {
                                        min_h = 0;
                                }
                                break;
                                case 4 :
                                min_l ++;
                                if (min_l > 9)
                                {
                                        min_l = 0;
                                }
                                break;
                                case 5 :
                                sec_h ++;
                                if (sec_h > 5)
                                {
                                        sec_h = 0;
                                }
                                break;
                                case 6 :
                                sec_l ++;
                                if (sec_l > 9)
                                {
                                        sec_l = 0;
                                }
                                break;
                        }
                        while (SET == 0)
                        {
                                Delay_ms (5);
                        }
                }
                while (MODE == 0)
                {
                        Delay_ms (5);
                        i ++;
                        if (i > 6)
                        {
        sec_l--;
        TMR1H = 0x80;
        TMR1L = 0x00;
        PIE1.TMR1IE = 1;
        setting_time = 0;
                                break;
                        }
                        while (MODE == 0)
                        {
                                Delay_ms (5);
                        }
                }
        }
}
void check_bt ()
{
        myTimer = 0;
        if (setting_time == 0)
        {
                while (MODE == 0)
                {
                        Delay_ms (5);
                        myTimer ++;
                        if (myTimer > 200)
                        {
                                setting_time = 1;
                                myTimer = 0;
                                break;
                        }
                }
        }
        while (MODE == 0)
        {
                PIE1.TMR1IE = 0;
                //Stop clock
                Delay_ms (5);
                blink_digit (1);
        }
        set_time ();
}

I don't have the picture of the prototype as I haven't made it yet. But I have tested the circuit and firmware with proteus already.

--- Update ---
- The prototype of this updated version is done. Please see its photographs at 7-Segment PIC Digital Clock : The photographs
- The PCB is ready: please check out PCB for PIC Digital Clock

14 comments:

Nathan True said...

Seems to me like this is a very part-heavy project, without really having to be. I would think it would be possible to use row/column addressing and reverse parallel to get all 44 LED segments on something like 10 I/O pins, all without supporting hardware. The PIC16F628 has 15 I/O pins (1 I-only) with all accessories off, minus two for the crystal oscillator gives us three extra pins to use still, if we wanted to get lazy with the multiplexing. Buttons can be multiplexed with the display pins too using a few resistors.

punkky said...

Thank you for your comment. You are right this clock uses too many components but it was my intention. I made this clock to test my ideas and to learn Microcontroller. However, it is not easy to do led multiplexing with PIC16F627A/628 without using port expansion via shift register or something like that. Because, multiplexing 6x7-segment led requires 7 (for driving each segment)+ 6 (for commond cathode/anode) = 13 I/O pins and 2 pins are used for x-tal osc so I don't have pins for time setting buttons and for blinking second dots. Anyways, it's doable by multiplexing buttons and display as you suggested. I have done another updated version using multiplexing via 74HC595 shift register. I will post it soon, please come back again.
Thank you.

PANDILLA FELIZ said...

Hi, your project is very good, but I can't do work fine. in all digits show zeros. what is the error?.

sorry for my english but I'm from PERU.

thank you

punkky said...

The clock should display 12:34:56 when it's powered on. I am not sure what's cause the problem. Please make this 10-second counter http://picnote.blogspot.com/2008/11/10-second-counter.html for testing your equipment and wiring.

PANDILLA FELIZ said...

OK. thank you.

now All work fine!!!

PANDILLA FELIZ said...

what is the config of pic?

wachtdog on/off?
lvp on/off?

i don't know the config(the fuses)

punkky said...

Please check out http://picnote.blogspot.com/2008/11/10-second-counter.html for the config.
WDT : Off
LVP : Off
INTOSC_OSC_NOCLKOUT

zohaibmalik said...

Hi, please send me eagle sch and brd files Thanks.
Also, is 4543n is bcd to seven segment or any oyhrt buffer?

Paulito240 said...

This might help you out if you want to cut down on display drivers.

http://datasheets.maxim-ic.com/en/ds/ICM7218-ICM7228.pdf

PANDILLA FELIZ said...

hi. i want to know if i can used the pic 16f628A.

or is only with 16f627A or 16f628

thanks.

punkky said...

Yes, you can use the PIC16f628A. But, the PIC16f627 is a little bit cheaper.

Ucok Agus said...

hii nathan, your digital clock are seems like this http://www.youtube.com/watch?v=BT-aQeqnPdw
but your clock doesnt have real time sync to PC. Actually i'm trying to build the same stuff as in youtube link, but i'm in stuck to figure out the sync and multiplexing process. i already contact alphap8, but there is no answer yet.
Do you have suggestion how the process does and how to build it?

thanks b4
uaps at gmail dot com

Ucok Agus said...

oupss sorry , my last comment is for punkky not nathan.

thanks b4
uaps at gmail dot com

thaithien said...

Hi,Please send me your circuit simulated in Proteus.My email:
ngththien@gmail.com.Thanks very much!!!