diff --git a/rust/pm-kit/Cargo.toml b/rust/pm-kit/Cargo.toml index cf2f974..f147079 100644 --- a/rust/pm-kit/Cargo.toml +++ b/rust/pm-kit/Cargo.toml @@ -2,13 +2,16 @@ name = "pm-kit" version = "0.1.0" 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] rp235x-hal = { version = "0.3", features = ["binary-info", "critical-section-impl", "rt"] } cortex-m-rt = "0.7" panic-halt = "1" embedded-hal = "1" +embedded-hal-bus = "0.2" +mipidsi = "0.9" +embedded-graphics = "0.8" [profile.release] opt-level = "s" diff --git a/rust/pm-kit/src/main.rs b/rust/pm-kit/src/main.rs index f8bbd13..94c2b42 100644 --- a/rust/pm-kit/src/main.rs +++ b/rust/pm-kit/src/main.rs @@ -1,29 +1,44 @@ -//! PM_K-1 firmware — Stage 3 bring-up milestone 1: prove our Rust boots on the Pico 2 (RP2350). -//! -//! Blinks the onboard LED (GP25). If it blinks, the whole path works: toolchain, RP2350 boot -//! block, memory layout, flash, and the cortex-m-rt entry. Drivers (display / audio / inputs / -//! USB-MIDI) and the `pm-core` engine come next, once boot is confirmed on hardware. +//! PM_K-1 firmware — Stage 3 bring-up. +//! Milestone 1 (done): blink GP25 → proved boot/flash. +//! Milestone 2 (this): init the ST7796 320×480 display over SPI0 and draw to it. +//! Pins (from the CircuitPython firmware): SCK=GP2, MOSI=GP3, CS=GP5, DC=GP6, RST=GP7; +//! BGR panel, colours inverted. LED on GP25 keeps blinking as a heartbeat. #![no_std] #![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::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 rp235x_hal as hal; +use hal::fugit::RateExtU32; +use hal::Clock; /// Image definition block — the RP2350 bootrom looks for this to boot the image. #[link_section = ".start_block"] #[used] 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] fn main() -> ! { let mut pac = hal::pac::Peripherals::take().unwrap(); let mut watchdog = hal::Watchdog::new(pac.WATCHDOG); - let clocks = hal::clocks::init_clocks_and_plls( XTAL_FREQ_HZ, pac.XOSC, @@ -37,11 +52,50 @@ fn main() -> ! { .unwrap(); let mut timer = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks); - 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 mut led = pins.gpio25.into_push_pull_output(); + // --- ST7796 over SPI0 --- + let sclk = pins.gpio2.into_function::(); + let mosi = pins.gpio3.into_function::(); + 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 { led.set_high().unwrap(); timer.delay_ms(250); @@ -54,4 +108,4 @@ fn main() -> ! { #[link_section = ".bi_entries"] #[used] 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")];