From 25389f2ee8ecc6d96b79f309791d5a945f1132ac Mon Sep 17 00:00:00 2001 From: Andreas Bergmeier Date: Thu, 25 Apr 2024 13:11:58 +0200 Subject: [PATCH] Add 7in3f support Based on epd5in65f --- src/epd7in3f/command.rs | 150 +++++++++++++++++++++++ src/epd7in3f/mod.rs | 264 ++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 3 files changed, 415 insertions(+) create mode 100644 src/epd7in3f/command.rs create mode 100644 src/epd7in3f/mod.rs diff --git a/src/epd7in3f/command.rs b/src/epd7in3f/command.rs new file mode 100644 index 00000000..f8f56e8d --- /dev/null +++ b/src/epd7in3f/command.rs @@ -0,0 +1,150 @@ +//! SPI Commands for the Waveshare 7.3" E-Ink Display + +use crate::traits; + +/// EPD7in3f commands +/// +/// Should rarely (never?) be needed directly. +/// +/// For more infos about the addresses and what they are doing look into the PDFs. +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub(crate) enum Command { + /// Set Resolution, LUT selection, BWR pixels, gate scan direction, source shift + /// direction, booster switch, soft reset. + PanelSetting = 0x00, + + /// Selecting internal and external power + PowerSetting = 0x01, + + /// After the Power Off command, the driver will power off following the Power Off + /// Sequence; BUSY signal will become "0". This command will turn off charge pump, + /// T-con, source driver, gate driver, VCOM, and temperature sensor, but register + /// data will be kept until VDD becomes OFF. Source Driver output and Vcom will remain + /// as previous condition, which may have 2 conditions: 0V or floating. + PowerOff = 0x02, + + /// Setting Power OFF sequence + PowerOffSequenceSetting = 0x03, + + /// Turning On the Power + /// + /// After the Power ON command, the driver will power on following the Power ON + /// sequence. Once complete, the BUSY signal will become "1". + PowerOn = 0x04, + + /// Starting data transmission + BoosterSoftStart = 0x06, + + /// This command makes the chip enter the deep-sleep mode to save power. + /// + /// The deep sleep mode would return to stand-by by hardware reset. + /// + /// The only one parameter is a check code, the command would be excuted if check code = 0xA5. + DeepSleep = 0x07, + + /// This command starts transmitting data and write them into SRAM. To complete data + /// transmission, command DSP (Data Stop) must be issued. Then the chip will start to + /// send data/VCOM for panel. + /// + /// BLACK/WHITE or OLD_DATA + DataStartTransmission1 = 0x10, + + /// To stop data transmission, this command must be issued to check the `data_flag`. + /// + /// After this command, BUSY signal will become "0" until the display update is + /// finished. + DataStop = 0x11, + + /// After this command is issued, driver will refresh display (data/VCOM) according to + /// SRAM data and LUT. + /// + /// After Display Refresh command, BUSY signal will become "0" until the display + /// update is finished. + DisplayRefresh = 0x12, + + /// Image Process Command + ImageProcess = 0x13, + + /// This command builds the VCOM Look-Up Table (LUTC). + LutForVcom = 0x20, + /// This command builds the Black Look-Up Table (LUTB). + LutBlack = 0x21, + /// This command builds the White Look-Up Table (LUTW). + LutWhite = 0x22, + /// This command builds the Gray1 Look-Up Table (LUTG1). + LutGray1 = 0x23, + /// This command builds the Gray2 Look-Up Table (LUTG2). + LutGray2 = 0x24, + /// This command builds the Red0 Look-Up Table (LUTR0). + LutRed0 = 0x25, + /// This command builds the Red1 Look-Up Table (LUTR1). + LutRed1 = 0x26, + /// This command builds the Red2 Look-Up Table (LUTR2). + LutRed2 = 0x27, + /// This command builds the Red3 Look-Up Table (LUTR3). + LutRed3 = 0x28, + /// This command builds the XON Look-Up Table (LUTXON). + LutXon = 0x29, + + /// The command controls the PLL clock frequency. + PllControl = 0x30, + + /// This command reads the temperature sensed by the temperature sensor. + TemperatureSensor = 0x40, + /// This command selects the Internal or External temperature sensor. + TemperatureCalibration = 0x41, + /// This command could write data to the external temperature sensor. + TemperatureSensorWrite = 0x42, + /// This command could read data from the external temperature sensor. + TemperatureSensorRead = 0x43, + + /// This command indicates the interval of Vcom and data output. When setting the + /// vertical back porch, the total blanking will be kept (20 Hsync). + VcomAndDataIntervalSetting = 0x50, + /// This command indicates the input power condition. Host can read this flag to learn + /// the battery condition. + LowPowerDetection = 0x51, + + /// This command defines non-overlap period of Gate and Source. + TconSetting = 0x60, + /// This command defines alternative resolution and this setting is of higher priority + /// than the RES\[1:0\] in R00H (PSR). + TconResolution = 0x61, + /// This command defines MCU host direct access external memory mode. + //SpiFlashControl = 0x65, + + /// The LUT_REV / Chip Revision is read from OTP address = 25001 and 25000. + //Revision = 0x70, + /// This command reads the IC status. + GetStatus = 0x71, + + /// This command implements related VCOM sensing setting. + //AutoMeasurementVcom = 0x80, + /// This command gets the VCOM value. + ReadVcomValue = 0x81, + /// This command sets `VCOM_DC` value. + VcmDcSetting = 0x82, + // /// This is in all the Waveshare controllers for EPD6in65f, but it's not documented + // /// anywhere in the datasheet `¯\_(ツ)_/¯` + FlashMode = 0xE3, +} + +impl traits::Command for Command { + /// Returns the address of the command + fn address(self) -> u8 { + self as u8 + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::traits::Command as CommandTrait; + + #[test] + fn command_addr() { + assert_eq!(Command::PanelSetting.address(), 0x00); + assert_eq!(Command::DisplayRefresh.address(), 0x12); + } +} diff --git a/src/epd7in3f/mod.rs b/src/epd7in3f/mod.rs new file mode 100644 index 00000000..aa876226 --- /dev/null +++ b/src/epd7in3f/mod.rs @@ -0,0 +1,264 @@ +//! A simple Driver for the Waveshare 7.3 inch E-Ink Display via SPI +//! +//! # References +//! +//! - [Datasheet](https://www.waveshare.com/wiki/7.3inch_e-Paper_HAT_(F)) + +use embedded_hal::{ + delay::DelayNs, + digital::{InputPin, OutputPin}, + spi::SpiDevice, +}; + +use crate::color::OctColor; +use crate::interface::DisplayInterface; +use crate::traits::{InternalWiAdditions, RefreshLut, WaveshareDisplay}; + +pub(crate) mod command; +use self::command::Command; +use crate::buffer_len; + +/// Full size buffer for use with the 5in65f EPD +#[cfg(feature = "graphics")] +pub type Display7in3f = crate::graphics::Display< + WIDTH, + HEIGHT, + false, + { buffer_len(WIDTH as usize, HEIGHT as usize * 4) }, + OctColor, +>; + +/// Width of the display +pub const WIDTH: u32 = 800; +/// Height of the display +pub const HEIGHT: u32 = 480; +/// Default Background Color +pub const DEFAULT_BACKGROUND_COLOR: OctColor = OctColor::White; +/// Default mode of writing data (single byte vs blockwise) +const SINGLE_BYTE_WRITE: bool = true; + +/// Epd7in3f driver +/// +pub struct Epd7in3f { + /// Connection Interface + interface: DisplayInterface, + /// Background Color + color: OctColor, +} + +impl InternalWiAdditions + for Epd7in3f +where + SPI: SpiDevice, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayNs, +{ + fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + // Reset the device + self.interface.reset(delay, 10_000, 2_000); + + self.cmd_with_data(spi, Command::PanelSetting, &[0xEF, 0x08])?; + self.cmd_with_data(spi, Command::PowerSetting, &[0x37, 0x00, 0x23, 0x23])?; + self.cmd_with_data(spi, Command::PowerOffSequenceSetting, &[0x00])?; + self.cmd_with_data(spi, Command::BoosterSoftStart, &[0xC7, 0xC7, 0x1D])?; + self.cmd_with_data(spi, Command::PllControl, &[0x3C])?; + self.cmd_with_data(spi, Command::TemperatureSensor, &[0x00])?; + self.update_vcom(spi)?; + self.cmd_with_data(spi, Command::TconSetting, &[0x22])?; + self.send_resolution(spi)?; + + self.cmd_with_data(spi, Command::FlashMode, &[0xAA])?; + + delay.delay_us(100_000); + + self.update_vcom(spi)?; + Ok(()) + } +} + +impl WaveshareDisplay + for Epd7in3f +where + SPI: SpiDevice, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayNs, +{ + type DisplayColor = OctColor; + fn new( + spi: &mut SPI, + busy: BUSY, + dc: DC, + rst: RST, + delay: &mut DELAY, + delay_us: Option, + ) -> Result { + let interface = DisplayInterface::new(busy, dc, rst, delay_us); + let color = DEFAULT_BACKGROUND_COLOR; + + let mut epd = Epd7in3f { interface, color }; + + epd.init(spi, delay)?; + + Ok(epd) + } + + fn wake_up(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.init(spi, delay) + } + + fn sleep(&mut self, spi: &mut SPI, _delay: &mut DELAY) -> Result<(), SPI::Error> { + self.cmd_with_data(spi, Command::DeepSleep, &[0xA5])?; + Ok(()) + } + + fn update_frame( + &mut self, + spi: &mut SPI, + buffer: &[u8], + delay: &mut DELAY, + ) -> Result<(), SPI::Error> { + self.wait_until_idle(spi, delay)?; + self.update_vcom(spi)?; + self.send_resolution(spi)?; + self.cmd_with_data(spi, Command::DataStartTransmission1, buffer)?; + Ok(()) + } + + fn update_partial_frame( + &mut self, + _spi: &mut SPI, + _delay: &mut DELAY, + _buffer: &[u8], + _x: u32, + _y: u32, + _width: u32, + _height: u32, + ) -> Result<(), SPI::Error> { + unimplemented!(); + } + + fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.wait_until_idle(spi, delay)?; + self.command(spi, Command::PowerOn)?; + self.wait_until_idle(spi, delay)?; + self.command(spi, Command::DisplayRefresh)?; + self.wait_until_idle(spi, delay)?; + self.command(spi, Command::PowerOff)?; + self.wait_busy_low(delay); + Ok(()) + } + + fn update_and_display_frame( + &mut self, + spi: &mut SPI, + buffer: &[u8], + delay: &mut DELAY, + ) -> Result<(), SPI::Error> { + self.update_frame(spi, buffer, delay)?; + self.display_frame(spi, delay)?; + Ok(()) + } + + fn clear_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + let bg = OctColor::colors_byte(self.color, self.color); + self.wait_until_idle(spi, delay)?; + self.update_vcom(spi)?; + self.send_resolution(spi)?; + self.command(spi, Command::DataStartTransmission1)?; + self.interface.data_x_times(spi, bg, WIDTH * HEIGHT / 2)?; + self.display_frame(spi, delay)?; + Ok(()) + } + + fn set_background_color(&mut self, color: OctColor) { + self.color = color; + } + + fn background_color(&self) -> &OctColor { + &self.color + } + + fn width(&self) -> u32 { + WIDTH + } + + fn height(&self) -> u32 { + HEIGHT + } + + fn set_lut( + &mut self, + _spi: &mut SPI, + _delay: &mut DELAY, + _refresh_rate: Option, + ) -> Result<(), SPI::Error> { + unimplemented!(); + } + + fn wait_until_idle(&mut self, _spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.interface.wait_until_idle(delay, true); + Ok(()) + } +} + +impl Epd7in3f +where + SPI: SpiDevice, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayNs, +{ + fn command(&mut self, spi: &mut SPI, command: Command) -> Result<(), SPI::Error> { + self.interface.cmd(spi, command) + } + + fn send_data(&mut self, spi: &mut SPI, data: &[u8]) -> Result<(), SPI::Error> { + self.interface.data(spi, data) + } + + fn cmd_with_data( + &mut self, + spi: &mut SPI, + command: Command, + data: &[u8], + ) -> Result<(), SPI::Error> { + self.interface.cmd_with_data(spi, command, data) + } + + fn wait_busy_low(&mut self, delay: &mut DELAY) { + self.interface.wait_until_idle(delay, false); + } + fn send_resolution(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + let w = self.width(); + let h = self.height(); + + self.command(spi, Command::TconResolution)?; + self.send_data(spi, &[(w >> 8) as u8])?; + self.send_data(spi, &[w as u8])?; + self.send_data(spi, &[(h >> 8) as u8])?; + self.send_data(spi, &[h as u8]) + } + + fn update_vcom(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + let bg_color = (self.color.get_nibble() & 0b111) << 5; + self.cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x17 | bg_color])?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn epd_size() { + assert_eq!(WIDTH, 800); + assert_eq!(HEIGHT, 480); + assert_eq!(DEFAULT_BACKGROUND_COLOR, OctColor::White); + } +} diff --git a/src/lib.rs b/src/lib.rs index fc1d392d..9b393528 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,6 +90,7 @@ pub mod epd4in2; pub mod epd5in65f; pub mod epd5in83_v2; pub mod epd5in83b_v2; +pub mod epd7in3f; pub mod epd7in5; pub mod epd7in5_hd; pub mod epd7in5_v2;