Author SHA1 Message Date
ede1998 bd124bf9c2 prepare alternative display driver support 2026-08-26 21:51:01 +02:00
5 changed files with 254 additions and 37 deletions
Generated
+81
View File
@@ -321,6 +321,7 @@ dependencies = [
"base64 0.23.1", "base64 0.23.1",
"ch1115", "ch1115",
"critical-section", "critical-section",
"display-interface",
"display-interface-spi", "display-interface-spi",
"embassy-executor", "embassy-executor",
"embassy-futures", "embassy-futures",
@@ -343,6 +344,7 @@ dependencies = [
"log", "log",
"num_enum", "num_enum",
"pn532", "pn532",
"ssd1306",
] ]
[[package]] [[package]]
@@ -607,6 +609,17 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ba2aab1ef3793e6f7804162debb5ac5edb93b3d650fbcc5aeb72fcd0e6c03a0" checksum = "7ba2aab1ef3793e6f7804162debb5ac5edb93b3d650fbcc5aeb72fcd0e6c03a0"
[[package]]
name = "display-interface-i2c"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d964fa85bbbb5a6ecd06e58699407ac5dc3e3ad72dac0ab7e6b0d00a1cd262d"
dependencies = [
"display-interface",
"embedded-hal 1.0.0",
"embedded-hal-async",
]
[[package]] [[package]]
name = "display-interface-spi" name = "display-interface-spi"
version = "0.5.0" version = "0.5.0"
@@ -1942,6 +1955,19 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
[[package]]
name = "maybe-async-cfg"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1e083394889336bc66a4eaf1011ffbfa74893e910f902a9f271fa624c61e1b2"
dependencies = [
"proc-macro-error",
"proc-macro2",
"pulldown-cmark",
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.8.3" version = "2.8.3"
@@ -2690,6 +2716,30 @@ dependencies = [
"toml_edit", "toml_edit",
] ]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn 1.0.109",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.107" version = "1.0.107"
@@ -2719,6 +2769,17 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "pulldown-cmark"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "679341d22c78c6c649893cbd6c3278dcbe9fc4faa62fea3a9296ae2b50c14625"
dependencies = [
"bitflags 2.13.1",
"memchr",
"unicase",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.47" version = "1.0.47"
@@ -3133,6 +3194,20 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0f368519fc6c85fc1afdb769fb5a51123f6158013e143656e25a3485a0d401c" checksum = "a0f368519fc6c85fc1afdb769fb5a51123f6158013e143656e25a3485a0d401c"
[[package]]
name = "ssd1306"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ea6aac2d078bbc71d9b8ac3f657335311f3b6625e9a1a96ccc29f5abfa77c56"
dependencies = [
"display-interface",
"display-interface-i2c",
"display-interface-spi",
"embedded-graphics-core",
"embedded-hal 1.0.0",
"maybe-async-cfg",
]
[[package]] [[package]]
name = "stable_deref_trait" name = "stable_deref_trait"
version = "1.2.1" version = "1.2.1"
@@ -3400,6 +3475,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69"
[[package]]
name = "unicase"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
[[package]] [[package]]
name = "unicode-id-start" name = "unicode-id-start"
version = "1.4.0" version = "1.4.0"
+2
View File
@@ -35,6 +35,7 @@ esp-hal-wifimanager = { git = "https://github.com/InvisibleUSA/esp-hal-wifimanag
] } ] }
esp-println = { version = "0.17.0", features = ["esp32s3", "log-04"] } esp-println = { version = "0.17.0", features = ["esp32s3", "log-04"] }
display-interface-spi = "0.5.0" display-interface-spi = "0.5.0"
display-interface = "*"
ili9341 = "0.6.0" ili9341 = "0.6.0"
embedded-hal = "1.0.0" embedded-hal = "1.0.0"
embedded-hal-bus = "0.3.0" embedded-hal-bus = "0.3.0"
@@ -47,6 +48,7 @@ embassy-sync = "0.8.0"
embedded-hal-async = "1.0.0" embedded-hal-async = "1.0.0"
i2c-character-display = "0.5.1" i2c-character-display = "0.5.1"
ch1115 = { version = "0.1.2", features = ["graphics"] } ch1115 = { version = "0.1.2", features = ["graphics"] }
ssd1306 = { version = "0.10.0", features = [] }
[patch.crates-io] [patch.crates-io]
+1 -1
View File
@@ -141,7 +141,7 @@ async fn main(spawner: Spawner) {
} }
fn display_shit(oled: &mut SecondaryDisplay<'static>, text: &str) { fn display_shit(oled: &mut SecondaryDisplay<'static>, text: &str) {
oled.clear().unwrap(); oled.clear(BinaryColor::Off).unwrap();
let text_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On); let text_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On);
Text::new(text, Point::new(10, 20), text_style) Text::new(text, Point::new(10, 20), text_style)
+155 -20
View File
@@ -1,6 +1,6 @@
use alloc::format; use alloc::{format, string::String};
use ch1115::{Ch1115, Size128x64};
use display_interface_spi::SPIInterface; use display_interface_spi::SPIInterface;
use embedded_graphics::pixelcolor::BinaryColor;
use embedded_hal_bus::spi::AtomicDevice; use embedded_hal_bus::spi::AtomicDevice;
use embedded_hal_bus::util::AtomicCell; use embedded_hal_bus::util::AtomicCell;
use esp_hal::delay::Delay; use esp_hal::delay::Delay;
@@ -8,10 +8,72 @@ use esp_hal::gpio::Output;
use crate::drivers::spi_bus::{DisplaySpiBus, SharedOutput, SharedReset}; use crate::drivers::spi_bus::{DisplaySpiBus, SharedOutput, SharedReset};
pub type GenericSecondaryDisplay<'a, BUS, RST, DC> = pub struct SecondaryDisplay<'a>(
Ch1115<SPIInterface<AtomicDevice<'a, BUS, Output<'a>, Delay>, DC>, RST, Size128x64>; #[cfg(feature = "wokwi")] ssd1306_aliases::SecondaryDisplay<'a>,
pub type SecondaryDisplay<'a> = #[cfg(not(feature = "wokwi"))] ch1115_aliases::SecondaryDisplay<'a>,
GenericSecondaryDisplay<'a, DisplaySpiBus, SharedReset<'a>, SharedOutput<'a>>; );
impl<'a> SecondaryDisplay<'a> {
pub fn flush(&mut self) -> Result<(), String> {
#[cfg(not(feature = "wokwi"))]
return self.0.flush().map_err(|e| format!("{e:?}"));
#[cfg(feature = "wokwi")]
return Ok(());
}
}
impl<'a> embedded_graphics::geometry::Dimensions for SecondaryDisplay<'a> {
fn bounding_box(&self) -> embedded_graphics::primitives::Rectangle {
self.0.bounding_box()
}
}
impl<'a> embedded_graphics::draw_target::DrawTarget for SecondaryDisplay<'a> {
type Color = BinaryColor;
type Error = String;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = embedded_graphics::prelude::Pixel<Self::Color>>,
{
self.0.draw_iter(pixels).map_err(|e| format!("{e:?}"))
}
fn fill_contiguous<I>(
&mut self,
area: &embedded_graphics::primitives::Rectangle,
colors: I,
) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Self::Color>,
{
self.0
.fill_contiguous(area, colors)
.map_err(|e| format!("{e:?}"))
}
fn fill_solid(
&mut self,
area: &embedded_graphics::primitives::Rectangle,
color: Self::Color,
) -> Result<(), Self::Error> {
self.0.fill_solid(area, color).map_err(|e| format!("{e:?}"))
}
fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
#[cfg(feature = "wokwi")]
return self.0.clear(color).map_err(|e| format!("{e:?}"));
#[cfg(not(feature = "wokwi"))]
{
let _ = color;
self.0.clear().map_err(|e| format!("{e:?}"))
}
}
}
pub const SECONDARY_DISPLAY_COUNT: usize = 3;
pub struct SecondaryDisplayPinConfiguration<'a> { pub struct SecondaryDisplayPinConfiguration<'a> {
pub spi_bus: &'a AtomicCell<DisplaySpiBus>, pub spi_bus: &'a AtomicCell<DisplaySpiBus>,
@@ -21,25 +83,46 @@ pub struct SecondaryDisplayPinConfiguration<'a> {
} }
impl<'a> SecondaryDisplayPinConfiguration<'a> { impl<'a> SecondaryDisplayPinConfiguration<'a> {
pub fn build(self) -> [SecondaryDisplay<'a>; 3] { pub fn build(self) -> [SecondaryDisplay<'a>; SECONDARY_DISPLAY_COUNT] {
let mut displays = self.cs_pins.map(|cs| { #[cfg(feature = "wokwi")]
let spi_dev = AtomicDevice::new(self.spi_bus, cs, Delay::new()).unwrap(); type Displays<'b> = [ssd1306_aliases::SecondaryDisplay<'b>; SECONDARY_DISPLAY_COUNT];
let interface = SPIInterface::new(spi_dev, self.dc.clone()); #[cfg(not(feature = "wokwi"))]
type Displays<'b> = [ch1115_aliases::SecondaryDisplay<'b>; SECONDARY_DISPLAY_COUNT];
Ch1115::new(interface, self.res.clone(), Size128x64) let reset_pin = self.res.clone();
}); let mut displays = Displays::new(self);
displays.reset(reset_pin);
init_single_reset(&mut displays, self.res); displays.map(SecondaryDisplay)
displays
} }
} }
fn init_single_reset<'a: 'b, 'b>( trait MultiDisplay<'a> {
displays: impl IntoIterator<Item = &'b mut SecondaryDisplay<'a>>, fn new(config: SecondaryDisplayPinConfiguration<'a>) -> Self;
shared_reset: SharedReset<'a>, fn reset(&mut self, shared_reset: SharedReset<'a>);
) { }
let mut displays = displays.into_iter().peekable();
mod ch1115_aliases {
use super::*;
use ch1115::{Ch1115, Size128x64};
type GenericSecondaryDisplay<'a, BUS, RST, DC> =
Ch1115<SPIInterface<AtomicDevice<'a, BUS, Output<'a>, Delay>, DC>, RST, Size128x64>;
pub type SecondaryDisplay<'a> =
GenericSecondaryDisplay<'a, DisplaySpiBus, SharedReset<'a>, SharedOutput<'a>>;
impl<'a> MultiDisplay<'a> for [SecondaryDisplay<'a>; SECONDARY_DISPLAY_COUNT] {
fn new(config: SecondaryDisplayPinConfiguration<'a>) -> Self {
config.cs_pins.map(|cs| {
let spi_dev = AtomicDevice::new(config.spi_bus, cs, Delay::new()).unwrap();
let interface = SPIInterface::new(spi_dev, config.dc.clone());
SecondaryDisplay::new(interface, config.res.clone(), Size128x64)
})
}
fn reset(&mut self, shared_reset: SharedReset<'a>) {
let mut displays = self.iter_mut().peekable();
// Reset all secondaries via shared reset // Reset all secondaries via shared reset
let Some(any_display) = displays.peek_mut() else { let Some(any_display) = displays.peek_mut() else {
@@ -56,4 +139,56 @@ fn init_single_reset<'a: 'b, 'b>(
.map_err(|e| format!("Failed to init secondary display {}: {e:?}", i + 1)) .map_err(|e| format!("Failed to init secondary display {}: {e:?}", i + 1))
.unwrap(); .unwrap();
} }
}
}
}
mod ssd1306_aliases {
use super::*;
use ssd1306::{
Ssd1306, mode::BufferedGraphicsMode, rotation::DisplayRotation, size::DisplaySize128x64,
};
type GenericSecondaryDisplay<'a, BUS, DC> = Ssd1306<
SPIInterface<AtomicDevice<'a, BUS, Output<'a>, Delay>, DC>,
DisplaySize128x64,
BufferedGraphicsMode<DisplaySize128x64>,
>;
pub type SecondaryDisplay<'a> = GenericSecondaryDisplay<'a, DisplaySpiBus, SharedOutput<'a>>;
impl<'a> MultiDisplay<'a> for [SecondaryDisplay<'a>; SECONDARY_DISPLAY_COUNT] {
fn new(config: SecondaryDisplayPinConfiguration<'a>) -> Self {
config.cs_pins.map(|cs| {
let spi_dev = AtomicDevice::new(config.spi_bus, cs, Delay::new()).unwrap();
let interface = SPIInterface::new(spi_dev, config.dc.clone());
Ssd1306::new(interface, DisplaySize128x64, DisplayRotation::Rotate0)
.into_buffered_graphics_mode()
})
}
fn reset(&mut self, mut shared_reset: SharedReset<'a>) {
use ssd1306::mode::DisplayConfig as _;
let mut displays = self.iter_mut().peekable();
// Reset all secondaries via shared reset
let Some(any_display) = displays.peek_mut() else {
// no display to initialize
return;
};
any_display
.reset(&mut shared_reset, &mut Delay::new())
.unwrap();
shared_reset.disable(true);
for (i, display) in displays.enumerate() {
display
.init()
.map_err(|e| format!("Failed to init secondary display {}: {e:?}", i + 1))
.unwrap();
}
}
}
} }
+4 -5
View File
@@ -1,6 +1,5 @@
use embedded_graphics::{ use embedded_graphics::{
draw_target::DrawTarget as _, draw_target::DrawTarget as _, pixelcolor::{BinaryColor, Rgb565, RgbColor as _},
pixelcolor::{Rgb565, RgbColor as _},
}; };
use crate::drivers::{ use crate::drivers::{
@@ -19,9 +18,9 @@ pub struct Outputs {
impl Outputs { impl Outputs {
pub fn clear_screens(&mut self) { pub fn clear_screens(&mut self) {
self.primary_display.clear(Rgb565::BLACK).unwrap(); self.primary_display.clear(Rgb565::BLACK).unwrap();
self.secondary_display_1.clear().unwrap(); self.secondary_display_1.clear(BinaryColor::Off).unwrap();
// self.secondary_display_2.clear().unwrap(); self.secondary_display_2.clear(BinaryColor::Off).unwrap();
// self.secondary_display_3.clear().unwrap(); self.secondary_display_3.clear(BinaryColor::Off).unwrap();
self.tertiary_display.clear().unwrap(); self.tertiary_display.clear().unwrap();
} }
} }