/** * \file main.c * \brief Firmware for the binary DCF-77 clock * \author Ronald Schaten * \version $Id: main.c,v 1.1 2007/01/02 21:30:40 rschaten Exp $ * * License: See documentation. */ #include #include #include #include #include "saa1064.h" #include "dcftime.h" uint8_t byte[4] = { 2, 3, 1, 0 }; /** the order of the connected output-LED-rows */ uint8_t output[4], outputOld[4]; /** current and old content of the LEDs */ /** the display-modes */ enum modes { timeasbinary, dateasbinary, timeasbcdhorizontal, dateasbcdhorizontal, timeasbcdvertical, dateasbcdvertical, timestamp }; enum modes mode; uint8_t demomode = 0; /** * sends the current content of output[] to the LEDs if it has changed. */ void setLeds(void) { uint8_t i; for (i = 0; i < 4; i++) { if (output[i] != outputOld[i]) { set_led_digit(byte[i], output[i]); outputOld[i] = output[i]; } } } // function setLeds /** * Takes the current time and converts it into different output-formats. * \param datetime: the current time */ void setOutput(dcf_datetime datetime) { uint8_t bcdlow, bcdhigh; /* takes the low and high parts when converting to BCD */ const uint32_t TS01012000 = 946681200UL; /* The UNIX-Timestamp of 1.1.2000 */ const uint16_t monthstarts[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; /* On which day does a new month start in non-leap-years */ const uint32_t SECONDSINADAY = (60UL * 60 * 24); /* Number of seconds in a day */ uint32_t ts; /* takes the UNIX-Timestamp */ switch (mode) { case timeasbinary: /* hour, minute and second are displayed in rows */ output[0] = datetime.time.hour; output[1] = datetime.time.minute; output[2] = datetime.time.second; output[3] = 0; break; case dateasbinary: /* day of month, month, year and day of week (starting with monday * = 1) are displayed in rows */ output[0] = datetime.date.dayofmonth; output[1] = datetime.date.month; output[2] = datetime.date.year; output[3] = datetime.date.dayofweek; break; case timeasbcdhorizontal: /* the time is converted to BCD, the digits are displayed by two in * a row */ bcdlow = datetime.time.hour % 10; bcdhigh = datetime.time.hour / 10; output[0] = (bcdhigh << 4) | bcdlow; bcdlow = datetime.time.minute % 10; bcdhigh = datetime.time.minute / 10; output[1] = (bcdhigh << 4) | bcdlow; bcdlow = datetime.time.second % 10; bcdhigh = datetime.time.second / 10; output[2] = (bcdhigh << 4) | bcdlow; output[3] = 0; break; case dateasbcdhorizontal: /* the date is converted to BCD, the digits are displayed by two in * a row */ bcdlow = datetime.date.dayofmonth % 10; bcdhigh = datetime.date.dayofmonth / 10; output[0] = (bcdhigh << 4) | bcdlow; bcdlow = datetime.date.month % 10; bcdhigh = datetime.date.month / 10; output[1] = (bcdhigh << 4) | bcdlow; bcdlow = datetime.date.year % 10; bcdhigh = datetime.date.year / 10; output[2] = (bcdhigh << 4) | bcdlow; bcdlow = datetime.date.dayofweek; bcdhigh = 0; output[3] = (bcdhigh << 4) | bcdlow; break; case timeasbcdvertical: /* the time is converted to BCD, the digits are displayed in * columns */ output[0] = 0; output[1] = 0; output[2] = 0; output[3] = 0; bcdlow = datetime.time.hour % 10; bcdhigh = datetime.time.hour / 10; output[0] |= ((bcdhigh & 8) << 4) | ((bcdlow & 8) << 3); output[1] |= ((bcdhigh & 4) << 5) | ((bcdlow & 4) << 4); output[2] |= ((bcdhigh & 2) << 6) | ((bcdlow & 2) << 5); output[3] |= ((bcdhigh & 1) << 7) | ((bcdlow & 1) << 6); bcdlow = datetime.time.minute % 10; bcdhigh = datetime.time.minute / 10; output[0] |= ((bcdhigh & 8) << 1) | ((bcdlow & 8) << 0); output[1] |= ((bcdhigh & 4) << 2) | ((bcdlow & 4) << 1); output[2] |= ((bcdhigh & 2) << 3) | ((bcdlow & 2) << 2); output[3] |= ((bcdhigh & 1) << 4) | ((bcdlow & 1) << 3); bcdlow = datetime.time.second % 10; bcdhigh = datetime.time.second / 10; output[0] |= ((bcdhigh & 8) >> 2) | ((bcdlow & 8) >> 3); output[1] |= ((bcdhigh & 4) >> 1) | ((bcdlow & 4) >> 2); output[2] |= ((bcdhigh & 2) << 0) | ((bcdlow & 2) >> 1); output[3] |= ((bcdhigh & 1) << 1) | ((bcdlow & 1) << 0); break; case dateasbcdvertical: /* the date is converted to BCD, the digits are displayed in * columns */ output[0] = 0; output[1] = 0; output[2] = 0; output[3] = 0; bcdlow = datetime.date.dayofmonth % 10; bcdhigh = datetime.date.dayofmonth / 10; output[0] |= ((bcdhigh & 8) << 4) | ((bcdlow & 8) << 3); output[1] |= ((bcdhigh & 4) << 5) | ((bcdlow & 4) << 4); output[2] |= ((bcdhigh & 2) << 6) | ((bcdlow & 2) << 5); output[3] |= ((bcdhigh & 1) << 7) | ((bcdlow & 1) << 6); bcdlow = datetime.date.month % 10; bcdhigh = datetime.date.month / 10; output[0] |= ((bcdhigh & 8) << 1) | ((bcdlow & 8) << 0); output[1] |= ((bcdhigh & 4) << 2) | ((bcdlow & 4) << 1); output[2] |= ((bcdhigh & 2) << 3) | ((bcdlow & 2) << 2); output[3] |= ((bcdhigh & 1) << 4) | ((bcdlow & 1) << 3); bcdlow = datetime.date.year % 10; bcdhigh = datetime.date.year / 10; output[0] |= ((bcdhigh & 8) >> 2) | ((bcdlow & 8) >> 3); output[1] |= ((bcdhigh & 4) >> 1) | ((bcdlow & 4) >> 2); output[2] |= ((bcdhigh & 2) << 0) | ((bcdlow & 2) >> 1); output[3] |= ((bcdhigh & 1) << 1) | ((bcdlow & 1) << 0); break; case timestamp: /* compute the UNIX-Timestamp. This function is based on http://www.mulder.franken.de/ntpdcfledclock/ */ ts = TS01012000 + SECONDSINADAY; /* 2000 was leap year */ ts += SECONDSINADAY * datetime.date.year * 365; /* Now account for the leap years. Yes, 2000 was a leap year too. */ ts += SECONDSINADAY * ((datetime.date.year - 1) / 4); ts += SECONDSINADAY * monthstarts[datetime.date.month - 1]; if (((datetime.date.year % 4) == 0) && (datetime.date.month > 2)) { /* We are in a leap year and past february */ ts += SECONDSINADAY; } ts += SECONDSINADAY * (datetime.date.dayofmonth - 1); ts += 3600UL * datetime.time.hour; ts += 60 * datetime.time.minute; ts += datetime.time.second; output[0] = (ts >> 24); output[1] = (ts >> 16); output[2] = (ts >> 8); output[3] = (ts >> 0); break; default: break; } } // function setOutput /** * Sets the output to a running light. This is used when no valid time can be * displayed. */ void setWaiting(void) { static uint8_t position = 0; output[0] = 0; output[1] = 0; output[2] = 0; output[3] = 0; if (position < 8) { output[0] = (1 << position); } else if (position == 8) { output[1] = 128; } else if (position == 9) { output[2] = 128; } else if (position == 18) { output[2] = 1; } else if (position == 19) { output[1] = 1; } else { output[3] = (128 >> (position - 10)); } position++; if (position > 19) { position = 0; } } // function setWaiting /** * Timer interrupt function. This is called on every timer-interrupt (which * happens 488 times per second. */ void timerInterrupt(void) { dcf_datetime datetime; /** takes the current time and date */ static uint8_t tickcounter; /** internal tick, is incremented with every timer-loop */ static uint8_t keycounter = 0; /** used to defeat bouncing buttons */ static uint8_t demomodecounter = 0; /** used to switch to demo mode */ static uint8_t modeswitched = 0; /** set when the mode has been switched, displays bars to indicate the new mode. */ tickcounter++; /* on every second interrupt, check the state of the DCF-signal */ if (tickcounter % 2) { if ((PINC & (1 << PINC0))) { // signal high dcf_signal(True); PORTC |= (1 << PINC1); } else { // signal low dcf_signal(False); PORTC &= ~(1 << PINC1); } } /* on every 32nd interrupt, check if the key is pressed. After 5 loops with * a pressed key, switch to the next display-mode. */ if (tickcounter % 32 == 0) { if (!(PINC & (1 << PINC2))) { // key pressed keycounter++; if (keycounter > 4) { // after 4 cycles with pressed key, switch to the next mode keycounter = 0; mode++; if (mode > timestamp) { mode = timeasbinary; } modeswitched = 5; } demomodecounter++; if (demomodecounter > 75) { // after 75 cycles with pressed key, switch to or from demo mode if (demomode == 0) { demomode = 1; mode = timeasbinary; output[0] = 255; output[1] = 255; output[2] = 255; output[3] = 255; } else { demomode = 0; mode = timeasbinary; output[0] = 255; output[1] = 129; output[2] = 129; output[3] = 255; } setLeds(); demomodecounter = 0; } } else { // key unpressed keycounter = 0; demomodecounter = 0; } } /* on every 128th interrupt (about 4 times per second), check if anything * new has to be displayed. */ if (tickcounter % 128 == 0) { if (demomode == 1) { // set the current date and time to a fixed value to demonstrate // how the time is displayed datetime.is_valid = 1; datetime.time.hour = 10; datetime.time.minute = 35; datetime.time.second = 10; datetime.date.dayofmonth = 30; datetime.date.month = 12; datetime.date.year = 6; datetime.date.dayofweek = 6; } else { datetime = dcf_current_datetime(); } if (modeswitched > 0) { output[0] = mode + 1; output[1] = mode + 1; output[2] = mode + 1; output[3] = mode + 1; modeswitched--; } else if (datetime.is_valid) { setOutput(datetime); } else { setWaiting(); } /* finally, set the output */ setLeds(); } } // function timerInterrupt /** * Main-function. Initializes the hardware and starts the main loop of the * application. * \return An integer. Whatever... :-) */ int main(void) { uint8_t i; /* set a default display-mode */ mode = timeasbinary; /* intialize ports */ DDRC = (1 << DDC1); // set pin 1 to output PORTC = (1 << PC1) | (1 << PC2); // activate pullups on pins 1 and 2 /* initialize SAA1064 on i2c-bus */ led_init(); set_led_brightness(1); for (i = 0; i <= 3; i++) { /* switch off all connected LEDs */ set_led_digit(i, 0); } /* initialize DCF77 */ dcf_init(); /* initialize timer */ TCCR0 = (0 << CS02) | (1 << CS01) | (0 << CS00); // set divider to 8. /* The interrupt is called every 8*256 CPU-cycles, at 1MHz we get 488 * calls per second. DCF_RATE in dcftime.h has to be set according to * this value. */ sei(); while (1) { /* main event loop */ if (TIFR & (1 << TOV0)) { TIFR |= 1 << TOV0; /* clear pending flag */ timerInterrupt(); } } return 0; }