feat: animated lcd scroller
This commit is contained in:
parent
87a8b04c35
commit
be26f8f688
1 changed files with 161 additions and 0 deletions
161
examples/lcd-animate.rs
Executable file
161
examples/lcd-animate.rs
Executable file
|
@ -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<Output<PushPull>>,
|
||||
PC7<Output<PushPull>>,
|
||||
PB5<Output<PushPull>>,
|
||||
PB4<Output<PushPull>>,
|
||||
PB10<Output<PushPull>>,
|
||||
PA8<Output<PushPull>>,
|
||||
>,
|
||||
>;
|
||||
|
||||
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<String<CHARS>, 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<const LINES: usize, const CHARS: usize>(
|
||||
buf: &Vec<String<CHARS>, 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<const LINES: usize, const CHARS: usize>(
|
||||
buf: &mut Vec<String<CHARS>, 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<const LINES: usize, const CHARS: usize>(buf: &mut Vec<String<CHARS>, LINES>) {
|
||||
buf.clear();
|
||||
for _ in 0..LINES {
|
||||
buf.push(String::new()).unwrap();
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue