VFD modular clock firmware

Dependencies:   DipCortex-EEprom RTC flw mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

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 }