123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731 |
- /*
- * Xytronic LF-1600
- * User menu
- *
- * Copyright (c) 2015-2017 Michael Buesch <m@bues.ch>
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
- #include "menu.h"
- #include "display.h"
- #include "buttons.h"
- #include "timer.h"
- #include "controller_temp.h"
- #include "controller_current.h"
- #include "measure_temp.h"
- #include "debug_uart.h"
- #include "settings.h"
- #include "presets.h"
- #include <string.h>
- #define MENU_SELPRESET_TIMEOUT 3000
- #define MENU_CHGPRESET_TIMEOUT 3000
- #define MENU_IDLETEMP_TIMEOUT 3000
- #define MENU_KCONF_PRE_TIMEOUT 1500
- #define MENU_ADJ_PRE_TIMEOUT 1500
- enum menu_state {
- MENU_CURTEMP, /* Show the current temperature. */
- MENU_SELPRESET, /* Select temperature preset. */
- MENU_CHGPRESET, /* Change temperature preset. */
- MENU_IDLETEMP, /* Idle temperature setpoint. */
- MENU_DEBUG, /* Debug enable. */
- MENU_ADJ_PRE, /* Temp adjust */
- MENU_ADJ, /* Temp adjust */
- MENU_KP_PRE, /* Temp KP config */
- MENU_KP, /* Temp KP config */
- MENU_KI_PRE, /* Temp KI config */
- MENU_KI, /* Temp KI config */
- MENU_KD_PRE, /* Temp KD config */
- MENU_KD, /* Temp KD config */
- };
- enum ramp_state {
- RAMP_NONE,
- RAMP_UP,
- RAMP_DOWN,
- RAMP_PRE_UP,
- RAMP_PRE_DOWN,
- };
- #define RAMP_PRE_TIMEOUT_MS 1000u
- #define RAMP_START_PERIOD_MS 400u
- #define RAMP_MIN_PERIOD_MS (RAMP_START_PERIOD_MS >> 4)
- typedef void (*ramp_handler_t)(bool up);
- struct menu_context {
- enum menu_state state;
- struct timer timeout;
- enum ramp_state ramp;
- struct timer ramp_timer;
- struct timer ramp_pre_timer;
- ramp_handler_t ramp_handler;
- uint16_t ramp_period;
- uint8_t displayed_error;
- bool displayed_heating;
- bool display_update_requested;
- };
- static struct menu_context menu;
- static void int_to_ascii_align_right(char *buf,
- uint8_t align_to_digit,
- int16_t val,
- int16_t min_val, int16_t max_val,
- char fill_char)
- {
- int8_t i, d;
- bool neg;
- val = clamp(val, min_val, max_val);
- neg = val < 0;
- if (neg)
- val = -val;
- i = (int8_t)align_to_digit;
- buf[i + 1] = '\0';
- do {
- d = (int8_t)(val % 10);
- val /= 10;
- buf[i--] = (char)('0' + d);
- if (i < 0)
- break;
- } while (val);
- if (neg && i >= 0)
- buf[i--] = '-';
- while (i >= 0)
- buf[i--] = fill_char;
- }
- #define menu_putstr(dest, str) strcpy((dest), (str))
- static void menu_put_temp(char *disp, fixpt_t temp, const char *symbol)
- {
- int16_t temp_int;
- temp_int = (int16_t)fixpt_to_int(temp);
- int_to_ascii_align_right(disp,
- 2,
- temp_int,
- -99,
- 999,
- ' ');
- menu_putstr(disp + 3, symbol);
- }
- static void menu_put_fixpt(char *disp, fixpt_t val, uint8_t fract_digits)
- {
- int16_t ipart, fpart;
- int16_t ipart_max, fpart_max;
- int16_t ipart_digits;
- uint8_t ipart_align, fpart_align;
- if (fract_digits == 2) {
- ipart_max = 99;
- ipart_align = 1;
- fpart_max = 99;
- fpart_align = 1;
- } else { /* fract_digits == 3 */
- ipart_max = 9;
- ipart_align = 0;
- fpart_max = 999;
- fpart_align = 2;
- }
- ipart_digits = 4 - fract_digits;
- ipart = (int16_t)clamp(fixpt_get_int_part(val), 0, ipart_max);
- fpart = (int16_t)fixpt_get_dec_fract(val, fract_digits);
- int_to_ascii_align_right(&disp[0], ipart_align, ipart,
- 0, ipart_max, ' ');
- disp[ipart_digits] = '.';
- int_to_ascii_align_right(&disp[ipart_digits + 1], fpart_align, fpart,
- 0, fpart_max, '0');
- }
- static void menu_update_display(void)
- {
- char disp[6];
- const char *symbol;
- char _symbol[3];
- uint8_t displayed_error;
- uint8_t active_index;
- bool show_heating;
- enum contrtemp_boostmode boost_mode;
- boost_mode = contrtemp_get_boost_mode();
- displayed_error = menu.displayed_error;
- show_heating = true;
- disp[0] = '\0';
- switch (menu.state) {
- case MENU_CURTEMP:
- if (displayed_error) {
- menu_putstr(disp, "Err ");
- disp[3] = (char)('0' + displayed_error);
- show_heating = false;
- } else {
- if (contrtemp_is_idle()) {
- symbol = "L.";
- } else {
- switch (boost_mode) {
- case NR_BOOST_MODES:
- default:
- case TEMPBOOST_NORMAL:
- symbol = "C.";
- break;
- #if CONF_BOOST
- case TEMPBOOST_BOOST1:
- symbol = "b.";
- break;
- case TEMPBOOST_BOOST2:
- symbol = "8.";
- break;
- #endif
- }
- }
- menu_put_temp(disp, contrtemp_get_feedback(), symbol);
- }
- break;
- case MENU_SELPRESET:
- case MENU_CHGPRESET:
- if (CONF_PRESETS) {
- active_index = presets_get_active_index();
- _symbol[0] = (char)('1' + active_index);
- _symbol[1] = '.';
- _symbol[2] = '\0';
- symbol = _symbol;
- } else
- symbol = " ";
- menu_put_temp(disp, presets_get_active_value(), symbol);
- break;
- case MENU_IDLETEMP:
- if (!CONF_IDLE)
- break;
- menu_put_temp(disp, get_settings()->temp_idle_setpoint, "L");
- break;
- case MENU_DEBUG:
- if (!CONF_DEBUG)
- break;
- menu_putstr(disp, "DBG");
- break;
- case MENU_ADJ_PRE:
- if (!CONF_ADJ)
- break;
- menu_putstr(disp, "ADJC.");
- show_heating = false;
- break;
- case MENU_ADJ:
- if (!CONF_ADJ)
- break;
- menu_put_temp(disp, meastemp_adjust_get(), "C.");
- show_heating = false;
- break;
- case MENU_KP_PRE:
- if (!CONF_KCONF)
- break;
- menu_putstr(disp, " P");
- show_heating = false;
- break;
- case MENU_KP:
- if (!CONF_KCONF)
- break;
- menu_put_fixpt(disp, get_settings()->temp_k[boost_mode].kp, 2);
- show_heating = false;
- break;
- case MENU_KI_PRE:
- if (!CONF_KCONF)
- break;
- menu_putstr(disp, " I");
- show_heating = false;
- break;
- case MENU_KI:
- if (!CONF_KCONF)
- break;
- menu_put_fixpt(disp, get_settings()->temp_k[boost_mode].ki, 3);
- show_heating = false;
- break;
- case MENU_KD_PRE:
- if (!CONF_KCONF)
- break;
- menu_putstr(disp, " D");
- show_heating = false;
- break;
- case MENU_KD:
- if (!CONF_KCONF)
- break;
- menu_put_fixpt(disp, get_settings()->temp_k[boost_mode].kd, 2);
- show_heating = false;
- break;
- }
- /* Show the 'is heating' dot. */
- display_force_dp(2, true, show_heating ? menu.displayed_heating : false);
- /* Update the display content. */
- display_show(disp);
- }
- void menu_request_display_update(void)
- {
- menu.display_update_requested = true;
- mb();
- }
- static enum menu_state next_menu_state(enum menu_state cur_state)
- {
- switch (cur_state) {
- case MENU_CURTEMP:
- if (CONF_IDLE)
- return MENU_IDLETEMP;
- if (CONF_DEBUG)
- return MENU_DEBUG;
- if (CONF_ADJ)
- return MENU_ADJ_PRE;
- if (CONF_KCONF)
- return MENU_KP_PRE;
- return MENU_CURTEMP;
- case MENU_SELPRESET:
- case MENU_CHGPRESET:
- return MENU_CURTEMP;
- case MENU_IDLETEMP:
- if (CONF_DEBUG)
- return MENU_DEBUG;
- if (CONF_ADJ)
- return MENU_ADJ_PRE;
- if (CONF_KCONF)
- return MENU_KP_PRE;
- return MENU_CURTEMP;
- case MENU_DEBUG:
- if (CONF_ADJ)
- return MENU_ADJ_PRE;
- if (CONF_KCONF)
- return MENU_KP_PRE;
- return MENU_CURTEMP;
- case MENU_ADJ_PRE:
- if (CONF_ADJ)
- return MENU_ADJ;
- if (CONF_KCONF)
- return MENU_KP_PRE;
- return MENU_CURTEMP;
- case MENU_ADJ:
- if (CONF_KCONF)
- return MENU_KP_PRE;
- return MENU_CURTEMP;
- case MENU_KP_PRE:
- if (CONF_KCONF)
- return MENU_KP;
- return MENU_CURTEMP;
- case MENU_KP:
- if (CONF_KCONF)
- return MENU_KI_PRE;
- return MENU_CURTEMP;
- case MENU_KI_PRE:
- if (CONF_KCONF)
- return MENU_KI;
- return MENU_CURTEMP;
- case MENU_KI:
- if (CONF_KCONF)
- return MENU_KD_PRE;
- return MENU_CURTEMP;
- case MENU_KD_PRE:
- if (CONF_KCONF)
- return MENU_KD;
- return MENU_CURTEMP;
- case MENU_KD:
- if (CONF_KCONF)
- return MENU_CURTEMP;
- return MENU_CURTEMP;
- }
- return MENU_CURTEMP;
- }
- static void menu_set_state(enum menu_state new_state)
- {
- if (menu.state == new_state)
- return;
- menu.state = new_state;
- switch (new_state) {
- case MENU_SELPRESET:
- timer_arm(&menu.timeout, MENU_SELPRESET_TIMEOUT);
- break;
- case MENU_CHGPRESET:
- timer_arm(&menu.timeout, MENU_CHGPRESET_TIMEOUT);
- break;
- case MENU_IDLETEMP:
- timer_arm(&menu.timeout, MENU_IDLETEMP_TIMEOUT);
- break;
- case MENU_ADJ_PRE:
- if (!CONF_ADJ)
- break;
- timer_arm(&menu.timeout, MENU_ADJ_PRE_TIMEOUT);
- /* fall through... */
- case MENU_ADJ:
- if (!CONF_ADJ)
- break;
- contrtemp_set_enabled(false);
- break;
- case MENU_KP_PRE:
- case MENU_KI_PRE:
- case MENU_KD_PRE:
- if (!CONF_KCONF)
- break;
- timer_arm(&menu.timeout, MENU_KCONF_PRE_TIMEOUT);
- /* fall through... */
- case MENU_KP:
- case MENU_KI:
- case MENU_KD:
- if (!CONF_KCONF)
- break;
- contrtemp_set_enabled(false);
- break;
- case MENU_CURTEMP:
- contrtemp_set_enabled(true);
- break;
- case MENU_DEBUG:
- break;
- }
- menu_request_display_update();
- }
- static void menu_set_next_state(void)
- {
- menu_set_state(next_menu_state(menu.state));
- }
- static fixpt_t do_ramp_temp(fixpt_t temp, bool up,
- fixpt_t min_val, fixpt_t max_val)
- {
- return fixpt_add_limited(temp,
- (up ? float_to_fixpt(CELSIUS(1.0)) :
- float_to_fixpt(CELSIUS(-1.0))),
- min_val, max_val);
- }
- static void settemp_ramp_handler(bool up)
- {
- fixpt_t setpoint;
- setpoint = presets_get_active_value();
- setpoint = do_ramp_temp(setpoint, up,
- float_to_fixpt(CONTRTEMP_NEGLIM),
- float_to_fixpt(CONTRTEMP_POSLIM));
- presets_set_active_value(setpoint);
- menu_set_state(MENU_CHGPRESET);
- }
- static void idletemp_ramp_handler(bool up)
- {
- fixpt_t setpoint;
- setpoint = get_settings()->temp_idle_setpoint;
- setpoint = do_ramp_temp(setpoint, up,
- float_to_fixpt(CONTRTEMP_NEGLIM),
- float_to_fixpt(CONTRTEMP_POSLIM));
- contrtemp_set_idle_setpoint(setpoint);
- }
- static void do_ramp_k(fixpt_t *k, fixpt_t inc, fixpt_t max_val, bool up)
- {
- *k = fixpt_add_limited(*k, (up ? inc : fixpt_neg(inc)),
- float_to_fixpt(0.0), max_val);
- store_settings();
- contrtemp_update_pid_config();
- }
- static void adj_ramp_handler(bool up)
- {
- fixpt_t temp_adj;
- temp_adj = meastemp_adjust_get();
- temp_adj = do_ramp_temp(temp_adj, up,
- float_to_fixpt(CELSIUS(-99)),
- float_to_fixpt(CELSIUS(99)));
- meastemp_adjust_set(temp_adj);
- }
- static void kconf_kp_ramp_handler(bool up)
- {
- do_ramp_k(&(get_settings()->temp_k[contrtemp_get_boost_mode()].kp),
- float_to_fixpt(0.03125),
- float_to_fixpt(99.0), up);
- }
- static void kconf_ki_ramp_handler(bool up)
- {
- do_ramp_k(&(get_settings()->temp_k[contrtemp_get_boost_mode()].ki),
- float_to_fixpt(0.015625),
- float_to_fixpt(9.0), up);
- }
- static void kconf_kd_ramp_handler(bool up)
- {
- do_ramp_k(&(get_settings()->temp_k[contrtemp_get_boost_mode()].kd),
- float_to_fixpt(0.03125),
- float_to_fixpt(99.0), up);
- }
- static void start_ramping(bool up, bool pre_ramp_wait, ramp_handler_t handler)
- {
- if (pre_ramp_wait) {
- menu.ramp = up ? RAMP_PRE_UP : RAMP_PRE_DOWN;
- timer_arm(&menu.ramp_pre_timer, RAMP_PRE_TIMEOUT_MS);
- } else {
- menu.ramp = up ? RAMP_UP : RAMP_DOWN;
- }
- menu.ramp_handler = handler;
- menu.ramp_period = RAMP_START_PERIOD_MS;
- timer_set_now(&menu.ramp_timer);
- }
- static void start_ramping_button(enum button_id button,
- enum button_state bstate,
- bool pre_ramp_wait,
- ramp_handler_t handler)
- {
- if (bstate == BSTATE_POSEDGE) {
- if (button == BUTTON_MINUS)
- start_ramping(false, pre_ramp_wait, handler);
- else if (button == BUTTON_PLUS)
- start_ramping(true, pre_ramp_wait, handler);
- }
- }
- static void stop_ramping(void)
- {
- menu.ramp = RAMP_NONE;
- }
- /* Handler for SET, MINUS and PLUS buttons */
- void menu_button_handler(enum button_id button,
- enum button_state bstate);
- void menu_button_handler(enum button_id button,
- enum button_state bstate)
- {
- if (button == BUTTON_IRON)
- return;
- switch (menu.state) {
- case MENU_CURTEMP:
- start_ramping_button(button, bstate, (CONF_PRESETS),
- settemp_ramp_handler);
- if (CONF_PRESETS) {
- if (bstate == BSTATE_NEGEDGE) {
- if (button != BUTTON_SET)
- menu_set_state(MENU_SELPRESET);
- }
- } else {
- if (bstate == BSTATE_POSEDGE) {
- if (button != BUTTON_SET)
- menu_set_state(MENU_CHGPRESET);
- }
- }
- break;
- case MENU_SELPRESET:
- if (!CONF_PRESETS)
- break;
- timer_arm(&menu.timeout, MENU_SELPRESET_TIMEOUT);
- if (bstate == BSTATE_NEGEDGE) {
- if (button == BUTTON_PLUS)
- presets_next();
- if (button == BUTTON_MINUS)
- presets_prev();
- }
- start_ramping_button(button, bstate, true,
- settemp_ramp_handler);
- break;
- case MENU_CHGPRESET:
- timer_arm(&menu.timeout, MENU_CHGPRESET_TIMEOUT);
- start_ramping_button(button, bstate, false,
- settemp_ramp_handler);
- break;
- case MENU_IDLETEMP:
- if (!CONF_IDLE)
- break;
- timer_arm(&menu.timeout, MENU_IDLETEMP_TIMEOUT);
- start_ramping_button(button, bstate, false,
- idletemp_ramp_handler);
- break;
- case MENU_DEBUG:
- if (!CONF_DEBUG)
- break;
- if (bstate == BSTATE_POSEDGE) {
- if (button == BUTTON_PLUS)
- debug_enable(true);
- if (button == BUTTON_MINUS) {
- debug_enable(false);
- menu_request_display_update();
- }
- }
- break;
- case MENU_ADJ_PRE:
- break;
- case MENU_ADJ:
- if (!CONF_ADJ)
- break;
- start_ramping_button(button, bstate, false,
- adj_ramp_handler);
- break;
- case MENU_KP_PRE:
- break;
- case MENU_KP:
- if (!CONF_KCONF)
- break;
- start_ramping_button(button, bstate, false,
- kconf_kp_ramp_handler);
- break;
- case MENU_KI_PRE:
- break;
- case MENU_KI:
- if (!CONF_KCONF)
- break;
- start_ramping_button(button, bstate, false,
- kconf_ki_ramp_handler);
- break;
- case MENU_KD_PRE:
- break;
- case MENU_KD:
- if (!CONF_KCONF)
- break;
- start_ramping_button(button, bstate, false,
- kconf_kd_ramp_handler);
- break;
- }
- if (bstate == BSTATE_NEGEDGE) {
- /* Stop ramping, if any button is released. */
- stop_ramping();
- /* Switch to the next menu via SET. */
- if (button == BUTTON_SET) {
- if (!debug_is_enabled())
- menu_set_next_state();
- }
- }
- }
- /* Periodic work. */
- void menu_work(void)
- {
- enum ramp_state ramp;
- uint8_t error;
- bool heating;
- /* Menu timeouts */
- switch (menu.state) {
- case MENU_CURTEMP:
- case MENU_DEBUG:
- case MENU_ADJ:
- case MENU_KP:
- case MENU_KI:
- case MENU_KD:
- break;
- case MENU_ADJ_PRE:
- case MENU_KP_PRE:
- case MENU_KI_PRE:
- case MENU_KD_PRE:
- case MENU_SELPRESET:
- case MENU_CHGPRESET:
- case MENU_IDLETEMP:
- if (timer_expired(&menu.timeout)) {
- if (menu.state == MENU_IDLETEMP)
- menu_set_state(MENU_CURTEMP);
- else
- menu_set_next_state();
- stop_ramping();
- break;
- }
- break;
- }
- /* Evaluate error conditions. */
- error = 0;
- if (contrcurr_get_emerg() & CONTRCURR_EMERG_UNPLAUS_FEEDBACK)
- error |= 1;
- if (contrtemp_in_emerg())
- error |= 2;
- if (error != menu.displayed_error) {
- menu.displayed_error = error;
- stop_ramping();
- menu.display_update_requested = true;
- }
- /* Update heating condition. */
- heating = contrtemp_is_heating_up();
- if (heating != menu.displayed_heating) {
- menu.displayed_heating = heating;
- menu.display_update_requested = true;
- }
- /* Generic ramping handler. */
- ramp = menu.ramp;
- if (ramp != RAMP_NONE) {
- if (ramp == RAMP_PRE_UP ||
- ramp == RAMP_PRE_DOWN) {
- if (timer_expired(&menu.ramp_pre_timer)) {
- start_ramping(ramp == RAMP_PRE_UP, false,
- menu.ramp_handler);
- }
- } else {
- if (timer_expired(&menu.ramp_timer)) {
- menu.ramp_handler(ramp == RAMP_UP);
- menu.display_update_requested = true;
- timer_add(&menu.ramp_timer, menu.ramp_period);
- if (menu.ramp_period > RAMP_MIN_PERIOD_MS)
- menu.ramp_period /= 2;
- }
- }
- }
- /* Update the display, if requested. */
- mb();
- if (menu.display_update_requested) {
- menu.display_update_requested = false;
- mb();
- menu_update_display();
- }
- }
- void menu_init(void)
- {
- if (CONF_DEBUG) {
- if (button_is_pressed(BUTTON_SET)) {
- debug_enable(true);
- menu_set_state(MENU_DEBUG);
- } else {
- menu_set_state(MENU_CURTEMP);
- }
- } else {
- menu_set_state(MENU_CURTEMP);
- }
- }
|