pm-kit milestone 2: ST7796 display bring-up

Init the Kit's ST7796 320x480 over SPI0 (SCK=GP2, MOSI=GP3, CS=GP5, DC=GP6,
RST=GP7; BGR, colours inverted, 16 MHz) via mipidsi 0.9 + embedded-graphics, and
draw a panel + "PM-KIT / RUST OK" so SPI + the graphics stack are verifiable on
screen. GP25 LED keeps blinking as a heartbeat.

Compiles for thumbv8m; runtime (does it draw? colours/orientation right?) is the
on-device check. Next: tune orientation/colour if needed, then inputs + audio + pm-core.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Me Here 2026-05-31 20:56:07 -05:00
parent 8b4fca2a74
commit 4275187008
2 changed files with 67 additions and 10 deletions

View file

@ -2,13 +2,16 @@
name = "pm-kit" name = "pm-kit"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
description = "PM_K-1 firmware (RP2350 / Pico 2). Stage 3 bring-up: boot-proof blink, then drivers + pm-core." description = "PM_K-1 firmware (RP2350 / Pico 2). Stage 3 bring-up: boot blink → display → drivers + pm-core."
[dependencies] [dependencies]
rp235x-hal = { version = "0.3", features = ["binary-info", "critical-section-impl", "rt"] } rp235x-hal = { version = "0.3", features = ["binary-info", "critical-section-impl", "rt"] }
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
panic-halt = "1" panic-halt = "1"
embedded-hal = "1" embedded-hal = "1"
embedded-hal-bus = "0.2"
mipidsi = "0.9"
embedded-graphics = "0.8"
[profile.release] [profile.release]
opt-level = "s" opt-level = "s"

View file

@ -1,29 +1,44 @@
//! PM_K-1 firmware — Stage 3 bring-up milestone 1: prove our Rust boots on the Pico 2 (RP2350). //! PM_K-1 firmware — Stage 3 bring-up.
//! //! Milestone 1 (done): blink GP25 → proved boot/flash.
//! Blinks the onboard LED (GP25). If it blinks, the whole path works: toolchain, RP2350 boot //! Milestone 2 (this): init the ST7796 320×480 display over SPI0 and draw to it.
//! block, memory layout, flash, and the cortex-m-rt entry. Drivers (display / audio / inputs / //! Pins (from the CircuitPython firmware): SCK=GP2, MOSI=GP3, CS=GP5, DC=GP6, RST=GP7;
//! USB-MIDI) and the `pm-core` engine come next, once boot is confirmed on hardware. //! BGR panel, colours inverted. LED on GP25 keeps blinking as a heartbeat.
#![no_std] #![no_std]
#![no_main] #![no_main]
use embedded_graphics::{
mono_font::{ascii::FONT_10X20, MonoTextStyle},
pixelcolor::Rgb565,
prelude::*,
primitives::{PrimitiveStyle, Rectangle},
text::Text,
};
use embedded_hal::delay::DelayNs; use embedded_hal::delay::DelayNs;
use embedded_hal::digital::OutputPin; use embedded_hal::digital::OutputPin;
use embedded_hal_bus::spi::ExclusiveDevice;
use mipidsi::interface::SpiInterface;
use mipidsi::models::ST7796;
use mipidsi::options::{ColorInversion, ColorOrder, Orientation};
use mipidsi::Builder;
use panic_halt as _; use panic_halt as _;
use rp235x_hal as hal; use rp235x_hal as hal;
use hal::fugit::RateExtU32;
use hal::Clock;
/// Image definition block — the RP2350 bootrom looks for this to boot the image. /// Image definition block — the RP2350 bootrom looks for this to boot the image.
#[link_section = ".start_block"] #[link_section = ".start_block"]
#[used] #[used]
pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
const XTAL_FREQ_HZ: u32 = 12_000_000; // Pico 2 crystal const XTAL_FREQ_HZ: u32 = 12_000_000;
const WIDTH: u16 = 320;
const HEIGHT: u16 = 480;
#[hal::entry] #[hal::entry]
fn main() -> ! { fn main() -> ! {
let mut pac = hal::pac::Peripherals::take().unwrap(); let mut pac = hal::pac::Peripherals::take().unwrap();
let mut watchdog = hal::Watchdog::new(pac.WATCHDOG); let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
let clocks = hal::clocks::init_clocks_and_plls( let clocks = hal::clocks::init_clocks_and_plls(
XTAL_FREQ_HZ, XTAL_FREQ_HZ,
pac.XOSC, pac.XOSC,
@ -37,11 +52,50 @@ fn main() -> ! {
.unwrap(); .unwrap();
let mut timer = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks); let mut timer = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
let sio = hal::Sio::new(pac.SIO); let sio = hal::Sio::new(pac.SIO);
let pins = hal::gpio::Pins::new(pac.IO_BANK0, pac.PADS_BANK0, sio.gpio_bank0, &mut pac.RESETS); let pins = hal::gpio::Pins::new(pac.IO_BANK0, pac.PADS_BANK0, sio.gpio_bank0, &mut pac.RESETS);
let mut led = pins.gpio25.into_push_pull_output(); let mut led = pins.gpio25.into_push_pull_output();
// --- ST7796 over SPI0 ---
let sclk = pins.gpio2.into_function::<hal::gpio::FunctionSpi>();
let mosi = pins.gpio3.into_function::<hal::gpio::FunctionSpi>();
let dc = pins.gpio6.into_push_pull_output();
let rst = pins.gpio7.into_push_pull_output();
let cs = pins.gpio5.into_push_pull_output();
let spi = hal::spi::Spi::<_, _, _, 8>::new(pac.SPI0, (mosi, sclk));
let spi = spi.init(
&mut pac.RESETS,
clocks.peripheral_clock.freq(),
16.MHz(),
embedded_hal::spi::MODE_0,
);
let spi_device = ExclusiveDevice::new_no_delay(spi, cs).unwrap();
let mut buffer = [0u8; 512];
let di = SpiInterface::new(spi_device, dc, &mut buffer);
let mut display = Builder::new(ST7796, di)
.reset_pin(rst)
.display_size(WIDTH, HEIGHT)
.color_order(ColorOrder::Bgr)
.invert_colors(ColorInversion::Inverted)
.orientation(Orientation::new())
.init(&mut timer)
.unwrap();
// background
display.clear(Rgb565::new(2, 4, 8)).unwrap();
// a panel + label so we can see SPI + the graphics stack working
Rectangle::new(Point::new(20, 60), Size::new((WIDTH - 40) as u32, 140))
.into_styled(PrimitiveStyle::with_fill(Rgb565::new(2, 40, 50)))
.draw(&mut display)
.unwrap();
let title = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE);
Text::new("PM-KIT", Point::new(40, 110), title).draw(&mut display).unwrap();
Text::new("RUST OK", Point::new(40, 150), title).draw(&mut display).unwrap();
loop { loop {
led.set_high().unwrap(); led.set_high().unwrap();
timer.delay_ms(250); timer.delay_ms(250);
@ -54,4 +108,4 @@ fn main() -> ! {
#[link_section = ".bi_entries"] #[link_section = ".bi_entries"]
#[used] #[used]
pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 1] = pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 1] =
[hal::binary_info::rp_program_name!(c"pm-kit blink")]; [hal::binary_info::rp_program_name!(c"pm-kit display")];