From f2cf3e3ed93d53c05af519f9fc0933f1e90ba688 Mon Sep 17 00:00:00 2001 From: Me Here Date: Fri, 5 Jun 2026 22:29:11 -0500 Subject: [PATCH] pm-kit: jog stops promptly on release (drop the decel ramp) Releasing the joystick used to ramp the speed down through zero (~0.3s), so the motor coasted after release. Decel isn't needed (only fast *starts* stall), so release now stops immediately; keep the gentle accel ramp on start. Also only rewrite the PIO clock when the rate actually changes (no 100Hz redundant writes). Co-Authored-By: Claude Opus 4.8 (1M context) --- pico-cp/README.md | 4 ++-- pico-cp/app.py | 24 +++++++++++------------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/pico-cp/README.md b/pico-cp/README.md index c7e2916..c0c44eb 100644 --- a/pico-cp/README.md +++ b/pico-cp/README.md @@ -121,8 +121,8 @@ The Kit can drive a **physical metronome pendulum**: a 4-input unipolar stepper `stepper_accel`, `stepper_jog_start`, `pend_swing_deg`, `stepper_steps_per_rev`) — edit in editor mode, power‑cycle. - **Jog / test mode** (hold **A + B** at boot): the joystick sets **direction only** — **L = CCW, R = CW** — and - the motor **accelerates to `STEPPER_MAX_RATE`** (reversing decelerates through zero first), with an on‑screen - needle + RGB LED and a **live step count + rate readout**. The step pulses are generated by **PIO + DMA** + the motor **accelerates to `STEPPER_MAX_RATE`** (gentle ramp on start so it doesn't stall; releasing stops it + promptly, no coasting), with an on‑screen needle + RGB LED and a **live step count + rate readout**. The step pulses are generated by **PIO + DMA** (hardware‑timed on a state machine), so the motor stays smooth even while the screen redraws — there's no CPU step loop to stall. *Tuning:* hold to spin; raise `STEPPER_MAX_RATE` until the motor skips, then back off; if it stalls *starting*, lower `STEPPER_ACCEL` / `STEPPER_JOG_START`. Power‑cycle (no buttons) to exit. diff --git a/pico-cp/app.py b/pico-cp/app.py index 6cb8e45..834b056 100644 --- a/pico-cp/app.py +++ b/pico-cp/app.py @@ -1237,7 +1237,7 @@ class App: self.display.refresh() # Joystick = DIRECTION only. The CPU just ramps the commanded rate (accel) and sets the PIO # clock; the motor steps autonomously. Reversing decelerates through zero, then the other way. - spin = 0; cur = 0.0; total = 0.0; peak = 0 + spin = 0; cur = 0.0; total = 0.0; peak = 0; rset = -1 now = time.monotonic(); tctl = now; tsample = now gc.collect() while True: @@ -1246,19 +1246,17 @@ class App: cdt = now - tctl; tctl = now dx = self.jx.value - center want = (1 if dx > 0 else -1) if abs(dx) > JOY_DEADZONE else 0 - if spin == 0 and want != 0: # start from rest at the safe pull-in rate + if want == 0: # released -> stop promptly (no coasting) + if spin != 0: + st.off(); spin = 0; cur = 0.0; rset = -1; set_needle(0); gc.collect() + elif want != spin: # (re)start in the requested direction at the pull-in rate spin = want; cur = float(STEPPER_JOG_START); st.run(want > 0); set_needle(spin) - target = float(STEPPER_MAX_RATE) if (spin != 0 and want == spin) else 0.0 - if cur < target: cur = min(target, cur + STEPPER_ACCEL * cdt) # accelerate - elif cur > target: cur = max(0.0, cur - STEPPER_ACCEL * cdt) # decelerate - if spin != 0: - if cur <= 0.0 and want != spin: # finished slowing -> stop, or flip direction - st.off() - if want == 0: spin = 0; set_needle(0); gc.collect() - else: spin = want; cur = float(STEPPER_JOG_START); st.run(want > 0); set_needle(spin) - else: - st.set_rate(cur if cur > 1.0 else 1.0) - total += cur * cdt; peak = max(peak, int(cur)) + rset = int(cur); st.set_rate(cur) + else: # holding -> accelerate up to top speed + if cur < STEPPER_MAX_RATE: + cur = min(float(STEPPER_MAX_RATE), cur + STEPPER_ACCEL * cdt) + total += cur * cdt; peak = max(peak, int(cur)) + if int(cur) != rset: rset = int(cur); st.set_rate(cur) # only rewrite the clock when it changes if now - tsample >= 0.5: # LIVE readout - safe now: the motor runs from PIO/DMA show_stats(int(total), int(cur), peak); tsample = now; self.display.refresh()