🚸 New encoder logic & debounce (#26723)

Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
This commit is contained in:
David Buezas
2024-05-09 23:20:57 +02:00
committed by GitHub
parent bab1917311
commit a3960dfa53
12 changed files with 284 additions and 364 deletions

View File

@@ -893,10 +893,11 @@
#endif
#endif
// FSMC/SPI TFT Panels (LVGL)
// FSMC/SPI TFT Panels (LVGL) with encoder click wheel
#if ENABLED(TFT_LVGL_UI)
#define HAS_TFT_LVGL_UI 1
#define SERIAL_RUNTIME_HOOK 1
#define STD_ENCODER_PULSES_PER_STEP 4
#endif
// FSMC/SPI TFT Panels
@@ -976,6 +977,17 @@
#define DETECT_I2C_LCD_DEVICE 1
#endif
/**
* Ender-3 V2 DWIN with Encoder
*/
#if ANY(DWIN_CREALITY_LCD, DWIN_LCD_PROUI)
#define HAS_DWIN_E3V2_BASIC 1
#endif
#if ANY(HAS_DWIN_E3V2_BASIC, DWIN_CREALITY_LCD_JYERSUI)
#define HAS_DWIN_E3V2 1
#define STD_ENCODER_PULSES_PER_STEP 4
#endif
// Encoder behavior
#ifndef STD_ENCODER_PULSES_PER_STEP
#if ENABLED(TOUCH_SCREEN)
@@ -997,10 +1009,12 @@
#define ENCODER_FEEDRATE_DEADZONE 6
#endif
// Shift register panels
// ---------------------
// 2 wire Non-latching LCD SR from:
// https://github.com/fmalpartida/New-LiquidCrystal/wiki/schematics#user-content-ShiftRegister_connection
/**
* Shift register panels
* ---------------------
* 2 wire Non-latching LCD SR from:
* https://github.com/fmalpartida/New-LiquidCrystal/wiki/schematics#user-content-ShiftRegister_connection
*/
#if ENABLED(FF_INTERFACEBOARD)
#define SR_LCD_3W_NL // Non latching 3 wire shift register
#define IS_ULTIPANEL 1
@@ -1040,11 +1054,6 @@
#define EXTENSIBLE_UI
#endif
// Aliases for LCD features
#if ANY(DWIN_CREALITY_LCD, DWIN_LCD_PROUI, DWIN_CREALITY_LCD_JYERSUI)
#define HAS_DWIN_E3V2 1
#endif
// E3V2 extras
#if HAS_DWIN_E3V2 || IS_DWIN_MARLINUI
#define SERIAL_CATCHALL 0

View File

@@ -24,9 +24,9 @@
#include "../inc/MarlinConfig.h"
#if ((!HAS_ADC_BUTTONS && IS_NEWPANEL) || BUTTONS_EXIST(EN1, EN2)) && !IS_TFTGLCD_PANEL
#define HAS_ENCODER_WHEEL 1
#define HAS_MARLINUI_ENCODER 1
#endif
#if (HAS_ENCODER_WHEEL || ANY_BUTTON(ENC, BACK, UP, DOWN, LEFT, RIGHT)) && DISABLED(TOUCH_UI_FTDI_EVE)
#if (HAS_MARLINUI_ENCODER || ANY_BUTTON(ENC, BACK, UP, DOWN, LEFT, RIGHT)) && DISABLED(TOUCH_UI_FTDI_EVE)
#define HAS_DIGITAL_BUTTONS 1
#endif
#if !HAS_ADC_BUTTONS && (IS_RRW_KEYPAD || (HAS_WIRED_LCD && !IS_NEWPANEL))

View File

@@ -42,10 +42,6 @@
#include <stdlib.h>
#ifndef ENCODER_PULSES_PER_STEP
#define ENCODER_PULSES_PER_STEP 4
#endif
EncoderRate encoderRate;
// TODO: Replace with ui.quick_feedback
@@ -53,32 +49,12 @@ void Encoder_tick() {
TERN_(HAS_BEEPER, if (ui.sound_on) buzzer.click(10));
}
// Encoder initialization
void encoderConfiguration() {
#if BUTTON_EXISTS(EN1)
SET_INPUT_PULLUP(BTN_EN1);
#endif
#if BUTTON_EXISTS(EN2)
SET_INPUT_PULLUP(BTN_EN2);
#endif
#if BUTTON_EXISTS(ENC)
SET_INPUT_PULLUP(BTN_ENC);
#endif
#if HAS_BEEPER
SET_OUTPUT(BEEPER_PIN); // TODO: Use buzzer.h which already inits this
#endif
}
// Analyze encoder value and return state
EncoderState encoderReceiveAnalyze() {
const millis_t now = millis();
static uint8_t lastEncoderBits;
uint8_t newbutton = 0;
static signed char temp_diff = 0;
static int8_t temp_diff = 0; // Cleared on each full step, as configured
EncoderState temp_diffState = ENCODER_DIFF_NO;
if (BUTTON_PRESSED(EN1)) newbutton |= EN_A;
if (BUTTON_PRESSED(EN2)) newbutton |= EN_B;
if (BUTTON_PRESSED(ENC)) {
static millis_t next_click_update_ms;
if (ELAPSED(now, next_click_update_ms)) {
@@ -98,71 +74,47 @@ EncoderState encoderReceiveAnalyze() {
}
else return ENCODER_DIFF_NO;
}
if (newbutton != lastEncoderBits) {
switch (newbutton) {
case 0:
if (lastEncoderBits == 1) temp_diff++;
else if (lastEncoderBits == 2) temp_diff--;
break;
case 2:
if (lastEncoderBits == 0) temp_diff++;
else if (lastEncoderBits == 3) temp_diff--;
break;
case 3:
if (lastEncoderBits == 2) temp_diff++;
else if (lastEncoderBits == 1) temp_diff--;
break;
case 1:
if (lastEncoderBits == 3) temp_diff++;
else if (lastEncoderBits == 0) temp_diff--;
break;
}
lastEncoderBits = newbutton;
}
if (ABS(temp_diff) >= ENCODER_PULSES_PER_STEP) {
if (temp_diff > 0) temp_diffState = TERN(REVERSE_ENCODER_DIRECTION, ENCODER_DIFF_CCW, ENCODER_DIFF_CW);
else temp_diffState = TERN(REVERSE_ENCODER_DIRECTION, ENCODER_DIFF_CW, ENCODER_DIFF_CCW);
temp_diff += ui.get_encoder_delta();
const int8_t abs_diff = ABS(temp_diff);
if (abs_diff >= ENCODER_PULSES_PER_STEP) {
temp_diffState = temp_diff > 0
? TERN(REVERSE_ENCODER_DIRECTION, ENCODER_DIFF_CCW, ENCODER_DIFF_CW)
: TERN(REVERSE_ENCODER_DIRECTION, ENCODER_DIFF_CW, ENCODER_DIFF_CCW);
int32_t encoder_multiplier = 1;
#if ENABLED(ENCODER_RATE_MULTIPLIER)
millis_t ms = millis();
int32_t encoder_multiplier = 1;
const millis_t ms = millis();
// if must encoder rati multiplier
// Encoder rate multiplier
if (encoderRate.enabled) {
const float abs_diff = ABS(temp_diff),
encoderMovementSteps = abs_diff / (ENCODER_PULSES_PER_STEP);
if (encoderRate.lastEncoderTime) {
// Note that the rate is always calculated between two passes through the
// loop and that the abs of the temp_diff value is tracked.
const float encoderStepRate = encoderMovementSteps / float(ms - encoderRate.lastEncoderTime) * 1000;
if (ENCODER_100X_STEPS_PER_SEC > 0 && encoderStepRate >= ENCODER_100X_STEPS_PER_SEC)
encoder_multiplier = 100;
else if (ENCODER_10X_STEPS_PER_SEC > 0 && encoderStepRate >= ENCODER_10X_STEPS_PER_SEC)
encoder_multiplier = 10;
else if (ENCODER_5X_STEPS_PER_SEC > 0 && encoderStepRate >= ENCODER_5X_STEPS_PER_SEC)
encoder_multiplier = 5;
}
// Note that the rate is always calculated between two passes through the
// loop and that the abs of the temp_diff value is tracked.
const float encoderStepRate = ((float(abs_diff) / float(ENCODER_PULSES_PER_STEP)) * 1000.0f) / float(ms - encoderRate.lastEncoderTime);
encoderRate.lastEncoderTime = ms;
if (ENCODER_100X_STEPS_PER_SEC > 0 && encoderStepRate >= ENCODER_100X_STEPS_PER_SEC)
encoder_multiplier = 100;
else if (ENCODER_10X_STEPS_PER_SEC > 0 && encoderStepRate >= ENCODER_10X_STEPS_PER_SEC)
encoder_multiplier = 10;
else if (ENCODER_5X_STEPS_PER_SEC > 0 && encoderStepRate >= ENCODER_5X_STEPS_PER_SEC)
encoder_multiplier = 5;
}
#else
constexpr int32_t encoder_multiplier = 1;
#endif
// encoderRate.encoderMoveValue += (temp_diff * encoder_multiplier) / (ENCODER_PULSES_PER_STEP);
encoderRate.encoderMoveValue = (temp_diff * encoder_multiplier) / (ENCODER_PULSES_PER_STEP);
if (encoderRate.encoderMoveValue < 0) encoderRate.encoderMoveValue = -encoderRate.encoderMoveValue;
encoderRate.encoderMoveValue = abs_diff * encoder_multiplier / (ENCODER_PULSES_PER_STEP);
temp_diff = 0;
}
if (temp_diffState != ENCODER_DIFF_NO) {
TERN_(HAS_BACKLIGHT_TIMEOUT, ui.refresh_backlight_timeout());
if (!ui.backlight) ui.refresh_brightness();
}
return temp_diffState;
}

View File

@@ -47,9 +47,6 @@ typedef enum {
#define ENCODER_WAIT_MS TERN(DWIN_LCD_PROUI, 10, 20)
// Encoder initialization
void encoderConfiguration();
// Analyze encoder value and return state
EncoderState encoderReceiveAnalyze();

View File

@@ -4078,7 +4078,6 @@ void hmiInit() {
}
void dwinInitScreen() {
encoderConfiguration();
hmiInit();
hmiSetLanguageCache();
hmiStartFrame(true);

View File

@@ -5143,7 +5143,6 @@ void MarlinUI::init_lcd() {
if (dwinHandshake()) SERIAL_ECHOLNPGM("ok."); else SERIAL_ECHOLNPGM("error.");
dwinFrameSetDir(1); // Orientation 90°
dwinUpdateLCD(); // Show bootscreen (first image)
encoderConfiguration();
for (uint16_t t = 0; t <= 100; t += 2) {
dwinIconShow(ICON, ICON_Bar, 15, 260);
dwinDrawRectangle(1, COLOR_BG_BLACK, 15 + t * 242 / 100, 260, 257, 280);

View File

@@ -1910,7 +1910,6 @@ void MarlinUI::init_lcd() {
const bool hs = dwinHandshake(); UNUSED(hs);
dwinFrameSetDir(1);
dwinJPGCacheTo1(Language_English);
encoderConfiguration();
}
void dwinInitScreen() {

View File

@@ -216,7 +216,6 @@ void tft_lvgl_init() {
tft_style_init();
filament_pin_setup();
lv_encoder_pin_init();
#if ENABLED(MKS_WIFI_MODULE)
mks_esp_wifi_init();
@@ -331,12 +330,12 @@ bool my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data) {
}
int16_t enc_diff = 0;
lv_indev_state_t state = LV_INDEV_STATE_REL;
lv_indev_state_t indev_enc_state = LV_INDEV_STATE_REL; // ENC button is pressed or released
bool my_mousewheel_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) {
(void) indev_drv; // Unused
UNUSED(indev_drv);
data->state = state;
data->state = indev_enc_state;
data->enc_diff = enc_diff;
enc_diff = 0;
@@ -446,102 +445,50 @@ lv_fs_res_t sd_tell_cb(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p) {
return LV_FS_RES_OK;
}
void lv_encoder_pin_init() {
#if BUTTON_EXISTS(EN1)
SET_INPUT_PULLUP(BTN_EN1);
void lv_update_encoder() {
#if ANY_BUTTON(EN1, EN2)
constexpr uint8_t epps = ENCODER_PULSES_PER_STEP; // We can fill in
static uint8_t pulse_count;
pulse_count += ui.get_encoder_delta();
const int8_t fullSteps = pulse_count / epps;
pulse_count -= fullSteps * epps;
enc_diff += fullSteps;
#endif
#if BUTTON_EXISTS(EN2)
SET_INPUT_PULLUP(BTN_EN2);
#if ANY_BUTTON(ENC, BACK, UP, DOWN, LEFT, RIGHT)
static millis_t last_encoder_ms;
const millis_t now = millis(), diffTime = getTickDiff(now, last_encoder_ms);
if (diffTime <= 50) return;
#endif
#if BUTTON_EXISTS(ENC)
SET_INPUT_PULLUP(BTN_ENC);
static uint8_t old_button_enc = LV_INDEV_STATE_REL;
const uint8_t enc_c = BUTTON_PRESSED(ENC) ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
if (enc_c != old_button_enc) {
indev_enc_state = enc_c ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
old_button_enc = enc_c;
}
#endif
#if BUTTON_EXISTS(BACK)
SET_INPUT_PULLUP(BTN_BACK);
if (BUTTON_PRESSED(BACK)) {}
#endif
#if BUTTON_EXISTS(UP)
SET_INPUT(BTN_UP);
if (BUTTON_PRESSED(UP)) {}
#endif
#if BUTTON_EXISTS(DOWN)
SET_INPUT(BTN_DOWN);
if (BUTTON_PRESSED(DOWN)) {}
#endif
#if BUTTON_EXISTS(LEFT)
SET_INPUT(BTN_LEFT);
if (BUTTON_PRESSED(LEFT)) {}
#endif
#if BUTTON_EXISTS(RIGHT)
SET_INPUT(BTN_RIGHT);
if (BUTTON_PRESSED(RIGHT)) {}
#endif
}
#if 1 // HAS_ENCODER_ACTION
void lv_update_encoder() {
static uint32_t encoder_time1;
uint32_t tmpTime, diffTime = 0;
tmpTime = millis();
diffTime = getTickDiff(tmpTime, encoder_time1);
if (diffTime > 50) {
#if HAS_ENCODER_WHEEL
#if ANY_BUTTON(EN1, EN2, ENC, BACK)
uint8_t newbutton = 0;
if (BUTTON_PRESSED(EN1)) newbutton |= EN_A;
if (BUTTON_PRESSED(EN2)) newbutton |= EN_B;
if (BUTTON_PRESSED(ENC)) newbutton |= EN_C;
if (BUTTON_PRESSED(BACK)) newbutton |= EN_D;
#else
constexpr uint8_t newbutton = 0;
#endif
static uint8_t buttons = 0;
buttons = newbutton;
static uint8_t lastEncoderBits;
#define encrot0 0
#define encrot1 1
#define encrot2 2
uint8_t enc = 0;
if (buttons & EN_A) enc |= B01;
if (buttons & EN_B) enc |= B10;
if (enc != lastEncoderBits) {
switch (enc) {
case encrot1:
if (lastEncoderBits == encrot0) {
enc_diff--;
encoder_time1 = tmpTime;
}
break;
case encrot2:
if (lastEncoderBits == encrot0) {
enc_diff++;
encoder_time1 = tmpTime;
}
break;
}
lastEncoderBits = enc;
}
static uint8_t last_button_state = LV_INDEV_STATE_REL;
const uint8_t enc_c = (buttons & EN_C) ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
if (enc_c != last_button_state) {
state = enc_c ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
last_button_state = enc_c;
}
#endif // HAS_ENCODER_WHEEL
} // encoder_time1
}
#endif // HAS_ENCODER_ACTION
#ifdef __PLAT_NATIVE_SIM__
#include <lv_misc/lv_log.h>
typedef void (*lv_log_print_g_cb_t)(lv_log_level_t level, const char *, uint32_t, const char *);

View File

@@ -41,7 +41,6 @@ bool my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data);
bool my_mousewheel_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
void lcdClear(uint16_t color);
void lv_encoder_pin_init();
void lv_update_encoder();
lv_fs_res_t spi_flash_open_cb(lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode);

View File

@@ -227,34 +227,32 @@ void MarlinUI::init() {
init_lcd();
#if HAS_DIGITAL_BUTTONS
#if BUTTON_EXISTS(EN1)
SET_INPUT_PULLUP(BTN_EN1);
#endif
#if BUTTON_EXISTS(EN2)
SET_INPUT_PULLUP(BTN_EN2);
#endif
#if BUTTON_EXISTS(ENC)
SET_INPUT_PULLUP(BTN_ENC);
#endif
#if BUTTON_EXISTS(ENC_EN)
SET_INPUT_PULLUP(BTN_ENC_EN);
#endif
#if BUTTON_EXISTS(BACK)
SET_INPUT_PULLUP(BTN_BACK);
#endif
#if BUTTON_EXISTS(UP)
SET_INPUT(BTN_UP);
#endif
#if BUTTON_EXISTS(DOWN)
SET_INPUT(BTN_DOWN);
#endif
#if BUTTON_EXISTS(LFT)
SET_INPUT(BTN_LEFT);
#endif
#if BUTTON_EXISTS(RT)
SET_INPUT(BTN_RIGHT);
#endif
#if BUTTON_EXISTS(EN1)
SET_INPUT_PULLUP(BTN_EN1);
#endif
#if BUTTON_EXISTS(EN2)
SET_INPUT_PULLUP(BTN_EN2);
#endif
#if BUTTON_EXISTS(ENC)
SET_INPUT_PULLUP(BTN_ENC);
#endif
#if BUTTON_EXISTS(ENC_EN)
SET_INPUT_PULLUP(BTN_ENC_EN);
#endif
#if BUTTON_EXISTS(BACK)
SET_INPUT_PULLUP(BTN_BACK);
#endif
#if BUTTON_EXISTS(UP)
SET_INPUT(BTN_UP);
#endif
#if BUTTON_EXISTS(DOWN)
SET_INPUT(BTN_DOWN);
#endif
#if BUTTON_EXISTS(LFT)
SET_INPUT(BTN_LEFT);
#endif
#if BUTTON_EXISTS(RT)
SET_INPUT(BTN_RIGHT);
#endif
#if HAS_SHIFT_ENCODER
@@ -1026,72 +1024,56 @@ void MarlinUI::init() {
if (TERN0(IS_RRW_KEYPAD, handle_keypad()))
reset_status_timeout(ms);
uint8_t abs_diff = ABS(encoderDiff);
#if ENCODER_PULSES_PER_STEP > 1
// When reversing the encoder direction, a movement step can be missed because
// encoderDiff has a non-zero residual value, making the controller unresponsive.
// The fix clears the residual value when the encoder is idle.
// Also check if past half the threshold to compensate for missed single steps.
static int8_t lastEncoderDiff;
// Timeout? No decoder change since last check. 10 or 20 times per second.
if (encoderDiff == lastEncoderDiff && abs_diff <= epps / 2) // Same direction & size but not over a half-step?
encoderDiff = 0; // Clear residual pulses.
else if (WITHIN(abs_diff, epps / 2 + 1, epps - 1)) { // Past half of threshold?
abs_diff = epps; // Treat as a full step size
encoderDiff = (encoderDiff < 0 ? -1 : 1) * abs_diff; // ...in the spin direction.
}
if (lastEncoderDiff != encoderDiff) wake_display();
lastEncoderDiff = encoderDiff;
#endif
static int8_t lastEncoderDiff;
if (lastEncoderDiff != encoderDiff) wake_display();
lastEncoderDiff = encoderDiff;
const uint8_t abs_diff = ABS(encoderDiff);
const bool encoderPastThreshold = (abs_diff >= epps);
if (encoderPastThreshold || lcd_clicked) {
if (encoderPastThreshold && TERN1(IS_TFTGLCD_PANEL, !external_control)) {
if (encoderPastThreshold && TERN1(IS_TFTGLCD_PANEL, !external_control)) {
#if ALL(HAS_MARLINUI_MENU, ENCODER_RATE_MULTIPLIER)
int32_t encoder_multiplier = 1;
int32_t encoder_multiplier = 1;
#if ALL(HAS_MARLINUI_MENU, ENCODER_RATE_MULTIPLIER)
if (encoder_multiplier_enabled) {
// Note that the rate is always calculated between two passes through the
// loop and that the abs of the encoderDiff value is tracked.
static millis_t encoder_mult_prev_ms = 0;
const float encoderStepRate = ((float(abs_diff) / float(epps)) * 1000.0f) / float(ms - encoder_mult_prev_ms);
encoder_mult_prev_ms = ms;
if (encoder_multiplier_enabled) {
// Note that the rate is always calculated between two passes through the
// loop and that the abs of the encoderDiff value is tracked.
static millis_t encoder_mult_prev_ms = 0;
const float encoderStepRate = ((float(abs_diff) / float(epps)) * 1000.0f) / float(ms - encoder_mult_prev_ms);
encoder_mult_prev_ms = ms;
if (ENCODER_100X_STEPS_PER_SEC > 0 && encoderStepRate >= ENCODER_100X_STEPS_PER_SEC)
encoder_multiplier = 100;
else if (ENCODER_10X_STEPS_PER_SEC > 0 && encoderStepRate >= ENCODER_10X_STEPS_PER_SEC)
encoder_multiplier = 10;
else if (ENCODER_5X_STEPS_PER_SEC > 0 && encoderStepRate >= ENCODER_5X_STEPS_PER_SEC)
encoder_multiplier = 5;
if (ENCODER_100X_STEPS_PER_SEC > 0 && encoderStepRate >= ENCODER_100X_STEPS_PER_SEC)
encoder_multiplier = 100;
else if (ENCODER_10X_STEPS_PER_SEC > 0 && encoderStepRate >= ENCODER_10X_STEPS_PER_SEC)
encoder_multiplier = 10;
else if (ENCODER_5X_STEPS_PER_SEC > 0 && encoderStepRate >= ENCODER_5X_STEPS_PER_SEC)
encoder_multiplier = 5;
// Enable to output the encoder steps per second value
//#define ENCODER_RATE_MULTIPLIER_DEBUG
#if ENABLED(ENCODER_RATE_MULTIPLIER_DEBUG)
SERIAL_ECHO_MSG(
"Enc Step Rate: ", encoderStepRate,
" Mult: ", encoder_multiplier,
" 5X Steps: ", ENCODER_5X_STEPS_PER_SEC,
" 10X Steps: ", ENCODER_10X_STEPS_PER_SEC,
" 100X Steps: ", ENCODER_100X_STEPS_PER_SEC
);
#endif
}
// Enable to output the encoder steps per second value
//#define ENCODER_RATE_MULTIPLIER_DEBUG
#if ENABLED(ENCODER_RATE_MULTIPLIER_DEBUG)
SERIAL_ECHO_MSG(
"Enc Step Rate: ", encoderStepRate,
" Mult: ", encoder_multiplier,
" 5X Steps: ", ENCODER_5X_STEPS_PER_SEC,
" 10X Steps: ", ENCODER_10X_STEPS_PER_SEC,
" 100X Steps: ", ENCODER_100X_STEPS_PER_SEC
);
#endif
}
#else
#endif // ENCODER_RATE_MULTIPLIER
constexpr int32_t encoder_multiplier = 1;
#endif // ENCODER_RATE_MULTIPLIER
if (can_encode()) encoderPosition += (encoderDiff * encoder_multiplier) / epps;
encoderDiff = 0;
const int8_t fullSteps = encoderDiff / epps;
if (fullSteps != 0) {
encoderDiff -= fullSteps * epps;
if (can_encode() && !lcd_clicked)
encoderPosition += (fullSteps * encoder_multiplier);
}
}
if (encoderPastThreshold || lcd_clicked) {
reset_status_timeout(ms);
#if HAS_BACKLIGHT_TIMEOUT
@@ -1312,124 +1294,156 @@ void MarlinUI::init() {
*/
void MarlinUI::update_buttons() {
const millis_t now = millis();
if (ELAPSED(now, next_button_update_ms)) {
#if HAS_DIGITAL_BUTTONS
#if HAS_MARLINUI_ENCODER
#if ANY_BUTTON(EN1, EN2, ENC, BACK)
uint8_t newbutton = 0;
if (BUTTON_PRESSED(EN1)) newbutton |= EN_A;
if (BUTTON_PRESSED(EN2)) newbutton |= EN_B;
if (can_encode() && BUTTON_PRESSED(ENC)) newbutton |= EN_C;
if (BUTTON_PRESSED(BACK)) newbutton |= EN_D;
#else
constexpr uint8_t newbutton = 0;
#endif
//
// Directional buttons
//
#if ANY_BUTTON(UP, DOWN, LEFT, RIGHT)
const int8_t pulses = epps * encoderDirection;
if (BUTTON_PRESSED(UP)) {
encoderDiff = (ENCODER_STEPS_PER_MENU_ITEM) * pulses;
next_button_update_ms = now + 300;
}
else if (BUTTON_PRESSED(DOWN)) {
encoderDiff = -(ENCODER_STEPS_PER_MENU_ITEM) * pulses;
next_button_update_ms = now + 300;
}
else if (BUTTON_PRESSED(LEFT)) {
encoderDiff = -pulses;
next_button_update_ms = now + 300;
}
else if (BUTTON_PRESSED(RIGHT)) {
encoderDiff = pulses;
next_button_update_ms = now + 300;
}
#endif // UP || DOWN || LEFT || RIGHT
buttons = (newbutton | TERN0(HAS_SLOW_BUTTONS, slow_buttons)
#if ALL(HAS_TOUCH_BUTTONS, HAS_ENCODER_ACTION)
| (touch_buttons & TERN(HAS_ENCODER_WHEEL, ~(EN_A | EN_B), 0xFF))
#endif
);
#elif HAS_ADC_BUTTONS
buttons = 0;
#endif
#if HAS_ADC_BUTTONS
if (keypad_buttons == 0) {
const uint8_t b = get_ADC_keyValue();
if (WITHIN(b, 1, 8)) keypad_buttons = _BV(b - 1);
}
#endif
#if HAS_SHIFT_ENCODER
/**
* Set up Rotary Encoder bit values (for two pin encoders to indicate movement).
* These values are independent of which pins are used for EN_A / EN_B indications.
* The rotary encoder part is also independent of the LCD chipset.
*/
uint8_t val = 0;
WRITE(SHIFT_LD_PIN, LOW);
WRITE(SHIFT_LD_PIN, HIGH);
for (uint8_t i = 0; i < 8; ++i) {
val >>= 1;
if (READ(SHIFT_OUT_PIN)) SBI(val, 7);
WRITE(SHIFT_CLK_PIN, HIGH);
WRITE(SHIFT_CLK_PIN, LOW);
}
TERN(REPRAPWORLD_KEYPAD, keypad_buttons, buttons) = ~val;
#endif
#if IS_TFTGLCD_PANEL
next_button_update_ms = now + (LCD_UPDATE_INTERVAL / 2);
buttons = slow_buttons;
TERN_(AUTO_BED_LEVELING_UBL, external_encoder());
#endif
} // next_button_update_ms
#if HAS_ENCODER_WHEEL
static uint8_t lastEncoderBits;
// Manage encoder rotation
#define ENCODER_SPIN(_E1, _E2) switch (lastEncoderBits) { case _E1: encoderDiff += encoderDirection; break; case _E2: encoderDiff -= encoderDirection; }
uint8_t enc = 0;
if (buttons & EN_A) enc |= B01;
if (buttons & EN_B) enc |= B10;
if (enc != lastEncoderBits) {
switch (enc) {
case 0: ENCODER_SPIN(1, 2); break;
case 2: ENCODER_SPIN(0, 3); break;
case 3: ENCODER_SPIN(2, 1); break;
case 1: ENCODER_SPIN(3, 0); break;
}
const int8_t delta = get_encoder_delta(now);
if (delta) {
encoderDiff += delta * encoderDirection;
#if ALL(HAS_MARLINUI_MENU, AUTO_BED_LEVELING_UBL)
external_encoder();
#endif
lastEncoderBits = enc;
}
#endif // HAS_ENCODER_WHEEL
}
#endif
if (PENDING(now, next_button_update_ms)) return;
#if HAS_DIGITAL_BUTTONS
uint8_t newbuttons = 0;
#if ANY_BUTTON(ENC, BACK)
if (can_encode() && BUTTON_PRESSED(ENC)) newbuttons |= EN_C;
if (BUTTON_PRESSED(BACK)) newbuttons |= EN_D;
#endif
//
// Directional buttons
//
#if ANY_BUTTON(UP, DOWN, LEFT, RIGHT)
const int8_t pulses = epps * encoderDirection;
if (BUTTON_PRESSED(UP)) {
encoderDiff = (ENCODER_STEPS_PER_MENU_ITEM) * pulses;
next_button_update_ms = now + 300;
}
else if (BUTTON_PRESSED(DOWN)) {
encoderDiff = -(ENCODER_STEPS_PER_MENU_ITEM) * pulses;
next_button_update_ms = now + 300;
}
else if (BUTTON_PRESSED(LEFT)) {
encoderDiff = -pulses;
next_button_update_ms = now + 300;
}
else if (BUTTON_PRESSED(RIGHT)) {
encoderDiff = pulses;
next_button_update_ms = now + 300;
}
#endif // UP || DOWN || LEFT || RIGHT
buttons = (newbuttons | TERN0(HAS_SLOW_BUTTONS, slow_buttons)
#if ALL(HAS_TOUCH_BUTTONS, HAS_ENCODER_ACTION)
| (touch_buttons & TERN(HAS_MARLINUI_ENCODER, ~(EN_A | EN_B), 0xFF))
#endif
);
#elif HAS_ADC_BUTTONS
buttons = 0;
#endif
#if HAS_ADC_BUTTONS
if (keypad_buttons == 0) {
const uint8_t b = get_ADC_keyValue();
if (WITHIN(b, 1, 8)) keypad_buttons = _BV(b - 1);
}
#endif
#if HAS_SHIFT_ENCODER
/**
* Set up Rotary Encoder bit values (for two pin encoders to indicate movement).
* These values are independent of which pins are used for EN_A / EN_B indications.
* The rotary encoder part is also independent of the LCD chipset.
*/
uint8_t val = 0;
WRITE(SHIFT_LD_PIN, LOW);
WRITE(SHIFT_LD_PIN, HIGH);
for (uint8_t i = 0; i < 8; ++i) {
val >>= 1;
if (READ(SHIFT_OUT_PIN)) SBI(val, 7);
WRITE(SHIFT_CLK_PIN, HIGH);
WRITE(SHIFT_CLK_PIN, LOW);
}
TERN(REPRAPWORLD_KEYPAD, keypad_buttons, buttons) = ~val;
#endif
#if IS_TFTGLCD_PANEL
next_button_update_ms = now + (LCD_UPDATE_INTERVAL / 2);
buttons = slow_buttons;
TERN_(AUTO_BED_LEVELING_UBL, external_encoder());
#endif
} // update_buttons
#endif // HAS_ENCODER_ACTION
#endif // HAS_WIRED_LCD
#if MARLINUI_ENCODER_DELTA
#define ENCODER_DEBOUNCE_MS 2
/**
* Get the encoder delta (-2 -1 0 +1 +2) since the last call, reading the live encoder state.
* Pins may be debounced to filter noise.
*/
int8_t MarlinUI::get_encoder_delta(const millis_t &now/*=millis()*/) {
typedef struct { bool a:1, b:1; } enc_t;
const enc_t live_enc = { BUTTON_PRESSED(EN1), BUTTON_PRESSED(EN2) };
#if ENCODER_DEBOUNCE_MS
static enc_t enc;
static enc_t old_live;
static millis_t en_A_bounce_ms;
if (old_live.a != live_enc.a) en_A_bounce_ms = now + (ENCODER_DEBOUNCE_MS);
else if (ELAPSED(now, en_A_bounce_ms)) enc.a = live_enc.a;
static millis_t en_B_bounce_ms;
if (old_live.b != live_enc.b) en_B_bounce_ms = now + (ENCODER_DEBOUNCE_MS);
else if (ELAPSED(now, en_B_bounce_ms)) enc.b = live_enc.b;
old_live = live_enc;
#else
const enc_t &enc = live_enc;
#endif
static uint8_t old_pos;
const uint8_t pos = (enc.a ^ enc.b) | (enc.a << 1); // 0:00 1:10 2:11 3:01
int8_t delta = 0;
if (pos != old_pos) {
delta = (pos - old_pos + 4 + 1) % 4 - 1;
old_pos = pos;
static int8_t last_dir;
if (delta == 2) delta = last_dir * 2;
else last_dir = delta;
}
return delta;
} // get_encoder_delta
#endif // MARLINUI_ENCODER_DELTA
void MarlinUI::completion_feedback(const bool good/*=true*/) {
wake_display(); // Wake the screen for all audio feedback
#if HAS_SOUND

View File

@@ -254,6 +254,11 @@ public:
}
#endif
#if (HAS_WIRED_LCD && HAS_ENCODER_ACTION && HAS_MARLINUI_ENCODER) || HAS_DWIN_E3V2 || HAS_TFT_LVGL_UI
#define MARLINUI_ENCODER_DELTA 1
static int8_t get_encoder_delta(const millis_t &now=millis());
#endif
#if HAS_MEDIA
#define MEDIA_MENU_GATEWAY TERN(PASSWORD_ON_SD_PRINT_MENU, password.media_gatekeeper, menu_media)
static void media_changed(const uint8_t old_stat, const uint8_t stat);

View File

@@ -246,7 +246,7 @@ void menu_main() {
START_MENU();
BACK_ITEM(MSG_INFO_SCREEN);
#if HAS_MEDIA && !defined(MEDIA_MENU_AT_TOP) && !HAS_ENCODER_WHEEL
#if HAS_MEDIA && !defined(MEDIA_MENU_AT_TOP) && !HAS_MARLINUI_ENCODER
#define MEDIA_MENU_AT_TOP
#endif