#define F_CPU 16000000UL // 16 MHz #include #include #include #define SET_OUTPUT(P,B) { DDR##P |= _BV(B); } #define SET_INPUT(P,B) { DDR##P &= ~_BV(B); } #define SET_PIN(P,B) { PORT##P |= _BV(B); } #define CLR_PIN(P,B) { PORT##P &= ~_BV(B); } #define SET_RST() SET_PIN(D,5) #define CLR_RST() CLR_PIN(D,5) #define SET_DAT_0() SET_PIN(D,6) #define CLR_DAT_0() CLR_PIN(D,6) #define SET_DAT_1() SET_PIN(D,7) #define CLR_DAT_1() CLR_PIN(D,7) #define SET_DAT_2() SET_PIN(B,0) #define CLR_DAT_2() CLR_PIN(B,0) #define GET_PIN(P,B) ((PIN##P & _BV(B)) != 0) // Number of milliseconds per interrupt #define MS_PER_BIT 1000 // We need a pulse every (MS_PER_BIT*F_CPU)/1000 cycles. // We'll use a prescaler of 1/256 to bring this into the // 16-bit range. #define PRESCALE 256 #define CYCLES_PER_SECOND ((MS_PER_BIT*(F_CPU/1000)/PRESCALE)) // Seconds in the day long seconds = 0; #define DIGITS 6 #define DAY_IN_SECS (60L*60L*24L) void init(); void initClock(); void initPins(); void display(); void initClock() { // Reset on OCR1A match. // WGM1: 0100 CS1: 011 TCCR1A = 0x00; TCCR1B = 0x0c; TCCR1C = 0x00; // not using the force matches OCR1A = CYCLES_PER_SECOND; TIMSK1 = 0x02; // turn on interrupt // Timer 0 is used to scan the buttons as a crude debouncer. TCCR0A = 0x00; TCCR0B = 0x04; // prescaler at 1/256 TIMSK0 = 0x01; // rollover interrupt } void initPins() { SET_OUTPUT(D,5); SET_OUTPUT(D,6); SET_OUTPUT(D,7); SET_OUTPUT(B,0); SET_RST(); SET_DAT_0(); SET_DAT_1(); SET_DAT_2(); // Initialize buttons. SET_INPUT(C,1); SET_INPUT(C,2); SET_INPUT(C,3); SET_INPUT(C,4); SET_INPUT(C,5); SET_PIN(C,1); SET_PIN(C,2); SET_PIN(C,3); SET_PIN(C,4); SET_PIN(C,5); } void init() { seconds = 0L; cli(); initPins(); initClock(); set_sleep_mode( SLEEP_MODE_IDLE ); sei(); } volatile int update = 0; int main( void ) { init(); while (1) { if (update) { display(); update = 0; } sleep_cpu(); } return 0; } unsigned char lastPC = 0xff; #define UP_EDGE(old,new,bit) (((old & _BV(bit)) == 0) && ((new & _BV(bit)) != 0)) void scanKeys() { unsigned char newPC = PINC; long value = 0; if (UP_EDGE(lastPC,newPC,3)) { // minutes digit long digit = (seconds / 60L) % 10L; value = 60L; if (digit >= 9) value *= -9L; } if (UP_EDGE(lastPC,newPC,4)) { // tens of minutes digit long digit = (seconds / 600L) % 10L; value = 600L; if (digit >= 5) value *= -5L; } if (UP_EDGE(lastPC,newPC,5)) { // hours digit long digit = (seconds / (60L*60L)) % 24L; value = 60L*60L; if (digit >= 23) value *= -23L; } if (value != 0) { seconds += value; update = 1; } lastPC = newPC; } void display() { while (seconds > DAY_IN_SECS) { seconds -= DAY_IN_SECS; } while (seconds < DAY_IN_SECS) { seconds += DAY_IN_SECS; } long d0 = seconds % 60L; long d1 = (seconds / 60L) % 60L; long d2 = (seconds / (60L*60L)) % 24L; long cnt; SET_RST(); _delay_us(1); CLR_RST(); for (cnt = 100; cnt > 0; cnt--) { SET_DAT_0(); SET_DAT_1(); SET_DAT_2(); if (d0) { CLR_DAT_0(); d0--; } if (d1) { CLR_DAT_1(); d1--; } if (d2) { CLR_DAT_2(); d2--; } } } ISR(TIMER0_OVF_vect) { scanKeys(); } ISR(TIMER1_COMPA_vect) { seconds++; update = 1; }