VFD modular clock firmware
Dependencies: DipCortex-EEprom RTC flw mbed
main.cpp
- Committer:
- Backstrom
- Date:
- 2015-02-09
- Revision:
- 0:f6e68b4ce169
- Child:
- 5:5c073029c416
File content as of revision 0:f6e68b4ce169:
/* * VFD Modular Clock - mbed * (C) 2011-14 Akafugu Corporation * * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * */ #include "global.h" #include "mbed.h" #include "VFDDisplay.h" #include "IV18Display.h" #include "prefs.h" #include "gps.h" #include "menu.h" #include "button.h" #include "rtc.h" #include "ds3231m.h" #include "beep.h" #include "flw.h" IV18Display display(PinMap::data, PinMap::clock, PinMap::latch, PinMap::blank); Menu menu; I2C i2c(PinMap::sda, PinMap::scl); DS3231M rtc(i2c); //RTC rtc; DigitalOut led(PinMap::led); Beep piezo(PinMap::piezo); FourLetterWord flw(&i2c); char flwWord[5]; int flwOffset; int flwOffsetDirection = 1; volatile bool haveEEPROM; Ticker blanker; Ticker multiplexer; Ticker button_ticker; Ticker rtc_ticker; Ticker tenth_ticker; volatile state_t g_clock_state = STATE_CLOCK; uint32_t gps_last_update = 0xffff; uint8_t calculate_segments_7(uint8_t character); // Alarm volatile bool g_alarm_on; volatile bool g_alarming; void write_vfd_8bit(uint8_t data); void write_vfd_iv18(uint8_t digit, uint8_t segments); void write_vfd(uint8_t digit, uint8_t segments); void demo_cycle(char* buf); void blank_tick() { static uint32_t cnt = 0; if ((cnt%10) > (10-display.getBrightness())) { display.blank(false); } else { display.blank(true); } cnt++; } void multiplex_tick() { display.multiplexTick(); } void rtc_tick() { if (haveEEPROM) strncpy(flwWord, flw.getWord(), 4); flwOffset += flwOffsetDirection; if (flwOffset <= 0) { flwOffset = 0; flwOffsetDirection = 1; } else if (flwOffset > display.digits() -4) { flwOffset = 3; flwOffsetDirection = -1; } rtc.tick(); } void tenth_tick() { rtc.tenth_tick(); } void counterTest() { display.cls(); for (uint16_t i = 0; i < 200; i++) { display.printf("cnt %3d", i); wait(0.01); } } void timeAndDateTest() { uint8_t hour = 15; uint8_t min = 59; uint8_t sec = 55; display.cls(); for (uint8_t i = 0; i < 7; i++) { display.printf("%02d-%02d-%02d", hour, min, sec); sec++; if (sec == 60) { sec = 0; min++; } if (min == 60) { min = 0; hour++; } wait(1.0); } display.printf("%02d-%02d-%02d Monday 2014-05-12", hour, min, sec); while (!display.scrollFinished()) { display.scroll(); wait(0.25); } display.resetScroll(); for (uint8_t i = 0; i < 5; i++) { display.printf("%2d-%02d-%02d", hour, min, sec); sec++; if (sec == 60) { sec = 0; min++; } if (min == 60) { min = 0; hour++; } wait(1.0); } } int dayOfWeek(int y, int m, int d) { static const int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; y -= m < 3; return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7; } void welcomeMessage() { const char* buf = " Akafugu VFD Modular Clock"; display.cls(); display.printf(buf); // scroll forward while (!display.scrollFinished()) { display.scroll(); led = !led; wait(0.20); } display.cls(); } // helper function for handling time/alarm setting uint8_t handleTimeSetting(uint8_t setting, uint8_t min, uint8_t max, bool backButton, bool nextButton) { if (backButton) { if (setting == 0) { setting = max; } else { setting--; } } else if (nextButton) { if (setting == max) { setting = 0; } else { setting++; } } return setting; } const uint32_t MENU_TIMEOUT = 3 * 10; // each cycle 0.1s, 3 secs total struct tm* tm_; int main() { bool suppress_button_sound = false; uint8_t button3_holdcounter = 0; uint32_t menu_countdown = MENU_TIMEOUT; // Enable extra memory banks LPC_SYSCON->SYSAHBCLKCTRL |= 0x1 << 26; // RAM1 LPC_SYSCON->SYSAHBCLKCTRL |= 0x1 << 27; // USBSRAM // PWM // See: http://developer.mbed.org/forum/mbed/topic/3276/ /* LPC_CT16B0->TCR = 2; LPC_CT16B0->PR = 19; LPC_CT16B0->MR0 = 48000; // 1ms LPC_CT16B0->TCR = 1; */ BUTTON_STATE buttons; rtc.begin(); init_prefs(); // FLW initialization tm_ = rtc.getTime(); flw.begin(tm_->tm_min * 60 + tm_->tm_sec); flw.setCensored(true); memset(flwWord, 0, 5); haveEEPROM = flw.hasEeprom(); if (haveEEPROM) strncpy(flwWord, flw.getWord(), 4); else memset(flwWord, 0, 4); led = 0; initialize_buttons(); menu.setDigits(display.digits()); blanker.attach_us(&blank_tick, 100); multiplexer.attach_us(&multiplex_tick, 2000); button_ticker.attach_us(&button_tick, 5000); rtc_ticker.attach(&rtc_tick, 1.0); tenth_ticker.attach(&tenth_tick, 0.1); PrefsData* prefs = get_prefs(); display.setBrightness(prefs->prefs.brightness); wait(1.0); //welcomeMessage(); led = 0; #ifdef HAVE_GPS gps_init(); #endif // HAVE_GPS /* // button test while (1) { get_button_state(&buttons); display.printf("%d-%d-%d %d", buttons.b1_keydown, buttons.b2_keydown, buttons.b3_keydown, get_keystatus()); wait(0.1); } */ while (1) { get_button_state(&buttons); prefs = get_prefs(); led = 0; #ifdef HAVE_GPS // Read GPS and update RTC if needed if (gpsDataReady()) { char* nmea = gpsNMEA(); bool error = false; bool fix = false; if ( prefs->prefs.gps && strncmp( nmea, "$GPRMC,", 7 ) == 0 ) { time_t t = parseGPSdata(nmea, error, fix, prefs->prefs.gps_tzh, prefs->prefs.gps_tzm); time_t delta = t - gps_last_update; if (fix && !error && delta > 60) { led = 1; struct tm *tmp = localtime(&t); rtc.setTime(tmp); gps_last_update = t; } } } #endif // HAVE_GPS if (buttons.b1_keyup || buttons.b2_keyup || buttons.b3_keyup) { if (suppress_button_sound || g_alarming) { suppress_button_sound = false; buttons.b1_keyup = buttons.b2_keyup = buttons.b3_keyup = 0; } else { //piezo.play('g'); } button3_holdcounter = 0; g_alarming = false; } switch (g_clock_state) { case STATE_CLOCK: { tm_ = rtc.getTime(); // button 1 triggers menu if (buttons.b1_keyup) { //display.incBrightness(); display.printf("%s ", menu.reset()); display.setAlarmIndicator(false); display.setGPSIndicator(false); set_extra_prefs(tm_->tm_year+1900, tm_->tm_mon+1, tm_->tm_mday); g_clock_state = STATE_MENU; menu_countdown = MENU_TIMEOUT; buttons.b1_keyup = 0; break; } // Button 2 cycles through display mode if (buttons.b2_keyup) { display.toggleTimeMode(); buttons.b2_keyup = 0; } // Button 3 toggles FLW mode if (buttons.b3_keyup && haveEEPROM) { buttons.b3_keyup = 0; button3_holdcounter = 0; g_clock_state = STATE_FLW; } // Hold button 3 to set alarm if (buttons.b3_repeat) { button3_holdcounter++; buttons.b3_repeat = 0; suppress_button_sound = true; if (button3_holdcounter == 6) { g_alarm_on = !g_alarm_on; piezo.play('a'); } } // Enter auto date if (prefs->prefs.auto_date && tm_->tm_sec == 50) { display.cls(); display.printTimeLong(tm_, rtc.getTenths()); g_clock_state = STATE_AUTO_DATE; } else if (haveEEPROM && prefs->prefs.flw > 0 && tm_->tm_sec == 30) { g_clock_state = STATE_AUTO_FLW; } else { display.printTime(tm_, rtc.getTenths()); } } break; case STATE_MENU: { // button 1 goes to next menu item if (buttons.b1_keyup) { display.printf("%s ", menu.next()); menu_countdown = MENU_TIMEOUT; buttons.b1_keyup = 0; } // Button 2 selects menu item if (buttons.b2_keyup || buttons.b2_repeat) { bool setTime, setAlarm; display.printf("%8s ", menu.select(setTime, setAlarm)); if (setTime) { menu.leave(); display.setBlinkMode(VFDDisplay::Hours); display.blink(true); tm_ = rtc.getTime(); // get time from RTC to use as basis for settings tm_->tm_sec = 0; g_clock_state = STATE_SET_CLOCK_HH; } else if (setAlarm) { menu.leave(); display.setBlinkMode(VFDDisplay::Minutes); display.blink(true); tm_ = rtc.getAlarm(); g_clock_state = STATE_SET_ALARM_HH; } menu_countdown = MENU_TIMEOUT; buttons.b2_keyup = 0; } // Button 3 leaves menu else if (buttons.b3_keyup) { buttons.b3_keyup = 0; menu.leave(); g_clock_state = STATE_CLOCK; } menu_countdown--; // exit menu if unused for preset time if (menu_countdown == 0) { menu.leave(); g_clock_state = STATE_CLOCK; } // If g_clock_state has changed, we are leaving the menu, update parameters if (g_clock_state == STATE_CLOCK) { display.setGPSIndicator(true); tm_ = rtc.getTime(); int year, month, day; get_extra_prefs(year, month, day); tm_->tm_year = year - 1900; tm_->tm_mon = month-1; tm_->tm_mday = day; tm_->tm_wday = dayOfWeek(year, month, day); rtc.setTime(tm_); wait(0.05); display.setGPSIndicator(false); } } break; case STATE_AUTO_DATE: { // exit scroll if any button is pressed if (buttons.b1_keyup || buttons.b2_keyup || buttons.b3_keyup) { display.cls(); g_clock_state = STATE_CLOCK; buttons.b1_keyup = 0; buttons.b2_keyup = 0; buttons.b3_keyup = 0; } static uint8_t scroll_cnt = 0; if (display.scrollFinished()) { display.cls(); g_clock_state = STATE_CLOCK; } else { if (++scroll_cnt == 2) { display.scroll(); scroll_cnt = 0; } } } break; case STATE_SET_CLOCK_HH: { display.printTimeSet(tm_); if (buttons.b1_repeat || buttons.b2_repeat) { display.blink(false); tm_->tm_hour = handleTimeSetting(tm_->tm_hour, 0, 23, buttons.b1_repeat, buttons.b2_repeat); buttons.b1_repeat = 0; buttons.b2_repeat = 0; } if (buttons.b1_keyup) { display.blink(true); tm_->tm_hour = handleTimeSetting(tm_->tm_hour, 0, 23, true, false); buttons.b1_keyup = 0; } else if (buttons.b2_keyup) { // Button 2 display.blink(true); tm_->tm_hour = handleTimeSetting(tm_->tm_hour, 0, 23, false, true); buttons.b2_keyup = 0; } else if (buttons.b3_keyup) { // Button 3 moves to minute setting display.setBlinkMode(VFDDisplay::Minutes); g_clock_state = STATE_SET_CLOCK_MM; buttons.b3_keyup = 0; } } break; case STATE_SET_CLOCK_MM: { display.printTimeSet(tm_); if (buttons.b1_repeat || buttons.b2_repeat) { display.blink(false); tm_->tm_min = handleTimeSetting(tm_->tm_min, 0, 59, buttons.b1_repeat, buttons.b2_repeat); buttons.b1_repeat = 0; buttons.b2_repeat = 0; } if (buttons.b1_keyup) { display.blink(true); tm_->tm_min = handleTimeSetting(tm_->tm_min, 0, 59, true, false); buttons.b1_keyup = 0; } else if (buttons.b2_keyup) { // Button 2 display.blink(true); tm_->tm_min = handleTimeSetting(tm_->tm_min, 0, 59, false, true); buttons.b2_keyup = 0; } else if (buttons.b3_keyup) { // Button 3 moves to second setting display.setBlinkMode(VFDDisplay::Seconds); g_clock_state = STATE_SET_CLOCK_SS; buttons.b3_keyup = 0; } } break; case STATE_SET_CLOCK_SS: { display.printTimeSet(tm_); if (buttons.b1_repeat || buttons.b2_repeat) { display.blink(false); tm_->tm_sec = handleTimeSetting(tm_->tm_sec, 0, 59, buttons.b1_repeat, buttons.b2_repeat); buttons.b1_repeat = 0; buttons.b2_repeat = 0; } if (buttons.b1_keyup) { display.blink(true); tm_->tm_sec = handleTimeSetting(tm_->tm_sec, 0, 59, true, false); buttons.b1_keyup = 0; } else if (buttons.b2_keyup) { // Button 2 display.blink(true); tm_->tm_sec = handleTimeSetting(tm_->tm_sec, 0, 59, false, true); buttons.b2_keyup = 0; } else if (buttons.b3_keyup) { // Button 3 moves to minute setting display.setBlinkMode(VFDDisplay::Full); display.blink(false); rtc.setTime(tm_); g_clock_state = STATE_CLOCK; buttons.b3_keyup = 0; } } break; case STATE_SET_ALARM_HH: { display.printTimeSet(tm_, false); if (buttons.b1_repeat || buttons.b2_repeat) { display.blink(false); tm_->tm_hour = handleTimeSetting(tm_->tm_hour, 0, 23, buttons.b1_repeat, buttons.b2_repeat); buttons.b1_repeat = 0; buttons.b2_repeat = 0; } if (buttons.b1_keyup) { display.blink(true); tm_->tm_hour = handleTimeSetting(tm_->tm_hour, 0, 23, true, false); buttons.b1_keyup = 0; } else if (buttons.b2_keyup) { // Button 2 display.blink(true); tm_->tm_hour = handleTimeSetting(tm_->tm_hour, 0, 23, false, true); buttons.b2_keyup = 0; } else if (buttons.b3_keyup) { // Button 3 moves to minute setting display.setBlinkMode(VFDDisplay::Seconds); g_clock_state = STATE_SET_ALARM_MM; buttons.b3_keyup = 0; } } break; case STATE_SET_ALARM_MM: { display.printTimeSet(tm_, false); if (buttons.b1_repeat || buttons.b2_repeat) { display.blink(false); tm_->tm_min = handleTimeSetting(tm_->tm_min, 0, 59, buttons.b1_repeat, buttons.b2_repeat); buttons.b1_repeat = 0; buttons.b2_repeat = 0; } if (buttons.b1_keyup) { display.blink(true); tm_->tm_min = handleTimeSetting(tm_->tm_min, 0, 59, true, false); buttons.b1_keyup = 0; } else if (buttons.b2_keyup) { // Button 2 display.blink(true); tm_->tm_min = handleTimeSetting(tm_->tm_min, 0, 59, false, true); buttons.b2_keyup = 0; } else if (buttons.b3_keyup) { // Button 3 moves to minute setting display.setBlinkMode(VFDDisplay::Full); display.blink(false); rtc.setAlarm(tm_->tm_hour, tm_->tm_min, 0); g_clock_state = STATE_CLOCK; buttons.b3_keyup = 0; } } break; case STATE_AUTO_FLW: { tm_ = rtc.getTime(); if (tm_->tm_sec >= 37) { g_clock_state = STATE_CLOCK; } } // fall-through case STATE_FLW: { // exit if any button is pressed if (buttons.b1_keyup || buttons.b2_keyup || buttons.b3_keyup) { display.cls(); g_clock_state = STATE_CLOCK; buttons.b1_keyup = 0; buttons.b2_keyup = 0; buttons.b3_keyup = 0; } if (flwOffset == 0) display.printf("%s ", flwWord); else display.printf("%*s" "%s ", flwOffset, " ", flwWord); } break; default: break; } if (g_alarm_on && rtc.checkAlarm()) { g_alarming = true; } display.setAlarmIndicator(g_alarm_on); display.setGPSIndicator(g_alarming); if (g_alarming) { piezo.play('g'); } wait(0.1); } }