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:
parent
a61ccf61cb
commit
ca6098ec20
2 changed files with 179 additions and 27 deletions
167
src/display.rs
167
src/display.rs
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
39
src/main.rs
39
src/main.rs
|
|
@ -4,6 +4,8 @@ use profirust::dp;
|
|||
use profirust::fdl;
|
||||
use profirust::phy;
|
||||
|
||||
mod display;
|
||||
|
||||
// Bus Parameters
|
||||
const MASTER_ADDRESS: u8 = 3;
|
||||
const BUS_DEVICE: &'static str = "/dev/ttyAMA0";
|
||||
|
|
@ -20,7 +22,9 @@ pi::process_image! {
|
|||
pub bgb1: (X, 0, 0), // Left Limit Switch
|
||||
pub bgb2: (X, 0, 1), // Right Limit Switch
|
||||
pub bpb1: (X, 0, 2), // Pressure > 1.5 barg
|
||||
|
||||
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),
|
||||
}
|
||||
|
|
@ -105,33 +109,8 @@ fn main() {
|
|||
.format_timestamp_micros()
|
||||
.init();
|
||||
|
||||
let mut i2c = linux_embedded_hal::I2cdev::new("/dev/i2c-1").unwrap();
|
||||
let mut delay = linux_embedded_hal::Delay;
|
||||
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 display = display::Display::new();
|
||||
display.update_line(0, &format!("{:^20}", "torque tester"));
|
||||
|
||||
let mut dp_master = dp::DpMaster::new(vec![]);
|
||||
|
||||
|
|
@ -228,6 +207,12 @@ fn main() {
|
|||
|
||||
*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);
|
||||
|
||||
let remoteio = dp_master.get_mut(rio_handle);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue