Add Phase 1 Circle firmware
Greenfield bare-metal firmware for a Pi Zero that lights a WS2812B strip above an 88-key keybed. The Pi is a USB MIDI gadget; the PC is the host and owns the piano connection and everything else. No network stack, no filesystem, no shell. Circle is a submodule pinned to Step51. Builds kernel.img (RASPPI=1, Zero / Zero W) and kernel7.img (RASPPI=2, Zero 2 / Zero 2 W); both coexist on one card, so either model runs from the same SD. Resolves two open assumptions from the plan against the Circle sources rather than by guessing: - CWS28XXStripe clocks the waveform out over SPI at a fixed 6.4MHz, one SPI byte per LED bit. On device 0 that puts data on MOSI = GPIO10 = physical pin 19. The implied 5.28ms frame time matches the plan's arithmetic. - The USB gadget lifecycle follows sample/29-miniorgan, which already carries a USB_GADGET_MODE path. Three details the hardware forces: - The gadget destroys and recreates its CUSBMIDIDevice across a USB suspend, so the kernel re-fetches it and clears notes held at that moment. Otherwise a chord would stay lit forever when the PC sleeps. - Rendering blocks for ~5.3ms of SPI traffic, so the MIDI packet handler only records state and the main loop draws. - MAX_LIT_KEYS complements the global brightness ceiling. 176 LEDs at full white would draw ~10.5A against a 6A supply; together the two clamps make that unreachable rather than merely unlikely. Every Phase 0 product decision has a named slot in firmware/config.h, all overridable at build time via EXTRADEFINE. tests/run.sh compiles the real pianoleds.cpp against stubbed Circle headers and checks the mapping, note-off paths, range clamping and both power clamps across nine configuration variants. It verifies arithmetic, not wiring, and does not replace bench-testing on real hardware. Claude-Session: https://claude.ai/code/session_01TVCB25LBsmeteWvaSMz4Ne
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Builds the Piano LED Visualizer firmware for the Raspberry Pi Zero family.
|
||||
#
|
||||
# Produces both kernel images, which coexist on one SD card:
|
||||
#
|
||||
# kernel.img RASPPI=1 Pi Zero / Zero W (ARM1176)
|
||||
# kernel7.img RASPPI=2 Pi Zero 2 / Zero 2 W (Cortex-A7)
|
||||
#
|
||||
# The Pi picks the right one at boot, so the same card runs on either model.
|
||||
#
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
ROOT=$PWD
|
||||
PREFIX=${PREFIX:-arm-none-eabi-}
|
||||
|
||||
if ! command -v "${PREFIX}gcc" >/dev/null 2>&1; then
|
||||
echo "error: ${PREFIX}gcc not found." >&2
|
||||
echo " Debian/Ubuntu: sudo apt-get install gcc-arm-none-eabi" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f circle/Rules.mk ]; then
|
||||
echo "error: circle/ is empty. Run: git submodule update --init" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p boot
|
||||
|
||||
for SPEC in "1:kernel.img" "2:kernel7.img"; do
|
||||
RASPPI=${SPEC%%:*}
|
||||
IMAGE=${SPEC##*:}
|
||||
|
||||
echo
|
||||
echo "=============== RASPPI=$RASPPI -> $IMAGE ==============="
|
||||
|
||||
cd "$ROOT/circle"
|
||||
./configure -r "$RASPPI" -p "$PREFIX" -f
|
||||
./makeall clean >/dev/null
|
||||
./makeall -j "$(nproc)"
|
||||
|
||||
# The WS28XX driver is an addon and is not built by makeall.
|
||||
make -C addon/WS28XX clean >/dev/null 2>&1 || true
|
||||
make -C addon/WS28XX -j "$(nproc)"
|
||||
|
||||
cd "$ROOT/firmware"
|
||||
make clean >/dev/null 2>&1 || true
|
||||
make -j "$(nproc)"
|
||||
|
||||
cp "$ROOT/firmware/$IMAGE" "$ROOT/boot/$IMAGE"
|
||||
echo "wrote boot/$IMAGE"
|
||||
done
|
||||
|
||||
cd "$ROOT/firmware" && make clean >/dev/null 2>&1 || true
|
||||
|
||||
echo
|
||||
echo "Built:"
|
||||
ls -l "$ROOT/boot"
|
||||
echo
|
||||
echo "Copy boot/*.img plus the Raspberry Pi firmware files (bootcode.bin,"
|
||||
echo "start.elf, fixup.dat) and cmdline.txt onto a FAT32 SD card."
|
||||
echo "See README.md for the full card layout."
|
||||
Reference in New Issue
Block a user