#![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(); } }