Closed bentwire closed 6 months ago
@bentwire I ran into the same issue. I was able to work around it by creating my own "wrapper" around the GraphicsMode type:
type Display = GraphicsMode<SpiInterface<Spi<stm32f4xx_hal::pac::SPI1>, stm32f4xx_hal::gpio::Pin<'A', 8, stm32f4xx_hal::gpio::Output>>>;
struct MyDisplay {
display: Display,
}
impl MyDisplay {
/// Draw a `Pixel` that has a color defined as `Rgb565`.
fn draw_pixel(&mut self, pixel: Pixel<Rgb565>) -> Result<(), <MyDisplay as DrawTarget>::Error> {
let Pixel(coord, color) = pixel;
// Place an (x, y) pixel at the right index in the framebuffer. If the pixel coordinates
// are out of bounds (negative or greater than (127, 127)), this operation will be a
// noop.
if let Ok((x @ 0..=127, y @ 0..=127)) = coord.try_into() {
self.display.set_pixel(x, y, color.into_storage())
}
Ok(())
}
}
impl Dimensions for MyDisplay {
fn bounding_box(&self) -> Rectangle {
let (width, height) = self.display.get_dimensions();
Rectangle::new(Point::new(0, 0), Size::new(width as u32, height as u32))
}
}
impl DrawTarget for MyDisplay {
type Color = Rgb565;
type Error = core::convert::Infallible;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error> where I: IntoIterator<Item=Pixel<Self::Color>> {
for pixel in pixels {
self.draw_pixel(pixel)?
}
Ok(())
}
}
#[entry]
fn main() -> ! {
// Init peripherals and stuff ...
let spi = Spi::new(
dp.SPI1,
(sck, NoPin::new(), mosi),
SSD1351_SPI_MODE,
20.MHz(),
&clocks,
);
static mut BUFFER: [u8; 128 * 128 * 2] = [0; 128 * 128 * 2];
let mut display: Display = Builder::new().with_rotation(DisplayRotation::Rotate270).connect_spi(spi, dc, &mut BUFFER).into();
display.reset(&mut rst, &mut delay).unwrap();
display.init().unwrap();
let mut myDisplay = MyDisplay { display };
let text = Text::with_baseline("Hello world!", Point::zero(), text_style, Baseline::Top)
.draw(&mut myDisplay)
.unwrap();
loop {}
You would need to adjust types a bit since I'm using an STM32 rather than RP2040. In the end, I think this is probably unnecessary. I'm thinking that the error is a bit of a red herring and the problem is something other than the trait bound issue, but I'm still a rust noob so I wasn't able to determine the root cause of the error.
This took me forever to figure out, but here's how I got it working on the STM32F3 DISCOVERY. Thank you for sharing your workaround @tmbull. 🙏 If I keep using this library maybe I'll try to sort it out. It seems like GraphicsMode is designed to mostly work with embedded-graphics, but there's some issue with the types/traits not lining up quite right?
I'm not sure these types are exactly correct, particularly the integers in the type such as stm32f3xx_hal::gpio::U<6>,
, but... it works!
main.rs
#![deny(unsafe_code)]
#![no_std]
#![no_main]
#[cfg(not(debug_assertions))]
use panic_abort as _;
#[cfg(debug_assertions)]
use panic_semihosting as _;
use core::convert::TryInto;
use embedded_graphics::{
mono_font::{ascii::FONT_6X10, MonoTextStyle},
pixelcolor::Rgb565,
prelude::*,
primitives::Rectangle,
text::{Baseline, Text},
};
use embedded_time::rate::*;
use cortex_m::asm;
use cortex_m_rt::entry;
use cortex_m_semihosting::hprintln;
use ssd1351::{builder::Builder, properties::DisplayRotation};
use stm32f3xx_hal::pac;
use stm32f3xx_hal::prelude::*;
use stm32f3xx_hal::spi::Spi;
#[entry]
fn main() -> ! {
hprintln!("Let's go!");
let cp = cortex_m::Peripherals::take().unwrap();
let dp = pac::Peripherals::take().unwrap();
let mut flash = dp.FLASH.constrain();
let mut rcc = dp.RCC.constrain();
let mut gpioe = dp.GPIOE.split(&mut rcc.ahb);
let mut gpioa = dp.GPIOA.split(&mut rcc.ahb);
let clocks = rcc
.cfgr
.use_hse(8.MHz())
.sysclk(48.MHz())
.pclk1(24.MHz())
.freeze(&mut flash.acr);
// Configure pins for LEDs
let mut onboard_led = gpioe
.pe13
.into_push_pull_output(&mut gpioe.moder, &mut gpioe.otyper);
let mut external_led = gpioe
.pe9
.into_push_pull_output(&mut gpioe.moder, &mut gpioe.otyper);
// Configure SPI + OLED Screen (ssd1351)
let sck =
gpioa
.pa5
.into_af_push_pull::<5>(&mut gpioa.moder, &mut gpioa.otyper, &mut gpioa.afrl);
let miso =
gpioa
.pa6
.into_af_push_pull::<5>(&mut gpioa.moder, &mut gpioa.otyper, &mut gpioa.afrl);
let mosi =
gpioa
.pa7
.into_af_push_pull::<5>(&mut gpioa.moder, &mut gpioa.otyper, &mut gpioa.afrl);
let dc = gpioa
.pa3
.into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper);
let mut rst = gpioa
.pa2
.into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper);
let _cs = gpioa
.pa4
.into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper);
let spi = Spi::new(dp.SPI1, (sck, miso, mosi), 1.MHz(), clocks, &mut rcc.apb2);
let mut display: Display = Builder::new()
.with_rotation(DisplayRotation::Rotate270)
.connect_spi(spi, dc)
.into();
let mut delay = stm32f3xx_hal::delay::Delay::new(cp.SYST, clocks);
display.reset(&mut rst, &mut delay).unwrap();
display.init().unwrap();
let mut my_display = MyDisplay { display };
let text_style = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE);
let _text = Text::with_baseline(
"Hello Derek! I hope \nyou eat a lot of\nturkey this week!",
Point::zero(),
text_style,
Baseline::Top,
)
.draw(&mut my_display)
.unwrap();
loop {
onboard_led.toggle().unwrap();
external_led.toggle().unwrap();
asm::delay(2_000_000);
}
}
//
// HACKS
//
// To get ssd1351 driver to play nicely with embedded-graphics, see
// https://github.com/MabezDev/ssd1351/issues/18
//
type Display = ssd1351::mode::GraphicsMode<
ssd1351::interface::spi::SpiInterface<
Spi<
pac::SPI1,
(
stm32f3xx_hal::gpio::Pin<
stm32f3xx_hal::gpio::Gpioa,
stm32f3xx_hal::gpio::U<5>,
stm32f3xx_hal::gpio::Alternate<stm32f3xx_hal::gpio::PushPull, 5>,
>,
stm32f3xx_hal::gpio::Pin<
stm32f3xx_hal::gpio::Gpioa,
stm32f3xx_hal::gpio::U<6>,
stm32f3xx_hal::gpio::Alternate<stm32f3xx_hal::gpio::PushPull, 5>,
>,
stm32f3xx_hal::gpio::Pin<
stm32f3xx_hal::gpio::Gpioa,
stm32f3xx_hal::gpio::U<7>,
stm32f3xx_hal::gpio::Alternate<stm32f3xx_hal::gpio::PushPull, 5>,
>,
),
u8,
>,
stm32f3xx_hal::gpio::Pin<
stm32f3xx_hal::gpio::Gpioa,
stm32f3xx_hal::gpio::U<3>,
stm32f3xx_hal::gpio::Output<stm32f3xx_hal::gpio::PushPull>,
>,
>,
>;
struct MyDisplay {
display: Display,
}
impl MyDisplay {
/// Draw a `Pixel` that has a color defined as `Rgb565`.
fn draw_pixel(&mut self, pixel: Pixel<Rgb565>) -> Result<(), <MyDisplay as DrawTarget>::Error> {
let Pixel(coord, color) = pixel;
// Place an (x, y) pixel at the right index in the framebuffer. If the pixel coordinates
// are out of bounds (negative or greater than (127, 127)), this operation will be a
// noop.
if let Ok((x @ 0..=127, y @ 0..=127)) = coord.try_into() {
self.display.set_pixel(x, y, color.into_storage())
}
Ok(())
}
}
impl Dimensions for MyDisplay {
fn bounding_box(&self) -> Rectangle {
let (width, height) = self.display.get_dimensions();
Rectangle::new(Point::new(0, 0), Size::new(width as u32, height as u32))
}
}
impl DrawTarget for MyDisplay {
type Color = Rgb565;
type Error = core::convert::Infallible;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
for pixel in pixels {
self.draw_pixel(pixel)?
}
Ok(())
}
}
//
// (End HACKS)
//
Cargo.toml
[package]
authors = ["Ben Standefer <benstandefer@gmail.com>"]
edition = "2018"
readme = "README.md"
name = "np1"
version = "0.1.0"
[dependencies]
cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] }
cortex-m-rt = { version = "0.7.3", features = ["device"] }
cortex-m-semihosting = "0.5.0"
embedded-graphics = "0.8.1"
embedded-time = "0.12.1"
panic-halt = "0.2.0"
panic-semihosting = { version = "0.6.0", features = ["exit"] }
ssd1351 = "0.4.2"
stm32f3xx-hal = { version = "0.9.2", features = ["ld", "rt", "stm32f303xc"] }
# Uncomment for the panic example.
# panic-itm = "0.4.1"
# Uncomment for the allocator example.
# alloc-cortex-m = "0.4.0"
# Uncomment for the device example.
# Update `memory.x`, set target to `thumbv7em-none-eabihf` in `.cargo/config`,
# and then use `cargo build --examples device` to build it.
# [dependencies.stm32f3]
# features = ["stm32f303", "rt"]
# version = "0.7.1"
# this lets you use `cargo fix`!
[[bin]]
name = "np1"
test = false
bench = false
[profile.release]
codegen-units = 1 # better optimizations
debug = true # symbols are nice and they don't increase the size on Flash
lto = true # better optimizations
Hi. I have added embedded-hal v1.0.0 support and that seems to have fixed the issue. Can you try the git version of this crate if your HAL already supports e-h v1.0.0 to see if that resolved the issue for you?
You can add it like this:
ssd1351 = { git = "https://github.com/MabezDev/ssd1351.git" }
The following could save you some time and headaches ;):
Don't forget to activate the graphics feature. No need for custom wrappers as seen above.
Cargo.toml
...
[dependencies]
ssd1351 = { version = "0.5.0", features = ["graphics"] }
...
The following is specific for embassy-rs with an esp32: it did cost me plenty of time to figure out that after initializing SPI you get an SPIBus, not an SPIDevice. Something similar to the following is necessary :
let spi_bus = Spi::new(peripherals.SPI2,5000u32.kHz(),SpiMode::Mode0, &clocks)
.with_sck(sclk)
.with_mosi(mosi);
let spi_dev = ExclusiveDevice::new(spi_bus, cs, embedded_hal_bus::spi::NoDelay);
let di = SPIInterface::new(spi_dev, dc);
let display = ssd1351::display::Display::new(di, DisplaySize::Display128x128, DisplayRotation::Rotate0);
Thank you for confirming that it works with embassy and e-h-v1.0. I will probably add some code examples to the repository later on.
So I initialize the display like this:
` Won't compile: