diff options
| author | Dion Dokter <[email protected]> | 2025-11-20 17:48:04 +0100 |
|---|---|---|
| committer | Dion Dokter <[email protected]> | 2025-11-20 17:48:04 +0100 |
| commit | dd2a4d6126988d13e6ff21b26dc15deac1977531 (patch) | |
| tree | 0112da38fbc16ea5265ac6f5e645849734311272 /examples/stm32u0 | |
| parent | d111eceb4ba0094d34f58a4695bb3d43d188e591 (diff) | |
Create demo
Diffstat (limited to 'examples/stm32u0')
| -rw-r--r-- | examples/stm32u0/src/bin/lcd.rs | 331 |
1 files changed, 325 insertions, 6 deletions
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::*; | |||
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::{ | 6 | use embassy_stm32::{ |
| 7 | lcd::{Bias, Config, Duty, Lcd, LcdPin}, | 7 | lcd::{Bias, Config, Duty, Lcd, LcdPin}, |
| 8 | peripherals::LCD, | ||
| 8 | time::Hertz, | 9 | time::Hertz, |
| 9 | }; | 10 | }; |
| 11 | use embassy_time::Duration; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 13 | ||
| 12 | #[embassy_executor::main] | 14 | #[embassy_executor::main] |
| @@ -72,15 +74,332 @@ async fn main(_spawner: Spawner) { | |||
| 72 | ], | 74 | ], |
| 73 | ); | 75 | ); |
| 74 | 76 | ||
| 77 | { | ||
| 78 | let mut buffer = DisplayBuffer::new(); | ||
| 79 | for i in 0..4 { | ||
| 80 | buffer.write_colon(i); | ||
| 81 | buffer.write(&mut lcd); | ||
| 82 | embassy_time::Timer::after_millis(200).await; | ||
| 83 | buffer.write_dot(i); | ||
| 84 | buffer.write(&mut lcd); | ||
| 85 | embassy_time::Timer::after_millis(200).await; | ||
| 86 | } | ||
| 87 | for i in 0..4 { | ||
| 88 | buffer.write_bar(i); | ||
| 89 | buffer.write(&mut lcd); | ||
| 90 | embassy_time::Timer::after_millis(200).await; | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | embassy_time::Timer::after_millis(1000).await; | ||
| 95 | |||
| 96 | const MESSAGE: &str = "Hello embassy people. Hope you like this LCD demo :} "; | ||
| 75 | loop { | 97 | loop { |
| 76 | defmt::info!("Writing frame"); | 98 | print_message(MESSAGE, &mut lcd, Duration::from_millis(250)).await; |
| 77 | lcd.write_frame(&[0xAAAAAAAA; 16]); | 99 | print_message(characters::ALL_CHARS, &mut lcd, Duration::from_millis(500)).await; |
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | async fn print_message(message: &str, lcd: &mut Lcd<'_, LCD>, delay: Duration) { | ||
| 104 | let mut display_buffer = DisplayBuffer::new(); | ||
| 105 | |||
| 106 | let mut char_buffer = [' '; 6]; | ||
| 107 | for char in message.chars() { | ||
| 108 | char_buffer.copy_within(1.., 0); | ||
| 109 | char_buffer[5] = char; | ||
| 110 | |||
| 111 | display_buffer.clear(); | ||
| 112 | for (i, char) in char_buffer.iter().enumerate() { | ||
| 113 | display_buffer.write_char(i, *char); | ||
| 114 | } | ||
| 115 | display_buffer.write(lcd); | ||
| 116 | |||
| 117 | embassy_time::Timer::after(delay).await; | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | /// Display layout for the U0-DK | ||
| 122 | mod display_layout { | ||
| 123 | // Character layout. There are 6 characters, left-to-right | ||
| 124 | // T | ||
| 125 | // ───────── | ||
| 126 | // │ N │ | ||
| 127 | // │ │ │ │ │ | ||
| 128 | // TL │ └┐ │ ┌┘ │ TR | ||
| 129 | // │NW│ │ │NE│ | ||
| 130 | // │ │ │ | ||
| 131 | // W─── ───E | ||
| 132 | // │ │ │ | ||
| 133 | // │SW│ │ │SE│ | ||
| 134 | // BL │ ┌┘ │ └┐ │ BR | ||
| 135 | // │ │ │ │ │ | ||
| 136 | // │ S │ | ||
| 137 | // ───────── | ||
| 138 | // B | ||
| 139 | |||
| 140 | pub const CHAR_N_COM: u8 = 3; | ||
| 141 | pub const CHAR_N_SEG: [u8; 6] = [39, 37, 35, 48, 26, 33]; | ||
| 142 | pub const CHAR_NW_COM: u8 = 3; | ||
| 143 | pub const CHAR_NW_SEG: [u8; 6] = [49, 38, 36, 34, 27, 24]; | ||
| 144 | pub const CHAR_W_COM: u8 = 0; | ||
| 145 | pub const CHAR_W_SEG: [u8; 6] = CHAR_NW_SEG; | ||
| 146 | pub const CHAR_SW_COM: u8 = 2; | ||
| 147 | pub const CHAR_SW_SEG: [u8; 6] = CHAR_NW_SEG; | ||
| 148 | pub const CHAR_S_COM: u8 = 2; | ||
| 149 | pub const CHAR_S_SEG: [u8; 6] = [22, 6, 46, 11, 15, 29]; | ||
| 150 | pub const CHAR_SE_COM: u8 = 3; | ||
| 151 | pub const CHAR_SE_SEG: [u8; 6] = CHAR_S_SEG; | ||
| 152 | pub const CHAR_E_COM: u8 = 0; | ||
| 153 | pub const CHAR_E_SEG: [u8; 6] = [23, 45, 47, 14, 28, 32]; | ||
| 154 | pub const CHAR_NE_COM: u8 = 2; | ||
| 155 | pub const CHAR_NE_SEG: [u8; 6] = CHAR_N_SEG; | ||
| 156 | pub const CHAR_T_COM: u8 = 1; | ||
| 157 | pub const CHAR_T_SEG: [u8; 6] = CHAR_N_SEG; | ||
| 158 | pub const CHAR_TL_COM: u8 = 1; | ||
| 159 | pub const CHAR_TL_SEG: [u8; 6] = CHAR_NW_SEG; | ||
| 160 | pub const CHAR_BL_COM: u8 = 0; | ||
| 161 | pub const CHAR_BL_SEG: [u8; 6] = CHAR_S_SEG; | ||
| 162 | pub const CHAR_B_COM: u8 = 1; | ||
| 163 | pub const CHAR_B_SEG: [u8; 6] = CHAR_S_SEG; | ||
| 164 | pub const CHAR_BR_COM: u8 = 1; | ||
| 165 | pub const CHAR_BR_SEG: [u8; 6] = CHAR_E_SEG; | ||
| 166 | pub const CHAR_TR_COM: u8 = 0; | ||
| 167 | pub const CHAR_TR_SEG: [u8; 6] = CHAR_N_SEG; | ||
| 168 | |||
| 169 | pub const COLON_COM: u8 = 2; | ||
| 170 | pub const COLON_SEG: [u8; 4] = [23, 45, 47, 14]; | ||
| 171 | pub const DOT_COM: u8 = 3; | ||
| 172 | pub const DOT_SEG: [u8; 4] = COLON_SEG; | ||
| 173 | /// COM + SEG, bar from top to bottom | ||
| 174 | pub const BAR: [(u8, u8); 4] = [(2, 28), (3, 28), (2, 32), (3, 32)]; | ||
| 175 | } | ||
| 176 | |||
| 177 | mod characters { | ||
| 178 | use super::CharSegment::{self, *}; | ||
| 179 | |||
| 180 | pub const CHAR_0: &[CharSegment] = &[T, TL, BL, B, BR, TR, NW, SE]; | ||
| 181 | pub const CHAR_1: &[CharSegment] = &[NE, TR, BR]; | ||
| 182 | pub const CHAR_2: &[CharSegment] = &[T, BL, B, TR, E, W]; | ||
| 183 | pub const CHAR_3: &[CharSegment] = &[T, B, BR, TR, E]; | ||
| 184 | pub const CHAR_4: &[CharSegment] = &[TL, BR, TR, E, W]; | ||
| 185 | pub const CHAR_5: &[CharSegment] = &[T, TL, B, BR, E, W]; | ||
| 186 | pub const CHAR_6: &[CharSegment] = &[T, TL, BL, B, BR, E, W]; | ||
| 187 | pub const CHAR_7: &[CharSegment] = &[T, NE, S]; | ||
| 188 | pub const CHAR_8: &[CharSegment] = &[T, TL, BL, B, BR, TR, E, W]; | ||
| 189 | pub const CHAR_9: &[CharSegment] = &[T, TL, BR, TR, E, W]; | ||
| 190 | |||
| 191 | pub const CHAR_COLON: &[CharSegment] = &[N, S]; | ||
| 192 | pub const CHAR_SEMICOLON: &[CharSegment] = &[N, SW]; | ||
| 193 | pub const CHAR_EQUALS: &[CharSegment] = &[E, W, B]; | ||
| 194 | pub const CHAR_SLASH: &[CharSegment] = &[SW, NE]; | ||
| 195 | pub const CHAR_BACKSLASH: &[CharSegment] = &[SE, NW]; | ||
| 196 | pub const CHAR_PLUS: &[CharSegment] = &[N, E, S, W]; | ||
| 197 | pub const CHAR_STAR: &[CharSegment] = &[NE, N, NW, SE, S, SW]; | ||
| 198 | pub const CHAR_QUOTE: &[CharSegment] = &[N]; | ||
| 199 | pub const CHAR_BACKTICK: &[CharSegment] = &[NW]; | ||
| 200 | pub const CHAR_DASH: &[CharSegment] = &[W, E]; | ||
| 201 | pub const CHAR_COMMA: &[CharSegment] = &[SW]; | ||
| 202 | pub const CHAR_DOT: &[CharSegment] = &[S]; | ||
| 203 | pub const CHAR_CURLYOPEN: &[CharSegment] = &[T, NW, W, SW, B]; | ||
| 204 | pub const CHAR_CURLYCLOSE: &[CharSegment] = &[T, NE, E, SE, B]; | ||
| 205 | pub const CHAR_AMPERSAND: &[CharSegment] = &[T, NE, NW, W, BL, B, SE]; | ||
| 206 | |||
| 207 | pub const CHAR_A: &[CharSegment] = &[T, TL, TR, E, W, BL, BR]; | ||
| 208 | pub const CHAR_B: &[CharSegment] = &[T, TR, BR, B, N, S, E]; | ||
| 209 | pub const CHAR_C: &[CharSegment] = &[T, TL, BL, B]; | ||
| 210 | pub const CHAR_D: &[CharSegment] = &[T, TR, BR, B, N, S]; | ||
| 211 | pub const CHAR_E: &[CharSegment] = &[T, TL, BL, B, W]; | ||
| 212 | pub const CHAR_F: &[CharSegment] = &[T, TL, BL, W]; | ||
| 213 | pub const CHAR_G: &[CharSegment] = &[T, TL, BL, B, BR, E]; | ||
| 214 | pub const CHAR_H: &[CharSegment] = &[TL, BL, E, W, TR, BR]; | ||
| 215 | pub const CHAR_I: &[CharSegment] = &[T, N, S, B]; | ||
| 216 | pub const CHAR_J: &[CharSegment] = &[TR, BR, B, BL]; | ||
| 217 | pub const CHAR_K: &[CharSegment] = &[TL, BL, W, NE, SE]; | ||
| 218 | pub const CHAR_L: &[CharSegment] = &[TL, BL, B]; | ||
| 219 | pub const CHAR_M: &[CharSegment] = &[BL, TL, NW, NE, TR, BR]; | ||
| 220 | pub const CHAR_N: &[CharSegment] = &[BL, TL, NW, SE, BR, TR]; | ||
| 221 | pub const CHAR_O: &[CharSegment] = &[T, TL, BL, B, BR, TR]; | ||
| 222 | pub const CHAR_P: &[CharSegment] = &[BL, TL, T, TR, E, W]; | ||
| 223 | pub const CHAR_Q: &[CharSegment] = &[T, TL, BL, B, BR, TR, SE]; | ||
| 224 | pub const CHAR_R: &[CharSegment] = &[BL, TL, T, TR, E, W, SE]; | ||
| 225 | pub const CHAR_S: &[CharSegment] = &[T, NW, E, BR, B]; | ||
| 226 | pub const CHAR_T: &[CharSegment] = &[T, N, S]; | ||
| 227 | pub const CHAR_U: &[CharSegment] = &[TL, BL, B, BR, TR]; | ||
| 228 | pub const CHAR_V: &[CharSegment] = &[TL, BL, SW, NE]; | ||
| 229 | pub const CHAR_W: &[CharSegment] = &[TL, BL, SW, SE, BR, TR]; | ||
| 230 | pub const CHAR_X: &[CharSegment] = &[NE, NW, SE, SW]; | ||
| 231 | pub const CHAR_Y: &[CharSegment] = &[NE, NW, S]; | ||
| 232 | pub const CHAR_Z: &[CharSegment] = &[T, NE, SW, B]; | ||
| 233 | |||
| 234 | pub const CHAR_UNKNOWN: &[CharSegment] = &[N, NW, W, SW, S, SE, E, NE, T, TL, BL, B, BR, TR]; | ||
| 235 | |||
| 236 | pub const ALL_CHARS: &str = | ||
| 237 | "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 � "; | ||
| 238 | |||
| 239 | pub fn get_char_segments(val: char) -> &'static [CharSegment] { | ||
| 240 | match val { | ||
| 241 | val if val.is_whitespace() => &[], | ||
| 242 | |||
| 243 | '0' => CHAR_0, | ||
| 244 | '1' => CHAR_1, | ||
| 245 | '2' => CHAR_2, | ||
| 246 | '3' => CHAR_3, | ||
| 247 | '4' => CHAR_4, | ||
| 248 | '5' => CHAR_5, | ||
| 249 | '6' => CHAR_6, | ||
| 250 | '7' => CHAR_7, | ||
| 251 | '8' => CHAR_8, | ||
| 252 | '9' => CHAR_9, | ||
| 253 | |||
| 254 | ':' => CHAR_COLON, | ||
| 255 | ';' => CHAR_SEMICOLON, | ||
| 256 | '=' => CHAR_EQUALS, | ||
| 257 | '/' => CHAR_SLASH, | ||
| 258 | '\\' => CHAR_BACKSLASH, | ||
| 259 | '+' => CHAR_PLUS, | ||
| 260 | '*' => CHAR_STAR, | ||
| 261 | '\'' => CHAR_QUOTE, | ||
| 262 | '`' => CHAR_BACKTICK, | ||
| 263 | '-' => CHAR_DASH, | ||
| 264 | ',' => CHAR_COMMA, | ||
| 265 | '.' => CHAR_DOT, | ||
| 266 | '{' => CHAR_CURLYOPEN, | ||
| 267 | '}' => CHAR_CURLYCLOSE, | ||
| 268 | '&' => CHAR_AMPERSAND, | ||
| 78 | 269 | ||
| 79 | embassy_time::Timer::after_secs(1).await; | 270 | 'A' | 'a' => CHAR_A, |
| 271 | 'B' | 'b' => CHAR_B, | ||
| 272 | 'C' | 'c' => CHAR_C, | ||
| 273 | 'D' | 'd' => CHAR_D, | ||
| 274 | 'E' | 'e' => CHAR_E, | ||
| 275 | 'F' | 'f' => CHAR_F, | ||
| 276 | 'G' | 'g' => CHAR_G, | ||
| 277 | 'H' | 'h' => CHAR_H, | ||
| 278 | 'I' | 'i' => CHAR_I, | ||
| 279 | 'J' | 'j' => CHAR_J, | ||
| 280 | 'K' | 'k' => CHAR_K, | ||
| 281 | 'L' | 'l' => CHAR_L, | ||
| 282 | 'M' | 'm' => CHAR_M, | ||
| 283 | 'N' | 'n' => CHAR_N, | ||
| 284 | 'O' | 'o' => CHAR_O, | ||
| 285 | 'P' | 'p' => CHAR_P, | ||
| 286 | 'Q' | 'q' => CHAR_Q, | ||
| 287 | 'R' | 'r' => CHAR_R, | ||
| 288 | 'S' | 's' => CHAR_S, | ||
| 289 | 'T' | 't' => CHAR_T, | ||
| 290 | 'U' | 'u' => CHAR_U, | ||
| 291 | 'V' | 'v' => CHAR_V, | ||
| 292 | 'W' | 'w' => CHAR_W, | ||
| 293 | 'X' | 'x' => CHAR_X, | ||
| 294 | 'Y' | 'y' => CHAR_Y, | ||
| 295 | 'Z' | 'z' => CHAR_Z, | ||
| 80 | 296 | ||
| 81 | defmt::info!("Writing frame"); | 297 | _ => CHAR_UNKNOWN, |
| 82 | lcd.write_frame(&[!0xAAAAAAAA; 16]); | 298 | } |
| 299 | } | ||
| 300 | } | ||
| 301 | |||
| 302 | pub struct DisplayBuffer { | ||
| 303 | pixels: [u64; 4], | ||
| 304 | } | ||
| 305 | |||
| 306 | impl DisplayBuffer { | ||
| 307 | pub const fn new() -> Self { | ||
| 308 | Self { pixels: [0; 4] } | ||
| 309 | } | ||
| 310 | |||
| 311 | pub fn clear(&mut self) { | ||
| 312 | *self = Self::new(); | ||
| 313 | } | ||
| 314 | |||
| 315 | fn write_char_segment(&mut self, index: usize, value: CharSegment) { | ||
| 316 | defmt::assert!(index < 6); | ||
| 317 | let (com, segments) = value.get_com_seg(); | ||
| 318 | self.pixels[com as usize] |= 1 << segments[index]; | ||
| 319 | } | ||
| 320 | |||
| 321 | pub fn write_char(&mut self, index: usize, val: char) { | ||
| 322 | let segments = characters::get_char_segments(val); | ||
| 323 | |||
| 324 | for segment in segments { | ||
| 325 | self.write_char_segment(index, *segment); | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | pub fn write(&self, lcd: &mut Lcd<'_, LCD>) { | ||
| 330 | lcd.write_com_segments(0, self.pixels[0]); | ||
| 331 | lcd.write_com_segments(1, self.pixels[1]); | ||
| 332 | lcd.write_com_segments(2, self.pixels[2]); | ||
| 333 | lcd.write_com_segments(3, self.pixels[3]); | ||
| 334 | lcd.submit_frame(); | ||
| 335 | } | ||
| 336 | |||
| 337 | pub fn write_colon(&mut self, index: usize) { | ||
| 338 | defmt::assert!(index < 4); | ||
| 339 | self.pixels[display_layout::COLON_COM as usize] |= 1 << display_layout::COLON_SEG[index]; | ||
| 340 | } | ||
| 341 | |||
| 342 | pub fn write_dot(&mut self, index: usize) { | ||
| 343 | defmt::assert!(index < 4); | ||
| 344 | self.pixels[display_layout::DOT_COM as usize] |= 1 << display_layout::DOT_SEG[index]; | ||
| 345 | } | ||
| 346 | |||
| 347 | pub fn write_bar(&mut self, index: usize) { | ||
| 348 | defmt::assert!(index < 4); | ||
| 349 | let (bar_com, bar_seg) = display_layout::BAR[index]; | ||
| 350 | self.pixels[bar_com as usize] |= 1 << bar_seg; | ||
| 351 | } | ||
| 352 | } | ||
| 353 | |||
| 354 | #[derive(Debug, Clone, Copy)] | ||
| 355 | enum CharSegment { | ||
| 356 | /// North | ||
| 357 | N, | ||
| 358 | /// North west | ||
| 359 | NW, | ||
| 360 | /// West | ||
| 361 | W, | ||
| 362 | /// South west | ||
| 363 | SW, | ||
| 364 | /// South | ||
| 365 | S, | ||
| 366 | /// South East | ||
| 367 | SE, | ||
| 368 | /// East | ||
| 369 | E, | ||
| 370 | /// North East | ||
| 371 | NE, | ||
| 372 | /// Top | ||
| 373 | T, | ||
| 374 | /// Top left | ||
| 375 | TL, | ||
| 376 | /// Bottom left | ||
| 377 | BL, | ||
| 378 | /// Bottom | ||
| 379 | B, | ||
| 380 | /// Bottom right | ||
| 381 | BR, | ||
| 382 | /// Top right | ||
| 383 | TR, | ||
| 384 | } | ||
| 83 | 385 | ||
| 84 | embassy_time::Timer::after_secs(1).await; | 386 | impl CharSegment { |
| 387 | fn get_com_seg(&self) -> (u8, [u8; 6]) { | ||
| 388 | match self { | ||
| 389 | CharSegment::N => (display_layout::CHAR_N_COM, display_layout::CHAR_N_SEG), | ||
| 390 | CharSegment::NW => (display_layout::CHAR_NW_COM, display_layout::CHAR_NW_SEG), | ||
| 391 | CharSegment::W => (display_layout::CHAR_W_COM, display_layout::CHAR_W_SEG), | ||
| 392 | CharSegment::SW => (display_layout::CHAR_SW_COM, display_layout::CHAR_SW_SEG), | ||
| 393 | CharSegment::S => (display_layout::CHAR_S_COM, display_layout::CHAR_S_SEG), | ||
| 394 | CharSegment::SE => (display_layout::CHAR_SE_COM, display_layout::CHAR_SE_SEG), | ||
| 395 | CharSegment::E => (display_layout::CHAR_E_COM, display_layout::CHAR_E_SEG), | ||
| 396 | CharSegment::NE => (display_layout::CHAR_NE_COM, display_layout::CHAR_NE_SEG), | ||
| 397 | CharSegment::T => (display_layout::CHAR_T_COM, display_layout::CHAR_T_SEG), | ||
| 398 | CharSegment::TL => (display_layout::CHAR_TL_COM, display_layout::CHAR_TL_SEG), | ||
| 399 | CharSegment::BL => (display_layout::CHAR_BL_COM, display_layout::CHAR_BL_SEG), | ||
| 400 | CharSegment::B => (display_layout::CHAR_B_COM, display_layout::CHAR_B_SEG), | ||
| 401 | CharSegment::BR => (display_layout::CHAR_BR_COM, display_layout::CHAR_BR_SEG), | ||
| 402 | CharSegment::TR => (display_layout::CHAR_TR_COM, display_layout::CHAR_TR_SEG), | ||
| 403 | } | ||
| 85 | } | 404 | } |
| 86 | } | 405 | } |
