/*! ******************************************************************************* * main program ******************************************************************************/ int main(void) { //! initalization init(); task=0; //! Enable interrupts sei(); /* check EEPROM layout */ if (EEPROM_read((uint16_t)&ee_layout)!=EE_LAYOUT) { LCD_PrintStringID(LCD_STRING_EEPr,LCD_MODE_ON); task_lcd_update(); for(;;) {;} //fatal error, stop startup } COM_init(); // We should do the following once here to have valid data from the start /*! **************************************************************************** * main loop ***************************************************************************/ for (;;){ // go to sleep with ADC conversion start asm volatile ("cli"); if ( ! task && ((ASSR & (_BV(OCR2UB)|_BV(TCN2UB)|_BV(TCR2UB))) == 0) // ATmega169 datasheet chapter 17.8.1 ) { // nothing to do, go to sleep if(timer0_need_clock() || RS_need_clock()) { SMCR = (0<<SM1)|(0<<SM0)|(1<<SE); // Idle mode } else { if (sleep_with_ADC) { SMCR = (0<<SM1)|(1<<SM0)|(1<<SE); // ADC noise reduction mode } else { SMCR = (1<<SM1)|(1<<SM0)|(1<<SE); // Power-save mode } } if (sleep_with_ADC) { sleep_with_ADC=0; // start conversions ADCSRA |= (1<<ADSC); } DEBUG_BEFORE_SLEEP(); asm volatile ("sei"); // sequence from ATMEL datasheet chapter 6.8. asm volatile ("sleep"); asm volatile ("nop"); DEBUG_AFTER_SLEEP(); SMCR = (1<<SM1)|(1<<SM0)|(0<<SE); // Power-save mode } else { asm volatile ("sei"); } // update LCD task if (task & TASK_LCD) { task&=~TASK_LCD; task_lcd_update(); continue; // on most case we have only 1 task, iprove time to sleep } if (task & TASK_ADC) { task&=~TASK_ADC; if (task_ADC()==0) { // ADC is done // TODO } continue; // on most case we have only 1 task, iprove time to sleep } // communication if (task & TASK_COM) { task&=~TASK_COM; COM_commad_parse(); continue; // on most case we have only 1 task, iprove time to sleep } // motor stop if (task & TASK_MOTOR_STOP) { task&=~TASK_MOTOR_STOP; MOTOR_timer_stop(); continue; // on most case we have only 1 task, iprove time to sleep } // update motor possition if (task & TASK_MOTOR_PULSE) { task&=~TASK_MOTOR_PULSE; MOTOR_updateCalibration(mont_contact_pooling()); MOTOR_timer_pulse(); continue; // on most case we have only 1 task, iprove time to sleep } //! check keyboard and set keyboards events if (task & TASK_KB) { task&=~TASK_KB; task_keyboard(); } if (task & TASK_RTC) { task&=~TASK_RTC; { bool minute = RTC_AddOneSecond(); valve_wanted = CTL_update(minute,valve_wanted); if (minute && (RTC_GetDayOfWeek()==6) && (RTC_GetHour()==10) && (RTC_GetMinute()==0)) { // every sunday 10:00AM // TODO: improve this code! // valve protection / CyCL MOTOR_updateCalibration(0); } } MOTOR_updateCalibration(mont_contact_pooling()); MOTOR_Goto(valve_wanted); task_keyboard_long_press_detect(); if ((MOTOR_Dir==stop) || (config.allow_ADC_during_motor)) start_task_ADC(); if (menu_auto_update_timeout>=0) { menu_auto_update_timeout--; } menu_view(false); // TODO: move it, it is wrong place LCD_Update(); // TODO: move it, it is wrong place // do not use continue here (menu_auto_update_timeout==0) } // menu state machine if (kb_events || (menu_auto_update_timeout==0)) { bool update = menu_controller(false); if (update) { menu_controller(true); // menu updated, call it again } menu_view(update); // TODO: move it, it is wrong place LCD_Update(); // TODO: move it, it is wrong place } } //End Main loop
/*! ******************************************************************************* * \brief menu View ******************************************************************************/ void menu_view(bool clear) { switch (menu_state) { case menu_startup: LCD_AllSegments(LCD_MODE_ON); // all segments on break; case menu_version: clr_show1(LCD_SEG_COL1); LCD_PrintHexW(VERSION_N,LCD_MODE_ON); break; #if (! REMOTE_SETTING_ONLY) case menu_set_year: LCD_AllSegments(LCD_MODE_OFF); // all segments off LCD_PrintDecW(RTC_GetYearYYYY(),LCD_MODE_BLINK_1); break; case menu_set_month: case menu_set_day: clr_show1(LCD_SEG_COL1); // decimal point LCD_PrintDec(RTC_GetMonth(), 0, ((menu_state==menu_set_month)?LCD_MODE_BLINK_1:LCD_MODE_ON)); LCD_PrintDec(RTC_GetDay(), 2, ((menu_state==menu_set_day)?LCD_MODE_BLINK_1:LCD_MODE_ON)); break; case menu_set_hour: case menu_set_minute: case menu_home4: // time #if HAVE_NEWLCD if (clear) clr_show1(LCD_SEG_COL2); #else if (clear) clr_show2(LCD_SEG_COL1,LCD_SEG_COL2); #endif LCD_PrintDec(RTC_GetHour(), 2, ((menu_state == menu_set_hour) ? LCD_MODE_BLINK_1 : LCD_MODE_ON)); LCD_PrintDec(RTC_GetMinute(), 0, ((menu_state == menu_set_minute) ? LCD_MODE_BLINK_1 : LCD_MODE_ON)); break; #else case menu_home4: // time #if HAVE_NEWLCD if (clear) clr_show1(LCD_SEG_COL2); #else if (clear) clr_show2(LCD_SEG_COL1,LCD_SEG_COL2); #endif LCD_PrintDec(RTC_GetHour(), 2, LCD_MODE_ON); LCD_PrintDec(RTC_GetMinute(), 0, LCD_MODE_ON); break; #endif #if MENU_SHOW_BATTERY case menu_home5: // battery LCD_AllSegments(LCD_MODE_OFF); LCD_PrintDec(bat_average/100, 2, LCD_MODE_ON); LCD_PrintDec(bat_average%100, 0, LCD_MODE_ON); break; #endif case menu_home: // wanted temp / error code / adaptation status if (MOTOR_calibration_step>0) { clr_show1(LCD_SEG_BAR24); LCD_PrintChar(LCD_CHAR_A,3,LCD_MODE_ON); if (MOTOR_ManuCalibration==-1) LCD_PrintChar(LCD_CHAR_d,2,LCD_MODE_ON); LCD_PrintChar(LCD_CHAR_0 + (MOTOR_calibration_step%10), 0, LCD_MODE_ON); goto MENU_COMMON_STATUS; // optimization } else { if (clear) clr_show1(LCD_SEG_BAR24); if (CTL_error!=0) { #if HAVE_NEWLCD==0 if (CTL_error & CTL_ERR_BATT_LOW) { LCD_PrintStringID(LCD_STRING_BAtt,LCD_MODE_BLINK_1); } else #endif if (CTL_error & CTL_ERR_MONTAGE) { LCD_PrintStringID(LCD_STRING_E2,LCD_MODE_ON); } else if (CTL_error & CTL_ERR_MOTOR) { LCD_PrintStringID(LCD_STRING_E3,LCD_MODE_ON); } else #if HAVE_NEWLCD==0 if (CTL_error & CTL_ERR_BATT_WARNING) { LCD_PrintStringID(LCD_STRING_BAtt,LCD_MODE_ON); } else #endif if (CTL_error & CTL_ERR_RFM_SYNC) { LCD_PrintStringID(LCD_STRING_E4,LCD_MODE_ON); } goto MENU_COMMON_STATUS; // optimization } else { if (mode_window()) { LCD_PrintStringID(LCD_STRING_OPEn,LCD_MODE_ON); goto MENU_COMMON_STATUS; // optimization } } } // do not use break at this position / optimization case menu_home_no_alter: // wanted temp if (clear) clr_show1(LCD_SEG_BAR24); LCD_PrintTemp(CTL_temp_wanted,LCD_MODE_ON); //! \note hourbar status calculation is complex we don't want calculate it every view, use chache MENU_COMMON_STATUS: LCD_SetSeg(LCD_SEG_AUTO, (CTL_test_auto()?LCD_MODE_ON:LCD_MODE_OFF)); LCD_SetSeg(LCD_SEG_MANU, (CTL_mode_auto?LCD_MODE_OFF:LCD_MODE_ON)); #if HAVE_NEWLCD if (CTL_error & (CTL_ERR_BATT_LOW | CTL_ERR_BATT_WARNING)) LCD_SetSeg(LCD_SEG_BAT, (CTL_error & CTL_ERR_BATT_LOW)?LCD_MODE_BLINK_1:LCD_MODE_ON); #endif LCD_HourBarBitmap(hourbar_buff); break; case menu_home2: // real temperature if (clear) LCD_AllSegments(LCD_MODE_OFF); LCD_PrintTempInt(temp_average,LCD_MODE_ON); break; case menu_home3: // valve pos if (clear) LCD_AllSegments(LCD_MODE_OFF); // LCD_PrintDec3(MOTOR_GetPosPercent(), 1 ,LCD_MODE_ON); // LCD_PrintChar(LCD_CHAR_2lines,0,LCD_MODE_ON); { uint8_t prc = MOTOR_GetPosPercent(); if (prc<=100) { LCD_PrintDec3(MOTOR_GetPosPercent(), 0 ,LCD_MODE_ON); } else { LCD_PrintStringID(LCD_STRING_minusCminus,LCD_MODE_ON); } } break; #if (! REMOTE_SETTING_ONLY) case menu_set_timmer_dow: clr_show1(LCD_SEG_PROG); // all segments off LCD_PrintDayOfWeek(menu_set_dow, LCD_MODE_BLINK_1); break; case menu_set_timmer: //! \todo calculate "hourbar" status, actual position in mode LCD_MODE_BLINK_1 // clr_show3(LCD_SEG_COL1,LCD_SEG_COL2,LCD_SEG_PROG); clr_show2(LCD_SEG_COL1,LCD_SEG_COL2); timmers_patch_offset=timers_get_raw_index(menu_set_dow, menu_set_slot); timmers_patch_data = menu_set_time + ((uint16_t)menu_set_mode<<12); LCD_HourBarBitmap(RTC_DowTimerGetHourBar(menu_set_dow)); timmers_patch_offset=0xff; LCD_SetHourBarSeg(menu_set_time/60, LCD_MODE_BLINK_1); if (menu_set_time < 24*60) { LCD_PrintDec(menu_set_time/60, 2, LCD_MODE_BLINK_1); LCD_PrintDec(menu_set_time%60, 0, LCD_MODE_BLINK_1); } else { LCD_PrintStringID(LCD_STRING_4xminus,LCD_MODE_BLINK_1); } show_selected_temperature_type(menu_set_mode,LCD_MODE_BLINK_1); break; #endif case menu_lock: // "bloc" message LCD_AllSegments(LCD_MODE_OFF); // all segments off LCD_PrintStringID(LCD_STRING_bloc,LCD_MODE_ON); break; case menu_service1: case menu_service2: // service menu; left side index, right value LCD_AllSegments(LCD_MODE_ON); LCD_PrintHex(service_idx, 2, ((menu_state == menu_service1) ? LCD_MODE_BLINK_1 : LCD_MODE_ON)); LCD_PrintHex(config_raw[service_idx], 0, ((menu_state == menu_service2) ? LCD_MODE_BLINK_1 : LCD_MODE_ON)); break; case menu_service_watch: LCD_AllSegments(LCD_MODE_ON); LCD_PrintHexW(watch(service_watch_n),LCD_MODE_ON); LCD_SetHourBarSeg(service_watch_n, LCD_MODE_BLINK_1); break; #if (! REMOTE_SETTING_ONLY) case menu_preset_temp0: case menu_preset_temp1: case menu_preset_temp2: case menu_preset_temp3: LCD_AllSegments(LCD_MODE_OFF); LCD_PrintTemp(menu_set_temp,LCD_MODE_BLINK_1); show_selected_temperature_type(menu_state-menu_preset_temp0,LCD_MODE_ON); #endif default: break; } LCD_Update(); }
/*! ******************************************************************************* * \brief menu Controller * * \returns true for controler restart ******************************************************************************/ bool menu_controller(bool new_state) { int8_t wheel = wheel_proccess(); //signed number bool ret = false; switch (menu_state) { case menu_startup: if (new_state) { menu_auto_update_timeout=2; } if (menu_auto_update_timeout==0) { menu_state = menu_version; ret=true; } break; case menu_version: if (new_state) { menu_auto_update_timeout=2; } if (menu_auto_update_timeout==0) { #if (DEBUG_SKIP_DATETIME_SETTING_AFTER_RESET) || (REMOTE_SETTING_ONLY) menu_state = menu_home; #else menu_state = menu_set_year; #endif ret=true; } break; #if (! REMOTE_SETTING_ONLY) case menu_set_year: if (wheel != 0) RTC_SetYear(RTC_GetYearYY()+wheel); if ( kb_events & KB_EVENT_PROG) { menu_state = menu_set_month; CTL_update_temp_auto(); ret=true; } break; case menu_set_month: if (wheel != 0) RTC_SetMonth(RTC_GetMonth()+wheel); if ( kb_events & KB_EVENT_PROG ) { menu_state = menu_set_day; CTL_update_temp_auto(); ret=true; } break; case menu_set_day: if (wheel != 0) RTC_SetDay(RTC_GetDay()+wheel); if ( kb_events & KB_EVENT_PROG ) { menu_state = menu_set_hour; CTL_update_temp_auto(); ret=true; } break; case menu_set_hour: if (wheel != 0) RTC_SetHour(RTC_GetHour()+wheel); if ( kb_events & KB_EVENT_PROG ) { menu_state = menu_set_minute; CTL_update_temp_auto(); ret=true; } break; case menu_set_minute: if (wheel != 0) { RTC_SetMinute(RTC_GetMinute()+wheel); RTC_SetSecond(0); } if ( kb_events & KB_EVENT_PROG ) { menu_state = menu_home; CTL_update_temp_auto(); ret=true; } break; #endif case menu_home_no_alter: // same as home screen, but without alternate contend case menu_home: // home screen case menu_home2: // alternate version, real temperature case menu_home3: // alternate version, valve pos case menu_home4: // alternate version, time #if MENU_SHOW_BATTERY case menu_home5: // alternate version, battery #endif if ( kb_events & KB_EVENT_C ) { menu_state++; // go to next alternate home screen #if MENU_SHOW_BATTERY if (menu_state > menu_home5) menu_state=menu_home; #else if (menu_state > menu_home4) menu_state=menu_home; #endif ret=true; } else { if (menu_locked) { if ( kb_events & ( KB_EVENT_WHEEL_PLUS | KB_EVENT_WHEEL_MINUS | KB_EVENT_PROG | KB_EVENT_AUTO | KB_EVENT_PROG_REWOKE | KB_EVENT_C_REWOKE | KB_EVENT_AUTO_REWOKE | KB_EVENT_PROG_LONG | KB_EVENT_C_LONG | KB_EVENT_AUTO_LONG )) { menu_auto_update_timeout=LONG_PRESS_THLD+1; menu_state=menu_lock; ret=true; } } else { // not locked if ((menu_state == menu_home) || (menu_state == menu_home_no_alter)) { if (wheel != 0) { CTL_temp_change_inc(wheel); menu_state = menu_home_no_alter; ret=true; } if ( kb_events & KB_EVENT_AUTO ) { CTL_change_mode(CTL_CHANGE_MODE); // change mode menu_state=menu_home_no_alter; ret=true; } else if ( kb_events & KB_EVENT_AUTO_REWOKE ) { CTL_change_mode(CTL_CHANGE_MODE_REWOKE); // change mode menu_state=menu_home_no_alter; ret=true; } } else { if ( kb_events & ( KB_EVENT_WHEEL_PLUS | KB_EVENT_WHEEL_MINUS | KB_EVENT_PROG | KB_EVENT_AUTO | KB_EVENT_PROG_REWOKE | KB_EVENT_C_REWOKE | KB_EVENT_AUTO_REWOKE | KB_EVENT_PROG_LONG | KB_EVENT_C_LONG | KB_EVENT_AUTO_LONG )) { menu_state = menu_home; ret = true; } } // TODO .... } } break; #if (! REMOTE_SETTING_ONLY) case menu_set_timmer_dow_start: if (new_state) menu_set_dow=((config.timer_mode==1)?RTC_GetDayOfWeek():0); menu_state = menu_set_timmer_dow; // do not use break here case menu_set_timmer_dow: if (wheel != 0) menu_set_dow=(menu_set_dow+wheel+8)%8; if ( kb_events & KB_EVENT_PROG ) { menu_state=menu_set_timmer; menu_set_slot=0; config.timer_mode = (menu_set_dow>0); eeprom_config_save((uint16_t)(&config.timer_mode)-(uint16_t)(&config)); // save value to eeprom // update hourbar menu_update_hourbar((config.timer_mode==1)?RTC_GetDayOfWeek():0); ret=true; } else if ( kb_events & KB_EVENT_AUTO ) { // exit without save menu_state=menu_home; ret=true; } break; case menu_set_timmer: if (new_state) { menu_set_time= RTC_DowTimerGet(menu_set_dow, menu_set_slot, &menu_set_mode); if (menu_set_time>24*60) menu_set_time=24*60; } if (wheel != 0) { menu_set_time=((menu_set_time/10+(24*6+1)+wheel)%(24*6+1))*10; } if ( kb_events & KB_EVENT_C ) { menu_set_mode=(menu_set_mode+5)%4; } else if ( kb_events & KB_EVENT_PROG ) { RTC_DowTimerSet(menu_set_dow, menu_set_slot, menu_set_time, menu_set_mode); if (++menu_set_slot>=RTC_TIMERS_PER_DOW) { if (menu_set_dow!=0) menu_set_dow=menu_set_dow%7+1; menu_state=menu_set_timmer_dow; } CTL_update_temp_auto(); menu_update_hourbar((config.timer_mode==1)?RTC_GetDayOfWeek():0); ret=true; } else if ( kb_events & KB_EVENT_AUTO ) { // exit without save menu_state=menu_home; ret=true; } break; #endif #if (! REMOTE_SETTING_ONLY) case menu_preset_temp0: case menu_preset_temp1: case menu_preset_temp2: case menu_preset_temp3: if (new_state) menu_set_temp=temperature_table[menu_state-menu_preset_temp0]; if (wheel != 0) { menu_set_temp+=wheel; if (menu_set_temp > TEMP_MAX+1) menu_set_temp = TEMP_MAX+1; if (menu_set_temp < TEMP_MIN-1) menu_set_temp = TEMP_MIN-1; } if ( kb_events & KB_EVENT_PROG ) { temperature_table[menu_state-menu_preset_temp0]=menu_set_temp; eeprom_config_save(menu_state+((temperature_table-config_raw)-menu_preset_temp0)); menu_state++; // menu_preset_temp3+1 == menu_home CTL_update_temp_auto(); ret=true; } else if ( kb_events & KB_EVENT_AUTO ) { // exit without save menu_state=menu_home; ret=true; } break; #endif default: case menu_lock: // "bloc" message if (menu_auto_update_timeout==0) { menu_state=menu_home; ret=true; } break; case menu_service1: // service menu case menu_service2: if (kb_events & KB_EVENT_AUTO) { menu_state=menu_home; ret=true; } else if (kb_events & KB_EVENT_C) { menu_state=menu_service_watch; ret=true; } else if (kb_events & KB_EVENT_PROG) { if (menu_state == menu_service2) { eeprom_config_save(service_idx); // save current value menu_state = menu_service1; } else { menu_state = menu_service2; } } else { if (menu_state == menu_service1) { // change index service_idx = (service_idx+wheel+CONFIG_RAW_SIZE)%CONFIG_RAW_SIZE; } else { // change value in RAM, to save press PROG int16_t min = (int16_t)config_min(service_idx); int16_t max_min_1 = (int16_t)(config_max(service_idx))-min+1; config_raw[service_idx] = (uint8_t) ( ((int16_t)(config_raw[service_idx])+(int16_t)wheel-min+max_min_1)%max_min_1+min); if (service_idx==0) LCD_Init(); } } break; case menu_service_watch: if (kb_events & KB_EVENT_AUTO) { menu_state=menu_home; ret=true; } else if (kb_events & KB_EVENT_C) { menu_state=menu_service1; ret=true; } else { service_watch_n=(service_watch_n+wheel+WATCH_N)%WATCH_N; if (wheel != 0) ret=true; } break; } if (events_common()) ret=true; if (ret && (service_idx<CONFIG_RAW_SIZE)) { // back config to default value config_raw[service_idx] = config_value(service_idx); service_idx = CONFIG_RAW_SIZE; } kb_events = 0; // clear unused keys return ret; }
UINT32 RTC_GetSecondsSinceEpoch() { UINT32 nowDays = RTC_GetTotalDays((RTC_GetCentury() * 100) + RTC_GetYear(), RTC_GetMonth(), RTC_GetDayOfTheMonth()); UINT32 epochDays = RTC_GetTotalDays(1970, 1, 1); return ((nowDays - epochDays) * 24 * 60 * 60) + (RTC_GetHour() * 60 * 60) + (RTC_GetMinute() * 60) + RTC_GetSecond(); }
LCD_Update(); // print DEC and HEX values LCD_PrintDec(i, 0, LCD_MODE_ON); LCD_PrintHex(i, 1, LCD_MODE_ON); // m_reftemp LCD_PrintTemp(m_reftemp, LCD_MODE_ON); LCD_PrintTemp (i, LCD_MODE_ON); // Hour Bar shows displaystate LCD_SetHourBarVal(i, LCD_MODE_ON); LCD_SetHourBarBar(i, LCD_MODE_ON); LCD_SetHourBarSeg(i, LCD_MODE_ON); // mm:ss LCD_PrintDec(RTC_GetMinute(), 1, LCD_MODE_ON); LCD_PrintDec(RTC_GetSecond(), 0, LCD_MODE_ON); // hh:mm LCD_PrintDec(RTC_GetHour() , 1, LCD_MODE_ON); LCD_PrintDec(RTC_GetMinute(), 0, LCD_MODE_ON); // DD.MM LCD_PrintDec(RTC_GetDay() , 1, LCD_MODE_ON); LCD_PrintDec(RTC_GetMonth() , 0, LCD_MODE_ON); // YY YY tmp= RTC_GetYearYY(); LCD_PrintDec( (20+(tmp/100) ), 1, LCD_MODE_ON); LCD_PrintDec( (tmp%100) , 0, LCD_MODE_ON); // Weekday LCD_PrintDayOfWeek(RTC_GetDayOfWeek(), LCD_MODE_ON); // 24 hour bar LCD_SetSeg(LCD_SEG_BAR24, LCD_MODE_ON); LCD_SetHourBarSeg(i, LCD_MODE_OFF); LCD_SetHourBarVal(12, LCD_MODE_ON); LCD_SetHourBarBat(12, LCD_MODE_ON);
//------------------------------------------------------------------------------ /// Currnet time is returned with packed into a DWORD value. /// The bit field is as follows: /// bit31:25 Year from 1980 (0..127) /// bit24:21 Month (1..12) /// bit20:16 Day in month(1..31) /// bit15:11 Hour (0..23) /// bit10:5 Minute (0..59) /// bit4:0 Second / 2 (0..29) //------------------------------------------------------------------------------ DWORD get_fattime(void) { U32 time; time = ((RTC_GetYear()+2000-1980)<<25) | (RTC_GetMonth()<<21) | (RTC_GetDate()<<16) | (RTC_GetHour()<<11) | (RTC_GetMinute()<<5) | ((RTC_GetSec()/2)<<0); return time; }