From 1bc31cb3fc36acc418cfef6277faba7f4d0439d4 Mon Sep 17 00:00:00 2001 From: zzlwd Date: Sun, 8 Oct 2023 01:19:05 +0800 Subject: [PATCH 1/4] Add new device: Epd2in9d --- src/epd2in9d/command.rs | 150 ++++++++++ src/epd2in9d/constants.rs | 61 ++++ src/epd2in9d/mod.rs | 567 ++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 4 files changed, 779 insertions(+) create mode 100644 src/epd2in9d/command.rs create mode 100644 src/epd2in9d/constants.rs create mode 100644 src/epd2in9d/mod.rs diff --git a/src/epd2in9d/command.rs b/src/epd2in9d/command.rs new file mode 100644 index 00000000..cd55ee11 --- /dev/null +++ b/src/epd2in9d/command.rs @@ -0,0 +1,150 @@ +//! SPI Commands for the Waveshare 2.9" FLEXIBLE E-PAPER DISPLAY +use crate::traits; + +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub(crate) enum Command { + PanelSetting = 0x00, + /// selecting internal and external power + /// self.send_data(0x03)?; //VDS_EN, VDG_EN + /// self.send_data(0x00)?; //VCOM_HV, VGHL_LV[1], VGHL_LV[0] + /// self.send_data(0x2b)?; //VDH + /// self.send_data(0x2b)?; //VDL + /// self.send_data(0xff)?; //VDHR + PowerSetting = 0x01, + /// After the Power Off command, the driver will power off following the Power Off Sequence. 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: floating. + PowerOff = 0x02, + /// Setting Power OFF sequence + PowerOffSequenceSetting = 0x03, + /// Turning On the Power + PowerOn = 0x04, + /// This command enables the internal bandgap, which will be cleared by the next POF. + PowerOnMeasure = 0x05, + /// Starting data transmission + /// 3-times: self.send_data(0x17)?; //07 0f 17 1f 27 2F 37 2f + BoosterSoftStart = 0x06, + /// After this command is transmitted, the chip would enter the deep-sleep mode to save power. + /// + /// The deep sleep mode would return to standby 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 + /// transmission Stop) must be issued. Then the chip will start to send data/VCOM for panel. + /// + /// - In B/W mode, this command writes “OLD” data to SRAM. + /// - In B/W/Red mode, this command writes “B/W” data to SRAM. + /// - In Program mode, this command writes “OTP” data to SRAM for programming. + DataStartTransmission1 = 0x10, + /// Stopping data transmission + DataStop = 0x11, + /// While user sent this command, driver will refresh display (data/VCOM) according to SRAM data and LUT. + /// + /// After Display Refresh command, BUSY_N signal will become “0” and the refreshing of panel starts. + DisplayRefresh = 0x12, + /// This command starts transmitting data and write them into SRAM. To complete data transmission, command DSP (Data + /// transmission Stop) must be issued. Then the chip will start to send data/VCOM for panel. + /// - In B/W mode, this command writes “NEW” data to SRAM. + /// - In B/W/Red mode, this command writes “RED” data to SRAM. + DataStartTransmission2 = 0x13, + + /// This command stores VCOM Look-Up Table with 7 groups of data. Each group contains information for one state and is stored + /// with 6 bytes, while the sixth byte indicates how many times that phase will repeat. + /// + /// from IL0373 + LutForVcom = 0x20, + /// This command stores White-to-White Look-Up Table with 7 groups of data. Each group contains information for one state and is + /// stored with 6 bytes, while the sixth byte indicates how many times that phase will repeat. + /// + /// from IL0373 + LutWhiteToWhite = 0x21, + /// This command stores Black-to-White Look-Up Table with 7 groups of data. Each group contains information for one state and is + /// stored with 6 bytes, while the sixth byte indicates how many times that phase will repeat. + /// + /// from IL0373 + LutBlackToWhite = 0x22, + /// This command stores White-to-Black Look-Up Table with 7 groups of data. Each group contains information for one state and is + /// stored with 6 bytes, while the sixth byte indicates how many times that phase will repeat. + /// + /// from IL0373 + LutWhiteToBlack = 0x23, + /// This command stores Black-to-Black Look-Up Table with 7 groups of data. Each group contains information for one state and is + /// stored with 6 bytes, while the sixth byte indicates how many times that phase will repeat. + /// + /// from IL0373 + LutBlackToBlack = 0x24, + /// The command controls the PLL clock frequency. + PllControl = 0x30, + /// This command reads the temperature sensed by the temperature sensor. + /// + /// Doesn't work! Waveshare doesn't connect the read pin + TemperatureSensor = 0x40, + /// Selects the Internal or External temperature sensor and offset + TemperatureSensorSelection = 0x41, + /// Write External Temperature Sensor + TemperatureSensorWrite = 0x42, + /// Read External Temperature Sensor + /// + /// Doesn't work! Waveshare doesn't connect the read pin + 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). + ResolutionSetting = 0x61, + /// This command defines the Fist Active Gate and First Active Source of active channels. + // GsstSetting = 0x65, + /// The LUT_REV / Chip Revision is read from OTP address = 0x001. + /// + /// Doesn't work! Waveshare doesn't connect the read pin + // Revision = 0x70, + /// Read Flags. This command reads the IC status + /// PTL, I2C_ERR, I2C_BUSY, DATA, PON, POF, BUSY + /// + /// Doesn't work! Waveshare doesn't connect the read pin + GetStatus = 0x71, + /// Automatically measure VCOM. This command reads the IC status + AutoMeasurementVcom = 0x80, + /// This command gets the VCOM value + /// + /// Doesn't work! Waveshare doesn't connect the read pin + ReadVcomValue = 0x81, + /// Set VCM_DC + VcmDcSetting = 0x82, + /// This command sets partial window + PartialWindow = 0x90, + /// This command makes the display enter partial mode + PartialIn = 0x91, + /// This command makes the display exit partial mode and enter normal mode + PartialOut = 0x92, + /// After this command is issued, the chip would enter the program mode. + /// + /// After the programming procedure completed, a hardware reset is necessary for leaving program mode. + /// + /// The only one parameter is a check code, the command would be excuted if check code = 0xA5. + ProgramMode = 0xA0, + /// After this command is transmitted, the programming state machine would be activated. + /// + /// The BUSY flag would fall to 0 until the programming is completed. + ActiveProgramming = 0xA1, + /// The command is used for reading the content of OTP for checking the data of programming. + /// + /// The value of (n) is depending on the amount of programmed data, tha max address = 0xFFF. + ReadOtp = 0xA2, + /// This command is set for saving power during fresh period. If the output voltage of VCOM / Source is from negative to positive or + /// from positive to negative, the power saving mechanism will be activated. The active period width is defined by the following two + /// parameters. + PowerSaving = 0xE3, +} + +impl traits::Command for Command { + /// Returns the address of the command + fn address(self) -> u8 { + self as u8 + } +} \ No newline at end of file diff --git a/src/epd2in9d/constants.rs b/src/epd2in9d/constants.rs new file mode 100644 index 00000000..aaf169f4 --- /dev/null +++ b/src/epd2in9d/constants.rs @@ -0,0 +1,61 @@ +//! This file contains look-up-tables used to set voltages used during +//! various categories of pixel refreshes. + +/** + * partial screen update LUT +**/ +#[rustfmt::skip] +pub(crate) const LUT_VCOM1: [u8; 44] = [ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, +]; + +#[rustfmt::skip] +pub(crate) const LUT_WW1: [u8; 42] =[ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +#[rustfmt::skip] +pub(crate) const LUT_BW1: [u8; 42] =[ + 0x80, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +#[rustfmt::skip] +pub(crate) const LUT_BB1: [u8; 42] =[ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +#[rustfmt::skip] +pub(crate) const LUT_WB1: [u8; 42] =[ + 0x40, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; \ No newline at end of file diff --git a/src/epd2in9d/mod.rs b/src/epd2in9d/mod.rs new file mode 100644 index 00000000..36db92ce --- /dev/null +++ b/src/epd2in9d/mod.rs @@ -0,0 +1,567 @@ +//! A simple Driver for the Waveshare 2.9" D E-Ink Display via SPI +//! +//! +//! 参考[Waveshare](https://www.waveshare.net/wiki/2.9inch_e-Paper_HAT_%28D%29)的文档/例程进行构建 +//! +//! Specification: https://www.waveshare.net/w/upload/b/b5/2.9inch_e-Paper_%28D%29_Specification.pdf + +use embedded_hal::{ + blocking::{delay::*, spi::Write}, + digital::v2::*, +}; + +use crate::interface::DisplayInterface; +use crate::traits::{RefreshLut, WaveshareDisplay}; + +//The Lookup Tables for the Display +mod constants; +use crate::epd2in9d::constants::*; + +/// Width of Epd2in9d in pixels +pub const WIDTH: u32 = 128; +/// Height of Epd2in9d in pixels +pub const HEIGHT: u32 = 296; +/// Default Background Color (white) +pub const DEFAULT_BACKGROUND_COLOR: Color = Color::Black; +const IS_BUSY_LOW: bool = false; +const SINGLE_BYTE_WRITE: bool = true; + +use crate::color::Color; + +pub(crate) mod command; +use self::command::Command; +use crate::buffer_len; + +/// Display with Fullsize buffer for use with the 2in9 EPD D +#[cfg(feature = "graphics")] +pub type Display2in9d = crate::graphics::Display< + WIDTH, + HEIGHT, + false, + { buffer_len(WIDTH as usize, HEIGHT as usize) }, + Color, +>; + +/// Epd2in9d driver +/// +pub struct Epd2in9d { + /// SPI + interface: DisplayInterface, + /// Color + // background_color: Color, + color: Color, + /// Refresh LUT + refresh: RefreshLut, +} + +impl Epd2in9d +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayUs, +{ + fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.interface.reset(delay, 20_000, 2_000); + + self.interface.cmd(spi, Command::PowerOn)?; + //waiting for the electronic paper IC to release the idle signal + self.wait_until_idle(spi, delay)?; + + //panel setting + //LUT from OTP,KW-BF KWR-AF BWROTP 0f BWOTP 1f + self.interface + .cmd_with_data(spi, Command::PanelSetting, &[0x1f])?; + + //resolution setting + self.interface + .cmd_with_data(spi, Command::ResolutionSetting, &[0x80, 0x01, 0x28])?; + + //VCOM AND DATA INTERVAL SETTING + self.interface + .cmd(spi, Command::VcomAndDataIntervalSetting)?; + + Ok(()) + } +} + +impl WaveshareDisplay + for Epd2in9d +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayUs, +{ + type DisplayColor = Color; + fn width(&self) -> u32 { + WIDTH + } + + fn height(&self) -> u32 { + HEIGHT + } + + fn new( + spi: &mut SPI, + cs: CS, + busy: BUSY, + dc: DC, + rst: RST, + delay: &mut DELAY, + delay_us: Option, + ) -> Result { + let interface = DisplayInterface::new(cs, busy, dc, rst, delay_us); + let color = DEFAULT_BACKGROUND_COLOR; + + let mut epd = Epd2in9d { + interface, + color, + refresh: RefreshLut::Full, + }; + + epd.init(spi, delay)?; + + Ok(epd) + } + + fn sleep(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.interface + .cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0xf7])?; + self.interface.cmd(spi, Command::PowerOff)?; + self.wait_until_idle(spi, delay)?; + self.interface + .cmd_with_data(spi, Command::DeepSleep, &[0xA5])?; + + //TODO: 这还有一个命令没实现,先放着等下回头写 + // DigitalWrite(reset_pin, LOW); + + Ok(()) + } + + fn wake_up(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.init(spi, delay)?; + Ok(()) + } + + // 对应的是Display函数 + fn update_frame( + &mut self, + spi: &mut SPI, + buffer: &[u8], + delay: &mut DELAY, + ) -> Result<(), SPI::Error> { + self.wait_until_idle(spi, delay)?; + + self.interface.cmd(spi, Command::DataStartTransmission1)?; + self.interface.data_x_times(spi, 0x00, WIDTH / 8 * HEIGHT)?; + + // self.interface.cmd(spi,Command::DataStartTransmission2)?; + // for j in 0..h { + // for i in 0..w { + // let mut miao = (i+j*w) as u8; + // self.interface.data(spi, &buffer[miao])?; + // } + // } + //TODO: 不太确定这样写对不对 + self.interface + .cmd_with_data(spi, Command::DataStartTransmission2, buffer)?; + Ok(()) + } + + // 这个是DisplayPart + 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> { + self.wait_until_idle(spi, delay)?; + + self.set_part_reg(spi, delay)?; + self.interface.cmd(spi, Command::PartialIn)?; + + // 这部分按照例程来说应该是下面这样 + // self.interface.cmd_with_data( + // spi, + // Command::PartialWindow, + // &[ + // 0x00, + // (WIDTH - 1) as u8, + // 0x00, + // 0x00, + // (HEIGHT / 256) as u8, + // (HEIGHT % 256 - 1) as u8, + // 0x28, + // ], + // )?; + // 但看了下隔壁4in2的代码,决定抄抄 + self.interface.cmd(spi, Command::PartialWindow)?; + self.interface.data(spi, &[(x >> 8) as u8])?; + let tmp = x & 0xf8; + self.interface.data(spi, &[tmp as u8])?; // x should be the multiple of 8, the last 3 bit will always be ignored + let tmp = tmp + width - 1; + self.interface.data(spi, &[(tmp >> 8) as u8])?; + self.interface.data(spi, &[(tmp | 0x07) as u8])?; + + self.interface.data(spi, &[(y >> 8) as u8])?; + self.interface.data(spi, &[y as u8])?; + + self.interface.data(spi, &[((y + height - 1) >> 8) as u8])?; + self.interface.data(spi, &[(y + height - 1) as u8])?; + + self.interface.data(spi, &[0x01])?; // Gates scan both inside and outside of the partial window. (default) + + self.interface + .cmd_with_data(spi, Command::DataStartTransmission2, buffer)?; + + self.turn_on_display(spi, delay)?; + Ok(()) + } + + /// actually is the "Turn on Display" sequence + fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.wait_until_idle(spi, delay)?; + self.interface.cmd(spi, Command::DisplayRefresh)?; + Ok(()) + + // 其实也可以是下面这样 + // self.wait_until_idle(spi, delay)?; + // self.turn_on_display(spi, delay)?; + } + + 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> { + self.wait_until_idle(spi, delay)?; + + // let mut w = if WIDTH % 8 == 0 { + // WIDTH / 8 + // } else { + // WIDTH / 8 + 1 + // }; + // let mut h = HEIGHT; + + self.interface.cmd(spi, Command::DataStartTransmission1)?; + // for _ in 0..h { + // for _ in 0..w { + // self.interface.data(spi, &[0x00])?; + // } + // } + self.interface.data_x_times(spi, 0x00, WIDTH / 8 * HEIGHT)?; + + self.interface.cmd(spi, Command::DataStartTransmission2)?; + // for _ in 0..h { + // for _ in 0..w { + // self.interface.data(spi, &[0xFF])?; + // } + // } + self.interface.data_x_times(spi, 0xFF, WIDTH / 8 * HEIGHT)?; + + self.turn_on_display(spi, delay)?; + + Ok(()) + } + + fn set_background_color(&mut self, background_color: Color) { + self.color = background_color; + } + + fn background_color(&self) -> &Color { + &self.color + } + + fn set_lut( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + refresh_rate: Option, + ) -> Result<(), SPI::Error> { + if let Some(refresh_lut) = refresh_rate { + self.refresh = refresh_lut; + } + self.set_lut_helper( + spi, delay, &LUT_VCOM1, &LUT_WW1, &LUT_BW1, &LUT_WB1, &LUT_BB1, + )?; + + Ok(()) + } + + fn wait_until_idle(&mut self, _spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.interface.wait_until_idle(delay, IS_BUSY_LOW); + Ok(()) + } +} + +impl Epd2in9d +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayUs, +{ + fn turn_on_display(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + //DISPLAY REFRESH + self.interface.cmd(spi, Command::DisplayRefresh)?; + //The delay here is necessary, 200uS at least!!! + delay.delay_us(200); + + self.wait_until_idle(spi, delay)?; + Ok(()) + } + + /// 唤醒屏幕 + /// + /// 在屏幕执行sleep之后,会进入深度睡眠模式。在深度睡眠模式下若需要刷新屏幕,必须先执行awaken() + /// 唤醒屏幕 + // fn awaken(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + // // reset the device + // self.interface.reset(delay, 20_000, 2_000); + // self.wait_until_idle(spi, delay)?; + + // // panel setting + // // LUT from OTP,KW-BF KWR-AF BWROTP 0f BWOTP 1f + // self.interface + // .cmd_with_data(spi, Command::PanelSetting, &[0x1f])?; + + // // resolution setting + // self.interface + // .cmd_with_data(spi, Command::ResolutionSetting, &[0x80, 0x01, 0x28])?; + + // // VCOM AND DATA INTERVAL SETTING + // self.interface + // .cmd(spi, Command::VcomAndDataIntervalSetting)?; + // Ok(()) + // } + + fn set_part_reg(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + // 重置EPD驱动电路 + //TODO: 这里在微雪的例程中反复刷新了3次,后面有显示问题再进行修改 + self.interface.reset(delay, 20_000, 2_000); + + // 电源设置 + //TODO: 文档中的数据为[0x03,0x00,0x2b,0x2b,0x09] + self.interface.cmd_with_data( + spi, + Command::PowerSetting, + &[0x03, 0x00, 0x2b, 0x2b, 0x03], + )?; + + // 软启动 + self.interface + .cmd_with_data(spi, Command::BoosterSoftStart, &[0x17, 0x17, 0x17])?; + + // 开启电源 + // self.interface.cmd_with_data( + // spi, + // Command::PowerOn, + // &[0x04], + // ); + self.interface.cmd(spi, Command::PowerOn)?; + + // 获取BUSY电平,高电平继续执行,低电平则等待屏幕响应 + //TODO: 这里是文档推荐的步骤,但我看其他屏幕的也没等待就先忽略了 + // self.wait_until_idle(spi, delay)?; + + // 面板设置 + self.interface + .cmd_with_data(spi, Command::PanelSetting, &[0xbf])?; + + // 设置刷新率 + // 3a 100HZ | 29 150Hz | 39 200HZ | 31 171HZ + // 例程中使用3a + self.interface + .cmd_with_data(spi, Command::PllControl, &[0x3a])?; + + // 分辨率设置 + self.interface + .cmd_with_data(spi, Command::ResolutionSetting, &[0x80, 0x01, 0x28])?; + + // vcom_DC设置 + self.interface + .cmd_with_data(spi, Command::VcmDcSetting, &[0x12])?; + + // vcom和数据间隔设置 + self.interface + .cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x97])?; + + self.set_lut(spi, delay, None)?; + Ok(()) + } + + #[allow(clippy::too_many_arguments)] + fn set_lut_helper( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + lut_vcom: &[u8], + lut_ww: &[u8], + lut_bw: &[u8], + lut_wb: &[u8], + lut_bb: &[u8], + ) -> Result<(), SPI::Error> { + self.wait_until_idle(spi, delay)?; + // LUT VCOM + self.interface + .cmd_with_data(spi, Command::LutForVcom, lut_vcom)?; + + // LUT WHITE to WHITE + self.interface + .cmd_with_data(spi, Command::LutWhiteToWhite, lut_ww)?; + + // LUT BLACK to WHITE + self.interface + .cmd_with_data(spi, Command::LutBlackToWhite, lut_bw)?; + + // LUT WHITE to BLACK + self.interface + .cmd_with_data(spi, Command::LutWhiteToBlack, lut_wb)?; + + // LUT BLACK to BLACK + self.interface + .cmd_with_data(spi, Command::LutBlackToBlack, lut_bb)?; + Ok(()) + } +} + +// impl QuickRefresh +// for Epd2in9d +// where +// SPI: Write, +// CS: OutputPin, +// BUSY: InputPin, +// DC: OutputPin, +// RST: OutputPin, +// DELAY: DelayUs, +// { +// /// To be followed immediately by `update_new_frame`. +// fn update_old_frame( +// &mut self, +// spi: &mut SPI, +// buffer: &[u8], +// delay: &mut DELAY, +// ) -> Result<(), SPI::Error> { +// self.wait_until_idle(spi, delay)?; +// self.interface +// .cmd_with_data(spi, Command::WriteRam2, buffer) +// } + +// /// To be used immediately after `update_old_frame`. +// fn update_new_frame( +// &mut self, +// spi: &mut SPI, +// buffer: &[u8], +// delay: &mut DELAY, +// ) -> Result<(), SPI::Error> { +// self.wait_until_idle(spi, delay)?; +// self.interface.reset(delay, 10_000, 2_000); + +// self.set_lut_helper(spi, delay, &LUT_PARTIAL_2IN9)?; +// self.interface.cmd_with_data( +// spi, +// Command::WriteOtpSelection, +// &[0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00], +// )?; +// self.interface +// .cmd_with_data(spi, Command::BorderWaveformControl, &[0x80])?; +// self.interface +// .cmd_with_data(spi, Command::DisplayUpdateControl2, &[0xC0])?; +// self.interface.cmd(spi, Command::MasterActivation)?; + +// self.wait_until_idle(spi, delay)?; + +// self.use_full_frame(spi, delay)?; + +// self.interface +// .cmd_with_data(spi, Command::WriteRam, buffer)?; +// Ok(()) +// } + +// /// For a quick refresh of the new updated frame. To be used immediately after `update_new_frame` +// fn display_new_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { +// self.wait_until_idle(spi, delay)?; +// self.interface +// .cmd_with_data(spi, Command::DisplayUpdateControl2, &[0x0F])?; +// self.interface.cmd(spi, Command::MasterActivation)?; +// self.wait_until_idle(spi, delay)?; +// Ok(()) +// } + +// /// Updates and displays the new frame. +// fn update_and_display_new_frame( +// &mut self, +// spi: &mut SPI, +// buffer: &[u8], +// delay: &mut DELAY, +// ) -> Result<(), SPI::Error> { +// self.update_new_frame(spi, buffer, delay)?; +// self.display_new_frame(spi, delay)?; +// Ok(()) +// } + +// /// Partial quick refresh not supported yet +// #[allow(unused)] +// fn update_partial_old_frame( +// &mut self, +// spi: &mut SPI, +// delay: &mut DELAY, +// buffer: &[u8], +// x: u32, +// y: u32, +// width: u32, +// height: u32, +// ) -> Result<(), SPI::Error> { +// //TODO supported by display? +// unimplemented!() +// } + +// /// Partial quick refresh not supported yet +// #[allow(unused)] +// fn update_partial_new_frame( +// &mut self, +// spi: &mut SPI, +// delay: &mut DELAY, +// buffer: &[u8], +// x: u32, +// y: u32, +// width: u32, +// height: u32, +// ) -> Result<(), SPI::Error> { +// //TODO supported by display? +// unimplemented!() +// } + +// /// Partial quick refresh not supported yet +// #[allow(unused)] +// fn clear_partial_frame( +// &mut self, +// spi: &mut SPI, +// delay: &mut DELAY, +// x: u32, +// y: u32, +// width: u32, +// height: u32, +// ) -> Result<(), SPI::Error> { +// //TODO supported by display? +// unimplemented!() +// } +// } diff --git a/src/lib.rs b/src/lib.rs index 677f16b9..405c299a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -92,6 +92,7 @@ pub mod epd7in5_hd; pub mod epd7in5_v2; pub mod epd7in5_v3; pub mod epd7in5b_v2; +pub mod epd2in9d; pub(crate) mod type_a; From f50c72b86fcec3ff069f9da55fb4a96aa55984fa Mon Sep 17 00:00:00 2001 From: zzlwd Date: Sun, 8 Oct 2023 11:03:03 +0800 Subject: [PATCH 2/4] Format document --- src/epd2in9d/command.rs | 2 +- src/epd2in9d/constants.rs | 2 +- src/epd2in9d/mod.rs | 4 ++-- src/lib.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/epd2in9d/command.rs b/src/epd2in9d/command.rs index cd55ee11..c7abc249 100644 --- a/src/epd2in9d/command.rs +++ b/src/epd2in9d/command.rs @@ -147,4 +147,4 @@ impl traits::Command for Command { fn address(self) -> u8 { self as u8 } -} \ No newline at end of file +} diff --git a/src/epd2in9d/constants.rs b/src/epd2in9d/constants.rs index aaf169f4..6a56d2e7 100644 --- a/src/epd2in9d/constants.rs +++ b/src/epd2in9d/constants.rs @@ -58,4 +58,4 @@ pub(crate) const LUT_WB1: [u8; 42] =[ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -]; \ No newline at end of file +]; diff --git a/src/epd2in9d/mod.rs b/src/epd2in9d/mod.rs index 36db92ce..b0f3e7ce 100644 --- a/src/epd2in9d/mod.rs +++ b/src/epd2in9d/mod.rs @@ -87,7 +87,7 @@ where } } -impl WaveshareDisplay +impl WaveshareDisplay for Epd2in9d where SPI: Write, @@ -139,7 +139,7 @@ where //TODO: 这还有一个命令没实现,先放着等下回头写 // DigitalWrite(reset_pin, LOW); - + Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 7cbed131..38ce84d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,6 +83,7 @@ pub mod epd2in7b; pub mod epd2in9; pub mod epd2in9_v2; pub mod epd2in9bc; +pub mod epd2in9d; pub mod epd3in7; pub mod epd4in2; pub mod epd5in65f; @@ -92,7 +93,6 @@ pub mod epd7in5_hd; pub mod epd7in5_v2; pub mod epd7in5_v3; pub mod epd7in5b_v2; -pub mod epd2in9d; pub(crate) mod type_a; From 84a5d349fac31ccb5b9ebc7e0db9aaeda4b2b56d Mon Sep 17 00:00:00 2001 From: zzlwd Date: Sun, 15 Oct 2023 18:52:27 +0800 Subject: [PATCH 3/4] Test and support partial refresh; Made some modifications with reference to the documentation of `GDEW029T5D`, and tested the support for partial refresh. (cherry picked from commit da2764dd41f29f69ecbcda09200698f4ce89850e) --- src/epd2in9d/mod.rs | 344 ++++++++++++-------------------------------- 1 file changed, 93 insertions(+), 251 deletions(-) diff --git a/src/epd2in9d/mod.rs b/src/epd2in9d/mod.rs index b0f3e7ce..e8047c9f 100644 --- a/src/epd2in9d/mod.rs +++ b/src/epd2in9d/mod.rs @@ -5,6 +5,8 @@ //! //! Specification: https://www.waveshare.net/w/upload/b/b5/2.9inch_e-Paper_%28D%29_Specification.pdf +use core::slice::from_raw_parts; + use embedded_hal::{ blocking::{delay::*, spi::Write}, digital::v2::*, @@ -21,6 +23,9 @@ use crate::epd2in9d::constants::*; pub const WIDTH: u32 = 128; /// Height of Epd2in9d in pixels pub const HEIGHT: u32 = 296; +/// EPD_ARRAY of Epd2in9d in pixels +/// WIDTH / 8 * HEIGHT +pub const EPD_ARRAY: u32 = 4736; /// Default Background Color (white) pub const DEFAULT_BACKGROUND_COLOR: Color = Color::Black; const IS_BUSY_LOW: bool = false; @@ -44,7 +49,7 @@ pub type Display2in9d = crate::graphics::Display< /// Epd2in9d driver /// -pub struct Epd2in9d { +pub struct Epd2in9d<'a, SPI, CS, BUSY, DC, RST, DELAY> { /// SPI interface: DisplayInterface, /// Color @@ -52,9 +57,13 @@ pub struct Epd2in9d { color: Color, /// Refresh LUT refresh: RefreshLut, + // 存放旧数据,以供部分刷新使用 + old_data: &'a [u8], + // 标记是否局刷的状态 + is_partial_refresh: bool, } -impl Epd2in9d +impl Epd2in9d<'_, SPI, CS, BUSY, DC, RST, DELAY> where SPI: Write, CS: OutputPin, @@ -64,31 +73,30 @@ where DELAY: DelayUs, { fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { - self.interface.reset(delay, 20_000, 2_000); - - self.interface.cmd(spi, Command::PowerOn)?; - //waiting for the electronic paper IC to release the idle signal - self.wait_until_idle(spi, delay)?; + self.interface.reset(delay, 10_000, 2_000); //panel setting //LUT from OTP,KW-BF KWR-AF BWROTP 0f BWOTP 1f self.interface - .cmd_with_data(spi, Command::PanelSetting, &[0x1f])?; + .cmd_with_data(spi, Command::PanelSetting, &[0x1f, 0x0D])?; //resolution setting self.interface .cmd_with_data(spi, Command::ResolutionSetting, &[0x80, 0x01, 0x28])?; + self.interface.cmd(spi, Command::PowerOn)?; + self.wait_until_idle(spi, delay)?; + //VCOM AND DATA INTERVAL SETTING self.interface - .cmd(spi, Command::VcomAndDataIntervalSetting)?; + .cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x97])?; Ok(()) } } impl WaveshareDisplay - for Epd2in9d + for Epd2in9d<'_, SPI, CS, BUSY, DC, RST, DELAY> where SPI: Write, CS: OutputPin, @@ -98,14 +106,6 @@ where DELAY: DelayUs, { type DisplayColor = Color; - fn width(&self) -> u32 { - WIDTH - } - - fn height(&self) -> u32 { - HEIGHT - } - fn new( spi: &mut SPI, cs: CS, @@ -117,11 +117,15 @@ where ) -> Result { let interface = DisplayInterface::new(cs, busy, dc, rst, delay_us); let color = DEFAULT_BACKGROUND_COLOR; + let old_data: &[u8] = &[]; + let is_partial_refresh = false; let mut epd = Epd2in9d { interface, color, refresh: RefreshLut::Full, + old_data, + is_partial_refresh, }; epd.init(spi, delay)?; @@ -130,16 +134,15 @@ where } fn sleep(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.is_partial_refresh = false; self.interface .cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0xf7])?; self.interface.cmd(spi, Command::PowerOff)?; self.wait_until_idle(spi, delay)?; + delay.delay_us(100_000); self.interface .cmd_with_data(spi, Command::DeepSleep, &[0xA5])?; - //TODO: 这还有一个命令没实现,先放着等下回头写 - // DigitalWrite(reset_pin, LOW); - Ok(()) } @@ -148,32 +151,47 @@ where Ok(()) } + fn set_background_color(&mut self, background_color: Color) { + self.color = background_color; + } + + fn background_color(&self) -> &Color { + &self.color + } + + fn width(&self) -> u32 { + WIDTH + } + + fn height(&self) -> u32 { + HEIGHT + } + // 对应的是Display函数 + // 用于将要显示的数据写入屏幕SRAM fn update_frame( &mut self, spi: &mut SPI, buffer: &[u8], delay: &mut DELAY, ) -> Result<(), SPI::Error> { + if self.is_partial_refresh { + // 若进行全刷则修改局刷状态 + self.is_partial_refresh = false; + } self.wait_until_idle(spi, delay)?; self.interface.cmd(spi, Command::DataStartTransmission1)?; - self.interface.data_x_times(spi, 0x00, WIDTH / 8 * HEIGHT)?; - - // self.interface.cmd(spi,Command::DataStartTransmission2)?; - // for j in 0..h { - // for i in 0..w { - // let mut miao = (i+j*w) as u8; - // self.interface.data(spi, &buffer[miao])?; - // } - // } - //TODO: 不太确定这样写对不对 + self.interface.data_x_times(spi, 0xFF, EPD_ARRAY)?; + self.interface .cmd_with_data(spi, Command::DataStartTransmission2, buffer)?; + self.old_data = unsafe { from_raw_parts(buffer.as_ptr(), buffer.len()) }; Ok(()) } // 这个是DisplayPart + // Partial refresh write address and data fn update_partial_frame( &mut self, spi: &mut SPI, @@ -184,58 +202,41 @@ where width: u32, height: u32, ) -> Result<(), SPI::Error> { - self.wait_until_idle(spi, delay)?; - - self.set_part_reg(spi, delay)?; + if !self.is_partial_refresh { + // 仅在初次调用时初始化 + self.set_part_reg(spi, delay)?; + self.is_partial_refresh = true; + } self.interface.cmd(spi, Command::PartialIn)?; - // 这部分按照例程来说应该是下面这样 - // self.interface.cmd_with_data( - // spi, - // Command::PartialWindow, - // &[ - // 0x00, - // (WIDTH - 1) as u8, - // 0x00, - // 0x00, - // (HEIGHT / 256) as u8, - // (HEIGHT % 256 - 1) as u8, - // 0x28, - // ], - // )?; - // 但看了下隔壁4in2的代码,决定抄抄 self.interface.cmd(spi, Command::PartialWindow)?; - self.interface.data(spi, &[(x >> 8) as u8])?; - let tmp = x & 0xf8; - self.interface.data(spi, &[tmp as u8])?; // x should be the multiple of 8, the last 3 bit will always be ignored - let tmp = tmp + width - 1; - self.interface.data(spi, &[(tmp >> 8) as u8])?; - self.interface.data(spi, &[(tmp | 0x07) as u8])?; - - self.interface.data(spi, &[(y >> 8) as u8])?; - self.interface.data(spi, &[y as u8])?; - - self.interface.data(spi, &[((y + height - 1) >> 8) as u8])?; - self.interface.data(spi, &[(y + height - 1) as u8])?; + self.interface.data(spi, &[(x - x % 8) as u8])?; + self.interface + .data(spi, &[(((x - x % 8) + width - 1) - 1) as u8])?; + self.interface.data(spi, &[(y / 256) as u8])?; + self.interface.data(spi, &[(y % 256) as u8])?; + self.interface + .data(spi, &[((y + height - 1) / 256) as u8])?; + self.interface + .data(spi, &[((y + height - 1) % 256 - 1) as u8])?; + self.interface.data(spi, &[0x28])?; - self.interface.data(spi, &[0x01])?; // Gates scan both inside and outside of the partial window. (default) + self.interface + .cmd_with_data(spi, Command::DataStartTransmission1, self.old_data)?; self.interface .cmd_with_data(spi, Command::DataStartTransmission2, buffer)?; + self.old_data = unsafe { from_raw_parts(buffer.as_ptr(), buffer.len()) }; - self.turn_on_display(spi, delay)?; Ok(()) } /// actually is the "Turn on Display" sequence fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { - self.wait_until_idle(spi, delay)?; self.interface.cmd(spi, Command::DisplayRefresh)?; + delay.delay_us(1_000); + self.wait_until_idle(spi, delay)?; Ok(()) - - // 其实也可以是下面这样 - // self.wait_until_idle(spi, delay)?; - // self.turn_on_display(spi, delay)?; } fn update_and_display_frame( @@ -250,44 +251,17 @@ where } fn clear_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { - self.wait_until_idle(spi, delay)?; - - // let mut w = if WIDTH % 8 == 0 { - // WIDTH / 8 - // } else { - // WIDTH / 8 + 1 - // }; - // let mut h = HEIGHT; - self.interface.cmd(spi, Command::DataStartTransmission1)?; - // for _ in 0..h { - // for _ in 0..w { - // self.interface.data(spi, &[0x00])?; - // } - // } - self.interface.data_x_times(spi, 0x00, WIDTH / 8 * HEIGHT)?; + self.interface.data_x_times(spi, 0x00, EPD_ARRAY)?; self.interface.cmd(spi, Command::DataStartTransmission2)?; - // for _ in 0..h { - // for _ in 0..w { - // self.interface.data(spi, &[0xFF])?; - // } - // } - self.interface.data_x_times(spi, 0xFF, WIDTH / 8 * HEIGHT)?; + self.interface.data_x_times(spi, 0xFF, EPD_ARRAY)?; - self.turn_on_display(spi, delay)?; + self.display_frame(spi, delay)?; Ok(()) } - fn set_background_color(&mut self, background_color: Color) { - self.color = background_color; - } - - fn background_color(&self) -> &Color { - &self.color - } - fn set_lut( &mut self, spi: &mut SPI, @@ -310,7 +284,7 @@ where } } -impl Epd2in9d +impl Epd2in9d<'_, SPI, CS, BUSY, DC, RST, DELAY> where SPI: Write, CS: OutputPin, @@ -319,16 +293,6 @@ where RST: OutputPin, DELAY: DelayUs, { - fn turn_on_display(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { - //DISPLAY REFRESH - self.interface.cmd(spi, Command::DisplayRefresh)?; - //The delay here is necessary, 200uS at least!!! - delay.delay_us(200); - - self.wait_until_idle(spi, delay)?; - Ok(()) - } - /// 唤醒屏幕 /// /// 在屏幕执行sleep之后,会进入深度睡眠模式。在深度睡眠模式下若需要刷新屏幕,必须先执行awaken() @@ -356,7 +320,7 @@ where fn set_part_reg(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { // 重置EPD驱动电路 //TODO: 这里在微雪的例程中反复刷新了3次,后面有显示问题再进行修改 - self.interface.reset(delay, 20_000, 2_000); + self.interface.reset(delay, 10_000, 2_000); // 电源设置 //TODO: 文档中的数据为[0x03,0x00,0x2b,0x2b,0x09] @@ -370,27 +334,15 @@ where self.interface .cmd_with_data(spi, Command::BoosterSoftStart, &[0x17, 0x17, 0x17])?; - // 开启电源 - // self.interface.cmd_with_data( - // spi, - // Command::PowerOn, - // &[0x04], - // ); - self.interface.cmd(spi, Command::PowerOn)?; - - // 获取BUSY电平,高电平继续执行,低电平则等待屏幕响应 - //TODO: 这里是文档推荐的步骤,但我看其他屏幕的也没等待就先忽略了 - // self.wait_until_idle(spi, delay)?; - // 面板设置 self.interface - .cmd_with_data(spi, Command::PanelSetting, &[0xbf])?; + .cmd_with_data(spi, Command::PanelSetting, &[0xbf, 0x0D])?; // 设置刷新率 // 3a 100HZ | 29 150Hz | 39 200HZ | 31 171HZ // 例程中使用3a self.interface - .cmd_with_data(spi, Command::PllControl, &[0x3a])?; + .cmd_with_data(spi, Command::PllControl, &[0x3C])?; // 分辨率设置 self.interface @@ -400,11 +352,24 @@ where self.interface .cmd_with_data(spi, Command::VcmDcSetting, &[0x12])?; + self.set_lut(spi, delay, None)?; + + // 开启电源 + // self.interface.cmd_with_data( + // spi, + // Command::PowerOn, + // &[0x04], + // ); + self.interface.cmd(spi, Command::PowerOn)?; + + // 获取BUSY电平,高电平继续执行,低电平则等待屏幕响应 + //TODO: 这里是文档推荐的步骤,但我看其他屏幕的也没等待就先忽略了 + self.wait_until_idle(spi, delay)?; + // vcom和数据间隔设置 - self.interface - .cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x97])?; + // self.interface + // .cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x97])?; - self.set_lut(spi, delay, None)?; Ok(()) } @@ -419,7 +384,7 @@ where lut_wb: &[u8], lut_bb: &[u8], ) -> Result<(), SPI::Error> { - self.wait_until_idle(spi, delay)?; + let _ = delay; // LUT VCOM self.interface .cmd_with_data(spi, Command::LutForVcom, lut_vcom)?; @@ -442,126 +407,3 @@ where Ok(()) } } - -// impl QuickRefresh -// for Epd2in9d -// where -// SPI: Write, -// CS: OutputPin, -// BUSY: InputPin, -// DC: OutputPin, -// RST: OutputPin, -// DELAY: DelayUs, -// { -// /// To be followed immediately by `update_new_frame`. -// fn update_old_frame( -// &mut self, -// spi: &mut SPI, -// buffer: &[u8], -// delay: &mut DELAY, -// ) -> Result<(), SPI::Error> { -// self.wait_until_idle(spi, delay)?; -// self.interface -// .cmd_with_data(spi, Command::WriteRam2, buffer) -// } - -// /// To be used immediately after `update_old_frame`. -// fn update_new_frame( -// &mut self, -// spi: &mut SPI, -// buffer: &[u8], -// delay: &mut DELAY, -// ) -> Result<(), SPI::Error> { -// self.wait_until_idle(spi, delay)?; -// self.interface.reset(delay, 10_000, 2_000); - -// self.set_lut_helper(spi, delay, &LUT_PARTIAL_2IN9)?; -// self.interface.cmd_with_data( -// spi, -// Command::WriteOtpSelection, -// &[0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00], -// )?; -// self.interface -// .cmd_with_data(spi, Command::BorderWaveformControl, &[0x80])?; -// self.interface -// .cmd_with_data(spi, Command::DisplayUpdateControl2, &[0xC0])?; -// self.interface.cmd(spi, Command::MasterActivation)?; - -// self.wait_until_idle(spi, delay)?; - -// self.use_full_frame(spi, delay)?; - -// self.interface -// .cmd_with_data(spi, Command::WriteRam, buffer)?; -// Ok(()) -// } - -// /// For a quick refresh of the new updated frame. To be used immediately after `update_new_frame` -// fn display_new_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { -// self.wait_until_idle(spi, delay)?; -// self.interface -// .cmd_with_data(spi, Command::DisplayUpdateControl2, &[0x0F])?; -// self.interface.cmd(spi, Command::MasterActivation)?; -// self.wait_until_idle(spi, delay)?; -// Ok(()) -// } - -// /// Updates and displays the new frame. -// fn update_and_display_new_frame( -// &mut self, -// spi: &mut SPI, -// buffer: &[u8], -// delay: &mut DELAY, -// ) -> Result<(), SPI::Error> { -// self.update_new_frame(spi, buffer, delay)?; -// self.display_new_frame(spi, delay)?; -// Ok(()) -// } - -// /// Partial quick refresh not supported yet -// #[allow(unused)] -// fn update_partial_old_frame( -// &mut self, -// spi: &mut SPI, -// delay: &mut DELAY, -// buffer: &[u8], -// x: u32, -// y: u32, -// width: u32, -// height: u32, -// ) -> Result<(), SPI::Error> { -// //TODO supported by display? -// unimplemented!() -// } - -// /// Partial quick refresh not supported yet -// #[allow(unused)] -// fn update_partial_new_frame( -// &mut self, -// spi: &mut SPI, -// delay: &mut DELAY, -// buffer: &[u8], -// x: u32, -// y: u32, -// width: u32, -// height: u32, -// ) -> Result<(), SPI::Error> { -// //TODO supported by display? -// unimplemented!() -// } - -// /// Partial quick refresh not supported yet -// #[allow(unused)] -// fn clear_partial_frame( -// &mut self, -// spi: &mut SPI, -// delay: &mut DELAY, -// x: u32, -// y: u32, -// width: u32, -// height: u32, -// ) -> Result<(), SPI::Error> { -// //TODO supported by display? -// unimplemented!() -// } -// } From 8986f935818812f52d9222273488fa2833302041 Mon Sep 17 00:00:00 2001 From: zzlwd Date: Sun, 15 Oct 2023 23:19:33 +0800 Subject: [PATCH 4/4] Follow the master branch to update to 1.0-rc1 --- src/epd2in9d/mod.rs | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/epd2in9d/mod.rs b/src/epd2in9d/mod.rs index e8047c9f..87b5e8ad 100644 --- a/src/epd2in9d/mod.rs +++ b/src/epd2in9d/mod.rs @@ -8,12 +8,13 @@ use core::slice::from_raw_parts; use embedded_hal::{ - blocking::{delay::*, spi::Write}, - digital::v2::*, + delay::DelayUs, + digital::{InputPin, OutputPin}, + spi::SpiDevice, }; use crate::interface::DisplayInterface; -use crate::traits::{RefreshLut, WaveshareDisplay}; +use crate::traits::{InternalWiAdditions, RefreshLut, WaveshareDisplay}; //The Lookup Tables for the Display mod constants; @@ -49,9 +50,9 @@ pub type Display2in9d = crate::graphics::Display< /// Epd2in9d driver /// -pub struct Epd2in9d<'a, SPI, CS, BUSY, DC, RST, DELAY> { +pub struct Epd2in9d<'a, SPI, BUSY, DC, RST, DELAY> { /// SPI - interface: DisplayInterface, + interface: DisplayInterface, /// Color // background_color: Color, color: Color, @@ -63,14 +64,14 @@ pub struct Epd2in9d<'a, SPI, CS, BUSY, DC, RST, DELAY> { is_partial_refresh: bool, } -impl Epd2in9d<'_, SPI, CS, BUSY, DC, RST, DELAY> +impl InternalWiAdditions + for Epd2in9d<'_, SPI, BUSY, DC, RST, DELAY> where - SPI: Write, - CS: OutputPin, + SPI: SpiDevice, BUSY: InputPin, DC: OutputPin, RST: OutputPin, - DELAY: DelayUs, + DELAY: DelayUs, { fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { self.interface.reset(delay, 10_000, 2_000); @@ -95,27 +96,25 @@ where } } -impl WaveshareDisplay - for Epd2in9d<'_, SPI, CS, BUSY, DC, RST, DELAY> +impl WaveshareDisplay + for Epd2in9d<'_, SPI, BUSY, DC, RST, DELAY> where - SPI: Write, - CS: OutputPin, + SPI: SpiDevice, BUSY: InputPin, DC: OutputPin, RST: OutputPin, - DELAY: DelayUs, + DELAY: DelayUs, { type DisplayColor = Color; fn new( spi: &mut SPI, - cs: CS, busy: BUSY, dc: DC, rst: RST, delay: &mut DELAY, delay_us: Option, ) -> Result { - let interface = DisplayInterface::new(cs, busy, dc, rst, delay_us); + let interface = DisplayInterface::new(busy, dc, rst, delay_us); let color = DEFAULT_BACKGROUND_COLOR; let old_data: &[u8] = &[]; let is_partial_refresh = false; @@ -284,14 +283,13 @@ where } } -impl Epd2in9d<'_, SPI, CS, BUSY, DC, RST, DELAY> +impl Epd2in9d<'_, SPI, BUSY, DC, RST, DELAY> where - SPI: Write, - CS: OutputPin, + SPI: SpiDevice, BUSY: InputPin, DC: OutputPin, RST: OutputPin, - DELAY: DelayUs, + DELAY: DelayUs, { /// 唤醒屏幕 ///