From be26f8f6882ee178f30d319c7563187ed789a2af Mon Sep 17 00:00:00 2001 From: cscherr Date: Tue, 20 May 2025 12:54:15 +0200 Subject: [PATCH] feat: animated lcd scroller --- examples/lcd-animate.rs | 161 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100755 examples/lcd-animate.rs diff --git a/examples/lcd-animate.rs b/examples/lcd-animate.rs new file mode 100755 index 0000000..3c62348 --- /dev/null +++ b/examples/lcd-animate.rs @@ -0,0 +1,161 @@ +#![no_main] +#![no_std] + +use heapless::{String, Vec}; +use panic_probe as _; + +use defmt_rtt as _; // global logger + +use cortex_m_rt::entry; +use hal::{ + delay::Delay, + gpio::{Output, PushPull, gpioa::*, gpiob::*, gpioc::*}, + pac, + prelude::*, + rcc::Config, +}; +use hd44780_driver::HD44780; + +#[defmt::panic_handler] +fn panic() -> ! { + cortex_m::asm::udf() +} + +type Lcd = HD44780< + hd44780_driver::bus::FourBitBus< + PA9>, + PC7>, + PB5>, + PB4>, + PB10>, + PA8>, + >, +>; + +const FPS: u32 = 12; +const LINES: usize = 4; +const CHARS: usize = 20; +const SIGNS_LEN: usize = 7; +const SIGNS: [char; SIGNS_LEN] = ['N', 'e', 'w', 'T', 'e', 'c', ' ']; + +#[entry] +fn main() -> ! { + let dp = pac::Peripherals::take().unwrap(); + let cp = cortex_m::Peripherals::take().unwrap(); + + let mut rcc = dp.RCC.freeze(Config::hsi16()); + + let gpioa = dp.GPIOA.split(&mut rcc); + let gpiob = dp.GPIOB.split(&mut rcc); + let gpioc = dp.GPIOC.split(&mut rcc); + + // literal D4-D7 ports etc as written on the nucleo board, mapped to the D4-D7 ports of the LCD + // controller + let d4 = gpiob.pb5.into_push_pull_output(); + let d5 = gpiob.pb4.into_push_pull_output(); + let d6 = gpiob.pb10.into_push_pull_output(); + let d7 = gpioa.pa8.into_push_pull_output(); + + // clock enable on D9 + let en = gpioc.pc7.into_push_pull_output(); + // register select on D8, the lib wants that but I'd just put it on ground otherwise + let rs = gpioa.pa9.into_push_pull_output(); + + // See https://en.wikipedia.org/wiki/Hitachi_HD44780_LCD_controller#Interface + // for the pins of the LCD + + let mut delay = cp.SYST.delay(rcc.clocks); + + let mut led = gpioa.pa5.into_push_pull_output(); + + let mut lcd: Lcd = HD44780::new_4bit(rs, en, d4, d5, d6, d7, &mut delay) + .expect("could not init HD44780 driver"); + lcd.set_display_mode( + hd44780_driver::DisplayMode { + cursor_visibility: hd44780_driver::Cursor::Invisible, + cursor_blink: hd44780_driver::CursorBlink::Off, + display: hd44780_driver::Display::On, + }, + &mut delay, + ) + .expect("could not set display properties"); + lcd.reset(&mut delay).expect("could not reset the lcd"); + + let mut i: usize = 0; + let mut buf: Vec, LINES> = Vec::new(); + + loop { + led.set_high().unwrap(); + + reset_buf(&mut buf); + animation(&mut buf, i); + display(&buf, &mut lcd, &mut delay, i % LINES); + + led.set_low().unwrap(); + + delay.delay_us(1_000_000 / FPS); + i += 1; + } +} + +fn display( + buf: &Vec, LINES>, + lcd: &mut Lcd, + delay: &mut Delay, + which: usize, +) { + match which { + 0 => { + lcd.set_cursor_pos(0, delay) + .expect("could not set cursor pos"); + lcd.write_str(&buf[0], delay) + .expect("could not display string"); + } + 1 => { + lcd.set_cursor_pos(60, delay) + .expect("could not set cursor pos"); + lcd.write_str(&buf[1], delay) + .expect("could not display string"); + } + 2 => { + lcd.set_cursor_pos(20, delay) + .expect("could not set cursor pos"); + lcd.write_str(&buf[2], delay) + .expect("could not display string"); + } + 3 => { + // line 4 is a bit weird and needs some offset + let mut tmp: String<30> = String::new(); + tmp.push_str(" ").unwrap(); + tmp.push_str(&buf[3]).unwrap(); + lcd.set_cursor_pos(80, delay) + .expect("could not set cursor pos"); + lcd.write_str(&tmp, delay) + .expect("could not display string"); + } + _ => unreachable!(), + } +} + +fn animation( + buf: &mut Vec, LINES>, + frame: usize, +) { + for i in 0..CHARS { + for (bi, buf) in buf.iter_mut().enumerate() { + match buf.push(SIGNS[(i + bi * 2 + frame) % SIGNS_LEN]) { + Ok(_) => (), + Err(_e) => { + panic!("Could not push string in animation. i={}, bi={}", i, bi); + } + } + } + } +} + +fn reset_buf(buf: &mut Vec, LINES>) { + buf.clear(); + for _ in 0..LINES { + buf.push(String::new()).unwrap(); + } +}