blackbox-rs
Board support crate for the Blackbox board — STM32H743XI (Cortex-M7 @ 399.36 MHz, rev.Y), built on Embassy.
One call brings the whole board up at its design clocks and hands you ready-to-use drivers for every on-board peripheral:
- 11 LEDs — active-high indicators
- 13 buttons — named, active-low (
Button::Play,Button::Rec, …) - 4 endless encoders — dual-wiper, absolute angle via ADC1 (
Knob::Tl, …) - GT9147 touchscreen — I2C, panel-aligned coordinates
- 320×240 RGB565 display — LTDC, vblank-synced double buffer,
embedded-graphicsdraw target, backlight - CS42528 audio — SAI + DMA, 8 DAC outputs / 2 ADC inputs, 48 kHz
The tricky bits (rev.Y cache erratum, the exact SDRAM timings, the SAI OLM topology the HAL can't express, the LTDC pin map with the unrouted R2 line) are all handled inside the crate — see Hardware below.
The bundled example renders a live debug screen — this is its framebuffer, captured straight off the panel's SDRAM over SWD:

Quick start
# Cargo.toml
[]
= "0.1"
= { = "0.7", = ["arch-cortex-m", "executor-thread"] }
= "0.4"
use Spawner;
use Button;
use ;
async
blackbox_rs::init() returns a [Board]:
Using the peripherals
// LEDs — set individually, or light exactly one
board.leds.set;
board.leds.only;
// Buttons — typed, no magic indices
for b in ALL
// Knobs — absolute angle from the two wipers
let r = board.knobs.read;
info!;
// Touch — panel coordinates, None when idle
if let Some = board.touch.poll
// Display — embedded-graphics on the back buffer, then swap
use ;
let mut fb = board.display.target;
new
.into_styled
.draw
.ok;
board.display.swap.await; // tear-free, paces to ~59 Hz
board.display.set_backlight; // percent, clamped to the 35% ceiling
// Audio — route a sine to a stereo output port, or to raw DAC channels
use ;
play_sine_out; // → DAC3/4
play_sine_on; // arbitrary channel set
Controls reference
| Control | Type | Variants |
|---|---|---|
buttons::Button |
enum | Pads Keys Seqs Song Fx Mix Pset Tools Rec Stop Play Back Info |
knobs::Knob |
enum | Tl Tr Bl Br |
audio::Dac |
enum | Dac1…Dac8 |
audio::Output |
enum | Phones Jack1 Jack2 Jack3 (stereo pairs → .left()/.right()/.channels()) |
audio::AudioIn |
enum | Adc1 Adc2 |
Output port → DAC mapping: Phones=DAC1/2, Jack1=DAC3/4, Jack2=DAC5/6, Jack3=DAC7/8
(assumed — adjust in audio::Output if the hardware differs).
Each enum has ::ALL, .index() and .label().
Prerequisites
A debug probe wired to the board's SWD. The repo's .cargo/config.toml sets the target and a
probe-rs run --chip STM32H743XI runner, so flashing the example is just:
DEFMT_LOG=debug
The bundled src/main.rs is a live debug screen example: every control and its state on
the panel, each LED lit while its button is held, and a 440 Hz tone — a quick bring-up smoke test.
Notes & safety
- Backlight is hard-capped at 35% (
display::MAX_BACKLIGHT_PCT) — the boost regulator's thermal limit.set_backlightclamps; do not raise without a thermal basis. - D-cache is off by design. SDRAM and the audio buffers are coherent with the LTDC/SAI DMA with no maintenance; the MPU still marks those regions non-cacheable so enabling D-cache later is correct (rev.Y erratum ES0392).
- Audio currently renders a sine. The DMA-ISR double-buffer is in place; swap the sine source for a real render to stream arbitrary audio.
- Touch latches its I2C address from the INT (PG12) level at the chip's own power-on:
low →
0x5D(operational, config loaded), high →0x14(degraded, no scanning).initdrives PG12 low across bring-up to bias0x5D, but the chip only re-latches on a real power cycle (not an SWD reset) — if touch is dead and the log shows@ 0x14, power-cycle the board.
Hardware
STM32H743XI (TFBGA240, rev.Y), Cortex-M7 @ 399.36 MHz. Cargo feature stm32h743
(not stm32h743v — the v variant hangs ADC power-up). Facts recovered from working silicon.
Clocks — HSE is a 6.144 MHz crystal (clean audio divisors):
| PLL | Output | Use |
|---|---|---|
| PLL1 P | 399.36 MHz | sysclk (AHB ÷2 → 199.68 MHz, SDCLK 100 MHz) |
| PLL2 P | 12.288 MHz | SAI kernel (256 × 48 kHz) |
| PLL3 R | 6.4 MHz | LTDC pixel clock (~59.4 Hz) |
Memory
| Region | Base | Size | Notes |
|---|---|---|---|
| FLASH | 0x0800_0000 | 2 MB | |
| AXI SRAM | 0x2400_0000 | 512 KB | main RAM (.data/.bss/stack) |
| D2 SRAM | 0x3000_0000 | 256 KB | audio DMA buffers; clock-gated off at reset |
| SDRAM | 0xC000_0000 | 64 MB | external (FMC), framebuffers |
rev.Y erratum ES0392: D-cache stays off; MPU marks SDRAM + D2 SRAM non-cacheable so the LTDC/SAI DMA stay coherent without maintenance.
Peripherals (pin → function)
| Block | Pins | Notes |
|---|---|---|
| Display LTDC | 25 pins, AF14 (R7=PJ0/G6=PI11 AF9) | 320×240 RGB565; panel power PK7; R2 not routed |
| Backlight | PJ6 (TIM8_CH2, AF3) | 50 kHz, 35% hard cap (regulator thermal limit) |
| Audio SAI1/2 | PE2/4/5 PB2 PE3 (AF6), PD11/12/13 (AF10) | CS42528 codec, OLM 20-bit, 48 kHz |
| SDRAM FMC | A/BA/D/NBL/ctrl, all AF12 | 2× IS42S16160J, CL2 |
| I2C1 | PB6 SCL / PB7 SDA (AF4) | 400 kHz blocking; codec 0x4C + touch share it |
| Touch GT9147 | INT PG12 | addr 0x5D (low at POR) / 0x14 (degraded) |
| Buttons ×13 | active-low, board pull-ups | PA0/PA1/PC2/PC3 need the SYSCFG dual-pad fix |
| LEDs ×11 | active-high | |
| Knobs ×4 | 8 wipers on ADC1 | endless encoders, atan2 angle |
License
MIT OR Apache-2.0