Skip to content

feat(rp2040): 5 kHz stepping, hardware info, SSD1306 display, CI coverage + TMC hold-current fix - #309

Merged
ClutchplateDude merged 28 commits into
developfrom
rp2040-support
Oct 17, 2026
Merged

ClutchplateDude merged 28 commits into
developfrom
rp2040-support

Conversation

@ClutchplateDude

@ClutchplateDude ClutchplateDude commented Oct 11, 2026 •

Copy link
Copy Markdown
Member

Follow-up to #298 (merged in 2f77d61): everything the maintainer-side verification branch carries on top of the merged RP2040 port, bench-verified on a BTT SKR Pico v1.0 over multiple hardware sessions.

Fixes

  • TMC2209 hold current (src/Mount.cpp): removes the ihold(31) override introduced with the OAE merge in V1.13.18 (V1.13.18 - Updates #274). IHOLD=31 is the top of the 5-bit current scale (~980 mA RMS per coil with 0.11 R sense and vsense active), so at standstill the driver held motors at nearly 3x the configured operating current instead of matching it. The adjacent rms_current(mA, 1.0f) already sets IHOLD = IRUN, which is the documented intent. The readback log cannot catch this (rms_current() reads back IRUN only). Affects all DRIVER_TYPE_TMC2209_UART boards using hardware serial since V1.13.18. Bench: 1.4 A at rest dropped to 0.12 A on a 470 mA motor at 75 %.
  • Timer rate semantics (src/InterruptCallback.cpp): the repeating alarm now uses a negative delay. Per the Pico SDK, a positive delay measures callback-end to next-start (adding the ISR's own runtime to every period, with load-dependent jitter); negative gives true fixed start-to-start rate, matching the AVR TimerInterrupt semantics the stepper code was designed around.
  • Headless-boot safe logging (src/Utility.cpp, src/b_setup.hpp): logv() composes each line into one print() call instead of seven writes racing the TinyUSB FIFO, and the ignoreFlowControl(true) call documents why unconditional bypass is safe on this core (SerialUSB::write breaks immediately once the CDC TX FIFO is full with no DTR host connected; 1 s no-progress cap otherwise).
  • SKR Pico info display pins corrected to GP0/GP1 (Configuration.hpp, ConfigurationValidation.hpp, matrix_build.py, example config, docs): per the BTT schematic, GP20/GP21 on the SKR Pico only drive on-board MOSFETs (the HB/FAN3 power outputs and the opto-isolated Laser interface) - they are never exposed as raw GPIO on any connector, and wiring an I2C display to those power pins hangs the bus and the firmware loop (USB stays enumerated, serial goes deaf). Info-display pins now default per board (SKR Pico: GP0/GP1 on the left-side header, shared with the default GPS wiring; JackW01: GP20/GP21), and compile-time guards reject GP20/GP21 display pins on the SKR Pico as well as the display+GPS combination. The CI matrix no longer generates that combination.
  • Boot logo and FW version on the info display (InfoDisplayRender.hpp, SSD1306_128x64_Display.*, all platforms): console-mode text updates now draw the header (logo + product name) on every frame instead of relying on a ~0.5 s ISR render window at the end of boot; and the FW line is uppercased - the Bitmap3x5 font has no lowercase glyphs and release-please's lowercase v version prefix rendered as an out-of-range glyph.
  • StallGuard DIAG noise removed (ConfigurationValidation.hpp, docs): the optional AZ/ALT *_DIAG_PIN pragma messages printed on every RP2040 build; StallGuard-based homing is not supported by the firmware on these boards (Hall sensor homing via the endstop inputs is the supported path), so the checks are gone and the docs say so explicitly.
  • DEBUG_LEVEL guards use != 0 instead of > 0 (semantically identical today; robust should DEBUG_NONE ever change value).

Features

  • 5 kHz stepper servicing on RP2040 (Configuration_adv.hpp, b_setup.hpp): new STEPPER_CONTROL_INTERVAL_MS knob (0.2 ms on RP2040, 0.5 ms / 2 kHz elsewhere - AVR keeps 2 kHz where higher rates disturb serial). Override in a local config to experiment.
  • USB serial defaults to 115200 on RP2040 (Constants.hpp, Configuration.hpp, platformio.ini): new SERIAL_BAUDRATE_FAST, selected automatically on ARDUINO_ARCH_RP2040 with matching monitor_speed. The 19200 ASCOM default exists because the 8-bit AVR platforms cannot keep up and is unchanged for them.
  • getMountHardwareInfo() reports RP2040 (src/Mount.cpp), so :XGM# returns a populated hardware field instead of an empty one.
  • SSD1306 info display on RP2040 (platformio.ini, SSD1306_128x64_Display.hpp, matrix_build.py): adds the OLED library to [common_rp2xxx], compiles the display TU again (drops the src_filter exclusion), wraps its includes in PUSH_NO_WARNINGS/POP_NO_WARNINGS, and extends the matrix SSD1306 constraint to rp2040_skr_pico.
  • CI coverage: both RP2040 environments added to the build matrix; matrix_build.py gains AZ/ALT driver variants and GPS for the SKR Pico.
  • Wiring guide (docs/RP2040-WIRING.md): pin tables for both boards - steppers, display, GPS, Hall homing, DEC endstops, fan, power cautions.
  • Config robustness (Configuration_adv.hpp): *_END_SWITCH_ACTIVE_STATE and *_ENDSWITCH_BACKSLEW_DEG are now defined unconditionally - DEC-only endstop configurations previously failed to compile on all platforms.

Bench verification (SKR Pico v1.0)

  • Boot, USB CDC, serial/Meade round-trip, :XGM# hardware info
  • Headless boot on 12 V external power (bounded 3 s console wait), late-connecting host gets replies
  • Replies verified with DTR deasserted (how OATControl and C# SerialPort apps open ports) - no host-side changes required anywhere
  • TMC2209 UART configuration on powered drivers at 5 kHz; motor stepping confirmed; rest current verified at ~0.12 A
  • Sustained slewing at 5 kHz: verified (manual moves and stop commands)
  • SSD1306 info display verified on GP0/GP1 (renders through boot and operation, serial stays responsive; display+GPS pin sharing validated by the new compile-time guards)
  • Note for JackW01 owners: the 5 kHz default is new for that board (previously 2 kHz); STEPPER_CONTROL_INTERVAL_MS reverts it in one line if needed.

Still to bench: Hall sensor homing (RA/DEC) and DEC endstop switches, pending sensor wiring.

🤖 Generated with Claude Code

ClutchplateDude and others added 25 commits October 10, 2026 16:45
Board ID defines (2101/2201), pins-file selection arms, local-config
arms, and validation gates for ARDUINO_ARCH_RP2040. Adds the
[common_rp2xxx] platformio section (Earle Philhower core via the
maxgerhardt platform wrapper) with rp2040_jackw01 and rp2040_skr_pico
environments, and unignores Configuration_local_examples/.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The RA/DEC END_SWITCH_ACTIVE_STATE and ENDSWITCH_BACKSLEW_DEG constants
were only defined inside the '#else' of '#ifndef USE_*_END_SWITCH', i.e.
only when that axis's end switches were enabled. EndSwitches.cpp
references both axes' backslew constants whenever either feature is on,
so a DEC-only (or RA-only) end switch configuration failed to compile
on every platform.

Co-Authored-By: Claude Code <noreply@anthropic.com>
- EPROMStore: flash-backed EEPROM section for the Philhower arduino-pico
  core (API-identical to the ESP32 path)
- InterruptCallback: repeating hardware-alarm timer via the Pico SDK
  add_repeating_timer_us() API for the 2 kHz stepper control interrupt
- b_setup: USB CDC flow-control bypass and connect wait, GPS/UART1 pin
  remapping, fan PWM, JackW01 shared-EN handling, and 115200 baud TMC
  serial on RP2040 (57600 kept on AVR/ESP32); AZ/ALT DIAG pinMode calls
  now guarded like RA/DEC (pins optional per board)
- c_buttons/f_serial: explicit serial polling - the Philhower core does
  not reliably run serialEvent() for USB CDC
- Mount: TMC2209 toff=4 on RP2040 only (toff>=3 required for StealthChop
  slow-decay phase); toff(1) unchanged on AVR/ESP32
- DayTime/Declination/Latitude/Longitude: defaulted copy assignments to
  silence -Wdeprecated-copy on the RP2040 toolchain
- SSD1306_128x64_Display: wrap Arduino.h/SSD1306Wire.h includes in
  PUSH_NO_WARNINGS for the arduino-pico core headers

Co-Authored-By: Claude Code <noreply@anthropic.com>
Pin definitions for the JackW01 Pi Pico printer carrier board and the
BigTreeTech SKR Pico v1.0 (Klipper-derived mappings, TMC2209 UART
address strapping documented per slot), plus a tracked example local
configuration for the SKR Pico.

Ported from PR #298 (EricSchubert); the orphaned bare-Pico pins file
from that PR is intentionally not carried over.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Adds rp2040_jackw01 (24 solutions) and rp2040_skr_pico (96 solutions)
to the CI build matrix with conservative BOARD_SUPPORT restrictions
(no LCD menu display, no gyro, no focuser; TMC2209 UART-only AZ/ALT).
The SSD1306 info display matrix is extended to rp2040_skr_pico.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The platform branch of getMountHardwareInfo() only knew ESP32 and
AVR, so RP2040 builds reported "Unknown" in the hardware info string.

Co-Authored-By: Claude Code <noreply@anthropic.com>
logv() issued seven separate print() calls per log line. On the
arduino-pico USB CDC each write re-enters the USB mutex and the
TinyUSB FIFO path, which multiplies exposure to the core's write-loop
edge cases. Compose the line first and send it with one write.

Co-Authored-By: Claude Code <noreply@anthropic.com>
- Wait for the console to connect before logging in debug builds:
  the CDC TX FIFO holds only ~250 bytes and with no host attached the
  remaining boot output is dropped, so a debug build boots invisibly.
  Release builds are unaffected (logging compiled out, boot headless).
- Make Serial.ignoreFlowControl() opt-in via RP2040_IGNORE_CDC_FLOW_CONTROL
  (default off): forcing TX while no host drains the port can wedge the
  TinyUSB write loop while it holds the USB mutex. Needed only for hosts
  that open the port with DTR=false (e.g. OATControl).
- Drop the RP2040 serial poll from serialLoop(): serialEvent() handles
  CDC RX on the Philhower core (verified on hardware with a display-less
  build answering :GVN#).

Known issue (under investigation, likely arduino-pico/TinyUSB): builds
with sparse debug levels (e.g. STEPPERS|MOUNT|INFO) can wedge within
milliseconds of boot; DEBUG_NONE and DEBUG_ANY builds are stable on the
same hardware.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Add a STEPPER_CONTROL_INTERVAL_MS configuration knob (overridable in
local config) with a 5 kHz default on RP2040; AVR stays at 2 kHz where
higher rates interfere with serial communications.

Also fix the RP2040 repeating-timer interval semantics: a positive
delay_us measures from callback END to next start, silently adding the
callback runtime to every period. Use a negative delay for a fixed
start-to-start rate, matching the AVR TimerInterrupt behavior the
stepper controller was designed for.

Co-Authored-By: Claude Code <noreply@anthropic.com>
A mount powered from an external supply boots with no USB host attached,
and Serial (tud_cdc_connected) never becomes true until a host asserts
DTR. The previous infinite while (!Serial) spin left the firmware parked
before any initialization indefinitely - no drivers, no timer, no
commands - whenever it was powered without an already-open console.

Wait at most 3 seconds for a console, then boot headless. Debug builds
that boot headless lose the boot log (CDC drops writes with no host),
but reset-with-console-open still shows the full boot.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The OAE firmware merge in V1.13.18 (#274) added ihold(31) after
rms_current(mA, 1.0f) in the hardware-UART RA and DEC driver setup.
IHOLD=31 is the top of the 5-bit current scale (~980 mA RMS per coil
with 0.11R sense and vsense active), so at standstill the driver holds
the motor at nearly three times the configured operating current
instead of matching it - hot motors and a supply draw about 4x the
expected value. The rms_current(..., 1.0f) call on the line above
already sets IHOLD = IRUN, which is the documented intent ("hold
current = run current"); the override only clobbers it.

Note the readback log "Actual rms_current" cannot catch this: it reads
cs2rms(IRUN) and never reflects IHOLD.

The software-serial driver variants never got the override, so this
also restores consistency between the two setup paths. Affects all
TMC2209_UART boards using hardware serial since V1.13.18.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…lies

Make Serial.ignoreFlowControl(true) unconditional on RP2040 instead of
opt-in via RP2040_IGNORE_CDC_FLOW_CONTROL. Hosts that open the port
without asserting DTR (OATControl and other C# SerialPort apps) were
getting all firmware output - including Meade replies - silently
dropped, since tud_cdc_connected() is the DTR bit.

The opt-in hedge guarded against a write-path stall this core version
cannot produce: SerialUSB::write() breaks immediately once the CDC TX
FIFO is full and no DTR host is connected, and drops after 1 second
without progress otherwise (framework-arduinopico SerialUSB.cpp). So
unconditional bypass is safe for headless boots while making the mount
usable from every host application without changes. As a bonus the
first ~257 bytes of the boot log survive a headless boot and reach the
first host that connects.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Develop now carries the RP2040 port via the rebuilt #298. Take our side
where this branch holds the verified deltas (5 kHz stepping and timer
semantics, TMC hold-current fix, RP2040 hardware info, SSD1306 info
display, CI matrix entries, single-write logging), and develop's side
for the ConfigurationValidation DIAG advisories and the .gitignore
ordering.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…branch

The PassthroughCmd arm of the action chain used a single = instead of
==, so reaching that arm assigned the String and evaluated it as truth:
the branch always fired, and any unrecognized action silently entered
passthrough-command mode instead of being ignored. On the RP2040
toolchain this also fails to compile (-Werror=parentheses via the
Philhower core String header), which is how the CI matrix caught it.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The generated matrix solutions previously included A4988 and standalone
driver types for the RA/DEC axes of both RP2040 boards. Those driver
types need MS pins, which the boards do not route (on-board TMC2209s
wired for UART only), so the affected solutions failed to build in CI
with "Missing pin assignments for MS pins". Restrict RA_DRIVER_TYPE and
DEC_DRIVER_TYPE to DRIVER_TYPE_TMC2209_UART for both boards, matching
the AZ/ALT restrictions that were already in place.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Documents the default pin assignments for the SKR Pico and JackW01
boards: power (including the USB-jumper/VIN contention gotcha),
steppers and TMC2209 UART addresses, USB serial behavior, SSD1306 info
display (with the SDA=5/SCL=4 default colliding on the SKR Pico), GPS
on the SKR Pico left header, Hall homing and DEC endstop inputs, the
FAN1 output, and the features that are not available on RP2040.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Bench testing on the SKR Pico v1.0 revealed that GP20/GP21 cannot be used
for the I2C info display: both only drive on-board MOSFETs (HB/FAN3 and
the opto-isolated Laser output) and are never exposed as raw GPIO on any
connector. Wiring a display to those power pins hangs the I2C bus and with
it the firmware loop (USB stays enumerated, serial goes deaf).

- Default the info display to GP0/GP1 on the SKR Pico (the only
  header-accessible I2C0 pair, on the left-side header) and to GP20/GP21
  on the JackW01; ConfigurationValidation now rejects display pins on
  GP20/GP21 for the SKR Pico, and the display+GPS combination (shared pair)
- matrix_build: stop generating skr_pico display+GPS solutions (would now fail)
- Example config, wiring doc and wiki page updated accordingly

Bench-verified: SSD1306 on GP0/GP1 renders, serial stays responsive.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…upported

The TMC2209 DIAG outputs are irrelevant for the OAT firmware on these
boards (StallGuard-based autohome is not supported - Hall sensor homing
uses the endstop inputs instead), so the wiring tables now name the column
plainly 'Endstop input' and the not-supported lists spell out the
limitation. Example config comments no longer mention RA_DIAG_PIN.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The optional AZ/ALT DIAG pin checks printed a pragma message on every
RP2040 build (both boards leave AZ/ALT DIAG unwired). StallGuard-based
homing is not supported by the OAT firmware on these boards - Hall sensor
homing via the endstop inputs is the supported path - so the messages are
pure noise. Remove the checks; the pins stay optional.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…ersion

Boot console updates (addConsoleText/updateConsoleText render with a null
content callback) never drew the header, so the logo only appeared via
ISR-driven renders in the ~0.5s window between stepper-timer start and
console-mode end - invisible on a fast RP2040 boot and fragile everywhere.
Extract drawHeader() from drawScreen() and call it from the base render()
on every console-mode frame, so the logo and product name show from the
first boot line through BOOT COMPLETE on all platforms.

Also uppercase the FW version line: Bitmap3x5 covers only chars 32-90 and
release-please now stamps VERSION with a lowercase v prefix, which
rendered as an out-of-range glyph. Console lines already uppercase.

Bench-verified on the SKR Pico (SSD1306 on GP0/GP1).

Co-Authored-By: Claude Code <noreply@anthropic.com>
The 19200 ASCOM default exists because the 8-bit AVR platforms cannot keep
up; the RP2040 USB CDC port has no such constraint. Add SERIAL_BAUDRATE_FAST
and select it automatically on ARDUINO_ARCH_RP2040, and set monitor_speed
accordingly for the RP2040 environments. AVR and ESP32 are unchanged.

Co-Authored-By: Claude Code <noreply@anthropic.com>
ClutchplateDude and others added 2 commits October 13, 2026 21:52
The old comment said the alarm API works "without relying on FreeRTOS or
a second core", which reads as if FreeRTOS were an option on this platform.
It was meant as a contrast to the ESP32 port, which steps from a FreeRTOS
task. State that explicitly and note the Philhower core has no RTOS.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Comment thread src/SSD1306_128x64_Display.hpp Outdated
Comment thread src/Mount.cpp Outdated
Comment thread Example_Configuration_local_rp2040_skr_pico.hpp Outdated
Comment thread Example_Configuration_local_rp2040_skr_pico.hpp Outdated
Comment thread docs/RP2040-WIRING.md Outdated
Comment thread src/Utility.cpp
Comment thread .gitignore
- SSD1306 display header: provide no-op PUSH/POP_NO_WARNINGS fallbacks
  (mirroring inc/Globals.hpp) so Arduino IDE builds, which lack the
  PlatformIO -include of macros/gcc/Macros.hpp, can still compile it.
- updateInfoDisplay(): declare isSlewingAny inside the RP2040 arm; it was
  unused (and -Werror=unused-variable under -Wall) on display-enabled
  AVR/ESP32 configs.
- Utility logv(): restore flush() on all non-RP2040 platforms so AVR/ESP32
  logging behavior is unchanged; only the arduino-pico USB CDC path skips
  it (CDC flush can stall without a reader).
- Example config: fix AZ_CIRCUMFERENCE pi typo; align the stepper interval
  with the 5 kHz PR default (10 kHz noted as possible but unverified).
- Docs: drop the nonexistent USE_AUTOHOME flag reference.
- .gitignore: remove duplicated negation lines from the develop merge.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@ClutchplateDude
ClutchplateDude merged commit 82b8592 into develop Oct 17, 2026
10 checks passed
@ClutchplateDude
ClutchplateDude deleted the rp2040-support branch October 17, 2026 21:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants