VFD modular clock firmware
Dependencies: DipCortex-EEprom RTC flw mbed
main.cpp
00001 /* 00002 * VFD Modular Clock - mbed 00003 * (C) 2011-14 Akafugu Corporation 00004 * 00005 * This program is free software; you can redistribute it and/or modify it under the 00006 * terms of the GNU General Public License as published by the Free Software 00007 * Foundation; either version 2 of the License, or (at your option) any later 00008 * version. 00009 * 00010 * This program is distributed in the hope that it will be useful, but WITHOUT ANY 00011 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 00012 * PARTICULAR PURPOSE. See the GNU General Public License for more details. 00013 * 00014 */ 00015 00016 #include "global.h" 00017 #include "mbed.h" 00018 00019 #include "VFDDisplay.h" 00020 #include "IV18Display.h" 00021 00022 #include "prefs.h" 00023 #include "gps.h" 00024 #include "menu.h" 00025 #include "button.h" 00026 #include "rtc.h" 00027 #include "ds3231m.h" 00028 #include "beep.h" 00029 #include "flw.h" 00030 00031 IV18Display display(PinMap::data, PinMap::clock, PinMap::latch, PinMap::blank); 00032 00033 Menu menu; 00034 00035 I2C i2c(PinMap::sda, PinMap::scl); 00036 DS3231M rtc(i2c); 00037 //RTC rtc; 00038 00039 DigitalOut led(PinMap::led); 00040 Beep piezo(PinMap::piezo); 00041 00042 FourLetterWord flw(&i2c); 00043 char flwWord[5]; 00044 int flwOffset; 00045 int flwOffsetDirection = 1; 00046 volatile bool haveEEPROM; 00047 00048 Ticker blanker; 00049 Ticker multiplexer; 00050 Ticker button_ticker; 00051 Ticker rtc_ticker; 00052 Ticker tenth_ticker; 00053 00054 volatile state_t g_clock_state = STATE_CLOCK; 00055 uint32_t gps_last_update = 0xffff; 00056 00057 uint8_t calculate_segments_7(uint8_t character); 00058 00059 // Alarm 00060 volatile bool g_alarm_on; 00061 volatile bool g_alarming; 00062 volatile bool g_updateFLW = false; 00063 00064 void write_vfd_8bit(uint8_t data); 00065 void write_vfd_iv18(uint8_t digit, uint8_t segments); 00066 void write_vfd(uint8_t digit, uint8_t segments); 00067 void demo_cycle(char* buf); 00068 00069 void blank_tick() 00070 { 00071 static uint32_t cnt = 0; 00072 00073 if ((cnt%10) > (10-display.getBrightness())) { 00074 display.blank(false); 00075 } 00076 else { 00077 display.blank(true); 00078 } 00079 00080 cnt++; 00081 } 00082 00083 void multiplex_tick() 00084 { 00085 display.multiplexTick(); 00086 } 00087 00088 void rtc_tick() 00089 { 00090 if (haveEEPROM) 00091 g_updateFLW = true; 00092 00093 rtc.tick(); 00094 } 00095 00096 void tenth_tick() 00097 { 00098 rtc.tenth_tick(); 00099 } 00100 00101 void counterTest() 00102 { 00103 display.cls(); 00104 for (uint16_t i = 0; i < 200; i++) { 00105 display.printf("cnt %3d", i); 00106 wait(0.01); 00107 } 00108 } 00109 00110 void timeAndDateTest() 00111 { 00112 uint8_t hour = 15; 00113 uint8_t min = 59; 00114 uint8_t sec = 55; 00115 00116 display.cls(); 00117 for (uint8_t i = 0; i < 7; i++) { 00118 display.printf("%02d-%02d-%02d", hour, min, sec); 00119 sec++; 00120 if (sec == 60) { sec = 0; min++; } 00121 if (min == 60) { min = 0; hour++; } 00122 wait(1.0); 00123 } 00124 00125 display.printf("%02d-%02d-%02d Monday 2014-05-12", hour, min, sec); 00126 00127 while (!display.scrollFinished()) { 00128 display.scroll(); 00129 wait(0.25); 00130 } 00131 00132 display.resetScroll(); 00133 00134 for (uint8_t i = 0; i < 5; i++) { 00135 display.printf("%2d-%02d-%02d", hour, min, sec); 00136 sec++; 00137 if (sec == 60) { sec = 0; min++; } 00138 if (min == 60) { min = 0; hour++; } 00139 wait(1.0); 00140 } 00141 00142 } 00143 00144 int dayOfWeek(int y, int m, int d) 00145 { 00146 static const int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; 00147 y -= m < 3; 00148 return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7; 00149 } 00150 00151 void welcomeMessage() 00152 { 00153 const char* buf = " Akafugu VFD Modular Clock"; 00154 00155 display.cls(); 00156 display.printf(buf); 00157 00158 // scroll forward 00159 while (!display.scrollFinished()) { 00160 display.scroll(); 00161 led = !led; 00162 wait(0.20); 00163 } 00164 00165 display.cls(); 00166 } 00167 00168 // helper function for handling time/alarm setting 00169 uint8_t handleTimeSetting(uint8_t setting, uint8_t min, uint8_t max, 00170 bool backButton, bool nextButton) 00171 { 00172 if (backButton) { 00173 if (setting == 0) { 00174 setting = max; 00175 } 00176 else { 00177 setting--; 00178 } 00179 } 00180 else if (nextButton) { 00181 if (setting == max) { 00182 setting = 0; 00183 } 00184 else { 00185 setting++; 00186 } 00187 } 00188 00189 return setting; 00190 } 00191 00192 const uint32_t MENU_TIMEOUT = 3 * 10; // each cycle 0.1s, 3 secs total 00193 struct tm* tm_; 00194 00195 int main() 00196 { 00197 bool suppress_button_sound = false; 00198 uint8_t button3_holdcounter = 0; 00199 uint32_t menu_countdown = MENU_TIMEOUT; 00200 00201 // Enable extra memory banks 00202 LPC_SYSCON->SYSAHBCLKCTRL |= 0x1 << 26; // RAM1 00203 LPC_SYSCON->SYSAHBCLKCTRL |= 0x1 << 27; // USBSRAM 00204 00205 // PWM 00206 // See: http://developer.mbed.org/forum/mbed/topic/3276/ 00207 /* 00208 LPC_CT16B0->TCR = 2; 00209 LPC_CT16B0->PR = 19; 00210 LPC_CT16B0->MR0 = 48000; // 1ms 00211 LPC_CT16B0->TCR = 1; 00212 */ 00213 00214 BUTTON_STATE buttons; 00215 00216 rtc.begin(); 00217 init_prefs(); 00218 00219 // FLW initialization 00220 tm_ = rtc.getTime(); 00221 00222 flw.begin(tm_->tm_min * 60 + tm_->tm_sec); 00223 flw.setCensored(true); 00224 00225 memset(flwWord, 0, 5); 00226 haveEEPROM = flw.hasEeprom(); 00227 if (haveEEPROM) 00228 strncpy(flwWord, flw.getWord(), 4); 00229 else 00230 memset(flwWord, 0, 4); 00231 00232 led = 0; 00233 00234 initialize_buttons(); 00235 menu.setDigits(display.digits()); 00236 00237 blanker.attach_us(&blank_tick, 100); 00238 multiplexer.attach_us(&multiplex_tick, 2000); 00239 button_ticker.attach_us(&button_tick, 5000); 00240 rtc_ticker.attach(&rtc_tick, 1.0); 00241 tenth_ticker.attach(&tenth_tick, 0.1); 00242 00243 PrefsData* prefs = get_prefs(); 00244 display.setBrightness(prefs->prefs.brightness); 00245 00246 wait(1.0); 00247 //welcomeMessage(); 00248 led = 0; 00249 00250 #ifdef HAVE_GPS 00251 gps_init(); 00252 #endif // HAVE_GPS 00253 00254 /* 00255 // button test 00256 while (1) { 00257 get_button_state(&buttons); 00258 00259 display.printf("%d-%d-%d %d", buttons.b1_keydown, buttons.b2_keydown, buttons.b3_keydown, get_keystatus()); 00260 wait(0.1); 00261 } 00262 */ 00263 00264 while (1) { 00265 get_button_state(&buttons); 00266 prefs = get_prefs(); 00267 00268 led = 0; 00269 00270 #ifdef HAVE_GPS 00271 // Read GPS and update RTC if needed 00272 if (gpsDataReady()) { 00273 char* nmea = gpsNMEA(); 00274 bool error = false; 00275 bool fix = false; 00276 00277 if ( prefs->prefs.gps && strncmp( nmea, "$GPRMC,", 7 ) == 0 ) { 00278 time_t t = parseGPSdata(nmea, error, fix, prefs->prefs.gps_tzh, prefs->prefs.gps_tzm); 00279 time_t delta = t - gps_last_update; 00280 00281 if (fix && !error && delta > 60) { 00282 led = 1; 00283 struct tm *tmp = localtime(&t); 00284 rtc.setTime(tmp); 00285 gps_last_update = t; 00286 } 00287 } 00288 } 00289 #endif // HAVE_GPS 00290 00291 if (buttons.b1_keyup || buttons.b2_keyup || buttons.b3_keyup) { 00292 if (suppress_button_sound || g_alarming) { 00293 suppress_button_sound = false; 00294 buttons.b1_keyup = buttons.b2_keyup = buttons.b3_keyup = 0; 00295 } 00296 else { 00297 piezo.play('g'); 00298 } 00299 00300 button3_holdcounter = 0; 00301 g_alarming = false; 00302 } 00303 00304 switch (g_clock_state) { 00305 case STATE_CLOCK: 00306 { 00307 tm_ = rtc.getTime(); 00308 00309 // button 1 triggers menu 00310 if (buttons.b1_keyup) { 00311 //display.incBrightness(); 00312 display.printf("%s ", menu.reset()); 00313 display.setAlarmIndicator(false); 00314 display.setGPSIndicator(false); 00315 set_extra_prefs(tm_->tm_year+1900, tm_->tm_mon+1, tm_->tm_mday); 00316 g_clock_state = STATE_MENU; 00317 menu_countdown = MENU_TIMEOUT; 00318 buttons.b1_keyup = 0; 00319 break; 00320 } 00321 00322 // Button 2 cycles through display mode 00323 if (buttons.b2_keyup) { 00324 display.toggleTimeMode(); 00325 buttons.b2_keyup = 0; 00326 } 00327 00328 // Button 3 toggles FLW mode 00329 if (buttons.b3_keyup && haveEEPROM) { 00330 buttons.b3_keyup = 0; 00331 button3_holdcounter = 0; 00332 g_clock_state = STATE_FLW; 00333 } 00334 00335 // Hold button 3 to set alarm 00336 if (buttons.b3_repeat) { 00337 button3_holdcounter++; 00338 buttons.b3_repeat = 0; 00339 suppress_button_sound = true; 00340 00341 if (button3_holdcounter == 6) { 00342 g_alarm_on = !g_alarm_on; 00343 piezo.play('a'); 00344 } 00345 } 00346 00347 // Enter auto date 00348 if (prefs->prefs.auto_date && tm_->tm_sec == 50) { 00349 display.cls(); 00350 display.printTimeLong(tm_, rtc.getTenths()); 00351 g_clock_state = STATE_AUTO_DATE; 00352 } 00353 else if (haveEEPROM && prefs->prefs.flw > 0 && tm_->tm_sec == 30) { 00354 g_clock_state = STATE_AUTO_FLW; 00355 } 00356 else { 00357 display.printTime(tm_, rtc.getTenths()); 00358 } 00359 } 00360 break; 00361 case STATE_MENU: 00362 { 00363 // button 1 goes to next menu item 00364 if (buttons.b1_keyup) { 00365 display.printf("%s ", menu.next()); 00366 menu_countdown = MENU_TIMEOUT; 00367 buttons.b1_keyup = 0; 00368 } 00369 00370 // Button 2 selects menu item 00371 if (buttons.b2_keyup || buttons.b2_repeat) { 00372 bool setTime, setAlarm; 00373 00374 display.printf("%8s ", menu.select(setTime, setAlarm)); 00375 00376 if (setTime) { 00377 menu.leave(); 00378 display.setBlinkMode(VFDDisplay::Hours); 00379 display.blink(true); 00380 tm_ = rtc.getTime(); // get time from RTC to use as basis for settings 00381 tm_->tm_sec = 0; 00382 g_clock_state = STATE_SET_CLOCK_HH; 00383 } 00384 else if (setAlarm) { 00385 menu.leave(); 00386 display.setBlinkMode(VFDDisplay::Minutes); 00387 display.blink(true); 00388 tm_ = rtc.getAlarm(); 00389 g_clock_state = STATE_SET_ALARM_HH; 00390 } 00391 00392 menu_countdown = MENU_TIMEOUT; 00393 buttons.b2_keyup = 0; 00394 } 00395 // Button 3 leaves menu 00396 else if (buttons.b3_keyup) { 00397 buttons.b3_keyup = 0; 00398 menu.leave(); 00399 g_clock_state = STATE_CLOCK; 00400 } 00401 00402 menu_countdown--; 00403 00404 // exit menu if unused for preset time 00405 if (menu_countdown == 0) { 00406 menu.leave(); 00407 g_clock_state = STATE_CLOCK; 00408 } 00409 00410 // If g_clock_state has changed, we are leaving the menu, update parameters 00411 if (g_clock_state == STATE_CLOCK) { 00412 display.setGPSIndicator(true); 00413 00414 tm_ = rtc.getTime(); 00415 int year, month, day; 00416 00417 get_extra_prefs(year, month, day); 00418 tm_->tm_year = year - 1900; 00419 tm_->tm_mon = month-1; 00420 tm_->tm_mday = day; 00421 00422 tm_->tm_wday = dayOfWeek(year, month, day); 00423 00424 rtc.setTime(tm_); 00425 00426 wait(0.05); 00427 display.setGPSIndicator(false); 00428 } 00429 } 00430 break; 00431 case STATE_AUTO_DATE: 00432 { 00433 // exit scroll if any button is pressed 00434 if (buttons.b1_keyup || buttons.b2_keyup || buttons.b3_keyup) { 00435 display.cls(); 00436 g_clock_state = STATE_CLOCK; 00437 00438 buttons.b1_keyup = 0; 00439 buttons.b2_keyup = 0; 00440 buttons.b3_keyup = 0; 00441 } 00442 00443 static uint8_t scroll_cnt = 0; 00444 00445 if (display.scrollFinished()) { 00446 display.cls(); 00447 g_clock_state = STATE_CLOCK; 00448 } 00449 else { 00450 if (++scroll_cnt == 2) { 00451 display.scroll(); 00452 scroll_cnt = 0; 00453 } 00454 } 00455 } 00456 break; 00457 case STATE_SET_CLOCK_HH: 00458 { 00459 display.printTimeSet(tm_); 00460 00461 if (buttons.b1_repeat || buttons.b2_repeat) { 00462 display.blink(false); 00463 tm_->tm_hour = handleTimeSetting(tm_->tm_hour, 0, 23, buttons.b1_repeat, buttons.b2_repeat); 00464 buttons.b1_repeat = 0; 00465 buttons.b2_repeat = 0; 00466 } 00467 if (buttons.b1_keyup) { 00468 display.blink(true); 00469 tm_->tm_hour = handleTimeSetting(tm_->tm_hour, 0, 23, true, false); 00470 buttons.b1_keyup = 0; 00471 } 00472 else if (buttons.b2_keyup) { // Button 2 00473 display.blink(true); 00474 tm_->tm_hour = handleTimeSetting(tm_->tm_hour, 0, 23, false, true); 00475 buttons.b2_keyup = 0; 00476 } 00477 else if (buttons.b3_keyup) { // Button 3 moves to minute setting 00478 display.setBlinkMode(VFDDisplay::Minutes); 00479 g_clock_state = STATE_SET_CLOCK_MM; 00480 buttons.b3_keyup = 0; 00481 } 00482 } 00483 break; 00484 case STATE_SET_CLOCK_MM: 00485 { 00486 display.printTimeSet(tm_); 00487 00488 if (buttons.b1_repeat || buttons.b2_repeat) { 00489 display.blink(false); 00490 tm_->tm_min = handleTimeSetting(tm_->tm_min, 0, 59, buttons.b1_repeat, buttons.b2_repeat); 00491 buttons.b1_repeat = 0; 00492 buttons.b2_repeat = 0; 00493 } 00494 if (buttons.b1_keyup) { 00495 display.blink(true); 00496 tm_->tm_min = handleTimeSetting(tm_->tm_min, 0, 59, true, false); 00497 buttons.b1_keyup = 0; 00498 } 00499 else if (buttons.b2_keyup) { // Button 2 00500 display.blink(true); 00501 tm_->tm_min = handleTimeSetting(tm_->tm_min, 0, 59, false, true); 00502 buttons.b2_keyup = 0; 00503 } 00504 else if (buttons.b3_keyup) { // Button 3 moves to second setting 00505 display.setBlinkMode(VFDDisplay::Seconds); 00506 g_clock_state = STATE_SET_CLOCK_SS; 00507 buttons.b3_keyup = 0; 00508 } 00509 } 00510 break; 00511 case STATE_SET_CLOCK_SS: 00512 { 00513 display.printTimeSet(tm_); 00514 00515 if (buttons.b1_repeat || buttons.b2_repeat) { 00516 display.blink(false); 00517 tm_->tm_sec = handleTimeSetting(tm_->tm_sec, 0, 59, buttons.b1_repeat, buttons.b2_repeat); 00518 buttons.b1_repeat = 0; 00519 buttons.b2_repeat = 0; 00520 } 00521 if (buttons.b1_keyup) { 00522 display.blink(true); 00523 tm_->tm_sec = handleTimeSetting(tm_->tm_sec, 0, 59, true, false); 00524 buttons.b1_keyup = 0; 00525 } 00526 else if (buttons.b2_keyup) { // Button 2 00527 display.blink(true); 00528 tm_->tm_sec = handleTimeSetting(tm_->tm_sec, 0, 59, false, true); 00529 buttons.b2_keyup = 0; 00530 } 00531 else if (buttons.b3_keyup) { // Button 3 moves to minute setting 00532 display.setBlinkMode(VFDDisplay::Full); 00533 display.blink(false); 00534 rtc.setTime(tm_); 00535 g_clock_state = STATE_CLOCK; 00536 buttons.b3_keyup = 0; 00537 } 00538 } 00539 break; 00540 case STATE_SET_ALARM_HH: 00541 { 00542 display.printTimeSet(tm_, false); 00543 00544 if (buttons.b1_repeat || buttons.b2_repeat) { 00545 display.blink(false); 00546 tm_->tm_hour = handleTimeSetting(tm_->tm_hour, 0, 23, buttons.b1_repeat, buttons.b2_repeat); 00547 buttons.b1_repeat = 0; 00548 buttons.b2_repeat = 0; 00549 } 00550 if (buttons.b1_keyup) { 00551 display.blink(true); 00552 tm_->tm_hour = handleTimeSetting(tm_->tm_hour, 0, 23, true, false); 00553 buttons.b1_keyup = 0; 00554 } 00555 else if (buttons.b2_keyup) { // Button 2 00556 display.blink(true); 00557 tm_->tm_hour = handleTimeSetting(tm_->tm_hour, 0, 23, false, true); 00558 buttons.b2_keyup = 0; 00559 } 00560 else if (buttons.b3_keyup) { // Button 3 moves to minute setting 00561 display.setBlinkMode(VFDDisplay::Seconds); 00562 g_clock_state = STATE_SET_ALARM_MM; 00563 buttons.b3_keyup = 0; 00564 } 00565 } 00566 break; 00567 case STATE_SET_ALARM_MM: 00568 { 00569 display.printTimeSet(tm_, false); 00570 00571 if (buttons.b1_repeat || buttons.b2_repeat) { 00572 display.blink(false); 00573 tm_->tm_min = handleTimeSetting(tm_->tm_min, 0, 59, buttons.b1_repeat, buttons.b2_repeat); 00574 buttons.b1_repeat = 0; 00575 buttons.b2_repeat = 0; 00576 } 00577 if (buttons.b1_keyup) { 00578 display.blink(true); 00579 tm_->tm_min = handleTimeSetting(tm_->tm_min, 0, 59, true, false); 00580 buttons.b1_keyup = 0; 00581 } 00582 else if (buttons.b2_keyup) { // Button 2 00583 display.blink(true); 00584 tm_->tm_min = handleTimeSetting(tm_->tm_min, 0, 59, false, true); 00585 buttons.b2_keyup = 0; 00586 } 00587 else if (buttons.b3_keyup) { // Button 3 moves to minute setting 00588 display.setBlinkMode(VFDDisplay::Full); 00589 display.blink(false); 00590 rtc.setAlarm(tm_->tm_hour, tm_->tm_min, 0); 00591 g_clock_state = STATE_CLOCK; 00592 buttons.b3_keyup = 0; 00593 } 00594 } 00595 break; 00596 case STATE_AUTO_FLW: 00597 { 00598 tm_ = rtc.getTime(); 00599 00600 if (tm_->tm_sec >= 37) { 00601 g_clock_state = STATE_CLOCK; 00602 } 00603 } 00604 // fall-through 00605 case STATE_FLW: 00606 { 00607 // exit if any button is pressed 00608 if (buttons.b1_keyup || buttons.b2_keyup || buttons.b3_keyup) { 00609 display.cls(); 00610 g_clock_state = STATE_CLOCK; 00611 00612 buttons.b1_keyup = 0; 00613 buttons.b2_keyup = 0; 00614 buttons.b3_keyup = 0; 00615 } 00616 00617 if (flwOffset == 0) 00618 display.printf("%s ", flwWord); 00619 else 00620 display.printf("%*s" "%s ", flwOffset, " ", flwWord); 00621 } 00622 break; 00623 default: 00624 break; 00625 } 00626 00627 if (g_alarm_on && rtc.checkAlarm()) { 00628 g_alarming = true; 00629 } 00630 00631 display.setAlarmIndicator(g_alarm_on); 00632 display.setGPSIndicator(g_alarming); 00633 00634 if (g_alarming) { 00635 piezo.play('g'); 00636 } 00637 00638 if (g_updateFLW) 00639 { 00640 g_updateFLW = false; 00641 00642 strncpy(flwWord, flw.getWord(), 4); 00643 flwOffset += flwOffsetDirection; 00644 00645 if (flwOffset <= 0) { 00646 flwOffset = 0; 00647 flwOffsetDirection = 1; 00648 } 00649 else if (flwOffset > display.digits() -4) { 00650 flwOffset = 3; 00651 flwOffsetDirection = -1; 00652 } 00653 } 00654 00655 wait(0.1); 00656 } 00657 }
Generated on Wed Jul 13 2022 06:11:41 by 1.7.2