Firmware¶
paintress-firmware is not published yet. This page describes it so the
rest of the stack makes sense.
The firmware runs on a dual-core RP2350 with 8 MB of external PSRAM
mapped through XIP. The default PICO_BOARD in CMakeLists.txt is
solderparty_rp2350_stamp_xl. It runs at an overclocked 250 MHz, with the
core voltage raised before the clock speed.
Heads that share the same wire frame use one firmware binary. The generated header defines line size, clocks, edges and packing. Head name and nozzle count are checked on the host, since the board cannot detect the fitted head.
Dual-core split¶
The cores communicate through two SPSC queues: commands from core 0 to core 1, and events in the reverse direction.
Core 0 owns the USB link. It receives commands and swath data from the daemon, decodes frames with CRC-16-CCITT, drives the swath manager and forwards core 1's events to the host.
Core 1 runs the print engine. It brings up the printhead drivers and then loops on the inter-core command queue, executing arms (prints) and purges.
Fixed firing grid¶
The print loop schedules one column every line_delay_us. Each ARM carries
this interval, so it does not need to be configured separately on the device.
The next column's data DMA starts after the current column fires. Scheduling
the next tick before per-line bookkeeping prevents that work from delaying the
grid.
PSRAM double buffer¶
The 8 MB PSRAM is divided into two equal slots. One holds the printing swath while the other receives the next. Each packed 147-byte USB line expands to 196 bytes in PSRAM: 49 32-bit words.
Printhead drive¶
Three PIO and DMA interfaces drive the head:
- DAC: streams drive waveform samples (rise, default, fall) over a 10-bit parallel bus, with two clock pins selectable at runtime.
- Data bus: a DDR-style parallel bus, packing four 6-bit values into each 32-bit word the DMA pushes to PIO.
- Window selector: a 3-bit selector, latch / A / B.
An ADC reads the head's integrated thermistor. Its curve is unknown, with no datasheet and no reverse engineering done on it yet, so the conversion parameters are placeholders. The thermal monitor only logs readings by default; it does not enforce temperature limits.
Starting a swath: hardware trigger¶
ARM latches the window selector, ramps up the DAC and preloads line 0. The
firmware then waits for a rising edge on TRIGGER_IN_PIN. It samples the pin
only while armed, after establishing a low baseline. If no edge arrives,
DEFAULT_TRIGGER_TIMEOUT_US limits the wait.
Stopping, resetting and the watchdog¶
ABORT cuts the electrical drive while the current swath loop continues.
RESET and the watchdog reboot the chip.
ABORTis a purely electrical kill-switch on core 0. It drops the DAC standby and reset pins and latches the DAC off across both cores. A swath already firing runs to its natural end dry, andARMandPURGEare NACKedDAC_LATCHEDuntil recovery.RESETis a full chip reboot. The firmware ACKs, drains the TX and reboots through the watchdog, clearing slots, the DAC latch and all other state. USB re-enumerates in a second or two; the daemon waits and reconnects.- A hardware watchdog, fed by core 0 only while core 1's heartbeat advances, reboots the chip when either core wedges. That drop is unexpected, so it cancels the print.
A commanded reboot leaves a marker in watchdog scratch register 0. IDENTIFY
reports boot_flags so the host can distinguish a commanded reset (bit 0)
from watchdog fault recovery (bit 1).
Thermal enforcement remains disabled (SAFETY_ENFORCE 0) until the thermistor
has been characterised.
Build and flash¶
With the Raspberry Pi Pico SDK in place (PICO_SDK_PATH set), run
cmake .. && make -j in build. The output is paintress_firmware.uf2,
flashed over BOOTSEL mass storage or with picotool load -v -x. The
firmware emits no printf over USB; host output arrives as LOG frames on
the same CDC the daemon uses. Pure modules are host-tested under tests/
with CTest.