Thursday, December 9, 2010

A PIC16F819 DYMOCLOCK


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.



I wanted to build a clock as simple as possible :
  • built around a little 18 pins PIC
  • no 7 segment display, only LEDs
  • no decoder, no buffer, no driver for the LED display
  • a cheap temperature sensor
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 :


Click on the schematic to enlarge




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



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

*************************************************************************
 * 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
 *
  *************************************************************************

 */


/*
 * 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
                                }
                        }
                }
        }


*********************************************************************************

No comments:

Post a Comment