Reworked display code

This commit is contained in:
ZacJW 2025-07-12 15:34:21 +01:00
parent 639ee2af7f
commit bc3c25a5d3
2 changed files with 214 additions and 117 deletions

View file

@ -5,10 +5,7 @@ use core::panic::PanicInfo;
use cardputer_bsc_nostd::display::{DISPLAY_SIZE_HEIGHT, DISPLAY_SIZE_WIDTH}; use cardputer_bsc_nostd::display::{DISPLAY_SIZE_HEIGHT, DISPLAY_SIZE_WIDTH};
use embedded_graphics::pixelcolor::Rgb565; use embedded_graphics::pixelcolor::Rgb565;
use esp_hal::{ use esp_hal::main;
ledc::{LowSpeed, channel::ChannelIFace, timer::TimerIFace},
main,
};
use esp_println::println; use esp_println::println;
#[panic_handler] #[panic_handler]
@ -28,39 +25,24 @@ fn entrypoint() -> ! {
fn _main() { fn _main() {
let peripherals = esp_hal::init(esp_hal::Config::default()); let peripherals = esp_hal::init(esp_hal::Config::default());
let mut display = cardputer_bsc_nostd::display::build(
cardputer_bsc_nostd::setup_backlight! {
let (mut ledc, mut timer) = setup_backlight(peripherals.LEDC);
}
let mut display = cardputer_bsc_nostd::display::Display::new(
peripherals.SPI2, peripherals.SPI2,
peripherals.GPIO36, peripherals.GPIO36,
peripherals.GPIO35, peripherals.GPIO35,
peripherals.GPIO37, peripherals.GPIO37,
peripherals.GPIO34, peripherals.GPIO34,
peripherals.GPIO33, peripherals.GPIO33,
&ledc,
&timer,
peripherals.GPIO38,
) )
.unwrap(); .unwrap();
let mut ledc = esp_hal::ledc::Ledc::new(peripherals.LEDC);
ledc.set_global_slow_clock(esp_hal::ledc::LSGlobalClkSource::APBClk);
let mut timer = ledc.timer::<LowSpeed>(esp_hal::ledc::timer::Number::Timer3);
timer.configure(esp_hal::ledc::timer::config::Config {
duty: esp_hal::ledc::timer::config::Duty::Duty8Bit,
clock_source: esp_hal::ledc::timer::LSClockSource::APBClk,
frequency: esp_hal::time::Rate::from_hz(256),
}).unwrap();
let mut backlight: esp_hal::ledc::channel::Channel<'_, _> =
ledc.channel(esp_hal::ledc::channel::Number::Channel7, peripherals.GPIO38);
backlight
.configure(esp_hal::ledc::channel::config::Config {
timer: &timer,
duty_pct: 100,
pin_config: esp_hal::ledc::channel::config::PinConfig::PushPull,
})
.unwrap();
display display
.set_pixels( .set_pixels(
0, 0,
@ -86,4 +68,6 @@ fn _main() {
), ),
) )
.unwrap(); .unwrap();
display.set_backlight_brightness(100).unwrap();
} }

View file

@ -1,7 +1,10 @@
//! Create and initialize ST7789 display driver //! Create and initialize ST7789 display driver
// use display_interface_spi::SPIInterface; // use display_interface_spi::SPIInterface;
use core::ops::{Deref, DerefMut};
use embedded_hal_bus::spi::ExclusiveDevice; use embedded_hal_bus::spi::ExclusiveDevice;
use esp_hal::ledc::{LowSpeed, channel::ChannelIFace};
use mipidsi::{ use mipidsi::{
Builder, Display as MipiDisplay, Builder, Display as MipiDisplay,
interface::SpiInterface, interface::SpiInterface,
@ -10,7 +13,204 @@ use mipidsi::{
}; };
use thiserror::Error; use thiserror::Error;
pub struct Display {} /// The type for controlling the cardputer's display
///
/// Example usage:
/// ```rust
/// cardputer_bsc_nostd::setup_backlight! {
/// let (mut ledc, mut timer) = setup_backlight(peripherals.LEDC);
/// }
///
/// let mut display = cardputer_bsc_nostd::display::Display::build(
/// peripherals.SPI2,
/// peripherals.GPIO36,
/// peripherals.GPIO35,
/// peripherals.GPIO37,
/// peripherals.GPIO34,
/// peripherals.GPIO33,
/// &ledc,
/// &timer,
/// peripherals.GPIO38,
/// )
/// .unwrap();
///
/// display.set_backlight_brightness(100).unwrap();
/// ```
pub struct Display<'a> {
mipi_display: Drawable<'a>,
backlight: esp_hal::ledc::channel::Channel<'a, LowSpeed>,
}
impl<'a> Deref for Display<'a> {
type Target = Drawable<'a>;
fn deref(&self) -> &Self::Target {
&self.mipi_display
}
}
impl<'a> DerefMut for Display<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.mipi_display
}
}
/// A convenience macro which simplifies setting up the LED controller and timer for the display's backlight.
///
/// Example usage:
/// ```rust
/// cardputer_bsc_nostd::setup_backlight! {
/// let (mut ledc, mut timer) = setup_backlight(peripherals.LEDC);
/// }
///
/// let mut display = cardputer_bsc_nostd::display::Display::build(
/// peripherals.SPI2,
/// peripherals.GPIO36,
/// peripherals.GPIO35,
/// peripherals.GPIO37,
/// peripherals.GPIO34,
/// peripherals.GPIO33,
/// &ledc,
/// &timer,
/// peripherals.GPIO38,
/// )
/// .unwrap();
/// ```
///
/// You can rename the `ledc` and `timer` bindings, and choose a different expression in place of `peripherals.LEDC`,
/// but otherwise you must leave the rest unchanged.
///
/// Unfortunately the fictional `setup_backlight` function that the accepted syntax looks like it is invoking can't
/// be written as `timer` borrows from `ledc`.
#[macro_export]
macro_rules! setup_backlight {
(let (mut $ledc_ident:ident, mut $timer_ident:ident) = setup_backlight($ledc:expr)$(;)?) => {
let mut $ledc_ident = esp_hal::ledc::Ledc::new($ledc);
$ledc_ident.set_global_slow_clock(esp_hal::ledc::LSGlobalClkSource::APBClk);
let mut $timer_ident =
$ledc_ident.timer::<esp_hal::ledc::LowSpeed>(esp_hal::ledc::timer::Number::Timer3);
esp_hal::ledc::timer::TimerIFace::configure(
&mut $timer_ident,
esp_hal::ledc::timer::config::Config {
duty: esp_hal::ledc::timer::config::Duty::Duty8Bit,
clock_source: esp_hal::ledc::timer::LSClockSource::APBClk,
frequency: esp_hal::time::Rate::from_hz(256),
},
)
.unwrap();
};
}
impl<'a> Display<'a> {
/// Initialises the cardputer's display.
///
/// The backlight is left powered off as this function doesn't clear the LCD controller's display
/// buffer and it usually boots up with random noise. You can call [Display::set_backlight_brightness]
/// to switch the backlight on.
pub fn new(
spi: esp_hal::peripherals::SPI2<'a>,
sck: esp_hal::peripherals::GPIO36<'a>,
dc: esp_hal::peripherals::GPIO35<'a>,
cs: esp_hal::peripherals::GPIO37<'a>,
rs: esp_hal::peripherals::GPIO34<'a>,
rst: esp_hal::peripherals::GPIO33<'a>,
ledc: &'a esp_hal::ledc::Ledc<'a>,
timer: &'a esp_hal::ledc::timer::Timer<'a, LowSpeed>,
backlight: esp_hal::peripherals::GPIO38<'a>,
) -> Result<Self, DisplayInitError> {
static mut DISPLAY_BUFFER: [u8; DISPLAY_BUFFER_SIZE] = [0; DISPLAY_BUFFER_SIZE];
let spi = esp_hal::spi::master::Spi::new(
spi,
esp_hal::spi::master::Config::default()
.with_frequency(esp_hal::time::Rate::from_mhz(80)),
)?
.with_sck(sck)
.with_mosi(dc);
let Ok(spi) = ExclusiveDevice::new(
spi,
esp_hal::gpio::Output::new(
cs,
esp_hal::gpio::Level::Low,
esp_hal::gpio::OutputConfig::default(),
),
esp_hal::delay::Delay::new(),
);
let rs = esp_hal::gpio::Output::new(
rs,
esp_hal::gpio::Level::Low,
esp_hal::gpio::OutputConfig::default(),
);
let rst = esp_hal::gpio::Output::new(
rst,
esp_hal::gpio::Level::Low,
esp_hal::gpio::OutputConfig::default(),
);
#[allow(static_mut_refs)]
let mut drawable = Builder::new(
ST7789,
SpiInterface::new(spi, rs, unsafe { DISPLAY_BUFFER.as_mut_slice() }),
) //st7789(SpiInterface::new(spi, rs))
.invert_colors(ColorInversion::Inverted)
.display_size(DISPLAY_SIZE_HEIGHT, DISPLAY_SIZE_WIDTH) // deliberately reversed order
.display_offset(40, 53)
.reset_pin(rst)
.init(&mut esp_hal::delay::Delay::new())
.map_err(|_| DisplayInitError::MipidsiInitError)?;
// Can't capture the error since it's a public type
// in a private module and not re-exported. The master
// branch of the mipidsi repo does have a fix but it's
// not had a crates.io release yet.
drawable
.set_orientation(Orientation::new().rotate(mipidsi::options::Rotation::Deg90))
.map_err(|e| match e {
mipidsi::interface::SpiError::Spi(e) => match e {
embedded_hal_bus::spi::DeviceError::Spi(e) => DisplayInitError::SpiError(e),
embedded_hal_bus::spi::DeviceError::Cs(e) => match e {},
},
mipidsi::interface::SpiError::Dc(e) => match e {},
})?;
drawable
.set_vertical_scroll_offset(0)
.map_err(|e| match e {
mipidsi::interface::SpiError::Spi(e) => match e {
embedded_hal_bus::spi::DeviceError::Spi(e) => DisplayInitError::SpiError(e),
embedded_hal_bus::spi::DeviceError::Cs(e) => match e {},
},
mipidsi::interface::SpiError::Dc(e) => match e {},
})?;
let mut backlight: esp_hal::ledc::channel::Channel<'_, _> =
ledc.channel(esp_hal::ledc::channel::Number::Channel7, backlight);
backlight
.configure(esp_hal::ledc::channel::config::Config {
timer,
duty_pct: 0,
pin_config: esp_hal::ledc::channel::config::PinConfig::PushPull,
})
.unwrap();
Ok(Self {
mipi_display: drawable,
backlight,
})
}
/// Set the brightness of the display's backlight. `brightness_percentage` should be in the closed range \[0,100];
/// this function will return [esp_hal::ledc::channel::Error::Duty] if not.
pub fn set_backlight_brightness(
&self,
brightness_percentage: u8,
) -> Result<(), esp_hal::ledc::channel::Error> {
self.backlight.set_duty(brightness_percentage)
}
}
type Drawable<'a> = MipiDisplay< type Drawable<'a> = MipiDisplay<
SpiInterface< SpiInterface<
@ -35,7 +235,7 @@ pub const DISPLAY_BUFFER_SIZE: usize =
DISPLAY_SIZE_WIDTH as usize * DISPLAY_SIZE_HEIGHT as usize * 2; DISPLAY_SIZE_WIDTH as usize * DISPLAY_SIZE_HEIGHT as usize * 2;
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum BuildError { pub enum DisplayInitError {
#[error("SPI config error: {0}")] #[error("SPI config error: {0}")]
SpiConfigError(#[from] esp_hal::spi::master::ConfigError), SpiConfigError(#[from] esp_hal::spi::master::ConfigError),
#[error("Some error occurred when initialising the MIPI DSI display")] #[error("Some error occurred when initialising the MIPI DSI display")]
@ -43,90 +243,3 @@ pub enum BuildError {
#[error("SPI error: {0:?}")] #[error("SPI error: {0:?}")]
SpiError(esp_hal::spi::Error), SpiError(esp_hal::spi::Error),
} }
pub fn build<'a>(
spi: esp_hal::peripherals::SPI2<'a>,
sck: esp_hal::peripherals::GPIO36<'a>,
dc: esp_hal::peripherals::GPIO35<'a>,
cs: esp_hal::peripherals::GPIO37<'a>,
rs: esp_hal::peripherals::GPIO34<'a>,
rst: esp_hal::peripherals::GPIO33<'a>,
) -> Result<Drawable<'a>, BuildError> {
static mut DISPLAY_BUFFER: [u8; 1000] = [0; 1000];
// let spi_config = SpiConfig::new().baudrate(80.MHz().into());
// let device_config = DriverConfig::new();
// let spi = SpiDeviceDriver::new_single(
// spi,
// sck,
// dc,
// Option::<AnyIOPin>::None,
// Some(cs),
// &device_config,
// &spi_config,
// )?;
let spi = esp_hal::spi::master::Spi::new(
spi,
esp_hal::spi::master::Config::default().with_frequency(esp_hal::time::Rate::from_mhz(80)),
)?
.with_sck(sck)
.with_mosi(dc);
let Ok(spi) = ExclusiveDevice::new(
spi,
esp_hal::gpio::Output::new(
cs,
esp_hal::gpio::Level::Low,
esp_hal::gpio::OutputConfig::default(),
),
esp_hal::delay::Delay::new(),
);
let rs = esp_hal::gpio::Output::new(
rs,
esp_hal::gpio::Level::Low,
esp_hal::gpio::OutputConfig::default(),
);
let rst = esp_hal::gpio::Output::new(
rst,
esp_hal::gpio::Level::Low,
esp_hal::gpio::OutputConfig::default(),
);
#[allow(static_mut_refs)]
let mut drawable = Builder::new(
ST7789,
SpiInterface::new(spi, rs, unsafe { DISPLAY_BUFFER.as_mut_slice() }),
) //st7789(SpiInterface::new(spi, rs))
.invert_colors(ColorInversion::Inverted)
.display_size(DISPLAY_SIZE_HEIGHT, DISPLAY_SIZE_WIDTH) // deliberately reversed order
.display_offset(40, 53)
.reset_pin(rst)
.init(&mut esp_hal::delay::Delay::new())
.map_err(|_| BuildError::MipidsiInitError)?;
// Can't capture the error since it's a public type
// in a private module and not re-exported. The master
// branch of the mipidsi repo does have a fix but it's
// not had a crates.io release yet.
drawable
.set_orientation(Orientation::new().rotate(mipidsi::options::Rotation::Deg90))
.map_err(|e| match e {
mipidsi::interface::SpiError::Spi(e) => match e {
embedded_hal_bus::spi::DeviceError::Spi(e) => BuildError::SpiError(e),
embedded_hal_bus::spi::DeviceError::Cs(e) => match e {},
},
mipidsi::interface::SpiError::Dc(e) => match e {},
})?;
drawable
.set_vertical_scroll_offset(0)
.map_err(|e| match e {
mipidsi::interface::SpiError::Spi(e) => match e {
embedded_hal_bus::spi::DeviceError::Spi(e) => BuildError::SpiError(e),
embedded_hal_bus::spi::DeviceError::Cs(e) => match e {},
},
mipidsi::interface::SpiError::Dc(e) => match e {},
})?;
Ok(drawable)
}