prepare alternative display driver support

This commit is contained in:
2026-08-26 21:51:01 +02:00
parent e33262577d
commit bd124bf9c2
5 changed files with 254 additions and 37 deletions
Generated
+81
View File
@@ -321,6 +321,7 @@ dependencies = [
"base64 0.23.1",
"ch1115",
"critical-section",
"display-interface",
"display-interface-spi",
"embassy-executor",
"embassy-futures",
@@ -343,6 +344,7 @@ dependencies = [
"log",
"num_enum",
"pn532",
"ssd1306",
]
[[package]]
@@ -607,6 +609,17 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "display-interface-spi"
version = "0.5.0"
@@ -1942,6 +1955,19 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "memchr"
version = "2.8.3"
@@ -2690,6 +2716,30 @@ dependencies = [
"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]]
name = "proc-macro2"
version = "1.0.107"
@@ -2719,6 +2769,17 @@ dependencies = [
"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]]
name = "quote"
version = "1.0.47"
@@ -3133,6 +3194,20 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "stable_deref_trait"
version = "1.2.1"
@@ -3400,6 +3475,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69"
[[package]]
name = "unicase"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
[[package]]
name = "unicode-id-start"
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"] }
display-interface-spi = "0.5.0"
display-interface = "*"
ili9341 = "0.6.0"
embedded-hal = "1.0.0"
embedded-hal-bus = "0.3.0"
@@ -47,6 +48,7 @@ embassy-sync = "0.8.0"
embedded-hal-async = "1.0.0"
i2c-character-display = "0.5.1"
ch1115 = { version = "0.1.2", features = ["graphics"] }
ssd1306 = { version = "0.10.0", features = [] }
[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) {
oled.clear().unwrap();
oled.clear(BinaryColor::Off).unwrap();
let text_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On);
Text::new(text, Point::new(10, 20), text_style)
+166 -31
View File
@@ -1,6 +1,6 @@
use alloc::format;
use ch1115::{Ch1115, Size128x64};
use alloc::{format, string::String};
use display_interface_spi::SPIInterface;
use embedded_graphics::pixelcolor::BinaryColor;
use embedded_hal_bus::spi::AtomicDevice;
use embedded_hal_bus::util::AtomicCell;
use esp_hal::delay::Delay;
@@ -8,10 +8,72 @@ use esp_hal::gpio::Output;
use crate::drivers::spi_bus::{DisplaySpiBus, SharedOutput, SharedReset};
pub 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>>;
pub struct SecondaryDisplay<'a>(
#[cfg(feature = "wokwi")] ssd1306_aliases::SecondaryDisplay<'a>,
#[cfg(not(feature = "wokwi"))] ch1115_aliases::SecondaryDisplay<'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 spi_bus: &'a AtomicCell<DisplaySpiBus>,
@@ -21,39 +83,112 @@ pub struct SecondaryDisplayPinConfiguration<'a> {
}
impl<'a> SecondaryDisplayPinConfiguration<'a> {
pub fn build(self) -> [SecondaryDisplay<'a>; 3] {
let mut displays = self.cs_pins.map(|cs| {
let spi_dev = AtomicDevice::new(self.spi_bus, cs, Delay::new()).unwrap();
let interface = SPIInterface::new(spi_dev, self.dc.clone());
pub fn build(self) -> [SecondaryDisplay<'a>; SECONDARY_DISPLAY_COUNT] {
#[cfg(feature = "wokwi")]
type Displays<'b> = [ssd1306_aliases::SecondaryDisplay<'b>; SECONDARY_DISPLAY_COUNT];
#[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
displays.map(SecondaryDisplay)
}
}
fn init_single_reset<'a: 'b, 'b>(
displays: impl IntoIterator<Item = &'b mut SecondaryDisplay<'a>>,
shared_reset: SharedReset<'a>,
) {
let mut displays = displays.into_iter().peekable();
trait MultiDisplay<'a> {
fn new(config: SecondaryDisplayPinConfiguration<'a>) -> Self;
fn reset(&mut self, shared_reset: SharedReset<'a>);
}
// Reset all secondaries via shared reset
let Some(any_display) = displays.peek_mut() else {
// no display to initialize
return;
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
let Some(any_display) = displays.peek_mut() else {
// no display to initialize
return;
};
any_display.hard_reset(&mut Delay::new()).unwrap();
shared_reset.disable(true);
for (i, display) in displays.enumerate() {
display
.init(&mut Delay::new())
.map_err(|e| format!("Failed to init secondary display {}: {e:?}", i + 1))
.unwrap();
}
}
}
}
mod ssd1306_aliases {
use super::*;
use ssd1306::{
Ssd1306, mode::BufferedGraphicsMode, rotation::DisplayRotation, size::DisplaySize128x64,
};
any_display.hard_reset(&mut Delay::new()).unwrap();
shared_reset.disable(true);
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>>;
for (i, display) in displays.enumerate() {
display
.init(&mut Delay::new())
.map_err(|e| format!("Failed to init secondary display {}: {e:?}", i + 1))
.unwrap();
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::{
draw_target::DrawTarget as _,
pixelcolor::{Rgb565, RgbColor as _},
draw_target::DrawTarget as _, pixelcolor::{BinaryColor, Rgb565, RgbColor as _},
};
use crate::drivers::{
@@ -19,9 +18,9 @@ pub struct Outputs {
impl Outputs {
pub fn clear_screens(&mut self) {
self.primary_display.clear(Rgb565::BLACK).unwrap();
self.secondary_display_1.clear().unwrap();
// self.secondary_display_2.clear().unwrap();
// self.secondary_display_3.clear().unwrap();
self.secondary_display_1.clear(BinaryColor::Off).unwrap();
self.secondary_display_2.clear(BinaryColor::Off).unwrap();
self.secondary_display_3.clear(BinaryColor::Off).unwrap();
self.tertiary_display.clear().unwrap();
}
}