//! 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, PrimitiveStyleBuilder, 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; 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, pac.CLOCKS, pac.PLL_SYS, pac.PLL_USB, &mut pac.RESETS, &mut watchdog, ) .ok() .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().flip_horizontal()) // panel wants MX set (matches CircuitPython MADCTL 0x48) .init(&mut timer) .unwrap(); let w = WIDTH as i32; let h = HEIGHT as i32; let m = 36; // marker size // Full-screen fill via the SAME draw path as the shapes (clear() left snow last time). Rectangle::new(Point::zero(), Size::new(WIDTH as u32, HEIGHT as u32)) .into_styled(PrimitiveStyle::with_fill(Rgb565::new(0, 0, 12))) .draw(&mut display) .unwrap(); // Red 8px border on all four edges — if any edge is missing, the addressed area != panel. Rectangle::new(Point::zero(), Size::new(WIDTH as u32, HEIGHT as u32)) .into_styled( PrimitiveStyleBuilder::new() .stroke_color(Rgb565::RED) .stroke_width(8) .build(), ) .draw(&mut display) .unwrap(); // Distinct corner markers so orientation/mirror is unambiguous. let ms = Size::new(m as u32, m as u32); for (x, y, c) in [ (0, 0, Rgb565::GREEN), // top-left (w - m, 0, Rgb565::YELLOW), // top-right (0, h - m, Rgb565::CYAN), // bottom-left (w - m, h - m, Rgb565::MAGENTA), // bottom-right ] { Rectangle::new(Point::new(x, y), ms) .into_styled(PrimitiveStyle::with_fill(c)) .draw(&mut display) .unwrap(); } // Labels: "TL" near origin, big "PMK" centred. let label = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE); Text::new("TL", Point::new(44, 28), label).draw(&mut display).unwrap(); Text::new("PMK", Point::new(w / 2 - 30, h / 2), label).draw(&mut display).unwrap(); loop { led.set_high().unwrap(); timer.delay_ms(250); led.set_low().unwrap(); timer.delay_ms(250); } } /// picotool metadata (visible via `picotool info`). #[link_section = ".bi_entries"] #[used] pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 1] = [hal::binary_info::rp_program_name!(c"pm-kit display")];