diff --git a/ui-app/src/lib.rs b/ui-app/src/lib.rs index 9bf6bbc8..e4fee8ea 100644 --- a/ui-app/src/lib.rs +++ b/ui-app/src/lib.rs @@ -119,8 +119,21 @@ pub trait Led { } } +fn rgb565(r: u8, g: u8, b: u8) -> u16 { + // Discard the low-order bits of the colors + (((r as u16) >> 3) << 11) | (((g as u16) >> 2) << 5) | ((b as u16) >> 3) +} + +pub trait Screen { + fn width(&self) -> usize; + fn height(&self) -> usize; + fn fill(&mut self, color: u16); + fn draw(&mut self, left: usize, right: usize, top: usize, bottom: usize, data: &[u16]); +} + pub trait Outputs { fn status_led(&mut self) -> &mut impl Led; + fn screen(&mut self) -> &mut impl Screen; fn log(&mut self, message: &str); } @@ -140,7 +153,51 @@ impl App { } pub fn start(&mut self, out: &mut impl Outputs) { + // Extinguish the status LED out.status_led().set_color(Color::Black); + + // Draw a test pattern to the screen + const SIZE: usize = 10; + let screen = out.screen(); + let mut data = [0_u16; SIZE * SIZE]; + + // Background + screen.fill(rgb565(0x88, 0x88, 0x88)); + + // Upper left = R + let (x0, x1, y0, y1) = (SIZE, SIZE + SIZE, SIZE, SIZE + SIZE); + data.fill(rgb565(0xFF, 0x00, 0x00)); + screen.draw(x0, x1, y0, y1, &data); + + // Upper right = G + let (x0, x1, y0, y1) = ( + screen.width() - SIZE - SIZE, + screen.width() - SIZE, + SIZE, + SIZE + SIZE, + ); + data.fill(rgb565(0x00, 0xFF, 0x00)); + screen.draw(x0, x1, y0, y1, &data); + + // Lower left = B + let (x0, x1, y0, y1) = ( + SIZE, + SIZE + SIZE, + screen.height() - SIZE - SIZE, + screen.height() - SIZE, + ); + data.fill(rgb565(0x00, 0x00, 0xFF)); + screen.draw(x0, x1, y0, y1, &data); + + // Lower right = Y + let (x0, x1, y0, y1) = ( + screen.width() - SIZE - SIZE, + screen.width() - SIZE, + screen.height() - SIZE - SIZE, + screen.height() - SIZE, + ); + data.fill(rgb565(0xFF, 0xFF, 0x00)); + screen.draw(x0, x1, y0, y1, &data); } pub fn handle(&mut self, event: Event, out: &mut impl Outputs) { diff --git a/ui-app/tests/integration.rs b/ui-app/tests/integration.rs index 623bb421..2652442a 100644 --- a/ui-app/tests/integration.rs +++ b/ui-app/tests/integration.rs @@ -11,9 +11,32 @@ impl Led for MockLed { } } +// TODO: Test screen logic +#[derive(Default)] +struct MockScreen; + +impl Screen for MockScreen { + fn width(&self) -> usize { + 240 + } + + fn height(&self) -> usize { + 320 + } + + fn fill(&mut self, _color: u16) { + // TODO: Store pixels + } + + fn draw(&mut self, _left: usize, _right: usize, _top: usize, _bottom: usize, _data: &[u16]) { + // TODO: Store pixels + } +} + #[derive(Default)] struct MockOutputs { status_led: MockLed, + screen: MockScreen, last_message: String, } @@ -22,6 +45,10 @@ impl Outputs for MockOutputs { &mut self.status_led } + fn screen(&mut self) -> &mut impl Screen { + &mut self.screen + } + fn log(&mut self, message: &str) { self.last_message = message.into(); } diff --git a/ui-stm32/Cargo.toml b/ui-stm32/Cargo.toml index 8cdb6612..23bb70cc 100644 --- a/ui-stm32/Cargo.toml +++ b/ui-stm32/Cargo.toml @@ -20,12 +20,13 @@ defmt-rtt = "1.0.0" embassy-executor = { version = "0.8.0", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-futures = { version = "0.1.1", features = ["defmt"] } -embassy-stm32 = { version = "0.3.0", features = ["defmt", "stm32f405rg", "memory-x", "time-driver-tim4", "exti", "chrono"] } +embassy-stm32 = { version = "0.3.0", features = ["defmt", "stm32f405rg", "memory-x", "time-driver-tim1", "exti", "chrono"] } embassy-time = { version = "0.4.0", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } heapless = { version = "0.8", default-features = false } panic-probe = { version = "1.0.0", features = ["print-defmt"] } embassy-sync = { version = "0.7.1", features = ["defmt"] } +num_enum = { version = "0.7.4", default-features = false } [profile.release] debug = 2 diff --git a/ui-stm32/src/board/ev12.rs b/ui-stm32/src/board/ev12.rs index b58e5e5a..af90eff1 100644 --- a/ui-stm32/src/board/ev12.rs +++ b/ui-stm32/src/board/ev12.rs @@ -1,20 +1,52 @@ -use super::{Button, Keyboard, StatusLed}; +use super::{Button, Keyboard, Screen, StatusLed}; use embassy_stm32::{ exti::ExtiInput, gpio::{Input, Level, Output, Pull, Speed}, + spi::Spi, }; use ui_app::{Led, Outputs}; pub struct Board { status_led: StatusLed, + screen: Screen, pub ptt_button: Option