// stopwatch 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 toggle between running and paused (lap timer) displays. long-press button to stop stopwatch and reset to 00:00.00 // compile using SDCC: sdcc -mmcs51 --std-c99 stopwatch.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[6] = {5,3,4,2,1,0}; __code unsigned char digitLimit[6] = {9,9,9,5,9,9}; volatile unsigned char running = 0; // flag for stopwatch running volatile unsigned char paused = 1; // flag for 'frozen' (lap timer) display volatile unsigned char pausedDigits[6] = {0, 0, 0, 0, 0, 0}; volatile unsigned char runningDigits[6] = {0, 0, 0, 0, 0, 0}; void timer1_isr(void) __interrupt (3) __using (1) { // every 250us static unsigned char isrCount = 0; static unsigned char displayDigit = 0; static unsigned char pressedCount = 0; // debounce and long-press detect for pushbutton unsigned char i; if (++isrCount == 40) { // 40 * 250us = 0.01s isrCount = 0; } else if ((isrCount + 5) % 10 == 0) { // multiplex 6-digit display. one digit per 2500us, so all six at 66.67Hz P1 = 0xFF; // all segments off P3 |= 0x3F; // no digit selected if (++displayDigit == 6) { displayDigit = 0; } P3 &= ~(1 << displayDigit); // select digit if (paused) { P1 = sevenSeg[pausedDigits[displayDigit]]; } else { // running P1 = sevenSeg[runningDigits[displayDigit]]; } } else if (isrCount == 10 && running) { // run clock for (i = 0; i < 6; i++) { if (++runningDigits[digitOrder[i]] <= digitLimit[i]) { break; } else { runningDigits[digitOrder[i]] = 0; } } } else if (isrCount == 20 && !paused) { // copy runningDigits to pausedDigits for (i = 0; i < 6; i++) { pausedDigits[i] = runningDigits[i]; } } else if (isrCount == 30) { // debounce button if (P3_7) { // button released pressedCount = 0; } else { // button pressed if (++pressedCount == 1) { // start of press if (running) { paused = !paused; // toggle between paused display (lap timer) and running display } else { paused = 0; running = 1; // start running on first press after reset } } else if (pressedCount > 100) { // long press >= 1s pressedCount = 100; running = paused = 0; for (i = 0; i < 6; i++) { runningDigits[i] = 0; } } } } } 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; }