From ba713b6b8d032d532a627b4d8231f612c43bacdf Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 19 Oct 2022 12:36:29 +0200 Subject: [PATCH] feat: Further improve rotary encoder acceleration (#2584) * Improve rotary encoder acceleration Fixes #2417 * Further improvement * Fix simu compilation --- radio/src/gui/colorlcd/LvglWrapper.cpp | 42 ++++++----- radio/src/gui/colorlcd/color_editor.cpp | 10 +++ radio/src/gui/colorlcd/gvar_numberedit.h | 3 + radio/src/gui/colorlcd/model_gvars.cpp | 3 + radio/src/gui/colorlcd/output_edit.cpp | 20 ++++-- radio/src/gui/colorlcd/timer_setup.cpp | 1 + .../arm/stm32/rotary_encoder_driver.cpp | 72 ++++++++++--------- radio/src/targets/simu/simpgmspace.cpp | 1 + radio/src/thirdparty/libopenui | 2 +- 9 files changed, 98 insertions(+), 56 deletions(-) diff --git a/radio/src/gui/colorlcd/LvglWrapper.cpp b/radio/src/gui/colorlcd/LvglWrapper.cpp index 4672d6b11..e011f7ef8 100644 --- a/radio/src/gui/colorlcd/LvglWrapper.cpp +++ b/radio/src/gui/colorlcd/LvglWrapper.cpp @@ -232,16 +232,18 @@ extern "C" void touchDriverRead(lv_indev_drv_t *drv, lv_indev_data_t *data) } #if defined(ROTARY_ENCODER_NAVIGATION) +extern volatile uint32_t rotencDt; +static int8_t _rotary_enc_accel = 0; + static void rotaryDriverRead(lv_indev_drv_t *drv, lv_indev_data_t *data) { static rotenc_t prevPos = 0; static int8_t prevDir = 0; - static int8_t accel = 0; - static tmr10ms_t lastEvent = 0; + static uint32_t lastDt = 0; - rotenc_t newPos = (ROTARY_ENCODER_NAVIGATION_VALUE / ROTARY_ENCODER_GRANULARITY); - auto diff = newPos - prevPos; - prevPos = newPos; + rotenc_t newPos = ROTARY_ENCODER_NAVIGATION_VALUE; + rotenc_t diff = (newPos - prevPos) / ROTARY_ENCODER_GRANULARITY; + prevPos += diff * ROTARY_ENCODER_GRANULARITY; data->enc_diff = (int16_t)diff; data->state = LV_INDEV_STATE_RELEASED; @@ -264,23 +266,31 @@ static void rotaryDriverRead(lv_indev_drv_t *drv, lv_indev_data_t *data) else if (diff > 0) dir = 1; if (use_accel && (dir == prevDir)) { - auto speed = (g_tmr10ms - lastEvent) / abs(diff); - if (speed <= ROTENC_DELAY_HIGHSPEED/2) { // 80 ms - // below ROTENC_DELAY_HIGHSPEED btw. events: accelerate - accel = min((int8_t)(accel + 1), (int8_t)25); - } else if (speed >= ROTENC_DELAY_MIDSPEED/2) { // 160 ms - // above ROTENC_DELAY_MIDSPEED btw. events: normal speed - accel = 0; + auto dt = rotencDt - lastDt; + auto dx_dt = (abs(diff) * 50) / max(dt, (uint32_t)1); + + _rotary_enc_accel = (int8_t)dx_dt; + if (_rotary_enc_accel > 0) { + data->enc_diff = (int16_t)diff * (int16_t)_rotary_enc_accel; } - lastEvent = g_tmr10ms; - data->enc_diff = (int16_t)diff * max((int16_t)accel, (int16_t)1); } else { - accel = 0; + _rotary_enc_accel = 0; } + prevDir = dir; + lastDt = rotencDt; } } -#endif + +// libopenui_depends.h +int8_t rotaryEncoderGetAccel() { return _rotary_enc_accel; } + +#else // !defined(ROTARY_ENCODER_NAVIGATION) + +// libopenui_depends.h +int8_t rotaryEncoderGetAccel() { return 0; } + +#endif // defined(ROTARY_ENCODER_NAVIGATION) /** * Helper function to translate a colorFlags value to a lv_color_t suitable diff --git a/radio/src/gui/colorlcd/color_editor.cpp b/radio/src/gui/colorlcd/color_editor.cpp index c8959ba82..518d32084 100644 --- a/radio/src/gui/colorlcd/color_editor.cpp +++ b/radio/src/gui/colorlcd/color_editor.cpp @@ -74,12 +74,22 @@ void ColorBar::on_key(lv_event_t* e) uint32_t key = *(uint32_t*)lv_event_get_param(e); if (key == LV_KEY_LEFT) { if (bar->value > 0) { + auto accel = rotaryEncoderGetAccel(); bar->value--; + if (accel > 0) { + if (accel > bar->value) bar->value = 0; + else bar->value -= accel; + } lv_event_send(obj->parent, LV_EVENT_VALUE_CHANGED, nullptr); } } else if (key == LV_KEY_RIGHT) { if (bar->value < bar->maxValue) { + auto accel = rotaryEncoderGetAccel(); bar->value++; + if (accel > 0) { + if (accel < bar->maxValue - bar->value) bar->value += accel; + else bar->value = bar->maxValue; + } lv_event_send(obj->parent, LV_EVENT_VALUE_CHANGED, nullptr); } } diff --git a/radio/src/gui/colorlcd/gvar_numberedit.h b/radio/src/gui/colorlcd/gvar_numberedit.h index b0bbf5130..edc841acc 100644 --- a/radio/src/gui/colorlcd/gvar_numberedit.h +++ b/radio/src/gui/colorlcd/gvar_numberedit.h @@ -40,6 +40,9 @@ class GVarNumberEdit : public Window void switchGVarMode(); void setSuffix(std::string value); + void setFastStep(int value) { num_field->setFastStep(value); } + void setAccelFactor(int value) { num_field->setAccelFactor(value); } + protected: Choice* gvar_field = nullptr; NumberEdit* num_field = nullptr; diff --git a/radio/src/gui/colorlcd/model_gvars.cpp b/radio/src/gui/colorlcd/model_gvars.cpp index bbf737bd9..c5139664e 100644 --- a/radio/src/gui/colorlcd/model_gvars.cpp +++ b/radio/src/gui/colorlcd/model_gvars.cpp @@ -301,6 +301,7 @@ void GVarEditWindow::buildBody(FormWindow * window) setProperties(); } ); + min->setAccelFactor(16); grid.nextLine(); new StaticText(window, grid.getLabelSlot(), STR_MAX, 0, COLOR_THEME_PRIMARY1); @@ -312,6 +313,7 @@ void GVarEditWindow::buildBody(FormWindow * window) setProperties(); } ); + max->setAccelFactor(16); grid.nextLine(); char flightModeName[16]; FlightModeData * fmData; @@ -337,6 +339,7 @@ void GVarEditWindow::buildBody(FormWindow * window) values[flightMode] = new NumberEdit(window, grid.getFieldSlot(2, 1), GVAR_MIN + gvar->min, GVAR_MAX + MAX_FLIGHT_MODES - 1, GET_SET_DEFAULT(fmData->gvars[index])); + values[flightMode]->setAccelFactor(16); grid.nextLine(); } diff --git a/radio/src/gui/colorlcd/output_edit.cpp b/radio/src/gui/colorlcd/output_edit.cpp index 0294667f8..b3dc42920 100644 --- a/radio/src/gui/colorlcd/output_edit.cpp +++ b/radio/src/gui/colorlcd/output_edit.cpp @@ -137,8 +137,10 @@ void OutputEditWindow::buildBody(FormWindow* form) // Offset new StaticText(line, rect_t{}, TR_LIMITS_HEADERS_SUBTRIM, 0, COLOR_THEME_PRIMARY1); - new GVarNumberEdit(line, rect_t{}, -LIMIT_STD_MAX, +LIMIT_STD_MAX, - GET_SET_DEFAULT(output->offset), PREC1); + auto off = new GVarNumberEdit(line, rect_t{}, -LIMIT_STD_MAX, +LIMIT_STD_MAX, + GET_SET_DEFAULT(output->offset), PREC1); + off->setFastStep(20); + off->setAccelFactor(8); // Min line = form->newLine(&grid); @@ -146,12 +148,16 @@ void OutputEditWindow::buildBody(FormWindow* form) minEdit = new GVarNumberEdit(line, rect_t{}, -limit, 0, GET_SET_DEFAULT(output->min), PREC1, -LIMIT_STD_MAX); minText->setBackgroundColor(COLOR_THEME_ACTIVE); + minEdit->setFastStep(20); + minEdit->setAccelFactor(16); // Max maxText = new StaticText(line, rect_t{}, TR_MAX, 0, COLOR_THEME_PRIMARY1); maxEdit = new GVarNumberEdit(line, rect_t{}, 0, +limit, GET_SET_DEFAULT(output->max), PREC1, +LIMIT_STD_MAX); maxText->setBackgroundColor(COLOR_THEME_ACTIVE); + maxEdit->setFastStep(20); + maxEdit->setAccelFactor(16); // Direction line = form->newLine(&grid); @@ -177,10 +183,12 @@ void OutputEditWindow::buildBody(FormWindow* form) lv_label_set_long_mode(label->getLvObj(), LV_LABEL_LONG_WRAP); lv_obj_set_style_grid_cell_x_align(label->getLvObj(), LV_GRID_ALIGN_STRETCH, 0); - new NumberEdit(line, rect_t{}, PPM_CENTER - PPM_CENTER_MAX, - PPM_CENTER + PPM_CENTER_MAX, - GET_VALUE(output->ppmCenter + PPM_CENTER), - SET_VALUE(output->ppmCenter, newValue - PPM_CENTER)); + auto center = new NumberEdit(line, rect_t{}, PPM_CENTER - PPM_CENTER_MAX, + PPM_CENTER + PPM_CENTER_MAX, + GET_VALUE(output->ppmCenter + PPM_CENTER), + SET_VALUE(output->ppmCenter, newValue - PPM_CENTER)); + center->setFastStep(20); + center->setAccelFactor(8); // Subtrims mode label = new StaticText(line, rect_t{}, TR_LIMITS_HEADERS_SUBTRIMMODE, 0, diff --git a/radio/src/gui/colorlcd/timer_setup.cpp b/radio/src/gui/colorlcd/timer_setup.cpp index 0a285261a..c0e0394aa 100644 --- a/radio/src/gui/colorlcd/timer_setup.cpp +++ b/radio/src/gui/colorlcd/timer_setup.cpp @@ -94,6 +94,7 @@ TimerWindow::TimerWindow(uint8_t timer) : Page(ICON_STATS_TIMERS) auto timerValue = new TimeEdit(line, rect_t{}, 0, TIMER_MAX, GET_DEFAULT(p_timer->start), timerValueUpdater(timer)); + timerValue->setAccelFactor(16); // Timer direction auto timerDirLine = form->newLine(&grid); diff --git a/radio/src/targets/common/arm/stm32/rotary_encoder_driver.cpp b/radio/src/targets/common/arm/stm32/rotary_encoder_driver.cpp index fc8265f68..08e6ae4ec 100644 --- a/radio/src/targets/common/arm/stm32/rotary_encoder_driver.cpp +++ b/radio/src/targets/common/arm/stm32/rotary_encoder_driver.cpp @@ -28,13 +28,11 @@ #include "opentx.h" #endif -static uint8_t rotencPosition; -volatile rotenc_t rotencValue; +volatile rotenc_t rotencValue = 0; +volatile uint32_t rotencDt = 0; void rotaryEncoderInit() { - rotencPosition = ROTARY_ENCODER_POSITION(); - ROTARY_ENCODER_TIMER->ARR = 99; // 100uS ROTARY_ENCODER_TIMER->PSC = (PERI1_FREQUENCY * TIMER_MULT_APB1) / 1000000 - 1; // 1uS ROTARY_ENCODER_TIMER->CCER = 0; @@ -90,50 +88,58 @@ void rotaryEncoderInit() void rotaryEncoderCheck() { -#if (defined(RADIO_FAMILY_T16) && !defined(RADIO_T18)) || defined(RADIO_TX12) static uint8_t state = 0; uint8_t pins = ROTARY_ENCODER_POSITION(); +#if (defined(RADIO_FAMILY_T16) && !defined(RADIO_T18)) || defined(RADIO_TX12) if (pins != (state & 0x03) && !(readKeys() & (1 << KEY_ENTER))) { - if ((pins ^ (state & 0x03)) == 0x03) - { - if (pins == 3) - { - rotencValue += INC_ROT_2; - } - else - { - rotencValue -= INC_ROT_2; - } - } - else - { - if ((state & 0x01) ^ ((pins & 0x02) >> 1)) - { - rotencValue -= INC_ROT; - } - else - { - rotencValue += INC_ROT; - } - } + if ((pins ^ (state & 0x03)) == 0x03) { + if (pins == 3) { + rotencValue += INC_ROT_2; + } else { + rotencValue -= INC_ROT_2; + } + } else { + if ((state & 0x01) ^ ((pins & 0x02) >> 1)) { + rotencValue -= INC_ROT; + } else { + rotencValue += INC_ROT; + } + } state &= ~0x03; state |= pins; + } #else - uint8_t newPosition = ROTARY_ENCODER_POSITION(); - if (newPosition != rotencPosition && !(readKeys() & (1 << KEY_ENTER))) { + if (pins != state && !(readKeys() & (1 << KEY_ENTER))) { #if defined(RADIO_ZORRO) || defined(RADIO_TX12MK2) // def. rotation dir is inverse of other radios - if (!(rotencPosition & 0x01) ^ ((newPosition & 0x02) >> 1)) { + if (!(state & 0x01) ^ ((pins & 0x02) >> 1)) { #else - if ((rotencPosition & 0x01) ^ ((newPosition & 0x02) >> 1)) { + if ((state & 0x01) ^ ((pins & 0x02) >> 1)) { #endif rotencValue -= INC_ROT; } else { rotencValue += INC_ROT; } - rotencPosition = newPosition; -#endif + state = pins; } +#endif + +#if !defined(BOOT) && defined(COLORLCD) + static uint32_t last_tick = 0; + static rotenc_t last_value = 0; + + rotenc_t value = rotencValue; + rotenc_t diff = (value - last_value) / ROTARY_ENCODER_GRANULARITY; + + if (diff != 0) { + uint32_t now = RTOS_GET_MS(); + uint32_t dt = now - last_tick; + // pre-compute accumulated dt (dx/dt is done later in LVGL driver) + rotencDt += dt; + last_tick = now; + last_value += diff * ROTARY_ENCODER_GRANULARITY; + } +#endif } void rotaryEncoderStartDelay() diff --git a/radio/src/targets/simu/simpgmspace.cpp b/radio/src/targets/simu/simpgmspace.cpp index 1bca4a3e2..1d4d827f1 100644 --- a/radio/src/targets/simu/simpgmspace.cpp +++ b/radio/src/targets/simu/simpgmspace.cpp @@ -49,6 +49,7 @@ uint32_t telemetryErrors = 0; typedef int32_t rotenc_t; volatile rotenc_t rotencValue = 0; +volatile uint32_t rotencDt = 0; // TODO: remove all STM32 defs GPIO_TypeDef gpioa, gpiob, gpioc, gpiod, gpioe, gpiof, gpiog, gpioh, gpioi, gpioj; diff --git a/radio/src/thirdparty/libopenui b/radio/src/thirdparty/libopenui index 804ecd0e6..895e21d3f 160000 --- a/radio/src/thirdparty/libopenui +++ b/radio/src/thirdparty/libopenui @@ -1 +1 @@ -Subproject commit 804ecd0e64bf0316493a8e06814474f0cda9557b +Subproject commit 895e21d3f23b441d0988dd9221e2e1d3ef5413e8