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::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);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue