metronome/rust/pm-daisy/README.md
Me Here 802e46f5bb pm-daisy: wire Pod controls (encoder/buttons/knobs/RGB LEDs)
The Daisy Pod hardware arrived (Seed 1.2 / PCM3060). Build the controls
phase on top of the audio spike: encoder turn = tempo, push = play/pause,
buttons step the program list, knob 1 = volume, RGB LED 1 = beat colored
by dynamic, RGB LED 2 = transport. Boot still plays the spike groove so
the spike's decision criteria stay observable.

- pm-synth: add Player::{position,seek,set_volume,last_level} + Synth::
  set_master, with host tests (tests/player.rs). Live tempo/program
  changes rebuild the Player in thread context and swap it in under a
  short critical section, preserving loop phase (seek) on tempo changes.
- pm-daisy: SysTick 1 kHz millis tick, non-blocking beat flash, and
  PLAYING/VOLUME/LAST_LEVEL atomics the audio IRQ honors/publishes.
- New modules controls.rs (buttons/encoder/pots, libDaisy debounce +
  quadrature decode, 1 kHz poll), leds.rs (active-low RGB + boot
  self-test), programs.rs (spike groove at index 0 + pm-grid grooves).
- Pin map verified against libDaisy daisy_pod.cpp + the daisy crate's
  pins.rs. Builds clean for all three Seed revisions; 91 KB/128 KB flash.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 20:04:59 -05:00

83 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# pm-daisy — PolyMeter click engine on the Daisy Pod
A time-boxed spike (see [`docs/daisy-spike.md`](../../docs/daisy-spike.md)): play the PolyMeter
groove engine on real Cortex-M7 hardware to judge whether the [Electrosmith Daisy
Pod](https://electro-smith.com/products/pod) (STM32H750, onboard audio codec) is a credible home
for the audio engine if PolyMeter ever grows into a real-time audio workstation.
It boots playing a hardcoded 124-BPM 909 pattern (`pm_synth::SPIKE_PROGRAM`) out the Pod's audio
jack, flashing the Daisy Seed's USER LED on each click.
## What runs here
- **Shared engine, verified on host.** The audio is produced by `pm_synth::Player` — the *exact*
same code the host `synthrender` renders to `pm-daisy-preview.wav`. Listen to that WAV to hear
what the hardware should play before you flash anything.
- **Transport only is new.** This crate is a thin board-support binary: heap + board bring-up +
the SAI audio-DMA interrupt feeding `Player::next_sample()` into stereo frames. Structure follows
the `daisy` crate's `examples/audio.rs`.
## ⚠️ Set the board revision (or you get silence)
The Daisy Seed comes in revisions with different audio codecs. Pick the one on **your** Seed (check
the sticker / silkscreen) — it's a Cargo feature:
| Your Seed | Codec | Build with |
|---|---|---|
| Daisy Seed (original) | AK4556 | `./build.sh seed` |
| **Daisy Seed 1.1** (default) | WM8731 | `./build.sh` (or `seed_1_1`) |
| Daisy Seed 1.2 / Seed2 DFM | PCM3060 | `./build.sh seed_1_2` |
## Build
```sh
./build.sh [revision] # containerized (pm-rust:2); produces pm-daisy.bin + pm-daisy.elf
```
## Flash — two options
**A. Debug probe (recommended — gives you defmt logs over RTT):**
A probe wired to the Seed's SWD pins (e.g. the Raspberry Pi Debug Probe you already use for
pm-grid/pm-kit — see [`rust/probe-flash.md`](../probe-flash.md)).
```sh
probe-rs run --chip STM32H750VBTx pm-daisy.elf # or `cargo run --release` from this dir
```
**B. USB DFU (no probe needed):**
Hold the Daisy's **BOOT** button, tap **RESET**, release BOOT — the Seed enumerates as STM32 system
DFU. Then:
```sh
dfu-util -a 0 -s 0x08000000:leave -D pm-daisy.bin -d ,0483:df11
```
## Too big for 128 KB?
The STM32H750 has only **128 KB internal flash**. If the linker reports a `FLASH` overflow, flash via
the **Daisy Bootloader** to the 8 MB QSPI instead:
1. Install the bootloader once at <https://flash.daisy.audio/> (Bootloader tab, v6.x).
2. In [`memory.x`](memory.x), set `FLASH : ORIGIN = 0x90040000, LENGTH = 8M - 0x40000` (commented there).
3. Rebuild, then enter the bootloader (tap BOOT within 2 s of reset; LED pulses) and:
```sh
dfu-util -a 0 -s 0x90040000:leave -D pm-daisy.bin -d ,0483:df11
```
## Pod controls
The Pod's buttons, encoder, knobs, and RGB LEDs are wired up (in `src/controls.rs` + `src/leds.rs`).
Boot still plays the spike groove (program index 0), so the original spike criteria stay observable.
| Control | Action |
|---|---|
| Encoder turn | tempo ±1 BPM per detent (clamped 5300) |
| Encoder push | play / pause (ignored if you turned while holding it) |
| Button 1 / Button 2 | previous / next program (`src/programs.rs`, wraps) |
| Knob 1 | master volume (read at boot — no startup jump) |
| Knob 2 | reserved (read but unused) |
| RGB LED 1 | beat flash, colored by dynamic: accent = yellow, normal = cyan, ghost = magenta |
| RGB LED 2 | transport: green = playing, red = paused |
| Seed USER LED | unchanged spike beat flash |
At boot both RGB LEDs cycle R → G → B (a ~0.45 s self-test) so a miswired or dead leg is obvious
before audio starts. A live tempo or program change rebuilds the engine in the main loop (not the
audio IRQ) and swaps it in under a short critical section, preserving loop phase on tempo changes.
**Pin map** (verified against libDaisy `daisy_pod.cpp`, cross-checked with the `daisy` crate's
`pins.rs`): buttons D27/D28 (PG9/PA2), encoder A/B/click D26/D25/D13 (PD11/PA0/PB6), knobs D21/D15
(PC4/PC0 → ADC1), LED 1 D20/D19/D18 (PC1/PA6/PA7), LED 2 D17/D24/D23 (PB1/PA1/PA4). The RGB LEDs are
common-anode (active-low). If the encoder feels reversed or double-steps, adjust the decode in
`Pod::poll` (detents-per-quadrature-cycle varies by encoder).