// so said EVELYN the modified DOG
//#pragma less_pedantic
#define uint8_t unsigned char
#define uint16_t unsigned short int
// Clock related defines
#define CLOCK_TIMER_HIGH 0x3C
#define CLOCK_TIMER_LOW 0xD5 // original ASM source was 0xB5, but experimentation shows this should be a more accurate value
#define CLOCK_TIMER_COUNT 20
#define CLOCK_COLON_COUNT 10 // 1/2 of CLOCK_TIMER_COUNT
#define CLOCK_BLINK_COUNT 5
#define CLOCK_INCREMENT_COUNT 4
// Button related defines
#define BUTTON_PRESS 2
#define BUTTON_PRESS_LONG 40
// flag to determine whether or not to display the colon (every half second)
// can also be repurposed for blinking numbers during time set
volatile bit show_colon = 0;
// flag when to display a digit (for blinking purposes during set time mode)
volatile bit show_blink = 0;
// flag when to make the next digit increment
// used while holding down the digit increment button during set time mode
volatile bit clock_increment = 0;
// check button status and set appropriate flags
// Bn_PRESSED* flags will be set ONCE PER PRESS. This means software can UNSET these flags if desired.
void button_status(void) {
// increment minute by 1
void increment_alarm_minute() {
if (++alarm_minute == 60) {
alarm_minute = 0;
if (CLOCK_RUNNING) {
increment_alarm_hour();
}
}
}
/* i feel like i need a time object that i pass to the increment_* functions and current time, alarm, and timer would all be objects of this type.
* but what about RUNNING flag?
* that would have to be part of the object type as well
* ...
* why do the increment_* functions test for clock_running? the thing.... for setting the time/alarm. don't increment if we're in a SET CLOCK mode
* perhaps another flag to add to the clock object, a SET MODE boolean
*
* maybe call the 'running' flag ENABLE; this would be more applicable a word to the alarm state
*/
// Timer0 ISR, used to maintain the time, executes every 50ms
void timer0_isr(void) interrupt 1 using 1 {
// then keep track of when to increment the seconds
if (--next_second == 0) {
next_second = CLOCK_TIMER_COUNT;
show_colon = 1;
increment_second();
} else if (next_second == CLOCK_COLON_COUNT) {
show_colon = 0;
}
}
// control digit increment
if (--next_increment == 0) {
next_increment = CLOCK_INCREMENT_COUNT;
clock_increment = 1; // flag will be unset by software
}
// Display initialization
P1 = 0x00; // disable all segments
P3 |= 0x04; // disable all digits
// Timer0 initialization
TMOD = 0x01; // Set Timer0 to mode 1, 16-bit
TH0 = CLOCK_TIMER_HIGH;
TL0 = CLOCK_TIMER_LOW; // Set counter to 15541, overlfow at 65536, a difference of 49995; about 50 milliseconds per trigger
PT0 = 1; // Giver Timer0 high priority
ET0 = 1; // Enable Timer0 interrupt
TR0 = 1; // Enable Timer0
EA = 1; // Enable global interrupts
// Other
P3_7 = 1; // disable buzzer
}
void main(void) {
// initialization routine
init();
// main program loop
while(1) {
// alarm mode
if (ALARM_ENABLE && CLOCK_RUNNING && alarm_hour == clock_hour && alarm_minute == clock_minute) {
if (clock_second == 0 && clock_state != ALARMING) {
clock_state = ALARMING;
}
if (clock_state == ALARMING) {
if (show_colon == 1) {
P3_7 = !P3_7; // P3_7 = show_colon; (this toggling creates an interesting effect)
} else {
P3_7 = 1; // if doing the toggling effect, need to make sure buzzer is off during blink
}
}
} else if (clock_state == ALARMING) { // turn off the alarm after 1 minute
clock_state = NORMAL;
P3_7 = 1; // turn off the alarm
}
// handle button events based on current clock state
switch (clock_state) {
case ALARMING:
if (B1_RELEASED || B2_RELEASED) {
clock_state = NORMAL;
P3_7 = 1; // turn off alarm
B1_RELEASED = 0;
B2_RELEASED = 0;
}
break;
case EDIT_ALARM_MIN:
if (B1_PRESSED) {
clock_state = ENABLE_ALARM;
B1_PRESSED = 0;
} else if (B2_PRESSED) {
increment_alarm_minute();
B2_PRESSED = 0;
} else if (B2_PRESSED_LONG && clock_increment == 1) {
increment_alarm_minute();
clock_increment = 0;
}
break;
case EDIT_ALARM_HOUR:
if (B1_PRESSED) {
clock_state = EDIT_ALARM_MIN;
B1_PRESSED = 0;
} else if (B2_PRESSED) {
increment_alarm_hour();
B2_PRESSED = 0;
} else if (B2_PRESSED_LONG && clock_increment == 1) {
increment_alarm_hour();
clock_increment = 0;
}
break;
case ENABLE_ALARM:
if (B1_PRESSED) {
clock_state = NORMAL;
B1_PRESSED = 0;
} else if (B2_PRESSED) {
ALARM_ENABLE = !ALARM_ENABLE;
B2_PRESSED = 0;
}
break;
case SHOW_ALARM:
if (B1_PRESSED_LONG) {
clock_state = EDIT_ALARM_HOUR;
B1_PRESSED = 0;
B1_PRESSED_LONG = 0;
} else if (B1_RELEASED) {
clock_state = ENABLE_ALARM;
B1_RELEASED = 0;
}
break;
case TIMER:
if (B2_RELEASED) {
clock_state = NORMAL;
B2_RELEASED = 0;
} else if (B1_PRESSED_LONG) {
timer_reset();
// really need to create some sort of change/state or button clear routine
// otherwise possible to enter TIMER state with B1_PRESSED_LONG set if you push both buttons at the same time.
} else if (B1_RELEASED_LONG) {
// a LONG press is a reset, do not toggle the timmer on a LONG press
B1_RELEASED = 0;
B1_RELEASED_LONG = 0;
} else if (B1_RELEASED) {
TIMER_RUNNING = !TIMER_RUNNING; // toggle timer
B1_RELEASED = 0;
}
break;
case SET_24H:
if (B2_RELEASED) {
clock_state = TIMER;
B2_RELEASED = 0;
} else if (B1_RELEASED) {
TWELVE_TIME = !TWELVE_TIME;
B1_RELEASED = 0;
}
break;
case EDIT_MIN:
if (B1_PRESSED) { // exit edit mode
clock_state = NORMAL;
B1_PRESSED = 0;
CLOCK_RUNNING = 1;
} else if (B2_PRESSED) { // increment minute
increment_minute();
clock_second = 0; // reset seconds to 0 when time is changed
B2_PRESSED = 0;
} else if (B2_PRESSED_LONG && clock_increment == 1) {
increment_minute(); // hold down the button to rapidly increase minute
clock_increment = 0;
}
break;
case EDIT_HOUR:
CLOCK_RUNNING = 0;
if (B1_PRESSED) { // switch to edit minute mode
clock_state = EDIT_MIN;
B1_PRESSED = 0;
} else if (B2_PRESSED) { // increment hour
increment_hour();
clock_second = 0; // reset seconds to 0 when time is changed
B2_PRESSED = 0;
} else if (B2_PRESSED_LONG && clock_increment == 1) {
increment_hour(); // hold down the button to rapidly increase hour
clock_increment = 0;
}
break;
case MIN_SEC:
if (B2_RELEASED) {
clock_state = SET_24H;
B2_RELEASED = 0;
}
break;
case NORMAL:
default:
if (B2_RELEASED) {
clock_state = MIN_SEC;
B2_RELEASED = 0;
} else if (B1_PRESSED_LONG) {
clock_state = EDIT_HOUR;
B1_PRESSED = 0;
B1_PRESSED_LONG = 0;
} else if (B1_RELEASED) {
clock_state = SHOW_ALARM;
B1_RELEASED = 0;
}
break;
}
// generate display based on current clock state
// button events and display are separated as state may change during button events
switch (clock_state) {
case ALARMING:
if (show_colon == 1) {
set_hour_dbuf(clock_hour);
dbuf[2] = ledtable[(clock_minute/10)];
dbuf[3] = ledtable[(clock_minute%10)];
dbuf[1] |= 1;
} else {
dbuf[0] = ledtable[LED_BLANK];
dbuf[1] = ledtable[LED_BLANK];
dbuf[2] = ledtable[LED_BLANK];
dbuf[3] = ledtable[LED_BLANK];
}
break;
// colon does not blink when setting time
// in 12 hour format, colon is only on when hour represents PM
if (!TWELVE_TIME || clock_hour > 11) {
dbuf[1] |= 1;
}
// colon does not blink when setting time
// in 12 hour format, colon is only on when hour represents PM
if (!TWELVE_TIME || clock_hour > 11) {
dbuf[1] |= 1;
}
break;
case MIN_SEC:
// update display buffer to show current time
dbuf[0] = ledtable[(clock_minute/10)];
dbuf[1] = ledtable[(clock_minute%10)];
dbuf[2] = ledtable[(clock_second/10)];
dbuf[3] = ledtable[(clock_second%10)];