diff options
| -rw-r--r-- | embassy-stm32/src/lcd.rs | 73 | ||||
| -rw-r--r-- | examples/stm32u0/src/bin/lcd.rs | 5 |
2 files changed, 75 insertions, 3 deletions
diff --git a/embassy-stm32/src/lcd.rs b/embassy-stm32/src/lcd.rs index 348865fbd..ea29f1398 100644 --- a/embassy-stm32/src/lcd.rs +++ b/embassy-stm32/src/lcd.rs | |||
| @@ -152,6 +152,7 @@ pub enum Drive { | |||
| 152 | pub struct Lcd<'d, T: Instance> { | 152 | pub struct Lcd<'d, T: Instance> { |
| 153 | _peri: PhantomData<&'d mut T>, | 153 | _peri: PhantomData<&'d mut T>, |
| 154 | duty: Duty, | 154 | duty: Duty, |
| 155 | ck_div: u32, | ||
| 155 | } | 156 | } |
| 156 | 157 | ||
| 157 | impl<'d, T: Instance> Lcd<'d, T> { | 158 | impl<'d, T: Instance> Lcd<'d, T> { |
| @@ -227,9 +228,11 @@ impl<'d, T: Instance> Lcd<'d, T> { | |||
| 227 | } | 228 | } |
| 228 | } | 229 | } |
| 229 | 230 | ||
| 231 | let ck_div = lcd_clk.0 / ((1 << ps) * (div + 16)); | ||
| 232 | |||
| 230 | trace!( | 233 | trace!( |
| 231 | "lcd_clk: {}, fps: {}, ps: {}, div: {}", | 234 | "lcd_clk: {}, fps: {}, ps: {}, div: {}, ck_div: {}", |
| 232 | lcd_clk, best_fps_match, ps, div | 235 | lcd_clk, best_fps_match, ps, div, ck_div |
| 233 | ); | 236 | ); |
| 234 | 237 | ||
| 235 | if best_fps_match == u32::MAX || ps > 0xF { | 238 | if best_fps_match == u32::MAX || ps > 0xF { |
| @@ -271,6 +274,7 @@ impl<'d, T: Instance> Lcd<'d, T> { | |||
| 271 | Self { | 274 | Self { |
| 272 | _peri: PhantomData, | 275 | _peri: PhantomData, |
| 273 | duty: config.duty, | 276 | duty: config.duty, |
| 277 | ck_div, | ||
| 274 | } | 278 | } |
| 275 | } | 279 | } |
| 276 | 280 | ||
| @@ -371,6 +375,42 @@ impl<'d, T: Instance> Lcd<'d, T> { | |||
| 371 | pub fn num_com_pins(&self) -> u8 { | 375 | pub fn num_com_pins(&self) -> u8 { |
| 372 | self.duty.num_com_pins() | 376 | self.duty.num_com_pins() |
| 373 | } | 377 | } |
| 378 | |||
| 379 | /// Set the blink behavior on some pixels. | ||
| 380 | /// | ||
| 381 | /// The blink frequency is an approximation. It's divided from the clock selected by the FPS. | ||
| 382 | /// Play with the FPS value if you want the blink frequency to be more accurate. | ||
| 383 | /// | ||
| 384 | /// If a blink frequency cannot be attained, this function will panic. | ||
| 385 | pub fn set_blink(&mut self, selector: BlinkSelector, freq: BlinkFreq) { | ||
| 386 | // Freq * 100 to be able to do integer math | ||
| 387 | let scaled_blink_freq = match freq { | ||
| 388 | BlinkFreq::Hz0_25 => 25, | ||
| 389 | BlinkFreq::Hz0_5 => 50, | ||
| 390 | BlinkFreq::Hz1 => 100, | ||
| 391 | BlinkFreq::Hz2 => 200, | ||
| 392 | BlinkFreq::Hz4 => 400, | ||
| 393 | }; | ||
| 394 | |||
| 395 | let desired_divider = self.ck_div * 100 / scaled_blink_freq; | ||
| 396 | let target_divider = desired_divider.next_power_of_two(); | ||
| 397 | let power_divisions = target_divider.trailing_zeros(); | ||
| 398 | |||
| 399 | trace!( | ||
| 400 | "Setting LCD blink frequency -> desired_divider: {}, target_divider: {}", | ||
| 401 | desired_divider, target_divider | ||
| 402 | ); | ||
| 403 | |||
| 404 | assert!( | ||
| 405 | (8..=1024).contains(&target_divider), | ||
| 406 | "LCD blink frequency cannot be attained" | ||
| 407 | ); | ||
| 408 | |||
| 409 | T::regs().fcr().modify(|reg| { | ||
| 410 | reg.set_blinkf((power_divisions - 3) as u8); | ||
| 411 | reg.set_blink(selector as u8); | ||
| 412 | }) | ||
| 413 | } | ||
| 374 | } | 414 | } |
| 375 | 415 | ||
| 376 | impl<'d, T: Instance> Drop for Lcd<'d, T> { | 416 | impl<'d, T: Instance> Drop for Lcd<'d, T> { |
| @@ -381,6 +421,35 @@ impl<'d, T: Instance> Drop for Lcd<'d, T> { | |||
| 381 | } | 421 | } |
| 382 | } | 422 | } |
| 383 | 423 | ||
| 424 | /// Blink frequency | ||
| 425 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 426 | pub enum BlinkFreq { | ||
| 427 | /// 0.25 hz | ||
| 428 | Hz0_25, | ||
| 429 | /// 0.5 hz | ||
| 430 | Hz0_5, | ||
| 431 | /// 1 hz | ||
| 432 | Hz1, | ||
| 433 | /// 2 hz | ||
| 434 | Hz2, | ||
| 435 | /// 4 hz | ||
| 436 | Hz4, | ||
| 437 | } | ||
| 438 | |||
| 439 | /// Blink pixel selector | ||
| 440 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 441 | #[repr(u8)] | ||
| 442 | pub enum BlinkSelector { | ||
| 443 | /// No pixels blink | ||
| 444 | None = 0b00, | ||
| 445 | /// The SEG0, COM0 pixel blinks if the pixel is set | ||
| 446 | Seg0Com0 = 0b01, | ||
| 447 | /// The SEG0 pixel of all COMs blinks if the pixel is set | ||
| 448 | Seg0ComAll = 0b10, | ||
| 449 | /// All pixels blink if the pixel is set | ||
| 450 | All = 0b11, | ||
| 451 | } | ||
| 452 | |||
| 384 | /// A type-erased pin that can be configured as an LCD pin. | 453 | /// A type-erased pin that can be configured as an LCD pin. |
| 385 | /// This is used for passing pins to the new function in the array. | 454 | /// This is used for passing pins to the new function in the array. |
| 386 | pub struct LcdPin<'d, T: Instance> { | 455 | pub struct LcdPin<'d, T: Instance> { |
diff --git a/examples/stm32u0/src/bin/lcd.rs b/examples/stm32u0/src/bin/lcd.rs index 6e4378074..2b34d4ef1 100644 --- a/examples/stm32u0/src/bin/lcd.rs +++ b/examples/stm32u0/src/bin/lcd.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::lcd::{Bias, Config, Duty, Lcd, LcdPin}; | 6 | use embassy_stm32::lcd::{Bias, BlinkFreq, BlinkSelector, Config, Duty, Lcd, LcdPin}; |
| 7 | use embassy_stm32::peripherals::LCD; | 7 | use embassy_stm32::peripherals::LCD; |
| 8 | use embassy_stm32::time::Hertz; | 8 | use embassy_stm32::time::Hertz; |
| 9 | use embassy_time::Duration; | 9 | use embassy_time::Duration; |
| @@ -72,6 +72,7 @@ async fn main(_spawner: Spawner) { | |||
| 72 | ], | 72 | ], |
| 73 | ); | 73 | ); |
| 74 | 74 | ||
| 75 | lcd.set_blink(BlinkSelector::All, BlinkFreq::Hz4); | ||
| 75 | { | 76 | { |
| 76 | let mut buffer = DisplayBuffer::new(); | 77 | let mut buffer = DisplayBuffer::new(); |
| 77 | for i in 0..4 { | 78 | for i in 0..4 { |
| @@ -91,6 +92,8 @@ async fn main(_spawner: Spawner) { | |||
| 91 | 92 | ||
| 92 | embassy_time::Timer::after_millis(1000).await; | 93 | embassy_time::Timer::after_millis(1000).await; |
| 93 | 94 | ||
| 95 | lcd.set_blink(BlinkSelector::None, BlinkFreq::Hz4); | ||
| 96 | |||
| 94 | const MESSAGE: &str = "Hello embassy people. Hope you like this LCD demo :} "; | 97 | const MESSAGE: &str = "Hello embassy people. Hope you like this LCD demo :} "; |
| 95 | loop { | 98 | loop { |
| 96 | print_message(MESSAGE, &mut lcd, Duration::from_millis(250)).await; | 99 | print_message(MESSAGE, &mut lcd, Duration::from_millis(250)).await; |
