diff --git a/ChangeLog b/ChangeLog index ec5366f..4318a88 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +*0.5.1 () + added possibility to connect potentiometer instead of CH3 button + *0.5.0 (23 Apr 2012) global menu was restructuralized added ability to select number of channels (2-MAX_CHANNELS) for each diff --git a/MANUAL.txt b/MANUAL.txt index e97fb2f..3cd6dd0 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -197,6 +197,14 @@ Standard menu: identified by "V" symbol - reverse_long: same as "reverse" + "V" symbol - prev_val_long: same as "prev_val" + "V" symbol + ----- + steps of settings of ch3 potentiometer (if enabled at global config): + - sequence: + function -> reverse + - function: selected function listed at the end of manual + - RE reverse: + 0 - no change + 1 - swap left/right sides @@ -255,14 +263,22 @@ Operating timers: Calibrate menu: =============== - enter it by wheel right-turn and ENTER-long -- 6 menu items starts to blink +- 6 menu items starts to blink (+ 2 arrows if CH3 is potentiometer) - use END or ROTATE to change channels - there is also channel 3 representing CH3 button (for those who will do some 3-position switch/potentiometer modification) + > There is 1K resistor connected between CH3 button and +5V, so + simply connecting linear potentiometer instead of CH3 button + will not work. Usefull results can be done with logarithmic + potentiometer 10K which has 1K at half of turn. Or eliminate + internal resistor to +5V, connect potentiometer between +5V + and GND and potentiometer output instead of CH3 button. - there is also channal 4 representing battery voltage - calibrate as usual (for channel 1/2 left+mid+right and ENTER) > calibrated value will disappear from menu > it is not needed to calibrate all values +- calibrate CH3 potentiometer (if selected at global config) + > only left + right positions - calibrate battery (it will not be probably needed to use this) > select channel 4 > press ENTER @@ -313,6 +329,7 @@ Global setup menu: d long press key delay 100...1000 miliseconds H setting of hardware features reverse rotate encoder E_N/E_R (Normal/Reverse) - for GT3C + ch3 is potentiometer P3N/P3Y (No/Yes) select ppm sync/frame PTS/PTF (constant SYNC/frame length) select ppm length Lxx - 3-18ms for constant SYNC length 9-24ms for constant frame length diff --git a/TODO b/TODO index 6b25c62..33938a6 100644 --- a/TODO +++ b/TODO @@ -1,10 +1,4 @@ -linear CH3 with potentiometer - - global option to choose CH3 is a pot - - don't read CH3 key in read_keys() - - key mapping assign trim function and reverse - - at begin of CALC, convert CH3 val to assigned function - - calibrate low and high values at calib menu ? use also flash memory to store model configs - some models in EEPROM - other models in FLASH, one empty place diff --git a/calc.c b/calc.c index 5e038f2..10316ad 100644 --- a/calc.c +++ b/calc.c @@ -269,6 +269,29 @@ static void calc_loop(void) { u16 adc_steering, adc_throttle; // last 4 or 1 ADC values while (1) { + + // handle channel3 potentiometer, cannot use channel_calib, + // because we don't have calib middle and dead zone + if (cg.ch3_pot && !menu_ch3_pot_disabled) { + // do it only if some function is assigned to CH3 pot + if (*ck_ch3_pot_func) { + if (cg.adc_ovs_last) val = adc_ch3_last << ADC_OVS_SHIFT; + else val = adc_ch3_ovs; + // map ch3 pot -5000..5000 range + val -= cg.calib_ch3_left << ADC_OVS_SHIFT; + val2 = (s16)((s32)(val) * PPM(1000) / + ((cg.calib_ch3_right - cg.calib_ch3_left) << ADC_OVS_SHIFT)) + - PPM(500); + // safety checks to -5000..5000 limits + if (val2 < PPM(-500)) val2 = PPM(-500); + else if (val2 > PPM(500)) val2 = PPM(500); + // do reverse + if (*ck_ch3_pot_rev) val2 = -val2; + // set value to function + menu_et_function_set_from_linear(*ck_ch3_pot_func, val2); + } + } + DIG_mix = menu_DIG_mix * PPM(5); // to -5000..5000 range // set used ADC values diff --git a/config.c b/config.c index 38d3b3a..e2fa26b 100644 --- a/config.c +++ b/config.c @@ -59,7 +59,6 @@ u8 config_global_set_default(void) { cg.reset_beep = 1; cg.poweron_beep = 1; cg.poweron_warn = 0; - cg.rotate_reverse = 0; // not-reversed cg.timer1_type = 0; // OFF cg.timer2_type = 0; @@ -68,12 +67,12 @@ u8 config_global_set_default(void) { cg.ppm_sync_frame = 0; // to constant SYNC length cg.ppm_length = 1; // 4ms constant SYNC length + cg.rotate_reverse = 0; // not-reversed + cg.ch3_pot = 0; // CH3 is button cg.unused1 = 0; cg.unused2 = 0; cg.unused3 = 0; - cg.unused4 = 0; - cg.unused5 = 0; // set calibrate values only when they are out of limits cc |= check_val(&cg.calib_steering_left, 0, CALIB_ST_LOW_MID, 0); @@ -82,6 +81,8 @@ u8 config_global_set_default(void) { cc |= check_val(&cg.calib_throttle_fwd, 0, CALIB_TH_LOW_MID, 0); cc |= check_val(&cg.calib_throttle_mid, CALIB_TH_LOW_MID, CALIB_TH_MID_HIGH, 600); cc |= check_val(&cg.calib_throttle_bck, CALIB_TH_MID_HIGH, 1023, 1023); + check_val(&cg.calib_ch3_left, 0, 512, 0); + check_val(&cg.calib_ch3_right, 512, 1023, 1023); return cc; } @@ -141,6 +142,10 @@ void config_model_set_default(void) { cm.channels = MAX_CHANNELS - 1; // it is one lower to fit also 8 cm.unused = 0; memcpy(&cm.key_mapping, &default_key_mapping, sizeof(config_key_mapping_s)); + if (cg.ch3_pot) { + *ck_ch3_pot_func = 0; + *ck_ch3_pot_rev = 0; + } } diff --git a/config.h b/config.h index 937b7b2..da21d3a 100644 --- a/config.h +++ b/config.h @@ -63,11 +63,12 @@ typedef struct { u8 ppm_length:4; // length of PPM sync signal (3..) or frame length (9..) u8 ppm_sync_frame:1; // 0 = constant SYNC length, 1 = constant frame length - u8 unused1:3; // reserve + u8 ch3_pot:1; // potentiometer connected instead of CH3 button + u8 unused1:2; // reserve u8 unused2; + u16 calib_ch3_left; + u16 calib_ch3_right; u16 unused3; - u16 unused4; - u16 unused5; } config_global_s; extern config_global_s config_global; @@ -164,8 +165,10 @@ typedef struct { } config_model_s; extern config_model_s config_model; -#define cm config_model -#define ck cm.key_mapping +#define cm config_model +#define ck cm.key_mapping +#define ck_ch3_pot_func ((u8 *)&ck.key_map[0]) +#define ck_ch3_pot_rev ((u8 *)&ck.key_map[0] + 1) diff --git a/input.c b/input.c index a16e209..c9c5615 100644 --- a/input.c +++ b/input.c @@ -196,8 +196,11 @@ static void read_keys(void) { // add CH3 button, middle state will be only in buttons_state, // not in buttons - if (adc_ch3_last <= BTN_CH3_LOW) buttons1 |= BTN_CH3; - else if (adc_ch3_last < BTN_CH3_HIGH) buttons1 |= BTN_CH3_MID; + // do only when CH3 is button, not potentiometer + if (!cg.ch3_pot) { + if (adc_ch3_last <= BTN_CH3_LOW) buttons1 |= BTN_CH3; + else if (adc_ch3_last < BTN_CH3_HIGH) buttons1 |= BTN_CH3_MID; + } // combine last 3 readed buttons buttons_state |= buttons1 & buttons2 & buttons3; diff --git a/menu.h b/menu.h index 5789a1e..6c13244 100644 --- a/menu.h +++ b/menu.h @@ -90,6 +90,8 @@ extern u16 battery_low_raw; extern u8 menu_check_keys; // temporary flag used when doing reset (global/all models/model) extern _Bool menu_tmp_flag; +// temporary disable ch3 potentiometer when in key mapping menu +extern _Bool menu_ch3_pot_disabled; @@ -114,6 +116,7 @@ extern u8 *menu_key_function_name(u8 n); extern s8 menu_key_function_idx(u8 n); extern u8 menu_key_function_2state(u8 n); extern u8 menu_key_function_is_allowed(u8 n); +extern void menu_et_function_set_from_linear(u8 n, s16 val); extern const u8 steps_map[]; #define STEPS_MAP_SIZE 11 extern const u16 et_buttons[][2]; diff --git a/menu_global.c b/menu_global.c index e8d2556..0478c14 100644 --- a/menu_global.c +++ b/menu_global.c @@ -279,6 +279,9 @@ static void gs_hardware(u8 action) { cg.rotate_reverse ^= 1; break; case 1: + cg.ch3_pot ^= 1; + break; + case 2: if (cg.ppm_sync_frame) { cg.ppm_sync_frame = 0; cg.ppm_length = 1; // 4ms SYNC @@ -288,7 +291,7 @@ static void gs_hardware(u8 action) { cg.ppm_length = 11; // 20ms frame } break; - case 2: + case 3: if (cg.ppm_sync_frame) // constant frame length cg.ppm_length = @@ -303,7 +306,7 @@ static void gs_hardware(u8 action) { // select next value else if (action == MLA_NEXT) { - if (++menu_set > 2) menu_set = 0; + if (++menu_set > 3) menu_set = 0; } // show values @@ -315,10 +318,14 @@ static void gs_hardware(u8 action) { lcd_char(LCHR3, (u8)(cg.rotate_reverse ? 'R' : 'N')); break; case 1: + lcd_chars("P3"); + lcd_char(LCHR3, (u8)(cg.ch3_pot ? 'Y' : 'N')); + break; + case 2: lcd_chars("PT"); lcd_char(LCHR3, (u8)(cg.ppm_sync_frame ? 'F' : 'S')); break; - case 2: + case 3: lcd_char_num3(cg.ppm_length + (u8)(cg.ppm_sync_frame ? 9 : 3)); lcd_char(LCHR1, 'L'); menu_blink |= MCB_CHR2; // blink char2 too diff --git a/menu_key.c b/menu_key.c index 46ea4f8..d11b060 100644 --- a/menu_key.c +++ b/menu_key.c @@ -191,6 +191,67 @@ static void km_trim(u8 action) { } +// special function for ch3 potentiometer, select function and reverse +static void km_ch3_pot(u8 action) { + s8 idx; + u8 new_idx = 0; + u8 *func = ck_ch3_pot_func; + u8 *rev = ck_ch3_pot_rev; + + if (action == 1) { + // change value + switch (menu_set) { + case 0: + // function + // select new function, map through trim_functions + idx = menu_et_function_idx(*func); + if (idx == -1) { + // there can be some bad value from timer when ch3 was not + // set to potentiometer + *func = 0; + idx = 0; + } + while (1) { + idx = (s8)menu_change_val(idx, 0, trim_functions_max, 1, 1); + new_idx = trim_functions[idx]; + if (!new_idx) continue; // empty slot + new_idx--; // was one more + if (menu_et_function_is_allowed(new_idx)) break; // we have it + } + // set values to defaults + *func = new_idx; + break; + case 1: + // reverse + if (*rev) *rev = 0; + else *rev = 1; + break; + } + } + + else if (action == 2) { + // switch to next setting + if (menu_set || *func) { + if (++menu_set > 1) menu_set = 0; + } + } + + // show value of menu_set + switch (menu_set) { + case 0: + // function + lcd_chars(menu_et_function_name(*func)); + break; + case 1: + // reverse + lcd_chars("RE"); + lcd_char(LCHR3, (u8)(*rev ? '1' : '0')); + menu_blink &= (u8)~(MCB_CHR1 | MCB_CHR2); + break; + } +} + + #define KEY_FUNCTIONS_SIZE 32 @@ -362,8 +423,11 @@ static void km_key(u8 action) { @inline static void km_trim_key(u8 action) { - if (menu_id < NUM_TRIMS) km_trim(action); - else km_key(action); + if (menu_id < NUM_TRIMS) + km_trim(action); + else if (cg.ch3_pot && menu_id == NUM_TRIMS) + km_ch3_pot(action); + else km_key(action); } static const u8 key_ids[] = { @@ -409,11 +473,15 @@ void menu_key_mapping_func(u8 action, void *p) { km_trim_key(0); } +// temporary disable ch3 potentiometer when in key mapping menu +_Bool menu_ch3_pot_disabled; void menu_key_mapping(void) { lcd_set_blink(LMENU, LB_SPC); + menu_ch3_pot_disabled = 1; menu_common(menu_key_mapping_func, NULL, MCF_NONE); + menu_ch3_pot_disabled = 0; lcd_set_blink(LMENU, LB_OFF); config_model_save(); apply_model_config(); diff --git a/menu_popup.c b/menu_popup.c index 82568d8..e728ae0 100644 --- a/menu_popup.c +++ b/menu_popup.c @@ -269,6 +269,24 @@ u8 menu_et_function_is_allowed(u8 n) { return 0; // channel too big } +// set function value from linear channel value +// lin_val in range -5000..5000 +#define AVAL(x) \ + if (etf->set_func) etf->set_func(&x, SF_ROTATE); \ + *(s8 *)etf->aval = (s8)(x) +#define SF_ROTATE 0 +void menu_et_function_set_from_linear(u8 n, s16 lin_val) { + et_functions_s *etf = &et_functions[n]; + s16 val; + if (n == 0 || n >= ET_FUNCTIONS_SIZE) return; // OFF or bad + // map lin_val between min and max + val = (s16)((s32)(lin_val + PPM(500)) * (etf->max - etf->min + 1) + / PPM(1000)) + etf->min; + if (val > etf->max) val = etf->max; // lin_val was full right + AVAL(val); +} +#undef SF_ROTATE + // find function by name static et_functions_s *menu_et_function_find_name(u8 *name) { u8 i, *n; @@ -310,9 +328,6 @@ const u8 steps_map[STEPS_MAP_SIZE] = { #define RVAL(x) \ if (etf->min >= 0) x = *(u8 *)etf->aval; \ else x = *(s8 *)etf->aval; -#define AVAL(x) \ - if (etf->set_func) etf->set_func(&x, SF_ROTATE); \ - *(s8 *)etf->aval = (s8)(x) static u8 menu_popup_et(u8 trim_id) { u16 delay_time; s16 val; diff --git a/menu_service.c b/menu_service.c index 50412f0..8fbec93 100644 --- a/menu_service.c +++ b/menu_service.c @@ -64,6 +64,13 @@ void menu_calibrate(u8 at_poweron) { lcd_7seg(channel); lcd_menu(LM_MODEL | LM_NAME | LM_REV | LM_TRIM | LM_DR | LM_EXP); lcd_set_blink(LMENU, LB_SPC); + // blink arrows for ch3 potentiometer also + if (cg.ch3_pot) { + lcd_segment(LS_SYM_LEFT, LS_ON); + lcd_segment(LS_SYM_RIGHT, LS_ON); + lcd_segment_blink(LS_SYM_LEFT, LB_SPC); + lcd_segment_blink(LS_SYM_RIGHT, LB_SPC); + } while (1) { // check keys @@ -79,7 +86,7 @@ void menu_calibrate(u8 at_poweron) { } else if (btn(BTN_ENTER)) { - // save calibrate value for channels 1 and 2 + // save calibrate value for channels 1 and 2 (and 3 if ch3_pot) // select actual voltage for channel 4 if (channel == 1) { key_beep(); @@ -117,6 +124,21 @@ void menu_calibrate(u8 at_poweron) { lcd_segment(seg, LS_OFF); // set corresponding LCD off lcd_update(); } + else if (channel == 3 && cg.ch3_pot) { + // only if ch3 is potentiometer + key_beep(); + val = ADC_OVS(ch3); + if (val < 512) { + cg.calib_ch3_left = val; + seg = LS_SYM_LEFT; + } + else { + cg.calib_ch3_right = val; + seg = LS_SYM_RIGHT; + } + lcd_segment(seg, LS_OFF); // set corresponding LCD off + lcd_update(); + } else if (channel == 4) { key_beep(); // allow to set actual battery voltage