Reworked display code
This commit is contained in:
parent
639ee2af7f
commit
bc3c25a5d3
2 changed files with 214 additions and 117 deletions
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
291
src/display.rs
291
src/display.rs
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue