pm-kit: full st7796 init as PRIMARY bring-up, then mipidsi for drawing
Host initdump proved mipidsi's MADCTL (0x48), COLMOD, and address window (CASET 0..319 / RASET 0..479) already match CircuitPython exactly — so the 1/4 + rotation wasn't an addressing bug. The missing piece was the ST7796 extension init (B6/power/gamma) running as the PRIMARY bring-up right after reset (grafting it onto mipidsi's already-DISPON'd panel blanked or under-configured it). Now: manual hw reset + full CircuitPython st7796_init via the raw interface, THEN Builder without reset_pin (re-asserts only the basics, extension setup persists). initdump extended to also dump CASET/RASET draw windows. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b2ea27f506
commit
026c20523d
2 changed files with 64 additions and 24 deletions
|
|
@ -55,7 +55,7 @@ fn main() -> ! {
|
||||||
let sclk = pins.gpio2.into_function::<hal::gpio::FunctionSpi>();
|
let sclk = pins.gpio2.into_function::<hal::gpio::FunctionSpi>();
|
||||||
let mosi = pins.gpio3.into_function::<hal::gpio::FunctionSpi>();
|
let mosi = pins.gpio3.into_function::<hal::gpio::FunctionSpi>();
|
||||||
let dc = pins.gpio6.into_push_pull_output();
|
let dc = pins.gpio6.into_push_pull_output();
|
||||||
let rst = pins.gpio7.into_push_pull_output();
|
let mut rst = pins.gpio7.into_push_pull_output();
|
||||||
let cs = pins.gpio5.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 = hal::spi::Spi::<_, _, _, 8>::new(pac.SPI0, (mosi, sclk));
|
||||||
|
|
@ -68,33 +68,54 @@ fn main() -> ! {
|
||||||
let spi_device = ExclusiveDevice::new_no_delay(spi, cs).unwrap();
|
let spi_device = ExclusiveDevice::new_no_delay(spi, cs).unwrap();
|
||||||
|
|
||||||
let mut buffer = [0u8; 512];
|
let mut buffer = [0u8; 512];
|
||||||
let di = SpiInterface::new(spi_device, dc, &mut buffer);
|
let mut di = SpiInterface::new(spi_device, dc, &mut buffer);
|
||||||
|
|
||||||
|
// Hardware reset, then the FULL known-good CircuitPython st7796_init as the PRIMARY bring-up
|
||||||
|
// (right after reset, the only time the panel accepts the extension setup cleanly). Confirmed
|
||||||
|
// necessary: mipidsi's address window + MADCTL already match CircuitPython (host initdump), so
|
||||||
|
// the only remaining difference was this extension init not running as the primary init.
|
||||||
|
rst.set_high().unwrap();
|
||||||
|
timer.delay_ms(5);
|
||||||
|
rst.set_low().unwrap();
|
||||||
|
timer.delay_ms(20);
|
||||||
|
rst.set_high().unwrap();
|
||||||
|
timer.delay_ms(150);
|
||||||
|
|
||||||
|
di.send_command(0x01, &[]).unwrap(); // SWRESET
|
||||||
|
timer.delay_ms(120);
|
||||||
|
di.send_command(0x11, &[]).unwrap(); // SLPOUT
|
||||||
|
timer.delay_ms(120);
|
||||||
|
di.send_command(0xF0, &[0xC3]).unwrap(); // unlock extension command set
|
||||||
|
di.send_command(0xF0, &[0x96]).unwrap();
|
||||||
|
di.send_command(0x36, &[0x48]).unwrap(); // MADCTL
|
||||||
|
di.send_command(0x3A, &[0x55]).unwrap(); // COLMOD 16bpp
|
||||||
|
di.send_command(0xB4, &[0x01]).unwrap();
|
||||||
|
di.send_command(0xB6, &[0x80, 0x02, 0x3B]).unwrap(); // display function control: 480 lines
|
||||||
|
di.send_command(0xE8, &[0x40, 0x8A, 0x00, 0x00, 0x29, 0x19, 0xA5, 0x33]).unwrap();
|
||||||
|
di.send_command(0xC1, &[0x06]).unwrap();
|
||||||
|
di.send_command(0xC2, &[0xA7]).unwrap();
|
||||||
|
di.send_command(0xC5, &[0x18]).unwrap();
|
||||||
|
timer.delay_ms(120);
|
||||||
|
di.send_command(0xE0, &[0xF0, 0x09, 0x0B, 0x06, 0x04, 0x15, 0x2F, 0x54, 0x42, 0x3C, 0x17, 0x14, 0x18, 0x1B]).unwrap();
|
||||||
|
di.send_command(0xE1, &[0xE0, 0x09, 0x0B, 0x06, 0x04, 0x03, 0x2B, 0x43, 0x42, 0x3B, 0x16, 0x14, 0x17, 0x1B]).unwrap();
|
||||||
|
di.send_command(0xF0, &[0x3C]).unwrap(); // lock
|
||||||
|
di.send_command(0xF0, &[0x69]).unwrap();
|
||||||
|
timer.delay_ms(120);
|
||||||
|
di.send_command(0x21, &[]).unwrap(); // INVON (INVERT_COLORS = true)
|
||||||
|
di.send_command(0x29, &[]).unwrap(); // DISPON
|
||||||
|
timer.delay_ms(50);
|
||||||
|
|
||||||
|
// Build mipidsi for DRAWING only — NO reset_pin (already reset), so it just re-asserts the
|
||||||
|
// basics (SLPOUT/MADCTL=0x48/INVON/COLMOD/NORON/DISPON) without touching the extension setup.
|
||||||
let mut display = Builder::new(ST7796, di)
|
let mut display = Builder::new(ST7796, di)
|
||||||
.reset_pin(rst)
|
|
||||||
.display_size(WIDTH, HEIGHT)
|
.display_size(WIDTH, HEIGHT)
|
||||||
.color_order(ColorOrder::Bgr)
|
.color_order(ColorOrder::Bgr)
|
||||||
.invert_colors(ColorInversion::Inverted)
|
.invert_colors(ColorInversion::Inverted)
|
||||||
.orientation(Orientation::new().flip_horizontal()) // panel wants MX set (matches CircuitPython MADCTL 0x48)
|
.orientation(Orientation::new().flip_horizontal())
|
||||||
.init(&mut timer)
|
.init(&mut timer)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Minimal: mipidsi's plain init already lit the panel (milestone 2 showed content, just in a
|
// Same UI code the host simulator renders (rust/uisim → PNG).
|
||||||
// sub-region because the gate scan wasn't set to 480 lines). 0xB6 (Display Function Control)
|
|
||||||
// fixes the line count and is a BASIC command — it does NOT need the 0xF0 extension unlock
|
|
||||||
// (the unlock gates gamma/power, and was the likely blanker). Bracket it with DISP off/on so
|
|
||||||
// the scan is reconfigured while the display is off, the way CircuitPython does it.
|
|
||||||
{
|
|
||||||
let di = unsafe { display.dcs() };
|
|
||||||
di.send_command(0x28, &[]).unwrap(); // DISPOFF
|
|
||||||
di.send_command(0xB6, &[0x80, 0x02, 0x3B]).unwrap(); // display function control: 480 lines
|
|
||||||
di.send_command(0x29, &[]).unwrap(); // DISPON
|
|
||||||
}
|
|
||||||
timer.delay_ms(50);
|
|
||||||
|
|
||||||
// Same UI code the host simulator renders (rust/uisim → PNG). If this is wrong, the sim
|
|
||||||
// shows it without the bench; if the sim is right but the panel is wrong, it's a controller
|
|
||||||
// issue (init/MADCTL/0xB6), not a draw bug.
|
|
||||||
pm_ui::draw_ui(&mut display).unwrap();
|
pm_ui::draw_ui(&mut display).unwrap();
|
||||||
|
|
||||||
// Reached the loop → display init + draw completed. Slow 1 Hz blink (vs the solid-ON during
|
// Reached the loop → display init + draw completed. Slow 1 Hz blink (vs the solid-ON during
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
//! guessing at the panel bring-up. `cargo run --bin initdump`.
|
//! guessing at the panel bring-up. `cargo run --bin initdump`.
|
||||||
|
|
||||||
use core::convert::Infallible;
|
use core::convert::Infallible;
|
||||||
|
use embedded_graphics::{pixelcolor::Rgb565, prelude::*, primitives::{PrimitiveStyle, Rectangle}};
|
||||||
use embedded_hal::delay::DelayNs;
|
use embedded_hal::delay::DelayNs;
|
||||||
use embedded_hal::digital::{ErrorType, OutputPin};
|
use embedded_hal::digital::{ErrorType, OutputPin};
|
||||||
use mipidsi::interface::Interface;
|
use mipidsi::interface::Interface;
|
||||||
|
|
@ -57,7 +58,7 @@ impl DelayNs for NoDelay {
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut delay = NoDelay;
|
let mut delay = NoDelay;
|
||||||
let display = Builder::new(ST7796, Rec::default())
|
let mut display = Builder::new(ST7796, Rec::default())
|
||||||
.reset_pin(NoPin)
|
.reset_pin(NoPin)
|
||||||
.display_size(320, 480)
|
.display_size(320, 480)
|
||||||
.color_order(ColorOrder::Bgr)
|
.color_order(ColorOrder::Bgr)
|
||||||
|
|
@ -65,11 +66,29 @@ fn main() {
|
||||||
.orientation(Orientation::new().flip_horizontal())
|
.orientation(Orientation::new().flip_horizontal())
|
||||||
.init(&mut delay)
|
.init(&mut delay)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
// boundary between init and draw commands (raw access to the recording interface)
|
||||||
|
let init_len = unsafe { display.dcs() }.log.len();
|
||||||
|
|
||||||
|
// Full-screen fill, then a single pixel at the far corner → reveals the address window.
|
||||||
|
Rectangle::new(Point::new(0, 0), Size::new(320, 480))
|
||||||
|
.into_styled(PrimitiveStyle::with_fill(Rgb565::BLUE))
|
||||||
|
.draw(&mut display)
|
||||||
|
.unwrap();
|
||||||
|
Pixel(Point::new(319, 479), Rgb565::RED).draw(&mut display).unwrap();
|
||||||
|
|
||||||
let (rec, _model, _rst) = display.release();
|
let (rec, _model, _rst) = display.release();
|
||||||
|
|
||||||
println!("mipidsi ST7796 init sequence ({} commands):", rec.log.len());
|
println!("init: {init_len} commands; full log {} commands", rec.log.len());
|
||||||
for (c, a) in &rec.log {
|
for (i, (c, a)) in rec.log.iter().enumerate() {
|
||||||
let args = a.iter().map(|b| format!("{b:02X}")).collect::<Vec<_>>().join(" ");
|
let args = a.iter().map(|b| format!("{b:02X}")).collect::<Vec<_>>().join(" ");
|
||||||
println!(" 0x{c:02X} {args}");
|
let tag = match c {
|
||||||
|
0x2A => " <- CASET (col x0..x1)",
|
||||||
|
0x2B => " <- RASET (row y0..y1)",
|
||||||
|
0x2C => " <- RAMWR",
|
||||||
|
_ => "",
|
||||||
|
};
|
||||||
|
let phase = if i < init_len { "init" } else { "draw" };
|
||||||
|
println!(" [{phase}] 0x{c:02X} {args}{tag}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue