From 2e88d8b5dfdcf0e8ea826166cb0ad1677390aafc Mon Sep 17 00:00:00 2001 From: Benjie Date: Tue, 9 Dec 2025 18:15:15 +0000 Subject: [PATCH 1/4] config: Clarify configuration settings for Ender 5 Pro (#7126) Signed-off-by: Benjie Gillam --- config/printer-creality-ender5pro-2020.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/printer-creality-ender5pro-2020.cfg b/config/printer-creality-ender5pro-2020.cfg index ded26b408..2321cfb6d 100644 --- a/config/printer-creality-ender5pro-2020.cfg +++ b/config/printer-creality-ender5pro-2020.cfg @@ -1,7 +1,7 @@ # This file contains pin mappings for the stock 2020 Creality Ender 5 # Pro with the 32-bit Creality 4.2.2 board. To use this config, during # "make menuconfig" select the STM32F103 with a "28KiB bootloader" and -# with "Use USB for communication" disabled. +# communication interface set to "Serial (on USART1 PA10/PA9)". # If you prefer a direct serial connection, in "make menuconfig" # select "Enable extra low-level configuration options" and select the From f9108496a18f158221d20a53197acc04d9c9b5f9 Mon Sep 17 00:00:00 2001 From: Timofey Titovets Date: Thu, 27 Nov 2025 01:26:52 +0100 Subject: [PATCH 2/4] ldc1612: fix data rate calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no need to remove 4 from data rate. Formula for conversion time is: (RCOUNT0×16)/ƒREF0 Signed-off-by: Timofey Titovets --- klippy/extras/ldc1612.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/ldc1612.py b/klippy/extras/ldc1612.py index dd41b43ae..973556af1 100644 --- a/klippy/extras/ldc1612.py +++ b/klippy/extras/ldc1612.py @@ -176,7 +176,7 @@ class LDC1612: "(e.g. faulty wiring) or a faulty ldc1612 chip." % (manuf_id, dev_id, LDC1612_MANUF_ID, LDC1612_DEV_ID)) # Setup chip in requested query rate - rcount0 = self.frequency / (16. * (self.data_rate - 4)) + rcount0 = self.frequency / (16. * self.data_rate) self.set_reg(REG_RCOUNT0, int(rcount0 + 0.5)) self.set_reg(REG_OFFSET0, 0) self.set_reg(REG_SETTLECOUNT0, int(SETTLETIME*self.frequency/16. + .5)) From 2b4c55ffd118a4982cfb04a01052746bb8cb45d9 Mon Sep 17 00:00:00 2001 From: Timofey Titovets Date: Wed, 3 Dec 2025 01:12:53 +0100 Subject: [PATCH 3/4] servo: sync pwm clock times Arriving of SW PWM out of sync can cause pulse width distortion - make them longer Synchronize the update clock to avoid that Signed-off-by: Timofey Titovets Signed-off-by: Kevin O'Connor --- klippy/extras/multi_pin.py | 2 ++ klippy/extras/output_pin.py | 9 +++++++++ klippy/extras/replicape.py | 2 ++ klippy/extras/servo.py | 7 ++++++- klippy/extras/sx1509.py | 2 ++ klippy/mcu.py | 17 +++++++++++++++++ 6 files changed, 38 insertions(+), 1 deletion(-) diff --git a/klippy/extras/multi_pin.py b/klippy/extras/multi_pin.py index c834ee077..f126f928c 100644 --- a/klippy/extras/multi_pin.py +++ b/klippy/extras/multi_pin.py @@ -46,6 +46,8 @@ class PrinterMultiPin: def set_digital(self, print_time, value): for mcu_pin in self.mcu_pins: mcu_pin.set_digital(print_time, value) + def next_aligned_print_time(self, print_time, allow_early=0.): + return print_time def set_pwm(self, print_time, value): for mcu_pin in self.mcu_pins: mcu_pin.set_pwm(print_time, value) diff --git a/klippy/extras/output_pin.py b/klippy/extras/output_pin.py index a51292990..a15d55166 100644 --- a/klippy/extras/output_pin.py +++ b/klippy/extras/output_pin.py @@ -46,6 +46,11 @@ class GCodeRequestQueue: if action == "discard": del rqueue[:pos+1] continue + if action == "reschedule": + del rqueue[:pos] + self.next_min_flush_time = max(self.next_min_flush_time, + min_wait) + continue if action == "delay": pos -= 1 del rqueue[:pos+1] @@ -75,6 +80,10 @@ class GCodeRequestQueue: action, min_wait = ret if action == "discard": break + if action == "reschedule": + self.next_min_flush_time = max(self.next_min_flush_time, + min_wait) + continue self.next_min_flush_time = next_time + max(min_wait, min_sched_time) if action != "delay": break diff --git a/klippy/extras/replicape.py b/klippy/extras/replicape.py index f7f7bb64b..eaca8b83d 100644 --- a/klippy/extras/replicape.py +++ b/klippy/extras/replicape.py @@ -67,6 +67,8 @@ class pca9685_pwm: cmd_queue = self._mcu.alloc_command_queue() self._set_cmd = self._mcu.lookup_command( "queue_pca9685_out oid=%c clock=%u value=%hu", cq=cmd_queue) + def next_aligned_print_time(self, print_time, allow_early=0.): + return print_time def set_pwm(self, print_time, value): clock = self._mcu.print_time_to_clock(print_time) if self._invert: diff --git a/klippy/extras/servo.py b/klippy/extras/servo.py index f1ce99763..303e39377 100644 --- a/klippy/extras/servo.py +++ b/klippy/extras/servo.py @@ -6,6 +6,7 @@ from . import output_pin SERVO_SIGNAL_PERIOD = 0.020 +RESCHEDULE_SLACK = 0.000500 class PrinterServo: def __init__(self, config): @@ -47,8 +48,12 @@ class PrinterServo: def _set_pwm(self, print_time, value): if value == self.last_value: return "discard", 0. + aligned_ptime = self.mcu_servo.next_aligned_print_time(print_time, + RESCHEDULE_SLACK) + if aligned_ptime > print_time + RESCHEDULE_SLACK: + return "reschedule", aligned_ptime self.last_value = value - self.mcu_servo.set_pwm(print_time, value) + self.mcu_servo.set_pwm(aligned_ptime, value) def _get_pwm_from_angle(self, angle): angle = max(0., min(self.max_angle, angle)) width = self.min_width + angle * self.angle_to_width diff --git a/klippy/extras/sx1509.py b/klippy/extras/sx1509.py index 99df55df3..ce25bd027 100644 --- a/klippy/extras/sx1509.py +++ b/klippy/extras/sx1509.py @@ -178,6 +178,8 @@ class SX1509_pwm(object): self._shutdown_value = max(0., min(1., shutdown_value)) self._sx1509.set_register(self._i_on_reg, ~int(255 * self._start_value) & 0xFF) + def next_aligned_print_time(self, print_time, allow_early=0.): + return print_time def set_pwm(self, print_time, value): self._sx1509.set_register(self._i_on_reg, ~int(255 * value) if not self._invert diff --git a/klippy/mcu.py b/klippy/mcu.py index 14be8a5c0..d9bd34fb8 100644 --- a/klippy/mcu.py +++ b/klippy/mcu.py @@ -422,6 +422,7 @@ class MCU_pwm: self._invert = pin_params['invert'] self._start_value = self._shutdown_value = float(self._invert) self._last_clock = 0 + self._last_value = .0 self._pwm_max = 0. self._set_cmd = None def get_mcu(self): @@ -437,6 +438,7 @@ class MCU_pwm: shutdown_value = 1. - shutdown_value self._start_value = max(0., min(1., start_value)) self._shutdown_value = max(0., min(1., shutdown_value)) + self._last_value = self._start_value def _build_config(self): if self._max_duration and self._start_value != self._shutdown_value: raise pins.error("Pin with max duration must have start" @@ -488,6 +490,20 @@ class MCU_pwm: % (self._oid, self._last_clock, svalue), is_init=True) self._set_cmd = self._mcu.lookup_command( "queue_digital_out oid=%c clock=%u on_ticks=%u", cq=cmd_queue) + def next_aligned_print_time(self, print_time, allow_early=0.): + # Filter cases where there is no need to sync anything + if self._hardware_pwm: + return print_time + if self._last_value == 1. or self._last_value == .0: + return print_time + # Simplify the calling and allow scheduling slightly earlier + req_ptime = print_time - min(allow_early, 0.5 * self._cycle_time) + cycle_ticks = self._mcu.seconds_to_clock(self._cycle_time) + req_clock = self._mcu.print_time_to_clock(req_ptime) + last_clock = self._last_clock + pulses = (req_clock - last_clock + cycle_ticks - 1) // cycle_ticks + next_clock = last_clock + pulses * cycle_ticks + return self._mcu.clock_to_print_time(next_clock) def set_pwm(self, print_time, value): if self._invert: value = 1. - value @@ -496,6 +512,7 @@ class MCU_pwm: self._set_cmd.send([self._oid, clock, v], minclock=self._last_clock, reqclock=clock) self._last_clock = clock + self._last_value = value class MCU_adc: def __init__(self, mcu, pin_params): From f52a6f9491e6bbb6c04f3634012ae23f41e8b857 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Wed, 3 Dec 2025 17:35:12 -0500 Subject: [PATCH 4/4] output_pin: Rename "delay" flag to "repeat" in GCodeRequestQueue() Rename the flag to make it more clear what it does. Signed-off-by: Kevin O'Connor --- klippy/extras/fan.py | 2 +- klippy/extras/output_pin.py | 25 +++++++++++++------------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index c5677ba06..2bcc512d7 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -63,7 +63,7 @@ class Fan: self.last_req_value = value self.last_fan_value = self.max_power self.mcu_fan.set_pwm(print_time, self.max_power) - return "delay", self.kick_start_time + return "repeat", print_time + self.kick_start_time self.last_fan_value = self.last_req_value = value self.mcu_fan.set_pwm(print_time, value) def set_speed(self, value, print_time=None): diff --git a/klippy/extras/output_pin.py b/klippy/extras/output_pin.py index a15d55166..58a7302ca 100644 --- a/klippy/extras/output_pin.py +++ b/klippy/extras/output_pin.py @@ -38,23 +38,23 @@ class GCodeRequestQueue: pos += 1 req_pt, req_val = rqueue[pos] # Invoke callback for the request - min_wait = 0. ret = self.callback(next_time, req_val) if ret is not None: # Handle special cases - action, min_wait = ret + action, next_min_time = ret + self.next_min_flush_time = max(self.next_min_flush_time, + next_min_time) if action == "discard": del rqueue[:pos+1] continue if action == "reschedule": del rqueue[:pos] - self.next_min_flush_time = max(self.next_min_flush_time, - min_wait) continue - if action == "delay": + if action == "repeat": pos -= 1 del rqueue[:pos+1] - self.next_min_flush_time = next_time + max(min_wait, min_sched_time) + self.next_min_flush_time = max(self.next_min_flush_time, + next_time + min_sched_time) # Ensure following queue items are flushed self.motion_queuing.note_mcu_movequeue_activity( self.next_min_flush_time, is_step_gen=False) @@ -73,19 +73,20 @@ class GCodeRequestQueue: while 1: next_time = max(print_time, self.next_min_flush_time) # Invoke callback for the request - action, min_wait = "normal", 0. + action, next_min_time = "normal", 0. ret = self.callback(next_time, value) if ret is not None: # Handle special cases - action, min_wait = ret + action, next_min_time = ret + self.next_min_flush_time = max(self.next_min_flush_time, + next_min_time) if action == "discard": break if action == "reschedule": - self.next_min_flush_time = max(self.next_min_flush_time, - min_wait) continue - self.next_min_flush_time = next_time + max(min_wait, min_sched_time) - if action != "delay": + self.next_min_flush_time = max(self.next_min_flush_time, + next_time + min_sched_time) + if action != "repeat": break