// 24-hour clock code for SH-E 879 clock kit board (6-digit, 7-segment display with AT89C2051 microcontroller and single push button) // ceptimus May 2022 // short-press button to increment currently selected (flashing) field or exit to normal (clock running) mode when seconds '00' is flashing // long-press button change to next field, or enter clock-setting mode from running mode // fields are selected in the order: units minutes, tens minutes, units hours, tens hours, seconds ('00') // compile using SDCC: sdcc -mmcs51 --std-c99 clock24h.c #include /* contrary to the circuit diagram included with the SH-E 879 kit, the seven segment display is addressed using these bits of P1: --0-- | | 2 1 | | --3-- | | 5 6 | | --4-- */ __code unsigned char sevenSeg[10] = {0x88, 0xBD, 0xC4, 0xA4, 0xB1, 0xA2, 0x82, 0xBC, 0x80, 0xA0}; // digits are addressed in slightly quirky order by P3_x: 0, 1, 2, 4, 3, 5 left to right __code unsigned char digitOrder[4] = {5,3,4,2}; __code unsigned char digitLimit[4] = {9,5,9,5}; volatile unsigned char running = 0; // flag for clock running volatile unsigned char runningDigits[6] = {1, 2, 0, 0, 0, 0}; // start setting at 12:00 hours volatile unsigned char hour = 12; volatile unsigned char selected = 0; void timer1_isr(void) __interrupt (3) __using (1) { // every 250us static unsigned char isrCountLow = 16; static unsigned char isrCountHigh = 250; // 250us * 16 * 250 = 1s static unsigned char displayDigit = 0; static unsigned char pressedCount = 0; // debounce and long-press detect for pushbutton unsigned char i; if (--isrCountLow == 0) { // every 16 * 250us = 4000us isrCountLow = 16; if (--isrCountHigh == 0) { isrCountHigh = 250; } } else if ((isrCountLow & 0x07) == 4) { // every 8 * 250us = 2000us // multiplex 6-digit display. one digit per 2000us, so all six at 83.33Hz P1 = 0xFF; // all segments off P3 |= 0x3F; // no digit selected if (++displayDigit == 6) { displayDigit = 0; } P3 &= ~(1 << displayDigit); // select digit i = sevenSeg[runningDigits[displayDigit]]; // flash selected digit(s) when in setting mode if (!running && isrCountHigh < 125) { if (selected == 4 && (displayDigit == 3 || displayDigit == 5)) { // seconds i = 0xFF; } else if (selected == 0 && displayDigit == 4) { // units minutes i = 0xFF; } else if (selected == 1 && displayDigit == 2) { // tens minutes i = 0xFF; } else if (selected == 2 && displayDigit == 1) { // units hours i = 0xFF; } else if (selected == 3 && displayDigit == 0) { // tens hours i = 0xFF; } } P1 = i; } else if (running && isrCountHigh == 1 && isrCountLow == 1) { // increment clock once per second for (i = 0; i < 4; i++) { if (++runningDigits[digitOrder[i]] <= digitLimit[i]) { break; } else { runningDigits[digitOrder[i]] = 0; if (i == 3) { // next hour if (++hour > 23) { hour = 0; } if (hour > 19) { runningDigits[0] = 2; runningDigits[1] = hour - 20; } else if (hour > 9) { runningDigits[0] = 1; runningDigits[1] = hour - 10; } else { runningDigits[0] = 0; runningDigits[1] = hour; } } } } } else if (isrCountLow == 8) { // debounce button every 4ms (but on a different interrupt to the display multiplex) if (P3_7) { // button released if (!running && pressedCount && pressedCount < 250) { // it was a short press if (selected == 4) { // seconds was selected isrCountLow = 16; isrCountHigh = 250; running = 1; // back to running mode } else if (selected == 0) { // units minutes if (++runningDigits[4] > 9) { runningDigits[4] = 0; } } else if (selected == 1) { // tens minutes if (++runningDigits[2] > 5) { runningDigits[2] = 0; } } else if (selected == 2) { // units hours if (++runningDigits[1] > 9) { runningDigits[1] = 0; } if (runningDigits[0] == 2 && runningDigits[1] > 3) { runningDigits[0] = 0; } hour = runningDigits[0] * 10 + runningDigits[1]; } else if (selected == 3) { // tens hours if (++runningDigits[0] > 2) { runningDigits[0] = 0; } if (runningDigits[0] == 2 && runningDigits[1] > 3) { runningDigits[1] = 3; } hour = runningDigits[0] * 10 + runningDigits[1]; } } pressedCount = 0; } else { // button pressed if (++pressedCount >= 250) { if (pressedCount == 250) { // long press if (running) { selected = 0; runningDigits[3] = runningDigits[5] = 0; running = 0; } else if (++selected > 4) { selected = 0; } } pressedCount = 251; } } } } void main(void) { TMOD = 0x20; // timer 1 in 8-bit auto-reload mode TH1 = 0x06; // count from 0x06 to 0xFF then wrap. with 12MHz crystal, will interrupt every 250us IE = 0x88; // enable timer 1 interrupts TCON |= 0x40; // run timer 1 while (1) continue; }