Add readme files for each of the crates
This commit is contained in:
128
crates/synth-embedded/README.md
Normal file
128
crates/synth-embedded/README.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# synth-embedded
|
||||
|
||||
Real-time audio synthesizer firmware for the **Raspberry Pi Pico 2** (RP2350), built on the [Embassy](https://embassy.dev) async runtime. Outputs 48 kHz stereo I2S audio to a PCM5102A DAC and accepts MIDI input over UART.
|
||||
|
||||
## Hardware
|
||||
|
||||
| GPIO | Signal | Connected to |
|
||||
|------|--------|-------------|
|
||||
| 9 | BCK (bit clock) | PCM5102A BCK |
|
||||
| 10 | LRCK (word select) | PCM5102A LRCK |
|
||||
| 11 | DATA | PCM5102A DIN |
|
||||
| 1 | UART0 RX | MIDI IN (optocoupler) |
|
||||
|
||||
Any PCM5102A-compatible I2S DAC (e.g., the CJMCU-5102 module) works. Standard MIDI current-loop interface: 5 mA optocoupler on GPIO 1, no UART TX required.
|
||||
|
||||
## Architecture
|
||||
|
||||
Two Embassy tasks run concurrently on core 0:
|
||||
|
||||
```
|
||||
┌──────────────┐ Mutex<SynthParams> ┌──────────────┐
|
||||
│ midi_task │ ──────────────────────────▶│ audio_task │
|
||||
│ (UART RX) │ NoteOn/Off, CC, PB, PC │ (PIO I2S) │
|
||||
└──────────────┘ └──────────────┘
|
||||
│ │
|
||||
GPIO 1 GPIO 9/10/11
|
||||
31 250 baud 48 kHz I2S
|
||||
```
|
||||
|
||||
### `audio_task`
|
||||
|
||||
1. Initialises the `PioI2sOut` driver (PIO0 state machine 0, 24-bit, 48 kHz).
|
||||
2. Creates the DSP chain: **VCO → SVF → VCA ← ADSR**.
|
||||
3. Runs a double-buffered DMA loop — while one 256-frame block transfers to the PIO FIFO, the next block is rendered on the CPU.
|
||||
|
||||
**Block size:** 256 frames × 2 channels × 4 bytes = 2 KB per buffer, 5.33 ms per block. At 150 MHz the Cortex-M33 has roughly 10× headroom for the DSP render.
|
||||
|
||||
**Sample format:** 24-bit signed PCM, left-justified in bits 31..8 of a `u32` word, interleaved L/R: `[L0, R0, L1, R1, …]`.
|
||||
|
||||
### `midi_task`
|
||||
|
||||
Reads raw bytes from UART0 at 31 250 baud (standard MIDI) and feeds them to `synth_core::MidiParser`. Decoded events update the shared `PARAMS` mutex:
|
||||
|
||||
| Event | Action |
|
||||
|-------|--------|
|
||||
| Note On | Set note, velocity, gate = true |
|
||||
| Note Off | Clear gate if note matches |
|
||||
| CC 1 (mod wheel) | Filter resonance |
|
||||
| CC 7 | Master volume |
|
||||
| CC 72 | Release time (0–8 s) |
|
||||
| CC 73 | Attack time (0–4 s) |
|
||||
| CC 74 | Filter cutoff (80 Hz–18 kHz) |
|
||||
| CC 75 | Decay time (0–4 s) |
|
||||
| Pitch Bend | ±2 semitones |
|
||||
| Program Change | Waveform (0=Sine 1=Saw 2=Square 3=Triangle 4=Pulse) |
|
||||
|
||||
## Building
|
||||
|
||||
### Prerequisites
|
||||
|
||||
```bash
|
||||
# Rust target for RP2350 Cortex-M33
|
||||
rustup target add thumbv8m.main-none-eabihf
|
||||
|
||||
# probe-rs for flashing
|
||||
cargo install probe-rs-tools
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
cargo build -p synth-embedded --release
|
||||
```
|
||||
|
||||
### Flash
|
||||
|
||||
Connect a debug probe (Raspberry Pi Debug Probe, J-Link, etc.) and run:
|
||||
|
||||
```bash
|
||||
cargo run -p synth-embedded --release
|
||||
# or equivalently:
|
||||
probe-rs run --chip RP2350 target/thumbv8m.main-none-eabihf/release/synth-embedded
|
||||
```
|
||||
|
||||
The `runner` in `.cargo/config.toml` calls `probe-rs run` automatically.
|
||||
|
||||
### Debug logging
|
||||
|
||||
`defmt` logs are streamed over RTT. View them with:
|
||||
|
||||
```bash
|
||||
probe-rs attach --chip RP2350 --log-format '{t} {L} {s}'
|
||||
```
|
||||
|
||||
The `DEFMT_LOG` environment variable (set to `debug` by default in `.cargo/config.toml`) controls verbosity.
|
||||
|
||||
## Linker Setup
|
||||
|
||||
The crate uses a custom `memory.x` to describe the RP2350's 2 MB flash and 520 KB SRAM. `build.rs` copies it to the build output directory so `cortex-m-rt`'s `link.x` can find it via `INCLUDE memory.x`.
|
||||
|
||||
`.cargo/config.toml` passes two linker scripts:
|
||||
- `-Tdefmt.x` — places the defmt log sections
|
||||
- `-Tlink.x` — cortex-m-rt startup, vector table, and `INCLUDE memory.x`
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Crate | Purpose |
|
||||
|-------|---------|
|
||||
| `synth-core` | DSP modules (VCO, SVF, ADSR, VCA, MIDI parser) |
|
||||
| `embassy-executor` | Async task executor (Cortex-M) |
|
||||
| `embassy-rp` | RP2350 HAL + PIO I2S driver |
|
||||
| `embassy-sync` | `Mutex` for shared parameter state |
|
||||
| `embassy-time` | Timekeeping |
|
||||
| `cortex-m` / `cortex-m-rt` | ARM Cortex-M runtime |
|
||||
| `defmt` + `defmt-rtt` | Structured logging over RTT |
|
||||
| `panic-probe` | Panic messages sent to debug probe |
|
||||
| `fixed` | Fixed-point clock divisor math |
|
||||
| `libm` | `no_std` math (`powf` for pitch bend) |
|
||||
|
||||
## Customisation
|
||||
|
||||
**Change the oscillator waveform default** — edit `SynthParams::new()` in `src/params.rs`.
|
||||
|
||||
**Change block size** — edit `BLOCK_FRAMES` in `src/audio.rs`. Larger blocks reduce CPU wake-up overhead; smaller blocks reduce latency.
|
||||
|
||||
**Add a second voice** — instantiate a second `(Vco, Adsr, Svf, Vca)` tuple in `audio_task`, mix both outputs before the DMA write.
|
||||
|
||||
**Change GPIO pins** — update the constants in `src/main.rs` and re-wire accordingly.
|
||||
Reference in New Issue
Block a user