//! Driver for the micro:bit led display. use embassy_nrf::gpio::{AnyPin, Level, Output, OutputDrive}; use embassy_time::Timer; use crate::{patterns::PATTERN_START, state}; pub const ROWS: usize = 5; pub const COLS: usize = 5; pub type Matrix = [[u8; COLS]; ROWS]; pub struct Display<'a> { rows: [Output<'a>; ROWS], cols: [Output<'a>; COLS], matrix: Matrix, } /// Create a low Output from an AnyPin instance. fn pin_to_output<'a>(x: AnyPin) -> Output<'a> { Output::new(x, Level::Low, OutputDrive::Standard) } impl<'a> Display<'a> { /// Initialize the display with the provided pins. pub fn new(rows: [AnyPin; ROWS], cols: [AnyPin; COLS]) -> Self { let rows = rows.map(pin_to_output); let cols = cols.map(pin_to_output); Self { rows, cols, matrix: [[0; COLS]; ROWS], } } /// Set which leds are active. pub fn set(&mut self, matrix: Matrix) { self.matrix = matrix; } /// Show the defined pattern on the leds. pub async fn show(&mut self) { for (i, row) in self.matrix.iter().enumerate() { self.rows[i].set_high(); for (j, cell) in row.iter().enumerate() { if *cell > 0 { self.cols[j].set_low(); } } // TODO: instead of waiting for a set time, try // implementing some kind of constant refresh rate Timer::after_micros(10).await; for col in &mut self.cols { col.set_high(); } self.rows[i].set_low(); } } } /// Show the active pattern on the led screen. /// React on new pattern messages by showing the new pattern. #[embassy_executor::task] pub async fn task(rows: [AnyPin; ROWS], cols: [AnyPin; COLS]) { let mut state_sub = state::STATE.subscriber().unwrap(); let mut display = Display::new(rows, cols); display.set(PATTERN_START); loop { if let Some(msg) = state_sub.try_next_message_pure() { if let state::StateChange::Pattern(new_pattern) = msg { display.set(new_pattern); } } display.show().await; Timer::after_micros(10).await; } }