diff --git a/src/epd7in5_v2/mod.rs b/src/epd7in5_v2/mod.rs index 8d0d45f3..5cc8f05c 100644 --- a/src/epd7in5_v2/mod.rs +++ b/src/epd7in5_v2/mod.rs @@ -131,7 +131,16 @@ where delay: &mut DELAY, ) -> Result<(), SPI::Error> { self.wait_until_idle(spi, delay)?; - self.cmd_with_data(spi, Command::DataStartTransmission2, buffer)?; + // Waveshare's reference C demo (EPD_7in5_V2.c::EPD_7IN5_V2_Display) + // sends the framebuffer to DTM1 (0x10) raw and to DTM2 (0x13) bitwise- + // inverted. The user-facing convention is bit 1 = white; the panel's + // DTM2 register expects bit 0 = white (datasheet ยง22, KW mode with + // NEW/OLD, DDX=00). Writing both DTM1 and DTM2 with opposite polarity + // forces a full LUTKW/LUTWK transition for every pixel, producing + // strong contrast. Without this, every framebuffer renders inverted. + self.cmd_with_data(spi, Command::DataStartTransmission1, buffer)?; + self.command(spi, Command::DataStartTransmission2)?; + self.interface.data_inverted(spi, buffer)?; Ok(()) } @@ -169,8 +178,11 @@ where self.wait_until_idle(spi, delay)?; self.send_resolution(spi)?; + // Match Waveshare's `EPD_7IN5_V2_Clear` (DTM1=0xFF, DTM2=0x00) so + // every pixel transitions black->white via LUTKW. See `update_frame` + // for the polarity rationale. self.command(spi, Command::DataStartTransmission1)?; - self.interface.data_x_times(spi, 0x00, WIDTH / 8 * HEIGHT)?; + self.interface.data_x_times(spi, 0xFF, WIDTH / 8 * HEIGHT)?; self.command(spi, Command::DataStartTransmission2)?; self.interface.data_x_times(spi, 0x00, WIDTH / 8 * HEIGHT)?; diff --git a/src/interface.rs b/src/interface.rs index f3a9a3d7..ed12d9c6 100644 --- a/src/interface.rs +++ b/src/interface.rs @@ -89,6 +89,23 @@ where self.data(spi, data) } + /// Sends data with bytewise bitwise-NOT applied. Streams via a stack chunk + /// to avoid heap allocation. Required for displays whose DTM2 register + /// expects bit polarity inverted from the user-facing framebuffer (e.g. + /// 7.5" V2, where Waveshare's reference C demo + /// `EPD_7in5_V2.c::EPD_7IN5_V2_Display` applies the same `~` before 0x13). + pub(crate) fn data_inverted(&mut self, spi: &mut SPI, data: &[u8]) -> Result<(), SPI::Error> { + let _ = self.dc.set_high(); + let mut chunk = [0u8; 256]; + for source in data.chunks(chunk.len()) { + for (index, &byte) in source.iter().enumerate() { + chunk[index] = !byte; + } + self.write(spi, &chunk[..source.len()])?; + } + Ok(()) + } + /// Basic function for sending the same byte of data (one u8) multiple times over spi /// /// Enables direct interaction with the device with the help of [command()](ConnectionInterface::command())