From cf479cc00a566fba8977331c4f0958bb742da6c0 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Fri, 27 Jun 2025 12:23:05 -0600 Subject: [PATCH] diy framebuffer --- Cargo.lock | 22 +++++++++++- Cargo.toml | 5 ++- src/display.rs | 96 ++++++++++---------------------------------------- src/main.rs | 39 +++++++++++++------- 4 files changed, 69 insertions(+), 93 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe98260..0b6773f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1357,8 +1357,10 @@ dependencies = [ "embedded-sdmmc", "panic-probe", "portable-atomic", + "spin", "st7365p-lcd", "static_cell", + "talc", "trouble-host", ] @@ -1727,13 +1729,22 @@ dependencies = [ "rgb", ] +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +dependencies = [ + "lock_api", +] + [[package]] name = "st7365p-lcd" version = "0.10.0" -source = "git+https://github.com/legitcamper/st7365p-lcd-rs#d751e8d30f1a3f964ffe05e4bb16f82112fbefce" dependencies = [ "embedded-graphics-core", "embedded-hal 1.0.0", + "embedded-hal-async", "nb 1.1.0", ] @@ -1792,6 +1803,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "talc" +version = "4.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3ae828aa394de34c7de08f522d1b86bd1c182c668d27da69caadda00590f26d" +dependencies = [ + "lock_api", +] + [[package]] name = "term" version = "0.7.0" diff --git a/Cargo.toml b/Cargo.toml index 5a9425d..7595054 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,7 +71,10 @@ defmt-rtt = "0.4.2" embedded-graphics = { version = "0.8.1" } embedded-sdmmc = { git = "https://github.com/Be-ing/embedded-sdmmc-rs", branch = "bisync", default-features = false } -st7365p-lcd = { git = "https://github.com/legitcamper/st7365p-lcd-rs" } +# st7365p-lcd = { git = "https://github.com/legitcamper/st7365p-lcd-rs" } +st7365p-lcd = { path = "../ST7365P-lcd-rs" } static_cell = "2.1.1" bitflags = "2.9.1" +talc = "4.4.3" +spin = "0.10.0" diff --git a/src/display.rs b/src/display.rs index fe3f2b0..47271da 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,7 +1,8 @@ +use defmt::info; use embassy_rp::{ gpio::{Level, Output}, peripherals::{PIN_13, PIN_14, PIN_15, SPI1}, - spi::{Blocking, Spi}, + spi::{Async, Blocking, Spi}, }; use embassy_time::{Delay, Timer}; use embedded_graphics::{ @@ -11,15 +12,10 @@ use embedded_graphics::{ primitives::{PrimitiveStyle, Rectangle}, }; use embedded_hal_bus::spi::ExclusiveDevice; -use st7365p_lcd::{Orientation, ST7365P}; +use st7365p_lcd::{FrameBuffer, Orientation, ST7365P}; #[embassy_executor::task] -pub async fn display_task( - spi: Spi<'static, SPI1, Blocking>, - cs: PIN_13, - data: PIN_14, - reset: PIN_15, -) { +pub async fn display_task(spi: Spi<'static, SPI1, Async>, cs: PIN_13, data: PIN_14, reset: PIN_15) { let spi_device = ExclusiveDevice::new(spi, Output::new(cs, Level::Low), Delay).unwrap(); let mut display = ST7365P::new( spi_device, @@ -30,84 +26,28 @@ pub async fn display_task( 320, 320, ); - display.init(&mut Delay).unwrap(); - display.set_orientation(&Orientation::Landscape).unwrap(); - let mut virtual_display = VirtualDisplay::new(display, 320 / 2, 320 / 2); + display.init(&mut Delay).await.unwrap(); + display.init(&mut Delay).await.unwrap(); + display.set_address_window(0, 0, 319, 319).await.unwrap(); + display.set_custom_orientation(0x40).await.unwrap(); // inverts X axis (reverts the natural mirroring) + let mut framebuffer: FrameBuffer< + 320, + 320, + ExclusiveDevice, Output<'_>, Delay>, + Output<'_>, + Output<'_>, + > = FrameBuffer::new(display); let thin_stroke = PrimitiveStyle::with_stroke(Rgb565::RED, 20); Rectangle::new(Point::new(10, 10), Size::new(100, 100)) .into_styled(thin_stroke) - .draw(&mut virtual_display) + .draw(&mut framebuffer) .unwrap(); loop { Timer::after_millis(500).await; - } -} - -/// simple abstraction over real display & resolution to reduce frame buffer size -/// by cutting the resolution by 1/4 -struct VirtualDisplay { - display: ST7365P< - ExclusiveDevice, Output<'static>, Delay>, - Output<'static>, - Output<'static>, - >, - width: u32, - height: u32, -} - -impl VirtualDisplay { - pub fn new( - display: ST7365P< - ExclusiveDevice, Output<'static>, Delay>, - Output<'static>, - Output<'static>, - >, - new_width: u32, - new_height: u32, - ) -> Self { - Self { - display, - width: new_width, - height: new_height, - } - } -} - -impl DrawTarget for VirtualDisplay { - type Color = Rgb565; - type Error = (); - - fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> - where - I: IntoIterator>, - { - for Pixel(coord, color) in pixels.into_iter() { - // Check bounds on the *virtual* (already reduced) resolution - if coord.x >= 0 - && coord.y >= 0 - && coord.x < self.width as i32 - && coord.y < self.height as i32 - { - let px = coord.x as u16 * 2; - let py = coord.y as u16 * 2; - let raw_color = RawU16::from(color).into_inner(); - - // Draw the 2x2 block on the underlying hardware - self.display.set_pixel(px, py, raw_color)?; - self.display.set_pixel(px + 1, py, raw_color)?; - self.display.set_pixel(px, py + 1, raw_color)?; - self.display.set_pixel(px + 1, py + 1, raw_color)?; - } - } - Ok(()) - } -} - -impl OriginDimensions for VirtualDisplay { - fn size(&self) -> Size { - Size::new(self.width, self.height) + info!("drawing"); + framebuffer.draw().await.unwrap(); } } diff --git a/src/main.rs b/src/main.rs index 56b63f8..f14f2ec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,7 @@ use embassy_time::Timer; use embedded_hal_bus::spi::ExclusiveDevice; use embedded_sdmmc::asynchronous::{File, SdCard, ShortFileName, VolumeIdx, VolumeManager}; use static_cell::StaticCell; +use talc::*; mod peripherals; use peripherals::{keyboard::KeyEvent, peripherals_task}; @@ -32,6 +33,16 @@ embassy_rp::bind_interrupts!(struct Irqs { I2C1_IRQ => i2c::InterruptHandler; }); +static mut ARENA: [u8; 300_000] = [0; 300_000]; + +#[global_allocator] +static ALLOCATOR: Talck, ClaimOnOom> = Talc::new(unsafe { + // if we're in a hosted environment, the Rust runtime may allocate before + // main() is called, so we need to initialize the arena automatically + ClaimOnOom::new(Span::from_array(core::ptr::addr_of!(ARENA).cast_mut())) +}) +.lock(); + #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); @@ -39,19 +50,21 @@ async fn main(spawner: Spawner) { static KEYBOARD_EVENTS: StaticCell> = StaticCell::new(); let keyboard_events = KEYBOARD_EVENTS.init(Channel::new()); - // configure keyboard event handler - let mut config = i2c::Config::default(); - config.frequency = 100_000; - let i2c1 = I2c::new_async(p.I2C1, p.PIN_7, p.PIN_6, Irqs, config); - spawner - .spawn(peripherals_task(i2c1, keyboard_events.sender())) - .unwrap(); - - // // configure display handler - // let mut config = spi::Config::default(); - // config.frequency = 16_000_000; - // let spi1 = spi::Spi::new_blocking(p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, config); + // // configure keyboard event handler + // let mut config = i2c::Config::default(); + // config.frequency = 100_000; + // let i2c1 = I2c::new_async(p.I2C1, p.PIN_7, p.PIN_6, Irqs, config); // spawner - // .spawn(display_task(spi1, p.PIN_13, p.PIN_14, p.PIN_15)) + // .spawn(peripherals_task(i2c1, keyboard_events.sender())) // .unwrap(); + + // configure display handler + let mut config = spi::Config::default(); + config.frequency = 16_000_000; + let spi1 = spi::Spi::new( + p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, p.DMA_CH0, p.DMA_CH1, config, + ); + spawner + .spawn(display_task(spi1, p.PIN_13, p.PIN_14, p.PIN_15)) + .unwrap(); }