コード例 #1
0
ファイル: menu.c プロジェクト: Andyboby/gt3b
// show model number, extra function to handle more than 10 models
static void show_model_number(u8 model) {
    lcd_7seg((u8)(model % 10));
    lcd_segment(LS_SYM_RIGHT, (u8)((u8)(model / 10) & 1));
    lcd_segment(LS_SYM_LEFT, (u8)((u8)(model / 20) & 1));
    lcd_segment(LS_SYM_PERCENT, (u8)((u8)(model / 40) & 1));
    lcd_segment(LS_SYM_MODELNO, LS_ON);
}
コード例 #2
0
ファイル: menu_mix.c プロジェクト: Micha500/gt3b
static void mix_brake_off(u8 action) {
    // change value
    if (action == MLA_CHG)  cm.brake_off ^= 1;

    // show value
    lcd_7seg(L7_B);
    lcd_chars(cm.brake_off ? "CUT" : "OFF");
}
コード例 #3
0
ファイル: menu_global.c プロジェクト: losikid/gt3b
// show firmware version
static void gs_firmware(u8 change) {
    if (change == 0xff) {
	lcd_set(L7SEG, LB_EMPTY);
	return;
    }
    lcd_7seg(L7_F);
    lcd_chars(VERSION);
}
コード例 #4
0
ファイル: menu.c プロジェクト: Micha500/gt3b
// set channel value, exclude 4WS and DIG channels
//   for channels 3..8 so add 2 to channel number
static void sf_channel_val(u8 channel, u8 change) {
    s8 *addr = &menu_channel3_8[channel];
    if (change && !(menu_channels_mixed & (u8)(1 << (channel + 2))))
	*addr = (s8)menu_change_val(*addr, -100, 100, CHANNEL_FAST, 0);

    // show value
    lcd_7seg((u8)(channel + 2 + 1));
    lcd_char_num3(*addr);
}
コード例 #5
0
ファイル: menu_global.c プロジェクト: losikid/gt3b
static void gs_key_beep(u8 change) {
    if (change == 0xff) {
	lcd_set(L7SEG, LB_EMPTY);
	return;
    }
    if (change)  cg.key_beep ^= 1;
    lcd_7seg(L7_B);
    if (cg.key_beep)  lcd_chars("ON ");
    else              lcd_chars("OFF");
}
コード例 #6
0
ファイル: menu_global.c プロジェクト: losikid/gt3b
static void gs_long_press_delay(u8 change) {
    u8 *addr = &cg.long_press_delay;
    if (change == 0xff) {
	lcd_set(L7SEG, LB_EMPTY);
	return;
    }
    if (change)  *addr = (u8)menu_change_val(*addr, 20, 200, 5, 0);
    lcd_7seg(L7_D);
    lcd_char_num3(*addr * 5);
}
コード例 #7
0
ファイル: menu_global.c プロジェクト: losikid/gt3b
static void gs_throttle_dead(u8 change) {
    u8 *addr = &cg.throttle_dead_zone;
    if (change == 0xff) {
	lcd_set(L7SEG, LB_EMPTY);
	return;
    }
    if (change)  *addr = (u8)menu_change_val(*addr, 0, 50, 2, 0);
    lcd_7seg(2);
    lcd_char_num3(*addr);
}
コード例 #8
0
ファイル: menu.c プロジェクト: Micha500/gt3b
static void menu_abs_func(u8 action, void *p) {
    // change value
    if (action == MCA_SET_CHG)
	cm.abs_type = (u8)menu_change_val(cm.abs_type, 0, ABS_LABEL_SIZE-1, 1, 1);
    
    // show value
    lcd_segment(LS_SYM_CHANNEL, LS_ON);
    lcd_7seg(2);
    lcd_chars(abs_labels[cm.abs_type]);
}
コード例 #9
0
ファイル: menu.c プロジェクト: Micha500/gt3b
// set number of model channels
static void menu_channels(u8 action) {
    // change value
    if (action == MLA_CHG)
	cm.channels = (u8)(menu_change_val(cm.channels + 1, 2,
					   MAX_CHANNELS, 1, 0) - 1);

    // show value
    lcd_7seg(L7_C);
    lcd_segment(LS_SYM_CHANNEL, LS_ON);
    lcd_char_num3(cm.channels + 1);
}
コード例 #10
0
ファイル: menu_popup.c プロジェクト: Andyboby/gt3b
// change 4WS crab/no-crab
static void kf_4ws(u8 *id, u8 *param, u8 flags, s16 *prev_val) {
    if (flags & FF_ON)
	menu_4WS_crab = (u8)(flags & FF_REVERSE ? 0 : 1);
    else
	menu_4WS_crab = (u8)(flags & FF_REVERSE ? 1 : 0);

    if (flags & FF_SHOW) {
	lcd_7seg(4);
	lcd_chars(menu_4WS_crab ? "CRB" : "NOC");
    }
}
コード例 #11
0
ファイル: menu_popup.c プロジェクト: Andyboby/gt3b
// change menu_brake bit
static void kf_brake(u8 *id, u8 *param, u8 flags, s16 *prev_val) {
    if (flags & FF_ON)
	menu_brake = (u8)(flags & FF_REVERSE ? 0 : 1);
    else
	menu_brake = (u8)(flags & FF_REVERSE ? 1 : 0);

    if (flags & FF_SHOW) {
	lcd_7seg(L7_B);
	lcd_chars(menu_brake ? "BRK" : "OFF");
    }
}
コード例 #12
0
ファイル: menu_popup.c プロジェクト: Andyboby/gt3b
// multi-position show and set value
static void show_MP(u8 *name, s16 val) {
    u8 mp_id = (u8)(name[2] - '1');
    s8 *multi_position;
    u8 channel_MP;
    u8 num_MP = config_get_MP(mp_id, &channel_MP, &multi_position);

    // show also selected channel/DIG
    if (channel_MP == MP_DIG) {
	lcd_7seg(L7_D);
	lcd_menu(LM_EPO);
	lcd_set_blink(LMENU, LB_SPC);
	lcd_segment(LS_SYM_CHANNEL, LS_OFF);
	lcd_segment(LS_SYM_PERCENT, LS_ON);
    }
    else {
	lcd_7seg(channel_MP);
	lcd_segment(LS_SYM_CHANNEL, LS_ON);
    }
    lcd_char_num3(multi_position[menu_MP_index[mp_id]]);
}
コード例 #13
0
ファイル: menu_popup.c プロジェクト: Andyboby/gt3b
// show functions ID
static void menu_et_function_show_id(et_functions_s *etf) {
    lcd_menu(etf->menu);
    if (etf->flags & EF_BLINK) lcd_set_blink(LMENU, LB_SPC);
    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, (u8)(etf->flags & EF_PERCENT ? LS_ON : LS_OFF));
    lcd_segment(LS_SYM_LEFT, (u8)(etf->flags & EF_LEFT ? LS_ON : LS_OFF));
    lcd_segment(LS_SYM_RIGHT, (u8)(etf->flags & EF_RIGHT ? LS_ON : LS_OFF));
    lcd_segment(LS_SYM_CHANNEL, (u8)(etf->flags & EF_NOCHANNEL ? LS_OFF : LS_ON));
    lcd_7seg(etf->channel);
}
コード例 #14
0
ファイル: menu_mix.c プロジェクト: Micha500/gt3b
static void mix_4WS(u8 action) {
    u8 val;

    if (action == MLA_CHG) {
	// change value
	switch (menu_set) {
	    case 0:
		// channel number/off
		val = cm.channel_4WS;
		if (!val)  val = 2;
		val = (u8)menu_change_val(val, 2, channels, 1, 1);
		if (val == 2)   cm.channel_4WS = 0;
		else		cm.channel_4WS = val;
		break;
	    case 1:
		// mix value
		menu_4WS_mix = (s8)menu_change_val(menu_4WS_mix, -100, 100,
						   MIX_FAST, 0);
		break;
	    case 2:
		// crab/no-crab
		menu_4WS_crab ^= 1;
	}
    }
    else if (action == MLA_NEXT) {
	// select next value
	if (++menu_set > 2)   menu_set = 0;
	if (!cm.channel_4WS)  menu_set = 0;
    }

    // show value
    lcd_7seg(4);
    switch (menu_set) {
	case 0:
	    // channel number/OFF
	    if (!cm.channel_4WS)  lcd_chars("OFF");
	    else		  lcd_char_num3(cm.channel_4WS);
	    lcd_segment(LS_SYM_CHANNEL, LS_ON);
	    break;
	case 1:
	    // mix value
	    lcd_char_num3(menu_4WS_mix);
	    lcd_segment(LS_SYM_PERCENT, LS_ON);
	    break;
	case 2:
	    // crab/no-crab
	    lcd_chars("CR");
	    lcd_char(LCHR3, (u8)(menu_4WS_crab + '0'));
	    menu_blink &= (u8)~(MCB_CHR1 | MCB_CHR2);
    }
}
コード例 #15
0
ファイル: menu_global.c プロジェクト: losikid/gt3b
static void gs_backlight_time(u8 change) {
    s8 i;
    u16 *addr = &cg.backlight_time;

    if (change == 0xff) {
	lcd_set(L7SEG, LB_EMPTY);
	return;
    }
    if (change) {
	if (btn(BTN_ROT_L)) {
	    // find lower value
	    for (i = BL_STEPS_SIZE - 1; i >= 0; i--) {
		if (bl_steps[i] >= *addr)  continue;
		*addr = bl_steps[i];
		break;
	    }
	    if (i < 0)
		*addr = bl_steps[BL_STEPS_SIZE - 1];
	}
	else {
	    // find upper value
	    for (i = 0; i < BL_STEPS_SIZE; i++) {
		if (bl_steps[i] <= *addr)  continue;
		*addr = bl_steps[i];
		break;
	    }
	    if (i == BL_STEPS_SIZE)
		*addr = bl_steps[0];
	}
    }
    lcd_7seg(L7_L);
    if (*addr < 60) {
	// seconds
	bl_num2((u8)*addr);
	lcd_char(LCHR3, 'S');
    }
    else if (*addr < 3600) {
	// minutes
	bl_num2((u8)(*addr / 60));
	lcd_char(LCHR3, 'M');
    }
    else if (*addr != BACKLIGHT_MAX) {
	// hours
	bl_num2((u8)(*addr / 3600));
	lcd_char(LCHR3, 'H');
    }
    else {
	// max
	lcd_chars("MAX");
    }
}
コード例 #16
0
ファイル: menu_global.c プロジェクト: losikid/gt3b
static void gs_inactivity_alarm(u8 change) {
    if (change == 0xff) {
	lcd_set(L7SEG, LB_EMPTY);
	return;
    }
    if (change) {
	cg.inactivity_alarm = (u8)menu_change_val(cg.inactivity_alarm, 0, 10,
						  1, 1);
	reset_inactivity_timer();
    }
    lcd_7seg(L7_A);
    if (!cg.inactivity_alarm)  lcd_chars("OFF");
    else {
	bl_num2(cg.inactivity_alarm);
	lcd_char(LCHR3, 'M');
    }
}
コード例 #17
0
ファイル: menu_global.c プロジェクト: losikid/gt3b
static void gs_reset_model_all(u8 change) {
    if (change == 0xff) {
	lcd_set(L7SEG, LB_EMPTY);
	if (gs_reset_flag) {
	    gs_reset_flag = 0;
	    cg.model = 0;
	    config_global_save();
	    eeprom_empty_models();
	    menu_load_model();
	}
	return;
    }
    if (change)  gs_reset_flag ^= 1;
    lcd_7seg(L7_R);
    if (gs_reset_flag)	lcd_chars("YES");
    else		lcd_chars("MOD");
}
コード例 #18
0
ファイル: menu.c プロジェクト: Micha500/gt3b
// reset model to defaults
static void menu_reset_model(u8 action) {
    // change value
    if (action == MLA_CHG)  menu_tmp_flag ^= 1;

    // select next value, reset when flag is set
    else if (action == MLA_NEXT) {
	if (menu_tmp_flag) {
	    menu_tmp_flag = 0;
	    config_model_set_default();
	    buzzer_on(60, 0, 1);
	}
    }

    // show value
    lcd_7seg(L7_R);
    lcd_chars(menu_tmp_flag ? "YES" : "NO ");
}
コード例 #19
0
ファイル: menu_global.c プロジェクト: losikid/gt3b
static void gs_endpoint_max(u8 change) {
    u8 *addr = &cg.endpoint_max;
    if (change == 0xff) {
	lcd_segment(LS_MENU_EPO, LS_OFF);
	lcd_segment(LS_SYM_PERCENT, LS_OFF);
	lcd_set(L7SEG, LB_EMPTY);
	return;
    }
    if (change)  *addr = (u8)menu_change_val(*addr, 100, 150, 5, 0);
    lcd_segment(LS_SYM_PERCENT, LS_ON);
    lcd_segment(LS_MENU_EPO, LS_ON);
    lcd_char_num3(*addr);
    if (*addr > 120) {
	lcd_7seg(L7_D);
	lcd_set_blink(L7SEG, LB_SPC);
    }
    else lcd_set(L7SEG, LB_EMPTY);
}
コード例 #20
0
ファイル: menu_mix.c プロジェクト: Micha500/gt3b
static void mix_DIG(u8 action) {
    u8 val;

    if (action == MLA_CHG) {
	// change value
	switch (menu_set) {
	    case 0:
		// channel number/off
		val = cm.channel_DIG;
		if (!val)  val = 2;
		val = (u8)menu_change_val(val, 1, channels, 1, 1);
		if (val == 2)   cm.channel_DIG = 0;
		else		cm.channel_DIG = val;
		break;
	    case 1:
		// mix value
		menu_DIG_mix = (s8)menu_change_val(menu_DIG_mix, -100, 100,
						   MIX_FAST, 0);
		break;
	}
    }
    else if (action == MLA_NEXT) {
	// select next value
	if (++menu_set > 1)   menu_set = 0;
	if (!cm.channel_DIG)  menu_set = 0;
    }

    // show value
    lcd_7seg(L7_D);
    switch (menu_set) {
	case 0:
	    // channel number/OFF
	    if (!cm.channel_DIG)  lcd_chars("OFF");
	    else		  lcd_char_num3(cm.channel_DIG);
	    lcd_segment(LS_SYM_CHANNEL, LS_ON);
	    break;
	case 1:
	    // mix value
	    lcd_char_num3(menu_DIG_mix);
	    lcd_segment(LS_SYM_PERCENT, LS_ON);
	    break;
    }
}
コード例 #21
0
ファイル: menu_service.c プロジェクト: Micha500/gt3b
void menu_key_test(void) {
    u8 i;
    u16 bit;
    
    // cleanup screen and disable possible low bat warning
    buzzer_off();
    key_beep();
    menu_battery_low = 0;	// it will be set automatically again
    battery_low_shutup = 0;

    // do full screen blink
    lcd_set_full_on();
    delay_menu_always(2);
    while (btns(BTN_ENTER))  stop();  // wait for release of ENTER
    lcd_clear();

    button_autorepeat(0);	// disable autorepeats
    btnra();

    // show intro text
    lcd_chars("KEY");
    lcd_update_stop();		// wait for key

    while (1) {
	if (btnl(BTN_BACK | BTN_ENTER))  break;

	for (i = 0, bit = 1; i < 16; i++, bit <<= 1) {
	    if (btn(bit)) {
		key_beep();
		lcd_chars(key_ids[i]);
		if (btnl(bit))  lcd_7seg(L7_L);
		else lcd_set(L7SEG, LB_EMPTY);
		lcd_update();
		break;
	    }
	}
	btnra();
	stop();
    }
    key_beep();
    apply_model_config();
}
コード例 #22
0
ファイル: menu_key.c プロジェクト: Micha500/gt3b
void menu_key_mapping_func(u8 action, void *p) {
    if (action == MCA_SET_CHG) {
	km_trim_key(1);
    }
    else if (action == MCA_SET_NEXT) {
	km_trim_key(2);
    }
    else if (action == MCA_ID_CHG) {
	while (1) {
	    // select prev/next menu_id
	    menu_id = (u8)menu_change_val(menu_id, 0,
			    3 * NUM_TRIMS + NUM_KEYS - 1, 1, 1);
	    // trims and 3keys (CH3/BACK/END) always
	    if (menu_id < NUM_TRIMS + NUM_KEYS)  break;
	    // check trim keys and use them only when corresponding
	    //   trim is off
	    if (ck.key_map[menu_id - NUM_TRIMS].is_trim)  continue;
	    break;
	}
    }

    // show value
    if (menu_id < NUM_TRIMS + NUM_KEYS)
	// standard trims and buttons
	lcd_7seg(key_ids[menu_id]);
    else {
	// trims as buttons, use arrows to show them
	lcd_7seg(key_ids[(u8)((u8)(menu_id - NUM_TRIMS - NUM_KEYS) >> 1)]);
	if ((u8)(menu_id - NUM_TRIMS - NUM_KEYS) & 1)
	    // right trim key
	    lcd_segment(LS_SYM_RIGHT, LS_ON);
	else
	    // left trim key
	    lcd_segment(LS_SYM_LEFT, LS_ON);
    }
    if (action != MCA_SET_NEXT && action != MCA_SET_CHG)
	km_trim_key(0);
}
コード例 #23
0
ファイル: menu.c プロジェクト: Micha500/gt3b
static void menu_channel_func(u8 action, menu_channel_t *p) {
    switch (action) {
	case MCA_INIT:
	    menu_set_adc_force(menu_id, p->use_adc, p->forced_values);
	    break;
	case MCA_SET_CHG:
	    p->func(menu_id, 1);
	    break;
	case MCA_ID_CHG:
	    menu_id = (u8)menu_change_val(menu_id, 0, p->end_channel - 1, 1, 1);
	    menu_set_adc_force(menu_id, p->use_adc, p->forced_values);
	    break;
	case MCA_ADC_PRE:
	    menu_set_adc_direction(menu_id);
	    return;	// show nothing at ADC_PRE
	case MCA_ADC_POST:
	    // do nothing if left-right didn't changed
	    if (p->last_direction == menu_adc_direction)  return;
	    // else flag it to show value
	    menu_id_set = 1;	// flag that new value is showed
	    return;
    }

    // show value
    lcd_segment(LS_SYM_CHANNEL, LS_ON);
    if (action != MCA_SET_CHG) {  // already showed
	lcd_7seg((u8)(menu_id + 1));
	p->func(menu_id, 0);
    }
    if (menu_adc_wakeup) {
	// show arrow
	if (menu_adc_direction)
	    lcd_segment(LS_SYM_RIGHT, LS_ON);
	else
	    lcd_segment(LS_SYM_LEFT, LS_ON);
    }
    p->last_direction = menu_adc_direction;
}
コード例 #24
0
ファイル: menu_service.c プロジェクト: Micha500/gt3b
// calibrate menu
void menu_calibrate(u8 at_poweron) {
    u8 channel = 1;
    u16 last_val = 0xffff;
    u16 val;
    u8 seg;
    u8 bat_volts;
    u16 update_time = 0;
    u16 update_val = 0;

    menu_adc_wakeup = 1;

    // cleanup screen and disable possible low bat warning
    buzzer_off();
    if (at_poweron)	buzzer_on(30, 30, 2);
    else		key_beep();
    menu_battery_low = 0;	// it will be set automatically again
    battery_low_shutup = 0;
    backlight_set_default(BACKLIGHT_MAX);
    backlight_on();
    lcd_clear();

    btnra();

    // show intro text
    lcd_chars("CAL");
    lcd_update();
    delay_menu_always(2);

    // show channel number and not-yet calibrated values
    lcd_segment(LS_SYM_CHANNEL, LS_ON);
    lcd_7seg(channel);
    lcd_menu(LM_MODEL | LM_NAME | LM_REV | LM_TRIM | LM_DR | LM_EXP);
    lcd_set_blink(LMENU, LB_SPC);

    while (1) {
	// check keys
	if (btnl(BTN_BACK | BTN_ENTER))  break;

	if (btn(BTN_END | BTN_ROT_ALL)) {
	    if (btn(BTN_END))  key_beep();
	    // change channel number
	    channel = (u8)menu_change_val(channel, 1, 4, 1, 1);
	    lcd_7seg(channel);
	    lcd_update();
	    update_time = 0;
	}

	else if (btn(BTN_ENTER)) {
	    // save calibrate value for channels 1 and 2
	    // select actual voltage for channel 4
	    if (channel == 1) {
		key_beep();
		val = ADC_OVS(steering);
		if (val < CALIB_ST_LOW_MID) {
		    cg.calib_steering_left = val;
		    seg = LS_MENU_MODEL;
		}
		else if (val <= CALIB_ST_MID_HIGH) {
		    cg.calib_steering_mid = val;
		    seg = LS_MENU_NAME;
		}
		else {
		    cg.calib_steering_right = val;
		    seg = LS_MENU_REV;
		}
		lcd_segment(seg, LS_OFF);
		lcd_update();
	    }
	    else if (channel == 2) {
		key_beep();
		val = ADC_OVS(throttle);
		if (val < CALIB_TH_LOW_MID) {
		    cg.calib_throttle_fwd = val;
		    seg = LS_MENU_TRIM;
		}
		else if (val <= CALIB_TH_MID_HIGH) {
		    cg.calib_throttle_mid = val;
		    seg = LS_MENU_DR;
		}
		else {
		    cg.calib_throttle_bck = val;
		    seg = LS_MENU_EXP;
		}
		lcd_segment(seg, LS_OFF);  // set corresponding LCD off
		lcd_update();
	    }
	    else if (channel == 4) {
		key_beep();
		// allow to set actual battery voltage
		lcd_segment(LS_SYM_DOT, LS_ON);
		lcd_segment(LS_SYM_VOLTS, LS_ON);
		bat_volts = (u8)(((u32)adc_battery * 100 + 300) / cg.battery_calib);
		lcd_char_num3(bat_volts);
		lcd_update();

		while (1) {
		    btnra();
		    stop();

		    if (btnl(BTN_BACK) || btn(BTN_ENTER | BTN_END))  break;
		    if (btn(BTN_ROT_ALL)) {
			if (btn(BTN_ROT_L))  bat_volts--;
			else                 bat_volts++;
			lcd_char_num3(bat_volts);
			lcd_update();
		    }
		}

		key_beep();
		lcd_segment(LS_SYM_DOT, LS_OFF);
		lcd_segment(LS_SYM_VOLTS, LS_OFF);
		last_val = 0xffff;	// show ADC value
		if (!btn(BTN_END)) {
		    // recalculate calibrate value for 10V
		    cg.battery_calib = (u16)(((u32)adc_battery * 100 + 40) / bat_volts);
		    if (btnl(BTN_BACK | BTN_ENTER))  break;
		}
	    }
	}

	// show ADC value if other than last val
	switch (channel) {
	    case 1:
		val = ADC_OVS(steering);
		break;
	    case 2:
		val = ADC_OVS(throttle);
		break;
	    case 3:
		val = ADC_OVS(ch3);
		break;
	    case 4:
		val = adc_battery;
		break;
	    default:		// to eliminate compiler warning
		val = 0;
	}
	// only update display every 1s
	if (time_sec >= update_time) {
	    update_time = time_sec + 1;
	    update_val = val;
	}
	if (update_val != last_val) {
	    last_val = update_val;
	    lcd_char_num3(val);
	    lcd_update();
	}

	btnra();
	stop();
    }

    menu_adc_wakeup = 0;
    beep(60);
    lcd_menu(0);
    lcd_update();
    config_global_save();
    apply_global_config();
}
コード例 #25
0
ファイル: menu.c プロジェクト: Micha500/gt3b
// show model number, extra function to handle more than 10 models
static void show_model_number(u8 model) {
    lcd_7seg((u8)(model % 10));
    lcd_segment(LS_SYM_RIGHT, (u8)((u8)(model / 10) & 1));
    lcd_segment(LS_SYM_LEFT, (u8)((u8)(model / 20) & 1));
}
コード例 #26
0
ファイル: menu_mix.c プロジェクト: Micha500/gt3b
static void mix_MultiPosition(u8 action) {
    s8 val;

    if (action == MLA_CHG) {
	// change value
	if (menu_set == 0) {
	    // channel number/off
	    val = cm.channel_MP;
	    if (!val)  val = 2;
	    else if (val == MP_DIG)  val = (s8)(channels + 1);
	    val = (u8)menu_change_val(val, 2, channels + 1, 1, 1);
	    if (val == 2)   			cm.channel_MP = 0;
	    else if (val == (s8)(channels + 1))	cm.channel_MP = MP_DIG;
	    else	    			cm.channel_MP = val;
	}
	else {
	    // position value + END state (END not for first position)
	    val = cm.multi_position[menu_set - 1];
	    if (val == MULTI_POSITION_END)  val = -101;
	    val = (s8)menu_change_val(val, menu_set == 1 ? -100 : -101, 100,
				      CHANNEL_FAST, 0);
	    if (val == -101) {
		// set all from this to END value
		memset(&cm.multi_position[menu_set - 1], (u8)MULTI_POSITION_END,
		       NUM_MULTI_POSITION + 1 - menu_set);
	    }
	    else cm.multi_position[menu_set - 1] = val;
	}
    }
    else if (action == MLA_NEXT) {
	// select next value
	if (cm.channel_MP) {
	    if (menu_set == 0)  menu_set = 1;
	    else if (cm.multi_position[menu_set - 1] == MULTI_POSITION_END
		    || ++menu_set > NUM_MULTI_POSITION)  menu_set = 0;
	}
	// allow forcing channel value
	if (menu_set && cm.channel_MP && cm.channel_MP <= channels) {
	    menu_force_value_channel = cm.channel_MP;
	}
	else menu_force_value_channel = 0;
    }

    // show value
    lcd_7seg(L7_P);
    if (menu_set == 0) {
	// channel number/OFF
	if (!cm.channel_MP)	lcd_chars("OFF");
	else if (cm.channel_MP == MP_DIG)
				lcd_chars("DIG");
	else			lcd_char_num3(cm.channel_MP);
	lcd_segment(LS_SYM_CHANNEL, LS_ON);
    }
    else {
	// position value
	val = cm.multi_position[menu_set - 1];
	if (val == MULTI_POSITION_END) {
	    lcd_chars("END");
	    val = -100;
	}
	else  lcd_char_num3(val);
	if (cm.channel_MP == MP_DIG)	menu_DIG_mix = val;
	else				menu_force_value = val * PPM(5);
    }
}