Back to www.micro-examples.com

A PIC16F819 DYMOCLOCK

DymoClock, Front side

Here is another clock... but what kind of clock !

Take a piece of wood, and drill 27 5mm holes.
Insert a LED into each hole.
Take a Dymo labeler, print a few numbers and stick them to the board.
Wire the LEDs, connect them to a PIC... That's all !

This example will show you how to connect up to 30 LEDs to a PIC, using only six I/O pins.

Circuit Schematic

I wanted to build a clock as simple as possible :

The solution of direct LED driving comes from a Microchip Application Note AN234, and as I'm using 25 mA LEDs, I simply removed all current limiting resistors.
A silicon diode is used as temperature sensor, and the rest of the circuit is very classic around a PIC16F819 :

DymoClock circuit schematic

Click on the schematic to enlarge

 

DymoClock, Front Side

 

Upper row : hours
Middle row : minutes or seconds or degrees C
Lower row : add it to middle row

For example, the clock is displaying 5 hours and 23 minutes

 

Dymoclock, back side

 

 

The wiring side of the LEDs is a little bit messy,
some colored wires are helpful !

See the source code for LED numbering.

 

 

The Source Code

The is the mikroC source code, feel free to use it at your own risk !

/*
 * file         : dymoclock.c
 * project      : Simple LED clock with thermometer
 * author       : Bruno Gavand
 * compiler     : mikroC V6.0.0.0
 * date         : september 15, 2006
 *
 * description  :
 *      This is a 12 hours clock with 27 LEDs display, with a 2°C resolution thermometer
 *
 * target device :
 *      PIC16F819 with 16 Mhz crystal
 *
 * configuration bits :
 *      HS clock
 *      no watchdog
 *      no power up timer
 *      RA5 as MCLR pin
 *      brown out detect
 *      LVP disabled
 *      data EE protect disabled
 *      ICD disabled
 *      CCP1 pin on RB2
 *
 * see more details and schematic on http://www.micro-examples.com/
 *
 */

/*
 * display modes
 */
#define MODE_HOURMN     0       // display hours:minutes
#define MODE_SS         1       // display seconds
#define MODE_TEMP       2       // display temperature
#define MAX_MODE        3       // number off display modes

/*
 * buttons
 */
#define BUTTON          ((PORTA.F1 == 0)  || (PORTA.F2 == 0))   // at least one
#define BUTTON_MODE     (PORTA.F1 == 0)                         // mode / advance
#define BUTTON_TOGGLE   (PORTA.F2 == 0)                         // toggle / valid
#define BUTTON_SET      ((PORTA.F1 == 0) && (PORTA.F2 == 0))    // both at the same time

#define TEMP_REF        115     // silicon junction offset : 600 mV
#define MAX_TEMP        20      // number of temperature samples

/*
 * LED multiplexing tables
 *
 * LED index   : 1 2 3 4 5  6 7 8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 * LED number  : 1 2 3 4 9 10 5 6 11 12 25 30  7  8  5 10 35 40 45 50  2  1 15 20  3  4 55
 * LED hours   : 1 2 3 4 5  6 7 8  9 10 11 12
 * LED min/deg :                               5 10 15 20 25 30 35 40 45 50 55
 * LED 1234    :                                                                1  2  3  4
 */

/*
 * upper row : hours from 1 to 12
 */
const   unsigned char hhTable[] = { 0, 1, 2, 3, 4, 7, 8, 13, 14, 5, 6, 9, 10 } ;

/*
 * middle row : minutes/seconds/°C from 5 to 55 step 5
 */
const   unsigned char mnTableH[] =
        {
        0, 0, 0, 0, 0,
        15, 15, 15, 15, 15,
        16, 16, 16, 16, 16,
        23, 23, 23, 23, 23,
        24, 24, 24, 24, 24,
        11, 11, 11, 11, 11,
        12, 12, 12, 12, 12,
        17, 17, 17, 17, 17,
        18, 18, 18, 18, 18,
        19, 19, 19, 19, 19,
        20, 20, 20, 20, 20,
        27, 27, 27, 27, 27
        } ;

/*
 * lower row : increment of minutes/seconds/°C from 1 to 4
 */
const   unsigned char mnTableL[] =
        {
        0, 22, 21, 25, 26,
        0, 22, 21, 25, 26,
        0, 22, 21, 25, 26,
        0, 22, 21, 25, 26,
        0, 22, 21, 25, 26,
        0, 22, 21, 25, 26,
        0, 22, 21, 25, 26,
        0, 22, 21, 25, 26,
        0, 22, 21, 25, 26,
        0, 22, 21, 25, 26,
        0, 22, 21, 25, 26,
        0, 22, 21, 25, 26
        } ;

/*
 * RAM variables
 */
unsigned char   mode ;                  // display mode
unsigned char   toggleMode ;            // toggle mode flag
unsigned int    scaler = 0 ;            // timer overflow divider
unsigned char   hh = 1 ;                // hours
unsigned char   mn = 1 ;                // minutes
unsigned char   ss = 0 ;                // seconds
int             temp ;                  // temperature in °C
unsigned char   tdeg[MAX_TEMP] ;        // array of temperatures samples
unsigned char   tidx = 0 ;              // index of the temperature sample

/*
 * interrupt routine service
 * called Fosc / 4 / 256 times per second
 */
void    interrupt(void)
        {
        if(INTCON.TMR0IF)                                       // timer overflow ?
                {
                scaler++ ;                                      // increment scaler
                if(scaler == 15625)                              // one second has expired ?
                        {
                        scaler = 0 ;                            // reset scaler

                        ss++ ;                                  // next second
                        if(ss == 60)                            // one minute has expired ?
                                {
                                ss = 0 ;                        // reset seconds
                                mn++ ;                          // next minute
                                if(mn == 60)                    // one hour has expired ?
                                        {
                                        mn = 0 ;                // reset minutes
                                        hh++ ;                  // next hour
                                        if(hh == 13)            // 12 hours mode
                                                {
                                                hh = 1 ;        // back to first hour
                                                }
                                        }
                                }
                        }

                INTCON.TMR0IF = 0 ;                             // clear interrupt flag
                }
        }

/*
 * light the LED number n during d milliseconds
 */
void    dymoLight(const unsigned char n, unsigned char d)
        {
        switch(n)
                {
                case  0 : TRISB = 0b11111111 ; PORTB = 0b00000000 ; break ;
                case  1 : TRISB = 0b11111100 ; PORTB = 0b00000001 ; break ;
                case  2 : TRISB = 0b11111100 ; PORTB = 0b00000010 ; break ;
                case  3 : TRISB = 0b11111010 ; PORTB = 0b00000001 ; break ;
                case  4 : TRISB = 0b11111010 ; PORTB = 0b00000100 ; break ;
                case  5 : TRISB = 0b11111001 ; PORTB = 0b00000010 ; break ;
                case  6 : TRISB = 0b11111001 ; PORTB = 0b00000100 ; break ;
                case  7 : TRISB = 0b11110110 ; PORTB = 0b00000001 ; break ;
                case  8 : TRISB = 0b11110110 ; PORTB = 0b00001000 ; break ;
                case  9 : TRISB = 0b11110101 ; PORTB = 0b00000010 ; break ;
                case 10 : TRISB = 0b11110101 ; PORTB = 0b00001000 ; break ;
                case 11 : TRISB = 0b11110011 ; PORTB = 0b00000100 ; break ;
                case 12 : TRISB = 0b11110011 ; PORTB = 0b00001000 ; break ;
                case 13 : TRISB = 0b11101110 ; PORTB = 0b00000001 ; break ;
                case 14 : TRISB = 0b11101110 ; PORTB = 0b00010000 ; break ;
                case 15 : TRISB = 0b11101101 ; PORTB = 0b00000010 ; break ;
                case 16 : TRISB = 0b11101101 ; PORTB = 0b00010000 ; break ;
                case 17 : TRISB = 0b11101011 ; PORTB = 0b00000100 ; break ;
                case 18 : TRISB = 0b11101011 ; PORTB = 0b00010000 ; break ;
                case 19 : TRISB = 0b11100111 ; PORTB = 0b00001000 ; break ;
                case 20 : TRISB = 0b11100111 ; PORTB = 0b00010000 ; break ;
                case 21 : TRISB = 0b11011110 ; PORTB = 0b00000001 ; break ;
                case 22 : TRISB = 0b11011110 ; PORTB = 0b00100000 ; break ;
                case 23 : TRISB = 0b11011101 ; PORTB = 0b00000010 ; break ;
                case 24 : TRISB = 0b11011101 ; PORTB = 0b00100000 ; break ;
                case 25 : TRISB = 0b11011011 ; PORTB = 0b00000100 ; break ;
                case 26 : TRISB = 0b11011011 ; PORTB = 0b00100000 ; break ;
                case 27 : TRISB = 0b11010111 ; PORTB = 0b00001000 ; break ;
                case 28 : TRISB = 0b11010111 ; PORTB = 0b00100000 ; break ;
                case 29 : TRISB = 0b11001111 ; PORTB = 0b00010000 ; break ;
                case 30 : TRISB = 0b11001111 ; PORTB = 0b00100000 ; break ;
                }
        VDelay_ms(d) ;
        }

/*
 * set clock time
 */
void    setTime()
        {
        INTCON.GIE = 0 ;                        // stop time counting
       
        hh = 1 ;                                // set hours
        for(;;)
                {
                dymoLight(hhTable[hh], 20) ;    // light hour

                if(BUTTON_MODE)                 // advance hour ?
                        {
                        VDelay_ms(15) ;          // debounce button
                        while(BUTTON_MODE) ;
                        VDelay_ms(15) ;

                        hh++ ;                  // next hour
                        if(hh > 12)             // overflow ?
                                {
                                hh = 1 ;        // back to first hour
                                }
                        }

                if(BUTTON_TOGGLE)               // valid ?
                        {
                        VDelay_ms(15) ;
                        while(BUTTON_TOGGLE) ;  // debounce button
                        VDelay_ms(15) ;
                        break ;                 // exit loop
                        }
                }

        mn = 1 ;                                // set minutes
        for(;;)
                {
                dymoLight(mnTableH[mn], 10) ;   // light minutes, upper row
                dymoLight(mnTableL[mn], 10) ;   // light minutes, lower row

                if(BUTTON_MODE)                 // advance minute ?
                        {
                        VDelay_ms(15) ;          // debounce button
                        while(BUTTON_MODE) ;
                        VDelay_ms(15) ;

                        mn++ ;                  // next minute
                        if(mn > 59)             // overflow ?
                                {
                                mn = 0 ;        // back to first minute
                                }
                        }

                if(BUTTON_TOGGLE)               // valid ?
                        {
                        VDelay_ms(15) ;          // debounce button
                        while(BUTTON_TOGGLE) ;
                        VDelay_ms(15) ;
                        break ;                 // exit loop
                        }
                }

        ss = 0 ;                                // reset seconds
       
        INTCON.GIE = 1 ;                        // start clock
       
        toggleMode = 0 ;                        // no toggle
        mode = MODE_HOURMN ;                    // display hours:minutes
        }

/*
 * main entry
 */
void    main()
        {
        unsigned int    loopCtr = 0 ;           // counter for temperature sampling delay
        unsigned int    toggleCtr = 0 ;         // counter for toggle delay
       
//        OSCCON |= 0b01110000 ;                // untag if you use internal RC clock, 8Mhz

        ADCON1 = 0b00001110 ;                   // RA0 as analog input, other input as digital I/O
        TRISA = 0b11111111 ;                    // all PORTA as inputs
        PORTA = 0 ;                             // clear PORTA

        TRISB = 0xff ;                          // switch off display

        INTCON.TMR0IF = 0 ;                     // clear timer 0 overflow interrupt flag
        INTCON.TMR0IE = 1 ;                     // allow timer 0 overflow interrupt
       
        OPTION_REG.T0CS = 0 ;                   // timer 0 clock source is internal instruction cycle clock

        OPTION_REG.PS2 = 0 ;                    // no prescaler
        OPTION_REG.PS1 = 0 ;
        OPTION_REG.PS0 = 0 ;
        OPTION_REG.PSA = 1 ;
       
        setTime() ;                             // set time

        for(;;)
                {
                loopCtr++ ;                                     // increment loop counters
                toggleCtr++ ;

                if(BUTTON)                                      // if at least one button is pressed
                        {
                        unsigned char   b = 0 ;
                       
                        VDelay_ms(15) ;                          // debounce
                        while(BUTTON)                           // while one button is pressed
                                {
                                if(BUTTON_SET)                  // are they both pressed ?
                                        {
                                        dymoLight(hhTable[0], 1) ;      // clear display
                                        dymoLight(mnTableH[0], 1) ;
                                        dymoLight(mnTableL[0], 1) ;

                                        VDelay_ms(1000) ;                // 1 second delay

                                        setTime() ;                     // enter time setting
                                       
                                        break ;
                                        }
                                else if(BUTTON_MODE)            // change mode ?
                                        {
                                        b = 1 ;
                                        }
                                else if(BUTTON_TOGGLE)          // toggle the toggle mode ?
                                        {
                                        b = 2 ;
                                        }

                                VDelay_ms(15) ;
                                }
                       
                        if(b == 1)                      // change mode
                                {
                                VDelay_ms(15) ;          // debounce button
                                while(BUTTON_MODE) ;
                                VDelay_ms(15) ;

                                mode++ ;                // next mode
                                toggleMode = 0 ;        // clear toggle mode
                                if(mode == MAX_MODE)    // last mode ?
                                        {
                                        mode = 0 ;      // back to first one
                                        }
                                }
                        else if(b == 2)                 // toggle mode
                                {
                                VDelay_ms(15) ;          // debounce button
                                while(BUTTON_TOGGLE) ;
                                VDelay_ms(15) ;

                                toggleMode = !toggleMode ;      // toggle display mode
                                }
                        }
                       
                if(mode == MODE_HOURMN)                         // display mode is hours:minutes
                        {
                        dymoLight(hhTable[hh], 5) ;             // light hours

                        dymoLight(mnTableH[mn], 5) ;            // light minutes, upper row
                        dymoLight(mnTableL[mn], 5) ;            // light minutes, lower row
                       
                        if(toggleMode && (toggleCtr > 400))     // if toggle mode and delay expired
                                {
                                toggleCtr = 0 ;                 // reset counter
                                mode = MODE_TEMP ;              // switch to temperature
                                }
                        }
                else if(mode == MODE_SS)                        // display mode is seconds
                        {
                        dymoLight(mnTableH[ss], 5) ;            // light seconds, upper row
                        dymoLight(mnTableL[ss], 5) ;            // light seconds, lower row
                        }
                else if(mode == MODE_TEMP)                      // display mode is temperature
                        {
                        unsigned char   i ;

                        if(loopCtr > 20)                        // if delay expired
                                {
                                loopCtr = 0 ;                   // reset counter
                                temp = TEMP_REF - Adc_Read(0) ;         // get sample

                                temp *= 221 ;           // temperature coefficient of the silicon junction
                                temp /= 102 ;
                                temp = 25 + temp ;      // get the result in celcius

                                tdeg[tidx] = temp ;     // store temperature sample into array
                                tidx++ ;                // advance index
                                if(tidx == MAX_TEMP)    // index overflow ?
                                        {
                                        tidx = 0 ;      // back to first slot
                                        }

                                temp = 0 ;              // compute average temperature
                                for(i = 0 ; i < MAX_TEMP ; i++) // for all samples
                                        {
                                        temp += tdeg[i] ;       // add them
                                        }
                                temp /= MAX_TEMP ;              // divide by sample number
                                }

                        if((temp > 0) && (temp < 60))           // if temperature wihtin 0...59
                                {
                                dymoLight(mnTableL[temp], 5) ;  // display temperature, upper row
                                dymoLight(mnTableH[temp], 5) ;  // display temperature, lower row
                                }

                        if(toggleMode && (toggleCtr > 200))     // if toggle mode and delay expired
                                {
                                toggleCtr = 0 ;                 // reset counter
                                mode = MODE_HOURMN ;            // switch to hours:minutes
                                }
                        }
                }
        }

How to use it ?

First, the clock enters time setting mode :

Only the hours row is displayed : press ADVANCE button to change hour, and press VALID button when it is correct.
The hours row is shut off, and the middle and lower rows of minutes are displayed : press ADVANCE to change minutes, and press VALID when it is correct.
The clock starts with seconds set to zero.

To change display : press the MODE button to switch display in this order :

Hours/minutes -> Seconds -> Temperature -> back to Hours/minutes

To toggle automatically from Hours/minute display to Temperature display : press the TOGGLE button.

To enter time setting : press both MODE and TOGGLE buttons at the same time.

How to read the display :

If both three rows are used : you are reading the time (hours/minutes).

If hour row is shut off : if the middle an lower rows count, you are reading the seconds. If it does not count, you are reading the temperature.

Note that it could be easy to add 3 more LEDs (30 are possible, 27 are used) to show display status : either time, seconds or temperature. Do it yourself !

Please report bugs & comments in my forum, thanks !

A user's Dymoclock

Lubo_plan built the PIC DymoClock, he made very good job and his clock looks great !

PIC dymocock, front view

As you can see, the most complicated part of this clock is the display panel :

PIC dymoclock, back side

and recently another one :

Dymoclock by lubo_plan

Thank you to Lubo_plan who sent me this pictures.


Here is another user's clock :Gérard sent me the picture, source code and circuit diagram of his bleu-blanc-rouge binary clock :

Gérard's PIC binary clock

see details and discussion here