Skip to content

Commit

Permalink
🚸 Encoder improvements (MarlinFirmware#26501)
Browse files Browse the repository at this point in the history
# Conflicts:
#	Marlin/src/lcd/marlinui.cpp
  • Loading branch information
dbuezas committed Feb 5, 2024
1 parent c0c9bd4 commit 400af01
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 77 deletions.
27 changes: 16 additions & 11 deletions Marlin/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -698,9 +698,14 @@
// #define DEFAULT_Ki 0.83
// #define DEFAULT_Kd 46.0

#define DEFAULT_Kp 10.63
#define DEFAULT_Ki 0.63
#define DEFAULT_Kd 44.66
// #define DEFAULT_Kp 10.63
// #define DEFAULT_Ki 0.63
// #define DEFAULT_Kd 44.66

#define DEFAULT_Kp 12.32
#define DEFAULT_Ki 1.07
#define DEFAULT_Kd 35.47


// UM2 (E3D V6 Hotend)
//#define DEFAULT_Kp 36.59
Expand Down Expand Up @@ -1360,7 +1365,7 @@
*
* See https://github.com/synthetos/TinyG/wiki/Jerk-Controlled-Motion-Explained
*/
//#define S_CURVE_ACCELERATION
#define S_CURVE_ACCELERATION

//===========================================================================
//============================= Z Probe Options =============================
Expand Down Expand Up @@ -1677,8 +1682,8 @@
* A total of 2 does fast/slow probes with a weighted average.
* A total of 3 or more adds more slow probes, taking the average.
*/
//#define MULTIPLE_PROBING 2
//#define EXTRA_PROBING 1
#define MULTIPLE_PROBING 2
#define EXTRA_PROBING 1

/**
* Z probes require clearance when deploying, stowing, and moving between
Expand All @@ -1694,9 +1699,9 @@
* Example: `M851 Z-5` with a CLEARANCE of 4 => 9mm from bed to nozzle.
* But: `M851 Z+1` with a CLEARANCE of 2 => 2mm from bed to nozzle.
*/
#define Z_CLEARANCE_DEPLOY_PROBE 10 // (mm) Z Clearance for Deploy/Stow
#define Z_CLEARANCE_DEPLOY_PROBE 5 // (mm) Z Clearance for Deploy/Stow
#define Z_CLEARANCE_BETWEEN_PROBES 2 // (mm) Z Clearance between probe points
#define Z_CLEARANCE_MULTI_PROBE 1 // (mm) Z Clearance between multiple probes
#define Z_CLEARANCE_MULTI_PROBE 1.5 // (mm) Z Clearance between multiple probes
#define Z_PROBE_ERROR_TOLERANCE 3 // (mm) Tolerance for early trigger (<= -probe.offset.z + ZPET)
//#define Z_AFTER_PROBING 5 // (mm) Z position after probing is done

Expand Down Expand Up @@ -1737,7 +1742,7 @@
//#define DELAY_BEFORE_PROBING 200 // (ms) To prevent vibrations from triggering piezo sensors

// Require minimum nozzle and/or bed temperature for probing
//#define PREHEAT_BEFORE_PROBING
#define PREHEAT_BEFORE_PROBING
#if ENABLED(PREHEAT_BEFORE_PROBING)
#define PROBING_NOZZLE_TEMP 120 // (°C) Only applies to E0 at this time
#define PROBING_BED_TEMP 50
Expand Down Expand Up @@ -3642,12 +3647,12 @@
/****** CUSTOM *******/


#define TEMP_0_CS_PIN E1_ENABLE_PIN
#define TEMP_0_CS_PIN E1_DIR_PIN
#define TEMP_0_SCK_PIN TMC_SPI_SCK
#define TEMP_0_MISO_PIN TMC_SPI_MISO
#define TEMP_0_MOSI_PIN TMC_SPI_MOSI

#define TEMP_BED_CS_PIN E1_DIR_PIN
#define TEMP_BED_CS_PIN E1_ENABLE_PIN
#define TEMP_BED_SCK_PIN TEMP_0_SCK_PIN
#define TEMP_BED_MISO_PIN TEMP_0_MISO_PIN
#define TEMP_BED_MOSI_PIN TEMP_0_MOSI_PIN
Expand Down
22 changes: 11 additions & 11 deletions Marlin/Configuration_adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@
* gets it spinning reliably for a short time before setting the requested speed.
* (Does not work on Sanguinololu with FAN_SOFT_PWM.)
*/
#define FAN_KICKSTART_TIME 200 // (ms)
#define FAN_KICKSTART_TIME 500 // (ms)
#define FAN_KICKSTART_POWER 255 // 64-255

// Some coolers may require a non-zero "off" state.
Expand Down Expand Up @@ -693,7 +693,7 @@
#define COOLER_AUTO_FAN_PIN -1

#define EXTRUDER_AUTO_FAN_TEMPERATURE 50
#define EXTRUDER_AUTO_FAN_SPEED 255 // 255 == full speed
#define EXTRUDER_AUTO_FAN_SPEED 127 // 255 == full speed
#define CHAMBER_AUTO_FAN_TEMPERATURE 30
#define CHAMBER_AUTO_FAN_SPEED 255
#define COOLER_AUTO_FAN_TEMPERATURE 18
Expand Down Expand Up @@ -1393,7 +1393,7 @@
* vibration and surface artifacts. The algorithm adapts to provide the best possible step smoothing at the
* lowest stepping frequencies.
*/
//#define ADAPTIVE_STEP_SMOOTHING
#define ADAPTIVE_STEP_SMOOTHING

/**
* Custom Microstepping
Expand Down Expand Up @@ -1553,7 +1553,7 @@
#define MANUAL_MOVE_DISTANCE_DEG 90, 45, 22.5, 5, 1 // (°)

// BACK menu items keep the highlight at the top
//#define TURBO_BACK_MENU_ITEM
#define TURBO_BACK_MENU_ITEM
// Insert a menu for preheating at the top level to allow for quick access
#define PREHEAT_SHORTCUT_MENU_ITEM
Expand Down Expand Up @@ -2320,7 +2320,7 @@
#define ADVANCE_K 0.25 // (mm) Compression length applying to all extruders
#endif
//#define ADVANCE_K_EXTRA // Add a second linear advance constant, configurable with M900 L.
//#define LA_DEBUG // Print debug information to serial during operation. Disable for production use.
// #define LA_DEBUG // Print debug information to serial during operation. Disable for production use.
//#define EXPERIMENTAL_I2S_LA // Allow I2S_STEPPER_STREAM to be used with LA. Performance degrades as the LA step rate reaches ~20kHz.
#endif

Expand Down Expand Up @@ -2889,7 +2889,7 @@
#define ADVANCED_PAUSE_FEATURE
#if ENABLED(ADVANCED_PAUSE_FEATURE)
#define PAUSE_PARK_RETRACT_FEEDRATE 10 // (mm/s) Initial retract feedrate.
#define PAUSE_PARK_RETRACT_LENGTH 2 // (mm) Initial retract.
#define PAUSE_PARK_RETRACT_LENGTH 4 // (mm) Initial retract.
// This short retract is done immediately, before parking the nozzle.
#define FILAMENT_CHANGE_UNLOAD_FEEDRATE 20 // (mm/s) Unload filament feedrate. This can be pretty fast.
#define FILAMENT_CHANGE_UNLOAD_ACCEL 10 // (mm/s^2) Lower acceleration may allow a faster feedrate.
Expand Down Expand Up @@ -3357,13 +3357,13 @@
* STEALTHCHOP_(XY|Z|E) must be enabled to use HYBRID_THRESHOLD.
* M913 X/Y/Z/E to live tune the setting
*/
//#define HYBRID_THRESHOLD
#define HYBRID_THRESHOLD

#define X_HYBRID_THRESHOLD 100 // [mm/s]
#define X_HYBRID_THRESHOLD 200 // [mm/s]
#define X2_HYBRID_THRESHOLD 100
#define Y_HYBRID_THRESHOLD 100
#define Y_HYBRID_THRESHOLD 200
#define Y2_HYBRID_THRESHOLD 100
#define Z_HYBRID_THRESHOLD 3
#define Z_HYBRID_THRESHOLD 2
#define Z2_HYBRID_THRESHOLD 3
#define Z3_HYBRID_THRESHOLD 3
#define Z4_HYBRID_THRESHOLD 3
Expand All @@ -3373,7 +3373,7 @@
#define U_HYBRID_THRESHOLD 3 // [mm/s]
#define V_HYBRID_THRESHOLD 3
#define W_HYBRID_THRESHOLD 3
#define E0_HYBRID_THRESHOLD 30
#define E0_HYBRID_THRESHOLD 15
#define E1_HYBRID_THRESHOLD 30
#define E2_HYBRID_THRESHOLD 30
#define E3_HYBRID_THRESHOLD 30
Expand Down
129 changes: 74 additions & 55 deletions Marlin/src/lcd/marlinui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ MarlinUI ui;

constexpr uint8_t epps = ENCODER_PULSES_PER_STEP;

#define BLOCK_CLICK_AFTER_MOVEMENT_MS 100
#define RESET_ENCODER_AFTER_MS (2000/ENCODER_PULSES_PER_STEP)

#if HAS_STATUS_MESSAGE
#if ENABLED(STATUS_MESSAGE_SCROLLING) && ANY(HAS_WIRED_LCD, DWIN_LCD_PROUI)
uint8_t MarlinUI::status_scroll_offset; // = 0
Expand Down Expand Up @@ -943,6 +946,7 @@ void MarlinUI::init() {
void MarlinUI::update() {

static uint16_t max_display_update_time = 0;
static millis_t next_click_enable_ms = 0;
const millis_t ms = millis();

#if LED_POWEROFF_TIMEOUT > 0
Expand Down Expand Up @@ -993,7 +997,12 @@ void MarlinUI::init() {
if (!touch_buttons) {
// Integrated LCD click handling via button_pressed
if (!external_control && button_pressed()) {
if (!wait_for_unclick) do_click(); // Handle the click
if (!wait_for_unclick) {
if (ELAPSED(ms, next_click_enable_ms))
do_click(); // Handle the click
else
wait_for_unclick = true;
}
}
else
wait_for_unclick = false;
Expand Down Expand Up @@ -1027,71 +1036,69 @@ 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.
static millis_t encoder_reset_timeout_ms;
if (lastEncoderDiff != encoderDiff) {
wake_display();
encoder_reset_timeout_ms = ms + RESET_ENCODER_AFTER_MS;
} else if (ELAPSED(ms, encoder_reset_timeout_ms)) {
// Reset encoder substeps after a while.
// This solves the issue of the haptic ticks of some encoders physically getting out of sync with the actual steps after a while .
encoderDiff = 0;
}
if (lastEncoderDiff != encoderDiff) wake_display();
lastEncoderDiff = encoderDiff;
#endif

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)
#if ALL(HAS_MARLINUI_MENU, ENCODER_RATE_MULTIPLIER)

int32_t encoderMultiplier = 1;
int32_t encoderMultiplier = 1;

if (encoderRateMultiplierEnabled) {
if (encoderRateMultiplierEnabled) {
if (lastEncoderMovementMillis) {
const float encoderMovementSteps = float(abs_diff) / epps;
// Note that the rate is always calculated between two passes through the
// loop and that the abs of the encoderDiff value is tracked.
const float encoderStepRate = encoderMovementSteps / float(ms - lastEncoderMovementMillis) * 1000;

if (encoderStepRate >= ENCODER_100X_STEPS_PER_SEC) encoderMultiplier = 100;
else if (encoderStepRate >= ENCODER_10X_STEPS_PER_SEC) encoderMultiplier = 10;

// Enable to output the encoder steps per second value
//#define ENCODER_RATE_MULTIPLIER_DEBUG
#if ENABLED(ENCODER_RATE_MULTIPLIER_DEBUG)
SERIAL_ECHO_START();
SERIAL_ECHOPGM("Enc Step Rate: ", encoderStepRate);
SERIAL_ECHOPGM(" Multiplier: ", encoderMultiplier);
SERIAL_ECHOPGM(" ENCODER_10X_STEPS_PER_SEC: ", ENCODER_10X_STEPS_PER_SEC);
SERIAL_ECHOPGM(" ENCODER_100X_STEPS_PER_SEC: ", ENCODER_100X_STEPS_PER_SEC);
SERIAL_EOL();
#endif
}

if (lastEncoderMovementMillis) {
// Note that the rate is always calculated between two passes through the
// loop and that the abs of the encoderDiff value is tracked.
const float encoderStepRate = encoderMovementSteps / float(ms - lastEncoderMovementMillis) * 1000;

if (encoderStepRate >= ENCODER_100X_STEPS_PER_SEC) encoderMultiplier = 100;
else if (encoderStepRate >= ENCODER_10X_STEPS_PER_SEC) encoderMultiplier = 10;

// Enable to output the encoder steps per second value
//#define ENCODER_RATE_MULTIPLIER_DEBUG
#if ENABLED(ENCODER_RATE_MULTIPLIER_DEBUG)
SERIAL_ECHO_START();
SERIAL_ECHOPGM("Enc Step Rate: ", encoderStepRate);
SERIAL_ECHOPGM(" Multiplier: ", encoderMultiplier);
SERIAL_ECHOPGM(" ENCODER_10X_STEPS_PER_SEC: ", ENCODER_10X_STEPS_PER_SEC);
SERIAL_ECHOPGM(" ENCODER_100X_STEPS_PER_SEC: ", ENCODER_100X_STEPS_PER_SEC);
SERIAL_EOL();
#endif
}

lastEncoderMovementMillis = ms;
} // encoderRateMultiplierEnabled

#else
lastEncoderMovementMillis = ms;
} // encoderRateMultiplierEnabled

constexpr int32_t encoderMultiplier = 1;
#else

#endif // ENCODER_RATE_MULTIPLIER
constexpr int32_t encoderMultiplier = 1;

if (can_encode()) encoderPosition += (encoderDiff * encoderMultiplier) / epps;
#endif // ENCODER_RATE_MULTIPLIER

encoderDiff = 0;
int8_t fullSteps = encoderDiff / epps;
if (fullSteps != 0) {
next_click_enable_ms = ms + BLOCK_CLICK_AFTER_MOVEMENT_MS;
encoderDiff -= fullSteps * epps;
if (can_encode() && !lcd_clicked)
encoderPosition += (fullSteps * encoderMultiplier);
}
}

if (encoderPastThreshold || lcd_clicked) {
reset_status_timeout(ms);

#if HAS_BACKLIGHT_TIMEOUT
Expand Down Expand Up @@ -1402,14 +1409,26 @@ void MarlinUI::init() {
} // next_button_update_ms

#if HAS_ENCODER_WHEEL
#define ENCODER_DEBOUNCE_MS 3
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;
static uint8_t enc;
static uint8_t buttons_was = buttons;
static millis_t en_A_blocked_ms;
static millis_t en_B_blocked_ms;

const bool en_A = (buttons & EN_A);
const bool en_B = (buttons & EN_B);
const bool en_A_was = (buttons_was & EN_A);
const bool en_B_was = (buttons_was & EN_B);

buttons_was = buttons;

if (en_A != en_A_was) en_A_blocked_ms = now + ENCODER_DEBOUNCE_MS;
else if (ELAPSED(now, en_A_blocked_ms)) SET_BIT_TO(enc, 0, en_A);
if (en_B != en_B_was) en_B_blocked_ms = now + ENCODER_DEBOUNCE_MS;
else if (ELAPSED(now, en_B_blocked_ms)) SET_BIT_TO(enc, 1, en_B);

#define ENCODER_SPIN(_E1, _E2) switch (lastEncoderBits) { case _E1: encoderDiff += encoderDirection; break; case _E2: encoderDiff -= encoderDirection; break; }
if (enc != lastEncoderBits) {
switch (enc) {
case 0: ENCODER_SPIN(1, 2); break;
Expand Down

0 comments on commit 400af01

Please sign in to comment.