mirror of
https://github.com/semerad/gt3b.git
synced 2026-02-19 19:11:23 +01:00
376 lines
10 KiB
C
376 lines
10 KiB
C
/*
|
|
menu_mix - handle menus for mix settings
|
|
Copyright (C) 2011 Pavel Semerad
|
|
|
|
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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
#include "menu.h"
|
|
#include "config.h"
|
|
#include "ppm.h"
|
|
#include "input.h"
|
|
#include "lcd.h"
|
|
#include "buzzer.h"
|
|
|
|
|
|
|
|
|
|
|
|
// variables to be used in CALC task
|
|
s8 menu_channel3_8[MAX_CHANNELS - 2]; // values -100..100 for channels >=3
|
|
u8 menu_channels_mixed; // channel with 1 here will not be set from
|
|
// menu_channel3_8
|
|
s8 menu_4WS_mix; // mix -100..100
|
|
_Bool menu_4WS_crab; // when 1, crab steering
|
|
s8 menu_DIG_mix; // mix -100..100
|
|
u8 menu_MP_index[MP_COUNT]; // index of MultiPosition channels
|
|
_Bool menu_brake; // when 1, full brake is applied
|
|
|
|
|
|
|
|
// battery low flag
|
|
_Bool menu_battery_low;
|
|
// raw battery ADC value for check to battery low
|
|
u16 battery_low_raw;
|
|
|
|
|
|
|
|
|
|
// set menu_channels_mixed to ignore them from menu_channel3_8
|
|
void set_menu_channels_mixed(void) {
|
|
menu_channels_mixed = 0;
|
|
if (cm.channel_4WS)
|
|
menu_channels_mixed |= (u8)(1 << (u8)(cm.channel_4WS - 1));
|
|
if (cm.channel_DIG)
|
|
menu_channels_mixed |= (u8)(1 << (u8)(cm.channel_DIG - 1));
|
|
if (cm.channel_brake)
|
|
menu_channels_mixed |= (u8)(1 << (u8)(cm.channel_brake - 1));
|
|
}
|
|
|
|
|
|
// apply model settings to variables
|
|
void apply_model_config(void) {
|
|
u8 i, autorepeat = 0;
|
|
|
|
// set number of channels for this model
|
|
ppm_set_channels((u8)(cm.channels + 1));
|
|
|
|
set_menu_channels_mixed();
|
|
|
|
// set autorepeat
|
|
for (i = 0; i < 4; i++) {
|
|
if (!ck.et_map[i].is_trim) continue; // trim is off, skip
|
|
if (ck.et_map[i].buttons == ETB_AUTORPT)
|
|
autorepeat |= (u8)((u8)et_buttons[i][0] | (u8)et_buttons[i][1]);
|
|
}
|
|
button_autorepeat(autorepeat);
|
|
}
|
|
|
|
|
|
// load model config from eeprom and set model settings
|
|
void menu_load_model(void) {
|
|
u8 i;
|
|
// load config
|
|
config_model_read();
|
|
|
|
// set values of channels >= 3 to default left state,
|
|
// for channels mapped to some trims/keys, it will next be set
|
|
// to corresponding centre/reset value
|
|
for (i = 0; i < MAX_CHANNELS - 2; i++)
|
|
menu_channel3_8[i] = -100;
|
|
|
|
// set 4WS, DIG, MP to defaults
|
|
menu_4WS_mix = 0;
|
|
menu_4WS_crab = 0;
|
|
menu_DIG_mix = 0;
|
|
|
|
memset(menu_MP_index, 0, sizeof(menu_MP_index));
|
|
if (cm.channel_MP0 && cm.channel_MP0 != MP_DIG)
|
|
menu_channel3_8[cm.channel_MP0 - 3] = cm.multi_position0[0];
|
|
if (cm.channel_MP1 && cm.channel_MP1 != MP_DIG)
|
|
menu_channel3_8[cm.channel_MP1 - 3] = cm.multi_position1[0];
|
|
if (cm.channel_MP2 && cm.channel_MP2 != MP_DIG)
|
|
menu_channel3_8[cm.channel_MP2 - 3] = cm.multi_position2[0];
|
|
if (cm.channel_MP3 && cm.channel_MP3 != MP_DIG)
|
|
menu_channel3_8[cm.channel_MP3 - 3] = cm.multi_position3[0];
|
|
|
|
menu_brake = 0;
|
|
|
|
// set state of buttons to do initialize
|
|
menu_buttons_initialize();
|
|
|
|
// apply config to radio setting
|
|
apply_model_config();
|
|
}
|
|
|
|
|
|
// apply global setting to variables
|
|
void apply_global_config(void) {
|
|
backlight_set_default(cg.backlight_time);
|
|
backlight_on();
|
|
// compute raw value for battery low voltage
|
|
battery_low_raw = (u16)(((u32)cg.battery_calib * cg.battery_low + 50) / 100);
|
|
}
|
|
|
|
|
|
// menu stop - checks low battery
|
|
_Bool battery_low_shutup;
|
|
void menu_stop(void) {
|
|
static _Bool battery_low_on;
|
|
stop();
|
|
// low_bat is disabled in calibrate, key-test and global menus,
|
|
// check it by buzzer_running
|
|
if (menu_battery_low && !buzzer_running && !battery_low_shutup)
|
|
battery_low_on = 0;
|
|
if (battery_low_on == menu_battery_low) return; // no change
|
|
|
|
// battery low status changed
|
|
if (menu_battery_low) {
|
|
// battery low firstly
|
|
battery_low_on = 1;
|
|
lcd_segment(LS_SYM_LOWPWR, LS_ON);
|
|
lcd_segment_blink(LS_SYM_LOWPWR, LB_SPC);
|
|
buzzer_on(40, 160, BUZZER_MAX);
|
|
}
|
|
else {
|
|
// battery low now OK
|
|
battery_low_on = 0;
|
|
lcd_segment(LS_SYM_LOWPWR, LS_OFF);
|
|
buzzer_off();
|
|
}
|
|
lcd_update();
|
|
}
|
|
|
|
|
|
// change value based on state of rotate encoder
|
|
s16 menu_change_val(s16 val, s16 min, s16 max, u8 amount_fast, u8 rotate) {
|
|
u8 amount = 1;
|
|
|
|
if (btn(BTN_ROT_L)) {
|
|
// left
|
|
if (btnl(BTN_ROT_L)) amount = amount_fast;
|
|
val -= amount;
|
|
if (val < min)
|
|
if (rotate) val = max;
|
|
else val = min;
|
|
}
|
|
else {
|
|
// right
|
|
if (btnl(BTN_ROT_R)) amount = amount_fast;
|
|
val += amount;
|
|
if (val > max)
|
|
if (rotate) val = min;
|
|
else val = max;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
|
|
// clear all symbols
|
|
void menu_clear_symbols(void) {
|
|
lcd_segment(LS_SYM_MODELNO, LS_OFF);
|
|
lcd_segment(LS_SYM_DOT, LS_OFF);
|
|
lcd_segment(LS_SYM_VOLTS, LS_OFF);
|
|
lcd_segment(LS_SYM_PERCENT, LS_OFF);
|
|
lcd_segment(LS_SYM_LEFT, LS_OFF);
|
|
lcd_segment(LS_SYM_RIGHT, LS_OFF);
|
|
lcd_segment(LS_SYM_CHANNEL, LS_OFF);
|
|
}
|
|
|
|
|
|
// common menu, select item at 7SEG and then set params at CHR3
|
|
u8 menu_set; // menu is in: 0 = menu_id, 1..X = menu setting 1..X
|
|
u8 menu_id; // id of selected menu
|
|
_Bool menu_id_set; // 0 = in menu-id, 1 = in menu-setting
|
|
u8 menu_blink; // what of chars should blink
|
|
|
|
static void func_init(u8 flags) {
|
|
// blink all
|
|
menu_blink = 0xff;
|
|
|
|
// clear display symbols
|
|
menu_clear_symbols();
|
|
if (flags & MCF_LOWPWR) lcd_segment(LS_SYM_LOWPWR, LS_OFF);
|
|
}
|
|
|
|
void menu_common(menu_common_t func, void *params, u8 flags) {
|
|
menu_id_set = flags & MCF_SET_ONLY ? 1 : 0;
|
|
menu_set = 0; // now in menu_id
|
|
menu_id = 0; // first menu item
|
|
|
|
// init and show setting
|
|
func_init(flags);
|
|
func(MCA_INIT, params);
|
|
if (menu_id_set) {
|
|
lcd_chars_blink_mask(LB_SPC, menu_blink);
|
|
}
|
|
else {
|
|
if (menu_blink & MCB_7SEG) lcd_set_blink(L7SEG, LB_SPC);
|
|
}
|
|
lcd_update();
|
|
|
|
while (1) {
|
|
|
|
// remove button flags and wait for wakeup
|
|
btnra();
|
|
if (flags & MCF_STOP) stop();
|
|
else menu_stop();
|
|
|
|
// end this menu with defined buttons
|
|
if (btn(BTN_BACK | BTN_END) || btnl(BTN_ENTER)) break;
|
|
if ((flags & MCF_ENTER) && btn(BTN_ENTER)) break;
|
|
|
|
// if menu ADC was activated, call func to read for example left-right pos
|
|
if (menu_adc_wakeup) func(MCA_ADC_PRE, params);
|
|
|
|
// rotate encoder changed, change menu-id or value
|
|
if (btn(BTN_ROT_ALL)) {
|
|
if (menu_id_set) {
|
|
func_init(flags);
|
|
func(MCA_SET_CHG, params);
|
|
lcd_chars_blink_mask(LB_SPC, menu_blink);
|
|
}
|
|
else {
|
|
// change menu-id
|
|
|
|
// reset some variables
|
|
menu_adc_wakeup = 0;
|
|
menu_force_value_channel = 0;
|
|
|
|
// select new menu id and show it
|
|
func_init(flags);
|
|
func(MCA_ID_CHG, params); // do own change based on BTN_ROT
|
|
|
|
if (menu_blink & MCB_7SEG) lcd_set_blink(L7SEG, LB_SPC);
|
|
}
|
|
lcd_update();
|
|
}
|
|
|
|
// ENTER pressed, switch between menu settings
|
|
else if (btn(BTN_ENTER)) {
|
|
// switch menu_id/menu-setting0/menu-setting1/...
|
|
key_beep();
|
|
if (flags & MCF_SWITCH) {
|
|
// switch will be done by function
|
|
func_init(flags);
|
|
func(MCA_SWITCH, params);
|
|
if (menu_set == 255) break; // exit menu when requested
|
|
// blinking
|
|
if (menu_id_set) {
|
|
lcd_chars_blink_mask(LB_SPC, menu_blink);
|
|
lcd_set_blink(L7SEG, LB_OFF);
|
|
}
|
|
else {
|
|
if (menu_blink & MCB_7SEG) lcd_set_blink(L7SEG, LB_SPC);
|
|
lcd_chars_blink(LB_OFF);
|
|
}
|
|
lcd_update();
|
|
}
|
|
else if (menu_id_set) {
|
|
// select next menu setting
|
|
func_init(flags);
|
|
func(MCA_SET_NEXT, params);
|
|
if (menu_set == 255) break; // exit menu when requested
|
|
if (menu_set || (flags & MCF_SET_ONLY)) {
|
|
// some > 0 menu setting
|
|
lcd_chars_blink_mask(LB_SPC, menu_blink);
|
|
}
|
|
else {
|
|
// rotated back to setting 0, switch to menu selection
|
|
menu_id_set = 0;
|
|
if (menu_blink & MCB_7SEG) lcd_set_blink(L7SEG, LB_SPC);
|
|
lcd_chars_blink(LB_OFF);
|
|
}
|
|
lcd_update();
|
|
}
|
|
else {
|
|
// switch to first menu setting
|
|
menu_id_set = 1;
|
|
// menu setting values is already showed
|
|
lcd_set_blink(L7SEG, LB_OFF);
|
|
lcd_chars_blink_mask(LB_SPC, menu_blink);
|
|
}
|
|
}
|
|
|
|
// if menu ADC was activated, call func to check if to show for example
|
|
// other value when left-right position changed
|
|
if (menu_adc_wakeup) {
|
|
u8 mis = menu_id_set; // save value
|
|
menu_id_set = 0; // func will set it to 1 when show call needed
|
|
func(MCA_ADC_POST, params);
|
|
if (menu_id_set) {
|
|
menu_id_set = mis;
|
|
// show changed value
|
|
func_init(flags);
|
|
func(MCA_SHOW, params);
|
|
if (menu_id_set) {
|
|
lcd_chars_blink_mask(LB_SPC, menu_blink);
|
|
}
|
|
else {
|
|
if (menu_blink & MCB_7SEG) lcd_set_blink(L7SEG, LB_SPC);
|
|
}
|
|
lcd_update();
|
|
}
|
|
else menu_id_set = mis;
|
|
}
|
|
}
|
|
|
|
// call to select next value which can do some action (such as reset)
|
|
if (menu_id_set && menu_set != 255) func(MCA_SET_NEXT, params);
|
|
|
|
// cleanup display
|
|
func_init(flags);
|
|
|
|
// reset variables
|
|
menu_adc_wakeup = 0;
|
|
menu_force_value_channel = 0;
|
|
key_beep();
|
|
}
|
|
|
|
|
|
// common list menu, given by list of functions, one for each menu item
|
|
typedef struct {
|
|
menu_list_t *funcs;
|
|
u8 nitems;
|
|
} menu_list_params_t;
|
|
|
|
static void menu_list_func(u8 action, menu_list_params_t *p) {
|
|
menu_list_t func = p->funcs[menu_id];
|
|
switch (action) {
|
|
case MCA_SET_CHG:
|
|
func(MLA_CHG);
|
|
return; // value already showed
|
|
case MCA_SET_NEXT:
|
|
func(MLA_NEXT);
|
|
return; // value already showed
|
|
case MCA_ID_CHG:
|
|
menu_id = (u8)menu_change_val(menu_id, 0, p->nitems - 1, 1, 1);
|
|
func = p->funcs[menu_id];
|
|
break;
|
|
}
|
|
// show value
|
|
func(MLA_SHOW);
|
|
}
|
|
|
|
void menu_list(menu_list_t *menu_funcs, u8 menu_nitems, u8 flags) {
|
|
menu_list_params_t params;
|
|
params.funcs = menu_funcs;
|
|
params.nitems = menu_nitems;
|
|
menu_common(menu_list_func, ¶ms, (u8)(flags & (MCF_STOP | MCF_LOWPWR)));
|
|
}
|
|
|