/* rtc.c realtime controller 2-Channel Time Switch V1.0 Motorola MC908QY4CP 16-pin MCU with external XTAL 4MHz for timebase realtime clock compiled with icc08 Copyright (c) 2004 Wichit Sirichote, kswichit@kmitl.ac.th mode settings state 0 set hour and min NA 1 set day NA 2 manual on/off NA 3 enter scheduler 0 :enter time 1 :enter day 2: set output 4 display scheduler NA */ #include #define key0 (PTA&0x01) #define key1 (PTA&0x02) #define key2 (PTA&0x04) #define key3 (PTA&0x08) char temp,k,mode; char i,n,timer1,timer2,flag; unsigned char sec100,sec,min,hour,day,output; unsigned char sec, warm_boot; char *title = "Time Switch V1.0"; char buffer[8]; char *day_str[] = {"AA","MO","TU","WE","TH","FR","SA","SU","__"}; // "AA" for all days control // "__" for end of program char state; struct pgm{ char set_day; char set_hour; char set_min; char set_control; }; struct pgm PGM[5]; /* signal for lcd interface E PTB1 RS PTB0 D4 PTB4 D5 PTB5 D6 PTB6 D7 PTB7 D0-D3 and R/W# are tied to GND */ // function prototype declarations void LCDWI(char c); void LCDWD(char c); void print_dec(char d,unsigned int n); void print_time(char d); void tick_display(void); void update_clock(void); void exe_keypad(void); void print_output(char n); void print_record(char d,char n); void compare_time(void); void fire_output(char N); void i_LCD(void); // enter timer overflow every 1/100s or 10ms #pragma interrupt_handler isr_TIM void isr_TIM(void) { //PTB ^= 0x08; // toggle PTB.3 to check period // found period was 20.012ms TSC &= ~0x80; // clear TOF flag |= 2; // tick 1/100Hz COPCTL = 0; // clear COP update_clock(); tick_display(); // tick every 0.5 sec exe_keypad(); } #pragma interrupt_handler isrDummy void isrDummy(void) { COPCTL = 0; // clear COP } #pragma abs_address:0xffde void (* const _vectab[])(void) = { isrDummy, /* ADC */ isrDummy, /* KEYBOARD */ isrDummy, /* NOT USED */ isrDummy, /* NOT USED */ isrDummy, /* NOT USED */ isrDummy, /* NOT USED */ isrDummy, /* NOT USED */ isrDummy, /* NOT USED */ isrDummy, /* NOT USED */ isrDummy, /* NOT USED */ isr_TIM, /* TIM OVERFLOW */ isrDummy, /* TIM1 */ isrDummy, /* TIM0 */ isrDummy, /* NOT USED */ isrDummy, /* IRQ */ isrDummy /* SWI */ }; #pragma end_abs_address void update_clock() { if(++sec100 >=100) { sec100 = 0; if(mode !=3 && mode !=4) { print_time(0x80); print_output(output); } if(++sec > 59) { sec = 0; compare_time(); if( ++min >59) { min = 0; if(++hour >23) { hour = 0; if(++day > 7) day = 1; } } } } } void tick_display() { if(mode !=3 && mode !=4) { if(++timer1> 50) { timer1 = 0; LCDWI(0x85); // swap display k^=1; if (k) LCDWD(' '); else LCDWD(':'); } } } void hour_setting() { ++hour; if(hour>23) hour=0; print_time(0x80); } void min_setting() { ++min; sec = 0; compare_time(); if(min>59) min =0; print_time(0x80); } void day_setting() { ++day; if(day>7) day = 1; print_time(0x80); } void output2_setting() { output^=2; print_output(output); } void output1_setting() { output^=1; print_output(output); } void pgm_setting1() { switch(state) { case 0: temp =PGM[n].set_hour; ++temp; if(temp>23) temp = 0; PGM[n].set_hour = temp; print_record(0x80,n); break; case 1: temp = PGM[n].set_day; ++temp; if(temp>8) temp = 0; PGM[n].set_day = temp; print_record(0x80,n); break; case 2: PGM[n].set_control ^= 2; print_record(0x80,n); break; } } void pgm_setting2() { switch(state) { case 0: temp =PGM[n].set_min; ++temp; if(temp>59) temp = 0; PGM[n].set_min = temp; print_record(0x80,n); break; case 2: PGM[n].set_control ^= 1; print_record(0x80,n); break; } } void servicekey2() { switch(mode) { case 0: hour_setting(); break; case 1: day_setting(); break; case 2: output2_setting();break; case 3: pgm_setting1(); break; case 4: ++n; if(n>4) n=0; print_record(0x80,n); LCDWI(0xc7); // mode 4 shows record number LCDWD(n+48); break; } } void servicekey3() { switch(mode) { case 0: min_setting(); break; case 2: output1_setting();break; case 3: pgm_setting2(); break; } } void servicekey0() { switch(mode) { case 0:i_LCD(); // reinit lcd manually break; case 3: ++state; if(state == 3) { state = 0; // back to initial sequence ++n; // next record } if(n>5) n = 0; // loop back to record 0 print_record(0x80,n); LCDWI(0xc7); LCDWD(state+48); break; } } // use bit 0 and bit 1 for two bits output at port B void print_output(char m) { LCDWI(0xc1); if(m&2) LCDWD('*'); else LCDWD('-'); LCDWI(0xc3); if(m&1) LCDWD('*'); else LCDWD('-'); fire_output(m); // send to PTB also } void servicekey1() { ++mode; if(mode>4) mode = 0; LCDWI(0xc5); LCDWD(mode+48); LCDWD('.'); LCDWD(state+48); // mode 3 has more state switch(mode) { case 3: n=0; print_record(0x80,n); state = 0; break; case 4: n=0; state = 0; print_record(0x80,n); // print record 0 LCDWI(0xc7); // mode 4 shows record number LCDWD(n+48); break; } } // check key every 250ms void exe_keypad() { if(++timer2 > 20) { timer2 = 0; if(key0 == 0) servicekey0(); if(key1 == 0) servicekey1(); if(key2 == 0) servicekey2(); if(key3 == 0) servicekey3(); } } void delay(int j) { unsigned int i; for (i = 0; i < j; i++) COPCTL = 0; // clear COP otherwise the cpu itself will be reset } void pulseE() { PTB |= 2; delay(1); PTB &= ~2; } void LCDWI(char c) /* write instruction to instruction register */ { char temp; temp = c; PTB &= ~3; // clear RS and E PTB &= 0x0f; // clear high nibbel to low c &= 0xf0; // prepare only high nibble PTB |= c; pulseE(); c = temp; c <<=4; PTB &= 0x0f; PTB |= c; pulseE(); delay(5); } void LCDWD(char c) /* write data to data register */ { char temp; temp = c; PTB |= 1; // set bit RS PTB &= ~2; // clear bit E PTB &= 0x0f; // clear high nibbel to low c &= 0xf0; // prepare only high nibble PTB |= c; // mearge high nibble first pulseE(); c = temp; c <<=4; PTB &= 0x0f; PTB |= c; pulseE(); delay(5); } void i_LCD() /* initialize LCD in accordance with Hitachi 44780 4-bit mode */ { PTB &= ~3; // clear RS and E PTB |= 0x30; pulseE(); delay(200); pulseE(); delay(50); pulseE(); delay(50); PTB &= 0x0f; PTB |= 0x20; pulseE(); pulseE(); pulseE(); LCDWI(0x28); // set 4-bit bus, 1/16 line, 5*7 dots LCDWI(0x0c); // display on/off on display,off cursor, no blink LCDWI(0x06); // entry mode DDRAM auto address increment LCDWI(1); // clear display delay(50); } // print LCD text >8 // the 16x1 line LCD has two blocks start address! void print_lcds(char *s) { char i; LCDWI(1); // clear display LCDWI(0x80); // start at left most position for(i=0; i<8; i++) LCDWD(*s++); LCDWI(0xc0); // new address for position 9 for(i=0; i<8; i++) LCDWD(*s++); } // print time on lcd at location 0x80 or 0xc0 void print_time(char d) { buffer[0] = *day_str[day]; buffer[1] = *(day_str[day]+1); buffer[2] = ' '; buffer[3] = hour/10+48; if(buffer[3] =='0') buffer[3] = ' '; buffer[4] = hour%10+48; // buffer[5] = ':'; left for tick display buffer[6] = min/10+48; buffer[7] = min%10+48; LCDWI(d); for(i=0; i<5; i++) LCDWD(buffer[i]); LCDWI(0x86); LCDWD(buffer[6]); LCDWD(buffer[7]); } // print record number #n on LCD at location d void print_record(char d,char n) { buffer[0] = *day_str[PGM[n].set_day]; buffer[1] = *(day_str[PGM[n].set_day]+1); buffer[2] = ' '; buffer[3] = PGM[n].set_hour/10+48; if(buffer[3] =='0') buffer[3] = ' '; buffer[4] = PGM[n].set_hour%10+48; buffer[5] = ':'; buffer[6] = PGM[n].set_min/10+48; buffer[7] = PGM[n].set_min%10+48; LCDWI(d); for(i=0; i<8; i++) LCDWD(buffer[i]); print_output(PGM[n].set_control); } // sink current driving, fire with logic '0' void fire_output(char N) { if(N&1) PTB &= ~0x08; // clear PTB.3 else PTB |= 0x08; // set PTB.3 if(N&2) PTB &= ~0x04; // clear PTB.2 else PTB |= 0x04; // set PTB.2 } // scan 5 records, compare // if matched, fires output void compare_time() { char i; for(i=0; i<5; i++) { if(PGM[i].set_day != 8) // skip comapre time if day = 8 { if(PGM[i].set_day == 0) // daily control { if(PGM[i].set_hour == hour && PGM[i].set_min == min) { output=PGM[i].set_control; print_output(output); } } else { // check day also if(PGM[i].set_day == day && PGM[i].set_hour == hour && PGM[i].set_min == min) { output = PGM[i].set_control; print_output(output); } } } } } void main() { //CONFIG2 = 0x18; // external XTAL osc! // INTSCR = 0x01; // falling edges and low trigger // OCSTRIM = 0x81; // trim internal oscillator delay(1000); // powerup delay for LCD DDRB = 0xff; // port B is output port PTB = 0x30; // clear port B delay(1000); n = k=0; TMODH = 0x04; TMODL = 0xe0; //0xe2; // for 100Hz generation! // value in TMODL can be adjusted for precise timing TSC = 0x43; // run timer with interrupt // 4Mhz/4 = 1Mhz/8 = 125000Hz DDRA = ~0xf; // PA0-PA3 are input PTA = 0xff; // drive oscillator high // DDRB = 0xff; // port B is output port // PTB = 0; // clear port B // PTAPUE = ~0x81; // osc2 pin is PTA4 I/O //ADCLK = 0x40; // ADC clock= fbus/4 i_LCD(); PTAPUE = 0x0f; // enable internal pullup CONFIG2 = 0x18; // external XTAL osc! delay(10); OCSTAT = 0x02; // enable external clock! print_lcds(title); delay(20000); LCDWI(1); // clear lcd // skip initial load if warm boot == '%' if(warm_boot != '%') { warm_boot = '%'; day = 1; hour = 10; min = 40; sec = 0; } asm(" cli"); //enable irq interrupt for(;;) { COPCTL = 0; // clear COP } }