/******************************************************************** * * * Project: LED Clock 1v2 * * Source: LED_Clock_1v2.c * * Author: Mike McLaren, K8LH * * Date: 06-Aug-08 * * Revised: 07-Aug-08 * * * * 16F88 Charlieplexed LED Clock/Calendar/Timer Experiment * * * * * * IDE: MPLAB 8.14 (tabs = 4) * * Lang: SourceBoost BoostC v6.87, Lite/Free version * * * * * ********************************************************************/ #include #pragma DATA _CONFIG1, _CCP1_RB0&_LVP_OFF&_MCLR_OFF&_WDT_OFF&_HS_OSC #pragma DATA _CONFIG2, _IESO_OFF & _FCMEN_OFF #pragma CLOCK_FREQ 16000000 // I'm using a 16 MHz crystal //--< function prototypes >------------------------------------------ //--< typedef and defines >------------------------------------------ typedef signed char s08; typedef unsigned char u08; typedef unsigned int u16; #define r08 const rom unsigned char #define smux 0 // switch matrix on RA0 #define spkr 1 // spkr on RA1 pin #define tmron porta.2 // tmr relay ouput on RA2 #define setmode swflags.0 // set sw on RB0 #define rtarrow swflags.1 // rt arrow on RB1 #define uparrow swflags.2 // up arrow on RB2 #define dnarrow swflags.3 // dn arrow on RB3 #define uplatch swlatch.2 // up arrow state latch bit #define dnlatch swlatch.3 // dn arrow state latch bit #define month set[0] // edit buffer month field #define date set[1] // edit buffer date field #define year set[2] // edit buffer year field #define tmrzero !(tmr[0]|tmr[1]|tmr[2]) //--< variables >---------------------------------------------------- u08 clk[]@0x60 = { 23,59,49 }; // clock array, HH MM SS u08 tmr[]@0x64 = { 0, 0, 0 }; // timer array, HH MM SS u08 cal[]@0x68 = { 07,08,08 }; // calendar array, MM DD YY u08 set[]@0x6C = { 00,00,00 }; // set/edit array u08 led = 1; // column 6 leds u08 mode = 0; // 0 Clk, 1 Tmr, 2 Cal, 3 off u08 data = 0; // isr, column segment data u08 test = 0; // isr, discrete led dimmer u08 colnbr = 0; // isr, column index u08 colsel = 1; // isr, column select bit u08 tens, ones; // r08 segdata[] = { 0b00111111, // "0" -|-|F|E|D|C|B|A 0b00000110, // "1" -|-|-|-|-|C|B|- 0b01011011, // "2" -|G|-|E|D|-|B|A 0b01001111, // "3" -|G|-|-|D|C|B|A 0b01100110, // "4" -|G|F|-|-|C|B|- 0b01101101, // "5" -|G|F|-|D|C|-|A 0b01111101, // "6" -|G|F|E|D|C|-|A 0b00000111, // "7" -|-|-|-|-|C|B|A 0b01111111, // "8" -|G|F|E|D|C|B|A 0b01101111, // "9" -|G|F|-|D|C|B|A 0b01110111, // "A" -|G|F|E|-|C|B|A 0b01111100, // "b" -|G|F|E|D|C|-|- 0b00111001, // "C" -|-|F|E|D|-|-|A 0b01011110, // "d" -|G|-|E|D|C|B|- 0b01111001, // "E" -|G|F|E|D|-|-|A 0b01110001 }; // "F" -|G|F|E|-|-|-|A u08 days_in[] = { 31, 28, 31, // jan, feb, mar 30, 31, 30, // apr, may, jun 31, 31, 30, // jul, aug, sep 31, 30, 31 }; // oct, nov, dec u08 rtcl = 0; // isr, 250 msec counter u08 rtch = 0; // isr, 1 second counter u08 utc = 4; // u08 sample = 0; // live/fresh switch bits u08 vcmask = 0; // u08 vcbit0 = 0; // vertical counter bit 0 u08 vcbit1 = 0; // vertical counter bit 1 u08 swlatch = 0; // debounced switch state latch u08 swflags = 0; // switch flag bits for Main u08 beepctr = 0; // u08 group; // u08 hilimit, lolimit; // //--< functions >---------------------------------------------------- inline void loadbuffer() { fsr = 0x60; // @clk[0] fsr += (mode * 4); // @clk[0], @tmr[0], or @cal[0] set[0] = indf; fsr++; // setup edit buffer set[1] = indf; fsr++; // set[2] = indf; // } inline void savebuffer() { fsr = 0x60; // @clk[0] fsr += (mode * 4); // @clk[0], @tmr[0], or @cal[0] indf = set[0]; fsr++; // update source array indf = set[1]; fsr++; // indf = set[2]; // } void setlimits() { if(mode == 2) // if calendar mode { lolimit = 1; // hilimit = days_in[month-1]; // limits 1..days_in_month if(group == 0) // if 'month' group hilimit = 12; // limits 1..12 if(group == 1) // if 'date' group and { if(month == 2) // if 'month' is february and if(!(year & 3)) // if a leap year then hilimit = 29; // limits 1..29 if(date > hilimit) // if 'date' field > hilimit date = hilimit; // set it to hilimit } if(group == 2) // if 'year' group { lolimit = 0; // hilimit = 99; // limits 0..99 } } else // clock or timer mode { lolimit = 0; // hilimit = 59; // limits 00..59 if(group == 0) // if 'hour' group hilimit = 23; // limits 00..23 } } inline void isr_b2d() { asm // { clrf _tens // movf _indf,W // btod99: // incf _tens,F // addlw 0xF6 // -10 btfsc _status,C // goto btod99 // decf _tens,F // addlw 10 // W = Ones, 0..9 movwf _ones // } } //--< main >--------------------------------------------------------- void main() { ansel = 0; // a2d off, digital i/o //osccon = 0b01110000; // set INTOSC to 8 MHz //while(!osccon.IOFS); // wait 'til oscillator stable trisb = 0b11111111; // set all pins to inputs trisa = 0b00000000; // set all pins to outputs portb = 0b00000000; // set all output latches to '0' porta = 0b00000000; // set all output latches to '0' // setup Timer 2 for 1 msec interrupts (16 MHz clock) for a display // refresh rate of 142.8 Hz tmr2 = 0; // clear Timer 2 register t2con = 0b01111100; // '0-------' unimplemented bit // '-1111---' TOUTPS<3:0>, postscale 16 // '-----1--' TMR2ON, turn Timer 2 on // '------00' T2CKPS<1:0>, prescale 1 pr2 = 250-1; // 250 x 4-usec 'ticks' = 1 msecs pir1 = 0; // clear peripheral interrupt flags pie1.TMR2IE = 1; // set Timer 2 interrupt enable bit intcon = 0b11000000; // '1-------' GIE, enable global ints // '-1------' PEIE, enable peripheral ints // '--0-----' T0IE, TMR0 ints disabled // '---0----' INTE, off // '----0---' GPIE, IOC disabled // '-----000' T0IF/INTF/GPIF flags while(1) { //--------------------------------------------------------------- if(setmode & mode < 3) // if set switch and display "on" { loadbuffer(); // copy source array to set[] buffer group = 0; // set starting group (hours/months) setlimits(); // set lower/upper field limits while(setmode) // while set switch "on" { if(rtarrow) // if rt arrow { rtarrow = 0; // if(group == 2) // bump display group, 0..2 group = 0; // else // group++; // setlimits(); // set lower/upper field limits } // while(uplatch) // while up arrow pressed { uparrow = 0; // increment display group value if(set[group] == hilimit) set[group] = lolimit; else // set[group]++; // delay_ms(220); // delay approx 1/4 sec if(uplatch) // if still pressed beepctr = 16; // send repeat 'click' } while(dnlatch) // while dn arrow pressed { dnarrow = 0; // decrement display group value if(set[group] == lolimit) set[group] = hilimit; else // set[group]--; // delay_ms(220); // delay approx 1/4 sec if(dnlatch) // if still pressed beepctr = 16; // send repeat 'click' } } intcon.GIE = 0; // suspend interrupts savebuffer(); // copy set[] buffer to source array if(mode == 0) // if 'clock' was 'source' then { rtcl = 25; // reset RTC counters including rtch = 0; // set switch debounce time... } intcon.GIE = 1; // restart interrupts } //--------------------------------------------------------------- if(dnarrow) // if "run" mode dn arrow { u08 temp; // dnarrow = 0; // increment display mode if(mode == 3) // { mode = 0; // led.0 = 1; // } else { mode++; // temp = led << 1; // temp &= 0b00000111; // led &= 0b11110000; // led |= temp; // } // } if(uparrow) // if "run" mode up arrow { u08 temp; // uparrow = 0; // decrement display mode if(mode == 0) // { mode = 3; // led.0 = 0; // } else // { mode--; // temp = led >> 1; // temp &= 0b00000111; // if(!temp) // temp = 0b00000100; // led &= 0b11110000; // led |= temp; // } } if(rtarrow) // if "run" mode rt arrow { rtarrow = 0; // if(mode == 1) // if "tmr" mode and { if(!tmrzero) // if timer not timed out { tmron ^= 1; // toggle timer output led.4 ^= 1; // toggle 'tmr' led } // } if(mode == 0) // if "clk" mode { led.5 ^= 1; // toggle 'utc' led if(led.5) // if "utc" on { if(clk[0] + utc > 24) // clk[0] += utc - 24; // else // clk[0] += utc; // } else // if "utc" off { if(clk[0] < utc) // clk[0] += 24 - utc; // else // clk[0] -= utc; // } } } if(setmode & mode == 3) // setmode = 0; // } } /******************************************************************** * interrupt service routine, 1 msec Timer 2 interrupts * ********************************************************************/ void interrupt() { pir1.TMR2IF = 0; // clear timer 2 interrupt flag /* * * sample switches and update display * * */ trisb = 0xFF; // blank the display option_reg.7 = 0; // weak pull-ups on porta.0 = 0; // power the switch matrix sample = ~portb; // sample "active low" switches porta.0 = 1; // turn off switch matrix option_reg.7 = 1; // portb weak pull-ups off portb = colsel; // select new column (digit) if(data & colsel) // if 'float' pin required data.7 = 1; // set the 'float' pin (RB7) data |= colsel; // pick up column select bit trisb = ~data; // display new column (digit) if(colsel.6) // if last column { colnbr = 0; // reset column number colsel = 1; // reset column select bit } else // else { colnbr++; // increment column number colsel <<= 1; // shift column select bit } /* * * debounce "press" and "release" switch states * * * * a switch is "debounced" when it has been sampled at the same * * state 22 times spanning 21 msecs * * */ sample &= 0b00001111; // switches are on RB3..RB0 pins vcmask = sample ^ swlatch; // changes (press or release) vcbit0 &= vcmask; // clear inactive vertical counters vcbit1 &= vcmask; // vcmask &= colsel; // use 'colsel' as 7 msec prescaler vcbit1 ^= (vcmask & vcbit0); // inc bit 1 on active counter vcbit0 ^= vcmask; // inc bit 0 on active counter vcmask = ~vcmask; // check for timed-out counters vcmask |= vcbit0; // vcmask |= vcbit1; // vcmask = ~vcmask; // any '1's are time-out counters swlatch ^= vcmask; // update debounced switch state if(vcmask &= sample) // get rid of "new release" bits beepctr = 32; // task a "new press" beep swflags ^= vcmask; // update switch flags for Main /* * * beep task produces a 500 Hz 'beep' with 1 msec interrupts * * */ if(beepctr) // if beep task running { porta ^= 1 << spkr; // toggle speaker pin beepctr--; // decrement msec counter } /* * * update RTC once per second (once every 1000 interrupt cycles) * * */ rtcl++; // bump counter lo if(rtcl == 250) // if 250 msec interval { rtcl = 0; // reset counter lo and rtch++; // bump counter hi if(rtch == 4) // if 1 second interval { rtch = 0; // reset counter hi bump clock fsr = 0x62; // @clk[2] 'seconds while(indf == 59) // while field == 59 { indf = 0; fsr--; // set to 00 and bump index } // indf++; // if(clk[0] == 24) // if 'hours' = 24 { clk[0] = 0; // reset to 00 and cal[1]++; // bump 'date' if(cal[1] > days_in[cal[0]-1]) { cal[1] = 1; // set 'date' to the 1st and cal[0]++; // bump 'month' if(cal[0] > 12) // if 'month' > december { cal[0] = 1; // reset to january and cal[2]++; // bump 'year' if(cal[2] > 99) // if 'year' > 99 cal[2] = 0; // reset to 00 } // } // } // if(tmron) // if appliance timer running { fsr = 0x66; // @tmr[2] 'seconds while(!indf) // while field == 00 { indf = 59; fsr--; // set to 59 and bump index } indf--; // if(tmrzero) // if timed out { tmron = 0; // turn off timer pin led.4 = 0; // turn off 'tmr' led beepctr = 32; // send short 'beep' } } } } /* * * prep display data for next interrupt cycle * * */ fsr = 0x60; // clk[] array address fsr += (mode << 2); // plus display mode offset if(setmode) // if set mode fsr = 0x6C; // use set[] array address fsr += (colnbr >> 1); // add display group, 0..2 if(colsel.6) // if column 6 { data = led & test; // use discrete LED data test ^= 63; // reduces discrete led duty cycle } else // else { isr_b2d(); // get 'tens' and 'ones' data = tens; // if(colnbr.0) // if odd column (ones) data = ones; // use ones digit, 0..9 data = segdata[data]; // segment data for next interrupt if(mode == 3) // if display off mode data = 0; // display "off" next interrupt if(setmode) // if set mode and if(group == colnbr / 2) // if this group and if(rtch.0) // if 250 msec blink off period if(!(swlatch & 12)) // if not up or dn arrow pressed data = 0; // display "off" next interrupt } }