1
0
Fork 0

Implement an asynchronous display driver

This driver updates the display contents in the background, to not block
the logic from running.
This commit is contained in:
rahix 2024-11-03 12:55:21 +01:00
parent a61ccf61cb
commit ca6098ec20
2 changed files with 179 additions and 27 deletions

View file

@ -0,0 +1,167 @@
use std::sync::{Arc, Condvar, Mutex};
#[derive(Debug)]
pub struct Display {
inner: Arc<DisplayInner>,
}
#[derive(Debug, Default)]
struct DisplayInner {
shared: Mutex<DisplayShared>,
signal: Condvar,
}
#[derive(Debug, Default)]
struct DisplayShared {
ready: bool,
pending: bool,
lines: [String; 4],
}
impl Display {
pub fn new() -> Self {
let inner: Arc<DisplayInner> = Default::default();
std::thread::spawn({
let inner = inner.clone();
move || {
display_thread(inner);
}
});
Self { inner }
}
pub fn is_ready(&self) -> bool {
self.inner.shared.lock().unwrap().ready
}
pub fn update_line(&mut self, line_index: u8, text: &str) {
assert!(line_index < 4);
let mut shared = self.inner.shared.lock().unwrap();
let line = &mut shared.lines[usize::from(line_index)];
if line != text {
line.clear();
line.push_str(text);
shared.pending = true;
self.inner.signal.notify_one();
}
}
}
fn display_thread(display_data: Arc<DisplayInner>) {
let i2c = linux_embedded_hal::I2cdev::new("/dev/i2c-1").unwrap();
let mut display_options =
hd44780::setup::DisplayOptionsI2C::new(hd44780::memory_map::MemoryMap2004::new())
.with_i2c_bus(i2c, 0x27);
let mut display = loop {
match hd44780::HD44780::new(display_options, &mut linux_embedded_hal::Delay) {
Err((options_back, error)) => {
log::warn!("Failed setting up display: {:?}, retrying...", error);
std::thread::sleep(std::time::Duration::from_millis(500));
display_options = options_back;
}
Ok(display) => break display,
}
};
loop {
let Err(e) = display.clear(&mut linux_embedded_hal::Delay) else {
break;
};
log::warn!("Error clearing display: {:?}, retrying...", e);
std::thread::sleep(std::time::Duration::from_millis(500));
}
loop {
let Err(e) = display.reset(&mut linux_embedded_hal::Delay) else {
break;
};
log::warn!("Error resetting display: {:?}, retrying...", e);
std::thread::sleep(std::time::Duration::from_millis(500));
}
loop {
let Err(e) = display.set_display_mode(
hd44780::DisplayMode {
cursor_visibility: hd44780::Cursor::Invisible,
cursor_blink: hd44780::CursorBlink::Off,
display: hd44780::Display::On,
},
&mut linux_embedded_hal::Delay,
) else {
break;
};
log::warn!("Error configuring display: {:?}, retrying...", e);
std::thread::sleep(std::time::Duration::from_millis(500));
}
display_data.shared.lock().unwrap().ready = true;
let mut needs_update = [false; 4];
let mut display_lines_old = [
String::with_capacity(20),
String::with_capacity(20),
String::with_capacity(20),
String::with_capacity(20),
];
let mut display_lines = [
String::with_capacity(20),
String::with_capacity(20),
String::with_capacity(20),
String::with_capacity(20),
];
loop {
{
let mut shared = display_data.shared.lock().unwrap();
if !shared.pending {
shared = display_data.signal.wait(shared).unwrap();
}
shared.pending = false;
shared.lines.clone_into(&mut display_lines);
};
for ((old, new), needs_update) in display_lines_old
.iter()
.zip(&display_lines)
.zip(needs_update.iter_mut())
{
*needs_update = old != new;
}
display_lines.clone_into(&mut display_lines_old);
for ((i, line), needs_update) in display_lines.iter_mut().enumerate().zip(needs_update) {
if !needs_update {
continue;
}
// Retry display write three times
for _ in 0..3 {
let res = display.set_cursor_xy(
(0, u8::try_from(i).unwrap()),
&mut linux_embedded_hal::Delay,
);
match res {
Ok(_) => (),
Err(e) => {
log::warn!("Error moving display cursor: {:?}, retrying...", e);
continue;
}
}
line.push_str(" ");
line.truncate(20);
let res = display.write_str(line, &mut linux_embedded_hal::Delay);
match res {
Ok(_) => break,
Err(e) => {
log::warn!("Error writing data to display: {:?}, retrying...", e);
continue;
}
}
}
}
}
}

View file

@ -4,6 +4,8 @@ use profirust::dp;
use profirust::fdl; use profirust::fdl;
use profirust::phy; use profirust::phy;
mod display;
// Bus Parameters // Bus Parameters
const MASTER_ADDRESS: u8 = 3; const MASTER_ADDRESS: u8 = 3;
const BUS_DEVICE: &'static str = "/dev/ttyAMA0"; const BUS_DEVICE: &'static str = "/dev/ttyAMA0";
@ -20,7 +22,9 @@ pi::process_image! {
pub bgb1: (X, 0, 0), // Left Limit Switch pub bgb1: (X, 0, 0), // Left Limit Switch
pub bgb2: (X, 0, 1), // Right Limit Switch pub bgb2: (X, 0, 1), // Right Limit Switch
pub bpb1: (X, 0, 2), // Pressure > 1.5 barg pub bpb1: (X, 0, 2), // Pressure > 1.5 barg
pub want_run: (X, 0, 4), // Start/Stop Switch pub want_run: (X, 0, 4), // Start/Stop Switch
pub want_run_once: (X, 0, 5), // Start Once Button
pub connection_alive: (X, 64, 0), pub connection_alive: (X, 64, 0),
} }
@ -105,33 +109,8 @@ fn main() {
.format_timestamp_micros() .format_timestamp_micros()
.init(); .init();
let mut i2c = linux_embedded_hal::I2cdev::new("/dev/i2c-1").unwrap(); let mut display = display::Display::new();
let mut delay = linux_embedded_hal::Delay; display.update_line(0, &format!("{:^20}", "torque tester"));
let mut display_options =
hd44780::setup::DisplayOptionsI2C::new(hd44780::memory_map::MemoryMap2004::new())
.with_i2c_bus(i2c, 0x27);
let mut display = loop {
match hd44780::HD44780::new(display_options, &mut delay) {
Err((options_back, error)) => {
log::warn!("Failed setting up display: {:?}", error);
display_options = options_back;
}
Ok(display) => break display,
}
};
display.clear(&mut delay).unwrap();
display.reset(&mut delay).unwrap();
display.set_display_mode(hd44780::DisplayMode {
cursor_visibility: hd44780::Cursor::Invisible,
cursor_blink: hd44780::CursorBlink::Off,
display: hd44780::Display::On,
}, &mut delay).unwrap();
display.set_cursor_xy((0, 0), &mut delay).unwrap();
display.write_str(&format!("{:^20}", "torque tester"), &mut delay).unwrap();
display.set_cursor_xy((0, 2), &mut delay).unwrap();
display.write_str(&format!("{:^20}", "W. in P."), &mut delay).unwrap();
let mut dp_master = dp::DpMaster::new(vec![]); let mut dp_master = dp::DpMaster::new(vec![]);
@ -228,6 +207,12 @@ fn main() {
*pi::tag_mut!(&mut pii, X, 64, 0) = rio_running; *pi::tag_mut!(&mut pii, X, 64, 0) = rio_running;
{
let pii = Pii::from(&pii);
display.update_line(2, &format!("Switch: {}", pii.want_run()));
display.update_line(3, &format!("Button: {}", pii.want_run_once()));
}
program(Pii::from(&pii), PiqMut::from(&mut piq), now); program(Pii::from(&pii), PiqMut::from(&mut piq), now);
let remoteio = dp_master.get_mut(rio_handle); let remoteio = dp_master.get_mut(rio_handle);