/************************************************************************
*                                                                       *
*   Filename:      Timer1                                               *
*   Date:          6/12/11                                              *
*   File Version:  1.6                                                  *
*                                                                       *
*   Author:        David Meiklejohn                                     *
*   Company:       Gooligum Electronics                                 *
*                                                                       *
*************************************************************************
*                                                                       *
*   Architecture:  Midrange PIC                                         *
*   Processor:     16F690                                               *
*   Compiler:      HI-TECH C v9.83 (Lite mode)                          *
*                                                                       *
*************************************************************************
*                                                                       *
*   Files required: none                                                *
*                                                                       *
*************************************************************************
*                                                                       *
*   Description:    Simple minute timer                                 *
*                                                                       *
*   Sounds alarm when input time (hh:mm) counts down to 0:00            *   
*                                                                       *
*   Timing is derived from 32.768 kHz crystal driving Timer1            *        
*                                                                       *
*   Time is entered using 0-9 keys on 3x4 numeric keypad,               *
*   with each keystroke displayed as a digit on the right,              *
*   pushing existing digits to the left.                                *
*                                                                       *
*   Timer is started and stopped using '#' key                          *
*                                                                       *
*   Display ':' flashes at 1 Hz when timer is running,                  *
*               steady when timer not running.                          * 
*                                                                       *
*   Alarm sounds when timer counts down to "0:00"                       *
*   Alarm stops sounding when '*' or "#' is pressed,                    *
*   or after time-out period of 20 seconds                              *
*                                                                       *
*   '*' resets time to "0:00" and stops timer                           *
*   and turns off alarm (if it is sounding)                             *
*                                                                       *
*   Holding '*' down for 2 secs (at any time),                          *
*   or not pressing any key for 5 mins (while display is zeroed)        *
*   places timer into sleep mode                                        *
*                                                                       *
*   Pressing '*' again (after release) wakes the device                 *
*                                                                       *
*   Low battery condition (Vdd < 2.4 V)                                 *
*     indicated by decimal point on digit 4                             *
*                                                                       *
*************************************************************************
*                                                                       *
*   Pin assignments:                                                    *
*       RB4-6, RA1          = keypad row inputs (active low)            *
*       RC2,7, RB7          = keypad column enables (active high)       *
*       RA0-2, RB4-6, RC0,1 = 7-segment display bus (active high)       *
*       RB7, RC2,3,6,7      = display segment enables (active high)     *
*       P1A, P1B            = piezo speaker                             *
*       OSC1, OSC2          = 32.768 kHz crystal                        *
*                                                                       *
************************************************************************/

#include <htc.h>


/***** CONFIGURATION *****/
#ifdef __DEBUG
//  no code or data protect, no brownout detect, no watchdog, no power-up timer
//  int reset, int clock with I/O, failsafe clock monitor, int/ext switch disabled   
__CONFIG(CP_OFF & CPD_OFF & BOREN_OFF & WDTE_OFF & PWRTE_OFF &
         MCLRE_OFF & FOSC_INTRCIO & FCMEN_ON & IESO_OFF);
#else
//  code and data protect, no brownout detect, no watchdog, power-up timer
//  int reset, int clock with I/O, failsafe clock monitor, int/ext switch disabled   
__CONFIG(CP_ON & CPD_ON & BOREN_OFF & WDTE_OFF & PWRTE_ON &
         MCLRE_OFF & FOSC_INTRCIO & FCMEN_ON & IESO_OFF);
#endif

// *** Pin assignments

// keypad row inputs (active low):
#define KP_R1       RB6         // row 1 (1,2,3), pin 1
#define KP_R2       RB5         // row 2 (4,5,6), pin 2
#define KP_R3       RB4         // row 3 (7,8,9), pin 3
#define KP_R4       RA1         // row 4 (*,0,#), pin 4

// keypad column enables (active high):
#define  KP_C1      RC7         // column 1 (1,4,7,*), pin 5
#define sKP_C1      sPORTC.RC7      
#define  KP_C2      RB7         // column 2 (2,5,8,0), pin 6
#define sKP_C2      sPORTB.RB7 
#define  KP_C3      RC2         // column 3 (3,6,9,#), pin 7
#define sKP_C3      sPORTC.RC2

// 7-segment display digit enables (active high):
#define  D1_EN      RC3         // 1: 10 hours
#define sD1_EN      sPORTC.RC3
#define  D2_EN      RC6         // 2:  1 hours
#define sD2_EN      sPORTC.RC6
#define  D3_EN      RB7         // 3: 10 mins 
#define sD3_EN      sPORTB.RB7
#define  D4_EN      RC2         // 4:  1 mins 
#define sD4_EN      sPORTC.RC2
#define  CL_EN      RC7         // 5: colon and indicator
#define sCL_EN      sPORTC.RC7

// 7-segment display indicators (active high):
#define  DP         RB6         // decimal point (on each digit)
#define sDP         sPORTB.RB6
#define  L1         RA2         // colon top
#define sL1         sPORTA.RA2
#define  L2         RA0         // colon bottom
#define sL2         sPORTA.RA0
#define  L3         RC0         // indicator LED
#define sL3         sPORTC.RC0

// 7-segment display bus TRIS masks (for enabling/disabling display output / keypad row pins)
#define t7SEG_A     0b000111    // RA0-2
#define t7SEG_B     0b01110000  // RB4-6
#define t7SEG_C     0b00000011  // RC0-1


/***** SYMBOLS *****/

// key codes (returned by keypad scan function)
#define KEY_STAR    10      // '*' key
#define KEY_HASH    11      // '#' key
#define KEY_NONE    12      // no key pressed

// display codes (sent to digit display function)
#define DSP_STAR    10      // '*'
#define DSP_HASH    11      // '#'
#define DSP_BLANK   12      // blank digit


/***** CONSTANTS *****/
#define MAX_DB_CNT      50/5        // key debounce period = 50 ms / 5 ms per sample
#define MAX_STAR_CNT    2000/5      // time to press '*' key to enter standby
                                    //  = 2 sec ( / 5 ms per count)
#define ALM_TIMEOUT     20          // seconds to sound alarm   
#define KEY_TIMEOUT     300         // seconds to enter standby if no key pressed   
#define MINVDD          2400        // minimum Vdd in mV (for low battery test)


/***** FUNCTION PROTOTYPES *****/
void initialise();                  // initialise processor
void set7seg(char digit);           // display digit on 7-segment display 


/***** GLOBAL VARIABLES *****/

// variables updated by ISR
volatile unsigned char  db_key = KEY_NONE;      // most recent debounced keypress:
                                                //      0-9 = '0' to '9'
                                                //      KEY_STAR = '*',
                                                //      KEY_HASH = '#'
                                                //      KEY_NONE = no key pressed
volatile bit            key_change = 0;         // keypad state change flag: 
                                                //      1 = changed                                                
volatile unsigned int   press_cnt = 0;          // measures keypress time
                                                // (increments every 5 ms while any key is held down, 
                                                //  cleared on release)
volatile unsigned char  digit[4] = {0,0,0,0};   // display digits
volatile unsigned char  hrs = 0;    	        // time remaining
volatile unsigned char  mins = 0;              
volatile unsigned char  secs = 0;
volatile unsigned int   t_secs = KEY_TIMEOUT;   // timeout counter (seconds)

// flags
volatile bit            buzz_on = 0;            // buzzer state (1 = on)
volatile bit            timer_on = 0;           // timer state (1 = countdown active)
volatile bit            standby = 0;            // sleep mode (1 = in standby mode)
volatile bit            low_bat = 0;            // low battery detected (1)

// shadow registers
volatile union {                            // PORTA
    unsigned char   byte;
    struct {
        unsigned    RA0     : 1;
        unsigned    RA1     : 1;
        unsigned    RA2     : 1;
        unsigned    RA3     : 1;
        unsigned    RA4     : 1;
        unsigned    RA5     : 1;
    };
} sPORTA;

volatile union {                            // PORTB
    unsigned char   byte;
    struct {
        unsigned            : 4;
        unsigned    RB4     : 1;
        unsigned    RB5     : 1;
        unsigned    RB6     : 1;
        unsigned    RB7     : 1;
    };
} sPORTB;

volatile union {                            // PORTC
    unsigned char   byte;
    struct {
        unsigned    RC0     : 1;
        unsigned    RC1     : 1;
        unsigned    RC2     : 1;
        unsigned    RC3     : 1;
        unsigned    RC4     : 1;
        unsigned    RC5     : 1;
        unsigned    RC6     : 1;
        unsigned    RC7     : 1;
    };
} sPORTC;


/***** MAIN PROGRAM *****/
void main()
{
    unsigned char   key;                // current keypress
    
        
    initialise();                       // configure ports and timers                                                                                                
    
    // enable interrupts
    PEIE = 1;                           // enable peripheral 
    ei();                               //   and global interrupts 
    
    
    // Main loop  
    for (;;)
    {
        // handle any key presses (detected by TMR0 interrupt)
        if (key_change)                
        {
            key = db_key;                       // get new keystroke
            key_change = 0;                     //   clear change flag
            t_secs = KEY_TIMEOUT;               //   restart keypad timeout counter
            switch (key)                        // and handle it:
            {
                case KEY_NONE:                      // key release:
                    break;                              // do nothing
                    
                case KEY_HASH:                      // '#' key:
                    if (buzz_on)
                        buzz_on = 0;                    // turn off buzzer if sounding
                    else
                        timer_on = !timer_on;           // else toggle timer on/off
                    break;
                    
                case KEY_STAR:                      // '*' key:
                    timer_on = 0;                       // disable and
                    digit[0] = 0;                       //  zero timer 
                    digit[1] = 0;
                    digit[2] = 0;
                    digit[3] = 0;
                    hrs = 0;
                    mins = 0;             
                    buzz_on = 0;                        // turn off buzzer
                    break;
                    
                default:                            // other keys (0-9):
                    digit[0] = digit[1];                // show new digit on display
                    digit[1] = digit[2];                //   shift digits to the left
                    digit[2] = digit[3];
                    digit[3] = key;                     //   display new key press on right
                    hrs = digit[0]*10 + digit[1];       // update time from new digits
                    mins = digit[2]*10 + digit[3];                    
                    secs = 60;                          // (new time is shown a full minute before it decrements)
                    break;
            }   
        }
        
        // update display digits with current time
        //  NOTE: time is maintained by TMR1 interrupt
        //        digits are displayed by TMR0 interrupt
        digit[0] = (unsigned)hrs / 10;
        digit[1] = (unsigned)hrs % 10;
        digit[2] = (unsigned)mins / 10;
        digit[3] = (unsigned)mins % 10;    
        
        // check for shutdown ('*' held down or keypad timeout)
        if ((db_key == KEY_STAR && press_cnt > MAX_STAR_CNT) || t_secs == 0)
        {
            // blank display until key release
            buzz_on = 0;                        // turn off alarm
            standby = 1;                        // flag standby mode (will blank display)
            digit[0] = 0;                       // zero timer 
            digit[1] = 0;                  
            digit[2] = 0;
            digit[3] = 0;
            hrs = 0;
            mins = 0;                         
            while (db_key != KEY_NONE)          // wait for key release
                ;
                
            // prepare to enter standby mode
            TMR2ON = 0;                         // disable PWM 
            TMR1 = 0;                           // reset Timer1 and interrupt flag,
            TMR1IF = 0;                         //   so that Timer1 wakeup will be 2 secs from now
            T0IE = 0;                           // ensure no wake-ups from Timer0
            di();                               // disable interrupts
            
            // sleep until '*' is pressed again
            do
            {
                // minimise power
                PORTA = 0;                      // drive all pins low
                TRISA = 0;                      
                PORTB = 0; 
                TRISB = 0;                         
                PORTC = 0;    
                TRISC = 0;  
                
                // enter standby mode (for 2 secs)           
                SLEEP();                        // sleep until woken by Timer1
                TMR1IF = 0;                     // clear interrupt flag for next time
                
                // setup to read '*' key
                TRISA = t7SEG_A;                // make display bus / keypad row pins inputs
                TRISB = t7SEG_B;
                TRISC = t7SEG_C;
                KP_C1 = 1;                      // enable keypad column 1 ('*' is C1, R4)
             }    
            while (KP_R4);                      // sleep again if '*' not pressed
            db_key = KEY_STAR;                  // record keypress

            // restart display and keyboard scanning
            initialise();                       // reset processor state  
            ei();                               // re-enable interrupts   
            standby = 0;                        // clearing standby mode will re-enable display
                       
            // wait for '*' key release
            while (db_key != KEY_NONE)          
                ;  
            t_secs = KEY_TIMEOUT;               // restart keypad timeout counter      
        }    
    }
}


/***** INTERRUPT SERVICE ROUTINE *****/
void interrupt isr(void)
{
    static unsigned char    mpx_cnt = 0;            // multiplex counter
    static unsigned char    db_cnt = MAX_DB_CNT;    // debounce counter
    static unsigned char    key;                    // current keypress    
    
    if (T0IF)    
    {
        // *** Service Timer0 interrupt
        //
        //  This interrupt is triggered by a TMR0 overflow every 1.024 ms
        //
        //  Displays current time on 4-digit 7-segment LED display
        //      Implements multiplexing; on successive interrupts:
        //          digit[0-3] output to digit 1-4
        //          ':' turned on, or flashed when timer enabled
        //      Entire display refresh every 5 interrupts (5.12 ms)
        //      Output changes are made via shadow registers
        //
        //  Scans keypad
        //      Each column read in turn, during display multiplexing "loop"
        //      Scanning complete after 5 interrupts (5.12 ms)
        //      Debounce performed at end of scan
        //      -> updates keypad variables:
        //          db_key      (current debounced keypad state)
        //          key_change  (new keypress or release indication)
        //          press_cnt   (time current key has been held down)
        //
        //  Sounds alarm
        //      Enables PWM (piezo) outputs, every 0.5 s, if alarm is on
        //
        //  Displays indicator if battery is low
        //
        //  Blanks display on shutdown (standby)
        //
        //  Copies shadow registers to output ports
        //
        //
        T0IF = 0;                           // clear interrupt flag
        
        // Setup for keypad read
        //   blank display during keypad read
        //   (re-enable display output at end of ISR)
        TRISA |= t7SEG_A;                   // make display bus / keypad row pins inputs
        TRISB |= t7SEG_B;
        TRISC |= t7SEG_C;
        
        // Multiplexing "loop" used to:
        //   display digits on 4 x 7-segment LED display
        //   handle ':' flashing
        //   scan and debounce keypad
        //   handle alarm
        // 
        //   mpx_cnt determines current keypad column to read and/or digit to display
        //        
        
        switch (mpx_cnt)
        {
            case 0:          
                // setup for keypad read cycle
                key = KEY_NONE;   
                
                // read keypad column 1
                // (pulled low for ':' display during last interrupt)
                if (!KP_R1) key = 1;        
                if (!KP_R2) key = 4; 
                if (!KP_R3) key = 7;
                if (!KP_R4) key = KEY_STAR;

                // display digit 1
                if (digit[0] > 0)                   // output digit 1
                    set7seg(digit[0]);              // (blank if leading zero)
                else                            
                    set7seg(DSP_BLANK);           
                sD1_EN = 1;                         // enable digit 1 
                
                // sound alarm (if enabled)
                if (buzz_on && TMR1 & 1<<14)        // sound alarm each second if alarm flag set
                {                                   // (TMR1<14> cycles at 1 Hz)
                    TRISC4 = 0;                     //    enable PWM (piezo) outputs to sound alarm
                    TRISC5 = 0;          
                }
                else
                {
                    TRISC4 = 1;                     //    disable PWM outputs to silence alarm
                    TRISC5 = 1;           
                }            

                break;
                
            case 1:                 
                // display digit 2
                set7seg(digit[1]);                  // output digit 2
                sD2_EN = 1;                         // enable digit 2 
                
                break;
                
            case 2:                 
                // display digit 3
                set7seg(digit[2]);                  // output digit 3
                sD3_EN = 1;                         // enable digit 3 
                
                break;

            case 3:       
                // read keypad column 2
                // (pulled low for digit 3 display during last interrupt)
                if (!KP_R1) key = 2;       
                if (!KP_R2) key = 5; 
                if (!KP_R3) key = 8;
                if (!KP_R4) key = 0;             
                               
                // display digit 4
                set7seg(digit[3]);                  // output digit 4
                sD4_EN = 1;                         // enable digit 4 
                
                // low battery indication
                if (low_bat)                        // if low battery,
                    sDP = 1;                        //   turn on decimal point on digit 4
                      
                break;
                                                       
            case 4:   
                //  read keypad column 3
                // (pulled low for digit 4 display during last interrupt)
                if (!KP_R1) key = 3;       
                if (!KP_R2) key = 6; 
                if (!KP_R3) key = 9;
                if (!KP_R4) key = KEY_HASH;  
                                      
                // scan is complete, so debounce keypad
                key_change = 0;
                if (key == db_key)                  // if no change
                    db_cnt = MAX_DB_CNT;            //   reset debounce count
                else                                // if change
                    if (--db_cnt == 0)              //   when change is stable
                    {
                        db_key = key;               //      accept it
                        key_change = 1;             //      flag it
                        db_cnt = MAX_DB_CNT;        //      reset debounce count
                    }
                
                // update key press counter
                if (key_change)                     // if a new key is pressed or released
                    press_cnt = 0;                  //   reset the count
                else
                    if (db_key != KEY_NONE)         // if a key is currently pressed
                        press_cnt++;                //   increment the count

                // display colon 
                sPORTA.byte = 0;                    // clear all display bits
                sPORTB.byte = 0;
                sPORTC.byte = 0;
                sL1 = 1;                            // turn on colon LEDs
                sL2 = 1;
                if (timer_on && TMR1 & 1<<14)       // flash colon if timer enabled
                {                                   // (TMR1<14> cycles at 1 Hz)                            
                    sL1 = 0;                        //    blank colon LEDs every alternate second
                    sL2 = 0;
                }
                sCL_EN = 1;                         // enable colon 
                        
                break;               
        }
        
        // increment multiplex counter, to select next time slice for next interrupt
        mpx_cnt++;
        if (mpx_cnt == 5)                       // reset count if at end of sequence
            mpx_cnt = 0;

        // blank display if entering standby mode (provides visual indication)
        if (standby)
        {
            sPORTA.byte &= ~(t7SEG_A);          // clear display bus port bits
            sPORTB.byte &= ~(t7SEG_B);
            sPORTC.byte &= ~(t7SEG_C);
        }    

        // copy shadow regs to ports
        PORTA = sPORTA.byte;
        PORTB = sPORTB.byte;
        PORTC = sPORTC.byte; 
        
        // start driving 7-segment display bus (display will light)
        TRISA &= ~(t7SEG_A);                    // make display bus pins outputs
        TRISB &= ~(t7SEG_B);
        TRISC &= ~(t7SEG_C);
    }
    
    if (TMR1IF)
    {
        // *** Service Timer1 interrupt
        //
        //  This interrupt is triggered by a TMR1 overflow every 2 s
        //
        //  Handles background time keeping:
        //      If timer active:
        //          Countdown time
        //          Stop timer and enable alarm when countdown complete
        //      If timer not active and time is 0:00
        //          Countdown keypad timeout
        //      If alarm sounding:
        //          Countown alarm timeout
        //          Disable alarm when timeout complete
        //      If in standby mode:
        //          Do nothing
        //
        //  Initiates low battery check (ADC read of 0.6 V ref)
        //
        //
        TMR1IF = 0;                 // clear interrupt flag
    
        //  If preparing to enter standby mode, do nothing
        if (standby)  
            return;
            
        //  If timer active, countdown time
        if (timer_on)                       
        {
            if (hrs > 0 || mins > 0)
            {
                // decrement time by 2 secs         
                if (secs > 0)
                    secs -= 2;              
                else                        // handle underflow
                {
                    secs = 58;
                    if (mins == 0)
                    {
                        mins = 59;
                        if (hrs > 0)
                            hrs--;
                    }
                    else
                        mins--;  
                }
            }
            // check for countdown complete
            if (hrs == 0 && mins == 0)
            {
                buzz_on = 1;                // turn on alarm
                secs = ALM_TIMEOUT;         // set seconds to countdown alarm timeout
                timer_on = 0;               // turn off timer
            }
        }
        //  If timer not active and time is 0:00, countdown keypad timeout  
        else
            if (hrs == 0 && mins == 0 && t_secs > 0)
                t_secs -= 2;
        
        
        //  If alarm sounding, countdown alarm timeout
        if (buzz_on)  
        {
            // decrement alarm timeout by 2 secs         
            if (secs > 0)
                secs -= 2;              
            else
                // timeout complete
                buzz_on = 0;            // turn off alarm
        } 
        
        //  Initiate battery check 
        GO = 1;                         // initiate ADC conversion (will trigger interrupt)
    } 
         
    if (ADIF)
    {
        // *** Service ADC interrupt
        // 
        //  Triggered every 2 secs by Timer1 interrupt
        //
        //  Compares sampled value of 0.6 V reference with a threshold,
        //  to test for low battery (Vdd) condition
        //
        ADIF = 0;                   // clear interrupt flag
        
        // test for low Vdd (measured 0.6 V > threshold)
        low_bat = (ADRESH > 255*600L/MINVDD);
    }
}

            

/***** FUNCTIONS *****/

/***** Initialise processor *****/
void initialise()
{
    // configure ports
    PORTA = 0;                          // start with all digits off
    PORTB = 0;                          //   -> all display pins low
    PORTC = 0;   
    sPORTA.byte = 0;                    //   update shadow registers
    sPORTB.byte = 0;
    sPORTC.byte = 0;      
    TRISA = 0b111000;                   // output pins are RA0-2, 
    TRISB = 0b00001111;                 //                 RB4-7,
    TRISC = 0b00110000;                 //                 RC0-3,6-7              
    ANSEL = 0;                          // all inputs are digital
    ANSELH = 0;
    WPUA = 0b00000010;                  // enable weak pull-ups on RA1 
    WPUB = 0b01110000;                  //   and RB4-6

    // setup timers + weak pull-ups
    OPTION_REG = 0b01000001;            // configure Timer0 and weak pull-ups:
                 //0-------                 enable global weak pull-ups (*RABPU = 0)
                 //--0-----                 timer mode (T0CS = 0)
                 //----0---                 prescaler assigned to Timer0 (PSA = 0)
                 //-----001                 prescale = 4 (PS = 001)
                                        //  -> increment every 4 us
                                        //  -> TMR0 overflows every 1.024 ms
    T0IE = 1;                           // enable Timer0 interrupt  
                                      
    T1CON = 0b00001110;                 // configure Timer1:
            //-0------                      gate disabled (TMR1GE = 0)
            //--00----                      prescale = 1 (T1CKPS = 00)
            //----1---                      LP oscillator enabled (T1OSCEN = 1)
            //-----1--                      asynchronous mode (/T1SYNC = 1)
            //------1-                      external clock (TMR1CS = 1)
            //-------0                      disable Timer1 (TMR1ON = 0)
                                        //  -> increment TMR1 at 32.768 kHz
    TMR1 = 0;                           // clear Timer1 (start counting from 0)       
    TMR1ON = 1;                         // start Timer1   
    TMR1IE = 1;                         // enable Timer1 interrupt    
    
    // setup PWM
    T2CONbits.T2CKPS = 0;               // TMR2 prescale = 1
                                        //  -> TMR2 increments every 1 us
    PR2 = 249;                          // period PR2+1 = 250 us
                                        //  -> PWM frequency = 4 kHz   
    CCPR1L = 125;                       // CCPR1L:CCP1CON<5:4> = 500
    CCP1CONbits.DC1B = 0;               //  -> PWM pulse width = 125 us
                                        //  -> PWM duty cycle = 50%
    CCP1CONbits.CCP1M = 0b1101;         // select PWM mode:
                                        //  P1A active-high, P1B active-low
    CCP1CONbits.P1M = 0b00;             //  single output   
    STRA = 1;                           // enable P1A and P1B as PWM outputs
    STRB = 1;                           //  (output pins disabled by TRIS bits)  
    TMR2ON = 1;                         // start PWM (enable TMR2)           
    
    // setup ADC (for low battery test)
    ADCON1 = 0b01010000;                // configure ADC:
             //-101----                     A/D conversion clock = Fosc/16    
                                        //  -> Tad = 4.0 us (with Fosc = 4 MHz)
    ADCON0 = 0b00110101;              
             //0-------                     MSB of result in ADRESH<7> (ADFM = 0)
             //-0------                     voltage reference is Vdd (VCFG = 0)
             //--1101--                     select 0.6 V reference (CHS = 1101)
             //-------1                     turn ADC on (ADON = 1)   
    ADIF = 0;                           // clear ADC interrupt flag  
    ADIE = 1;                           // enable ADC interrupt      
}
    

/***** Display digit on 7-segment display *****/
void set7seg(char digit)
{
    // Lookup pattern table for 7 segment display on PORTA
    // RA2:0 = AGB
    const char pat7segA[13] = {
        //   AGB
        0b000101,       // 0
        0b000001,       // 1
        0b000111,       // 2
        0b000111,       // 3
        0b000011,       // 4
        0b000110,       // 5
        0b000110,       // 6
        0b000101,       // 7
        0b000111,       // 8
        0b000111,       // 9
        0b000011,       // *
        0b000110,       // #
        0b000000        // (none)
    };     

    // Lookup pattern table for 7 segment display on PORTB
    // RB5:4 = EF
    const char pat7segB[13] = {
        //  EF
        0b00110000,     // 0
        0b00000000,     // 1
        0b00100000,     // 2
        0b00000000,     // 3
        0b00010000,     // 4
        0b00010000,     // 5
        0b00110000,     // 6
        0b00000000,     // 7
        0b00110000,     // 8
        0b00010000,     // 9
        0b00110000,     // *
        0b00000000,     // #
        0b00000000      // (none)
    }; 
    
    // Lookup pattern table for 7 segment display on PORTC
    // RC1:0 = DC
    const char pat7segC[13] = {
        //      DC
        0b00000011,     // 0
        0b00000001,     // 1
        0b00000010,     // 2
        0b00000011,     // 3
        0b00000001,     // 4
        0b00000011,     // 5
        0b00000011,     // 6
        0b00000001,     // 7
        0b00000011,     // 8
        0b00000011,     // 9
        0b00000001,     // *
        0b00000010,     // #
        0b00000000      // (none)
    }; 
        
    // lookup pattern bits and write to shadow registers
    sPORTA.byte = pat7segA[digit];     
    sPORTB.byte = pat7segB[digit];
    sPORTC.byte = pat7segC[digit];
}

