diff --git a/Cargo.lock b/Cargo.lock index 4d75097..9448429 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,6 +27,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "arrform" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7cf566ecc5c9d82b973e81d30babf6583c9b497f86295c952d538c3254ef4e6" + [[package]] name = "ascii-canvas" version = "3.0.0" @@ -759,6 +765,27 @@ dependencies = [ "embedded-io", ] +[[package]] +name = "embedded-layout" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a90553247f2b05c59ac7894ea13d830636c2b1203fa03bff400eddbd1fa9f52" +dependencies = [ + "embedded-graphics", + "embedded-layout-macros", +] + +[[package]] +name = "embedded-layout-macros" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f6e621fe4c7e05b695274b722dc0a60bacd1c8696b58191baa0154713d52400" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "embedded-sdmmc" version = "0.8.0" @@ -1336,6 +1363,7 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" name = "picocalc-os-rs" version = "0.1.0" dependencies = [ + "arrform", "bitflags 2.9.1", "bt-hci", "cortex-m", @@ -1352,9 +1380,12 @@ dependencies = [ "embassy-time", "embedded-graphics", "embedded-hal 0.2.7", + "embedded-hal 1.0.0", "embedded-hal-async", "embedded-hal-bus", + "embedded-layout", "embedded-sdmmc", + "heapless", "panic-probe", "portable-atomic", "st7365p-lcd", diff --git a/Cargo.toml b/Cargo.toml index dcca01b..d085ab9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,18 +60,21 @@ cyw43-pio = { version = "0.3.0", optional = true } embedded-hal-bus = { version = "0.3.0", features = ["async"] } embedded-hal = "0.2.7" +embedded-hal-1 = { version = "1.0.0", package = "embedded-hal" } embedded-hal-async = "1.0.0" cortex-m = { version = "0.7.7" } cortex-m-rt = "0.7.5" panic-probe = "0.3" portable-atomic = { version = "1.11", features = ["critical-section"] } +static_cell = "2.1.1" +bitflags = "2.9.1" +heapless = "0.8.0" +arrform = "0.1.1" defmt = { version = "0.3", optional = true } defmt-rtt = "0.4.2" embedded-graphics = { version = "0.8.1" } +embedded-layout = "0.4.2" 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", branch = "async" } - -static_cell = "2.1.1" -bitflags = "2.9.1" diff --git a/src/display.rs b/src/display.rs index 553c555..b9d919a 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,13 +1,10 @@ +use arrform::{ArrForm, arrform}; use embassy_rp::{ gpio::{Level, Output}, peripherals::{PIN_13, PIN_14, PIN_15, SPI1}, spi::{Async, Spi}, }; use embassy_time::{Delay, Timer}; -use embedded_hal_bus::spi::ExclusiveDevice; -use st7365p_lcd::{FrameBuffer, Orientation, ST7365P}; - -use arrform::{ArrForm, arrform}; use embedded_graphics::{ Drawable, draw_target::DrawTarget, @@ -15,17 +12,22 @@ use embedded_graphics::{ MonoTextStyle, ascii::{FONT_6X10, FONT_9X15, FONT_10X20}, }, - object_chain::Chain, pixelcolor::Rgb565, prelude::{Point, RgbColor, Size}, primitives::Rectangle, text::Text, }; +use embedded_hal_1::digital::OutputPin; +use embedded_hal_async::spi::SpiDevice; +use embedded_hal_bus::spi::ExclusiveDevice; use embedded_layout::{ align::{horizontal, vertical}, layout::linear::LinearLayout, + object_chain::Chain, prelude::*, }; +use heapless::{String, Vec}; +use st7365p_lcd::{FrameBuffer, Orientation, ST7365P}; pub const SCREEN_WIDTH: usize = 320; pub const SCREEN_HEIGHT: usize = 320; @@ -33,10 +35,20 @@ pub const SCREEN_HEIGHT: usize = 320; pub const STATUS_BAR_WIDTH: usize = 320; pub const STATUS_BAR_HEIGHT: usize = 40; -#[embassy_executor::task] -pub async fn display_task(spi: Spi<'static, SPI1, Async>, cs: PIN_13, data: PIN_14, reset: PIN_15) { +pub async fn init_display( + spi: Spi<'static, SPI1, Async>, + cs: PIN_13, + data: PIN_14, + reset: PIN_15, +) -> FrameBuffer< + SCREEN_WIDTH, + SCREEN_HEIGHT, + ExclusiveDevice, Output<'static>, Delay>, + Output<'static>, + Output<'static>, +> { let spi_device = ExclusiveDevice::new(spi, Output::new(cs, Level::Low), Delay).unwrap(); - let display = ST7365P::new( + let mut display = ST7365P::new( spi_device, Output::new(data, Level::Low), Some(Output::new(reset, Level::High)), @@ -51,22 +63,15 @@ pub async fn display_task(spi: Spi<'static, SPI1, Async>, cs: PIN_13, data: PIN_ .await .unwrap(); - let mut framebuffer = FrameBuffer::new(display); - - let mut ui = UI::new(); - ui.draw_status_bar(&mut framebuffer); - - loop { - framebuffer.draw().await.unwrap(); - Timer::after_millis(500).await; - } + FrameBuffer::new(display) } -pub struct UI { +pub struct UI { pub status_bar: StatusBar, + pub selections_list: SelectionList, } -impl UI { +impl UI { pub fn new() -> Self { Self { status_bar: StatusBar { @@ -74,10 +79,59 @@ impl UI { backlight: 100, volume: 100, }, + selections_list: SelectionList::new(Vec::new()), } } - pub fn draw_status_bar>(&mut self, target: &mut D) { + pub fn draw>(&mut self, target: &mut D) { + self.draw_status_bar(target); + self.draw_selection(target); + } + + fn draw_selection>(&mut self, target: &mut D) { + let text_style = MonoTextStyle::new(&FONT_9X15, Rgb565::WHITE); + + let selection = Rectangle::new( + Point::new(0, STATUS_BAR_HEIGHT as i32 + 1), + Size::new( + SCREEN_WIDTH as u32, + (SCREEN_HEIGHT - STATUS_BAR_HEIGHT) as u32 - 1, + ), + ); + + let _ = if self.selections_list.selections.is_empty() { + LinearLayout::horizontal(Chain::new(Text::new( + "No Programs found on SD Card\nEnsure programs end with '.rhai',\nand are located in the root directory", + Point::zero(), + text_style, + ))) + .arrange() + .align_to(&selection, horizontal::Center, vertical::Center).draw(target) + } else { + LinearLayout::horizontal( + Chain::new(Text::new( + arrform!(20, "Bat: {}", self.status_bar.battery).as_str(), + Point::zero(), + text_style, + )) + .append(Text::new( + arrform!(20, "Lght: {}", self.status_bar.backlight).as_str(), + Point::zero(), + text_style, + )) + .append(Text::new( + arrform!(20, "Vol: {}", self.status_bar.volume).as_str(), + Point::zero(), + text_style, + )), + ) + .arrange() + .align_to(&selection, horizontal::Left, vertical::Center) + .draw(target) + }; + } + + fn draw_status_bar>(&mut self, target: &mut D) { let text_style = MonoTextStyle::new(&FONT_9X15, Rgb565::WHITE); let status_bar = Rectangle::new( @@ -112,3 +166,31 @@ pub struct StatusBar { pub backlight: u8, pub volume: u8, } + +pub struct SelectionList { + current_selection: u16, + selections: Vec, MAX_SELECTION>, +} + +impl + SelectionList +{ + pub fn new(selections: Vec, MAX_SELECTION>) -> Self { + Self { + selections, + current_selection: 0, + } + } + + pub fn down(&mut self) { + if self.current_selection + 1 < self.selections.len() as u16 { + self.current_selection += 1 + } + } + + pub fn up(&mut self) { + if self.current_selection > self.selections.len() as u16 { + self.current_selection -= 1 + } + } +} diff --git a/src/main.rs b/src/main.rs index 56b63f8..1420e05 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,8 +6,10 @@ use defmt::*; use {defmt_rtt as _, panic_probe as _}; +use crate::display::{SCREEN_HEIGHT, SCREEN_WIDTH, UI, init_display}; +use crate::peripherals::{keyboard::KeyEvent, peripherals_task}; use embassy_executor::Spawner; -use embassy_rp::peripherals::I2C1; +use embassy_rp::peripherals::{I2C1, SPI1}; use embassy_rp::spi::Spi; use embassy_rp::{ bind_interrupts, @@ -18,15 +20,15 @@ use embassy_rp::{ }; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::channel::Channel; -use embassy_time::Timer; +use embassy_time::{Delay, Timer}; +use embedded_hal_1::spi::SpiDevice; use embedded_hal_bus::spi::ExclusiveDevice; use embedded_sdmmc::asynchronous::{File, SdCard, ShortFileName, VolumeIdx, VolumeManager}; +use st7365p_lcd::{FrameBuffer, ST7365P}; use static_cell::StaticCell; -mod peripherals; -use peripherals::{keyboard::KeyEvent, peripherals_task}; mod display; -use display::display_task; +mod peripherals; embassy_rp::bind_interrupts!(struct Irqs { I2C1_IRQ => i2c::InterruptHandler; @@ -47,11 +49,18 @@ async fn main(spawner: 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); - // spawner - // .spawn(display_task(spi1, p.PIN_13, p.PIN_14, p.PIN_15)) - // .unwrap(); + let mut config = spi::Config::default(); + config.frequency = 16_000_000; + let spi1 = Spi::new( + p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, p.DMA_CH0, p.DMA_CH1, config, + ); + let mut framebuffer = init_display(spi1, p.PIN_13, p.PIN_14, p.PIN_15).await; + + let mut ui: UI<50, 25> = UI::new(); + ui.draw(&mut framebuffer); + + loop { + framebuffer.draw().await.unwrap(); + Timer::after_millis(500).await; + } }