From dd2a4d6126988d13e6ff21b26dc15deac1977531 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 20 Nov 2025 17:48:04 +0100 Subject: Create demo --- examples/stm32u0/src/bin/lcd.rs | 331 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 325 insertions(+), 6 deletions(-) (limited to 'examples') diff --git a/examples/stm32u0/src/bin/lcd.rs b/examples/stm32u0/src/bin/lcd.rs index f27c4458b..f401b1dcd 100644 --- a/examples/stm32u0/src/bin/lcd.rs +++ b/examples/stm32u0/src/bin/lcd.rs @@ -5,8 +5,10 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::{ lcd::{Bias, Config, Duty, Lcd, LcdPin}, + peripherals::LCD, time::Hertz, }; +use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -72,15 +74,332 @@ async fn main(_spawner: Spawner) { ], ); + { + let mut buffer = DisplayBuffer::new(); + for i in 0..4 { + buffer.write_colon(i); + buffer.write(&mut lcd); + embassy_time::Timer::after_millis(200).await; + buffer.write_dot(i); + buffer.write(&mut lcd); + embassy_time::Timer::after_millis(200).await; + } + for i in 0..4 { + buffer.write_bar(i); + buffer.write(&mut lcd); + embassy_time::Timer::after_millis(200).await; + } + } + + embassy_time::Timer::after_millis(1000).await; + + const MESSAGE: &str = "Hello embassy people. Hope you like this LCD demo :} "; loop { - defmt::info!("Writing frame"); - lcd.write_frame(&[0xAAAAAAAA; 16]); + print_message(MESSAGE, &mut lcd, Duration::from_millis(250)).await; + print_message(characters::ALL_CHARS, &mut lcd, Duration::from_millis(500)).await; + } +} + +async fn print_message(message: &str, lcd: &mut Lcd<'_, LCD>, delay: Duration) { + let mut display_buffer = DisplayBuffer::new(); + + let mut char_buffer = [' '; 6]; + for char in message.chars() { + char_buffer.copy_within(1.., 0); + char_buffer[5] = char; + + display_buffer.clear(); + for (i, char) in char_buffer.iter().enumerate() { + display_buffer.write_char(i, *char); + } + display_buffer.write(lcd); + + embassy_time::Timer::after(delay).await; + } +} + +/// Display layout for the U0-DK +mod display_layout { + // Character layout. There are 6 characters, left-to-right + // T + // ───────── + // │ N │ + // │ │ │ │ │ + // TL │ └┐ │ ┌┘ │ TR + // │NW│ │ │NE│ + // │ │ │ + // W─── ───E + // │ │ │ + // │SW│ │ │SE│ + // BL │ ┌┘ │ └┐ │ BR + // │ │ │ │ │ + // │ S │ + // ───────── + // B + + pub const CHAR_N_COM: u8 = 3; + pub const CHAR_N_SEG: [u8; 6] = [39, 37, 35, 48, 26, 33]; + pub const CHAR_NW_COM: u8 = 3; + pub const CHAR_NW_SEG: [u8; 6] = [49, 38, 36, 34, 27, 24]; + pub const CHAR_W_COM: u8 = 0; + pub const CHAR_W_SEG: [u8; 6] = CHAR_NW_SEG; + pub const CHAR_SW_COM: u8 = 2; + pub const CHAR_SW_SEG: [u8; 6] = CHAR_NW_SEG; + pub const CHAR_S_COM: u8 = 2; + pub const CHAR_S_SEG: [u8; 6] = [22, 6, 46, 11, 15, 29]; + pub const CHAR_SE_COM: u8 = 3; + pub const CHAR_SE_SEG: [u8; 6] = CHAR_S_SEG; + pub const CHAR_E_COM: u8 = 0; + pub const CHAR_E_SEG: [u8; 6] = [23, 45, 47, 14, 28, 32]; + pub const CHAR_NE_COM: u8 = 2; + pub const CHAR_NE_SEG: [u8; 6] = CHAR_N_SEG; + pub const CHAR_T_COM: u8 = 1; + pub const CHAR_T_SEG: [u8; 6] = CHAR_N_SEG; + pub const CHAR_TL_COM: u8 = 1; + pub const CHAR_TL_SEG: [u8; 6] = CHAR_NW_SEG; + pub const CHAR_BL_COM: u8 = 0; + pub const CHAR_BL_SEG: [u8; 6] = CHAR_S_SEG; + pub const CHAR_B_COM: u8 = 1; + pub const CHAR_B_SEG: [u8; 6] = CHAR_S_SEG; + pub const CHAR_BR_COM: u8 = 1; + pub const CHAR_BR_SEG: [u8; 6] = CHAR_E_SEG; + pub const CHAR_TR_COM: u8 = 0; + pub const CHAR_TR_SEG: [u8; 6] = CHAR_N_SEG; + + pub const COLON_COM: u8 = 2; + pub const COLON_SEG: [u8; 4] = [23, 45, 47, 14]; + pub const DOT_COM: u8 = 3; + pub const DOT_SEG: [u8; 4] = COLON_SEG; + /// COM + SEG, bar from top to bottom + pub const BAR: [(u8, u8); 4] = [(2, 28), (3, 28), (2, 32), (3, 32)]; +} + +mod characters { + use super::CharSegment::{self, *}; + + pub const CHAR_0: &[CharSegment] = &[T, TL, BL, B, BR, TR, NW, SE]; + pub const CHAR_1: &[CharSegment] = &[NE, TR, BR]; + pub const CHAR_2: &[CharSegment] = &[T, BL, B, TR, E, W]; + pub const CHAR_3: &[CharSegment] = &[T, B, BR, TR, E]; + pub const CHAR_4: &[CharSegment] = &[TL, BR, TR, E, W]; + pub const CHAR_5: &[CharSegment] = &[T, TL, B, BR, E, W]; + pub const CHAR_6: &[CharSegment] = &[T, TL, BL, B, BR, E, W]; + pub const CHAR_7: &[CharSegment] = &[T, NE, S]; + pub const CHAR_8: &[CharSegment] = &[T, TL, BL, B, BR, TR, E, W]; + pub const CHAR_9: &[CharSegment] = &[T, TL, BR, TR, E, W]; + + pub const CHAR_COLON: &[CharSegment] = &[N, S]; + pub const CHAR_SEMICOLON: &[CharSegment] = &[N, SW]; + pub const CHAR_EQUALS: &[CharSegment] = &[E, W, B]; + pub const CHAR_SLASH: &[CharSegment] = &[SW, NE]; + pub const CHAR_BACKSLASH: &[CharSegment] = &[SE, NW]; + pub const CHAR_PLUS: &[CharSegment] = &[N, E, S, W]; + pub const CHAR_STAR: &[CharSegment] = &[NE, N, NW, SE, S, SW]; + pub const CHAR_QUOTE: &[CharSegment] = &[N]; + pub const CHAR_BACKTICK: &[CharSegment] = &[NW]; + pub const CHAR_DASH: &[CharSegment] = &[W, E]; + pub const CHAR_COMMA: &[CharSegment] = &[SW]; + pub const CHAR_DOT: &[CharSegment] = &[S]; + pub const CHAR_CURLYOPEN: &[CharSegment] = &[T, NW, W, SW, B]; + pub const CHAR_CURLYCLOSE: &[CharSegment] = &[T, NE, E, SE, B]; + pub const CHAR_AMPERSAND: &[CharSegment] = &[T, NE, NW, W, BL, B, SE]; + + pub const CHAR_A: &[CharSegment] = &[T, TL, TR, E, W, BL, BR]; + pub const CHAR_B: &[CharSegment] = &[T, TR, BR, B, N, S, E]; + pub const CHAR_C: &[CharSegment] = &[T, TL, BL, B]; + pub const CHAR_D: &[CharSegment] = &[T, TR, BR, B, N, S]; + pub const CHAR_E: &[CharSegment] = &[T, TL, BL, B, W]; + pub const CHAR_F: &[CharSegment] = &[T, TL, BL, W]; + pub const CHAR_G: &[CharSegment] = &[T, TL, BL, B, BR, E]; + pub const CHAR_H: &[CharSegment] = &[TL, BL, E, W, TR, BR]; + pub const CHAR_I: &[CharSegment] = &[T, N, S, B]; + pub const CHAR_J: &[CharSegment] = &[TR, BR, B, BL]; + pub const CHAR_K: &[CharSegment] = &[TL, BL, W, NE, SE]; + pub const CHAR_L: &[CharSegment] = &[TL, BL, B]; + pub const CHAR_M: &[CharSegment] = &[BL, TL, NW, NE, TR, BR]; + pub const CHAR_N: &[CharSegment] = &[BL, TL, NW, SE, BR, TR]; + pub const CHAR_O: &[CharSegment] = &[T, TL, BL, B, BR, TR]; + pub const CHAR_P: &[CharSegment] = &[BL, TL, T, TR, E, W]; + pub const CHAR_Q: &[CharSegment] = &[T, TL, BL, B, BR, TR, SE]; + pub const CHAR_R: &[CharSegment] = &[BL, TL, T, TR, E, W, SE]; + pub const CHAR_S: &[CharSegment] = &[T, NW, E, BR, B]; + pub const CHAR_T: &[CharSegment] = &[T, N, S]; + pub const CHAR_U: &[CharSegment] = &[TL, BL, B, BR, TR]; + pub const CHAR_V: &[CharSegment] = &[TL, BL, SW, NE]; + pub const CHAR_W: &[CharSegment] = &[TL, BL, SW, SE, BR, TR]; + pub const CHAR_X: &[CharSegment] = &[NE, NW, SE, SW]; + pub const CHAR_Y: &[CharSegment] = &[NE, NW, S]; + pub const CHAR_Z: &[CharSegment] = &[T, NE, SW, B]; + + pub const CHAR_UNKNOWN: &[CharSegment] = &[N, NW, W, SW, S, SE, E, NE, T, TL, BL, B, BR, TR]; + + pub const ALL_CHARS: &str = + "0 1 2 3 4 5 6 7 8 9 : ; = / \\ + * ' ` - , . { } & A B C D E F G H I J K L M N O P Q R S T U V W X Y Z � "; + + pub fn get_char_segments(val: char) -> &'static [CharSegment] { + match val { + val if val.is_whitespace() => &[], + + '0' => CHAR_0, + '1' => CHAR_1, + '2' => CHAR_2, + '3' => CHAR_3, + '4' => CHAR_4, + '5' => CHAR_5, + '6' => CHAR_6, + '7' => CHAR_7, + '8' => CHAR_8, + '9' => CHAR_9, + + ':' => CHAR_COLON, + ';' => CHAR_SEMICOLON, + '=' => CHAR_EQUALS, + '/' => CHAR_SLASH, + '\\' => CHAR_BACKSLASH, + '+' => CHAR_PLUS, + '*' => CHAR_STAR, + '\'' => CHAR_QUOTE, + '`' => CHAR_BACKTICK, + '-' => CHAR_DASH, + ',' => CHAR_COMMA, + '.' => CHAR_DOT, + '{' => CHAR_CURLYOPEN, + '}' => CHAR_CURLYCLOSE, + '&' => CHAR_AMPERSAND, - embassy_time::Timer::after_secs(1).await; + 'A' | 'a' => CHAR_A, + 'B' | 'b' => CHAR_B, + 'C' | 'c' => CHAR_C, + 'D' | 'd' => CHAR_D, + 'E' | 'e' => CHAR_E, + 'F' | 'f' => CHAR_F, + 'G' | 'g' => CHAR_G, + 'H' | 'h' => CHAR_H, + 'I' | 'i' => CHAR_I, + 'J' | 'j' => CHAR_J, + 'K' | 'k' => CHAR_K, + 'L' | 'l' => CHAR_L, + 'M' | 'm' => CHAR_M, + 'N' | 'n' => CHAR_N, + 'O' | 'o' => CHAR_O, + 'P' | 'p' => CHAR_P, + 'Q' | 'q' => CHAR_Q, + 'R' | 'r' => CHAR_R, + 'S' | 's' => CHAR_S, + 'T' | 't' => CHAR_T, + 'U' | 'u' => CHAR_U, + 'V' | 'v' => CHAR_V, + 'W' | 'w' => CHAR_W, + 'X' | 'x' => CHAR_X, + 'Y' | 'y' => CHAR_Y, + 'Z' | 'z' => CHAR_Z, - defmt::info!("Writing frame"); - lcd.write_frame(&[!0xAAAAAAAA; 16]); + _ => CHAR_UNKNOWN, + } + } +} + +pub struct DisplayBuffer { + pixels: [u64; 4], +} + +impl DisplayBuffer { + pub const fn new() -> Self { + Self { pixels: [0; 4] } + } + + pub fn clear(&mut self) { + *self = Self::new(); + } + + fn write_char_segment(&mut self, index: usize, value: CharSegment) { + defmt::assert!(index < 6); + let (com, segments) = value.get_com_seg(); + self.pixels[com as usize] |= 1 << segments[index]; + } + + pub fn write_char(&mut self, index: usize, val: char) { + let segments = characters::get_char_segments(val); + + for segment in segments { + self.write_char_segment(index, *segment); + } + } + + pub fn write(&self, lcd: &mut Lcd<'_, LCD>) { + lcd.write_com_segments(0, self.pixels[0]); + lcd.write_com_segments(1, self.pixels[1]); + lcd.write_com_segments(2, self.pixels[2]); + lcd.write_com_segments(3, self.pixels[3]); + lcd.submit_frame(); + } + + pub fn write_colon(&mut self, index: usize) { + defmt::assert!(index < 4); + self.pixels[display_layout::COLON_COM as usize] |= 1 << display_layout::COLON_SEG[index]; + } + + pub fn write_dot(&mut self, index: usize) { + defmt::assert!(index < 4); + self.pixels[display_layout::DOT_COM as usize] |= 1 << display_layout::DOT_SEG[index]; + } + + pub fn write_bar(&mut self, index: usize) { + defmt::assert!(index < 4); + let (bar_com, bar_seg) = display_layout::BAR[index]; + self.pixels[bar_com as usize] |= 1 << bar_seg; + } +} + +#[derive(Debug, Clone, Copy)] +enum CharSegment { + /// North + N, + /// North west + NW, + /// West + W, + /// South west + SW, + /// South + S, + /// South East + SE, + /// East + E, + /// North East + NE, + /// Top + T, + /// Top left + TL, + /// Bottom left + BL, + /// Bottom + B, + /// Bottom right + BR, + /// Top right + TR, +} - embassy_time::Timer::after_secs(1).await; +impl CharSegment { + fn get_com_seg(&self) -> (u8, [u8; 6]) { + match self { + CharSegment::N => (display_layout::CHAR_N_COM, display_layout::CHAR_N_SEG), + CharSegment::NW => (display_layout::CHAR_NW_COM, display_layout::CHAR_NW_SEG), + CharSegment::W => (display_layout::CHAR_W_COM, display_layout::CHAR_W_SEG), + CharSegment::SW => (display_layout::CHAR_SW_COM, display_layout::CHAR_SW_SEG), + CharSegment::S => (display_layout::CHAR_S_COM, display_layout::CHAR_S_SEG), + CharSegment::SE => (display_layout::CHAR_SE_COM, display_layout::CHAR_SE_SEG), + CharSegment::E => (display_layout::CHAR_E_COM, display_layout::CHAR_E_SEG), + CharSegment::NE => (display_layout::CHAR_NE_COM, display_layout::CHAR_NE_SEG), + CharSegment::T => (display_layout::CHAR_T_COM, display_layout::CHAR_T_SEG), + CharSegment::TL => (display_layout::CHAR_TL_COM, display_layout::CHAR_TL_SEG), + CharSegment::BL => (display_layout::CHAR_BL_COM, display_layout::CHAR_BL_SEG), + CharSegment::B => (display_layout::CHAR_B_COM, display_layout::CHAR_B_SEG), + CharSegment::BR => (display_layout::CHAR_BR_COM, display_layout::CHAR_BR_SEG), + CharSegment::TR => (display_layout::CHAR_TR_COM, display_layout::CHAR_TR_SEG), + } } } -- cgit