From 96d505647d5b668fef7ea7cf364a209c1bcb7892 Mon Sep 17 00:00:00 2001 From: Mitch Souders Date: Sun, 15 Nov 2020 03:25:10 -0800 Subject: [PATCH 1/6] Add support for ep5in65f --- Cargo.toml | 2 +- src/color.rs | 74 ++++++++++++ src/epd1in54/mod.rs | 1 + src/epd1in54b/mod.rs | 1 + src/epd2in13_v2/mod.rs | 1 + src/epd2in9/mod.rs | 1 + src/epd2in9bc/mod.rs | 1 + src/epd4in2/mod.rs | 1 + src/epd5in65f/command.rs | 151 +++++++++++++++++++++++ src/epd5in65f/graphics.rs | 173 ++++++++++++++++++++++++++ src/epd5in65f/mod.rs | 247 ++++++++++++++++++++++++++++++++++++++ src/epd7in5/mod.rs | 1 + src/epd7in5_v2/mod.rs | 1 + src/graphics.rs | 80 +++++++++++- src/lib.rs | 2 + src/traits.rs | 7 +- 16 files changed, 736 insertions(+), 8 deletions(-) create mode 100644 src/epd5in65f/command.rs create mode 100644 src/epd5in65f/graphics.rs create mode 100644 src/epd5in65f/mod.rs diff --git a/Cargo.toml b/Cargo.toml index fb127c6..5d69b64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ license = "ISC" name = "epd-waveshare" readme = "README.md" repository = "https://github.com/Caemor/epd-waveshare.git" -version = "0.4.0" +version = "0.5.0" edition = "2018" [badges] diff --git a/src/color.rs b/src/color.rs index a1e5ec7..ee446da 100644 --- a/src/color.rs +++ b/src/color.rs @@ -2,6 +2,8 @@ #[cfg(feature = "graphics")] use embedded_graphics::pixelcolor::BinaryColor; +#[cfg(feature = "graphics")] +use embedded_graphics::pixelcolor::PixelColor; #[cfg(feature = "graphics")] pub use BinaryColor::Off as White; @@ -28,6 +30,78 @@ pub enum TriColor { Chromatic, } +/// For the 5in65 8 Color Display +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum OctColor { + /// Black Color + Black = 0x00, + /// White Color + White = 0x01, + /// Green Color + Green = 0x02, + /// Blue Color + Blue = 0x03, + /// Red Color + Red = 0x04, + /// Yellow Color + Yellow = 0x05, + /// Orange Color + Orange = 0x06, +} + +impl From<()> for OctColor { + fn from(_: ()) -> OctColor { + OctColor::White + } +} + +#[cfg(feature = "graphics")] +impl PixelColor for OctColor { + type Raw = (); +} + +impl OctColor { + /// Gets the Nibble representation of the Color as needed by the display + pub fn get_nibble(self) -> u8 { + self as u8 + } + /// Converts two colors into a single byte for the Display + pub fn colors_byte(a: OctColor, b: OctColor) -> u8 { + a.get_nibble() << 4 | b.get_nibble() + } + + ///Take the nibble (lower 4 bits) and convert to an OctColor if possible + pub fn from_nibble(nibble: u8) -> Result { + match nibble & 0xf { + 0x00 => Ok(OctColor::Black), + 0x01 => Ok(OctColor::White), + 0x02 => Ok(OctColor::Green), + 0x03 => Ok(OctColor::Blue), + 0x04 => Ok(OctColor::Red), + 0x05 => Ok(OctColor::Yellow), + 0x06 => Ok(OctColor::Orange), + _ => Err(()) + } + } + ///Split the nibbles of a single byte and convert both to an OctColor if possible + pub fn split_byte(byte: u8) -> Result<(OctColor, OctColor), ()> { + let low = OctColor::from_nibble(byte & 0xf)?; + let high = OctColor::from_nibble((byte >> 4) & 0xf)?; + Ok((high, low)) + } + /// Converts to limited range of RGB values. + pub fn rgb(self) -> (u8, u8, u8) { + match self { + OctColor::White => (0xff, 0xff, 0xff), + OctColor::Black => (0x00, 0x00, 0x00), + OctColor::Green => (0x00, 0xff, 0x00), + OctColor::Blue => (0x00, 0x00, 0xff), + OctColor::Red => (0xff, 0x00, 0x00), + OctColor::Yellow => (0xff, 0xff, 0x00), + OctColor::Orange => (0xff, 0x80, 0x00), + } + } +} //TODO: Rename get_bit_value to bit() and get_byte_value to byte() ? impl Color { diff --git a/src/epd1in54/mod.rs b/src/epd1in54/mod.rs index e4f833f..66420ea 100644 --- a/src/epd1in54/mod.rs +++ b/src/epd1in54/mod.rs @@ -151,6 +151,7 @@ where DC: OutputPin, RST: OutputPin, { + type DisplayColor = Color; fn width(&self) -> u32 { WIDTH } diff --git a/src/epd1in54b/mod.rs b/src/epd1in54b/mod.rs index 11fd985..c14050e 100644 --- a/src/epd1in54b/mod.rs +++ b/src/epd1in54b/mod.rs @@ -142,6 +142,7 @@ where DC: OutputPin, RST: OutputPin, { + type DisplayColor = Color; fn new>( spi: &mut SPI, cs: CS, diff --git a/src/epd2in13_v2/mod.rs b/src/epd2in13_v2/mod.rs index b636c9f..704d262 100644 --- a/src/epd2in13_v2/mod.rs +++ b/src/epd2in13_v2/mod.rs @@ -166,6 +166,7 @@ where DC: OutputPin, RST: OutputPin, { + type DisplayColor = Color; fn new>( spi: &mut SPI, cs: CS, diff --git a/src/epd2in9/mod.rs b/src/epd2in9/mod.rs index 46a9155..d2fbabb 100644 --- a/src/epd2in9/mod.rs +++ b/src/epd2in9/mod.rs @@ -147,6 +147,7 @@ where DC: OutputPin, RST: OutputPin, { + type DisplayColor = Color; fn width(&self) -> u32 { WIDTH } diff --git a/src/epd2in9bc/mod.rs b/src/epd2in9bc/mod.rs index 9bab93b..300352c 100644 --- a/src/epd2in9bc/mod.rs +++ b/src/epd2in9bc/mod.rs @@ -197,6 +197,7 @@ where DC: OutputPin, RST: OutputPin, { + type DisplayColor = Color; fn new>( spi: &mut SPI, cs: CS, diff --git a/src/epd4in2/mod.rs b/src/epd4in2/mod.rs index 8894132..a3a972b 100644 --- a/src/epd4in2/mod.rs +++ b/src/epd4in2/mod.rs @@ -148,6 +148,7 @@ where DC: OutputPin, RST: OutputPin, { + type DisplayColor = Color; fn new>( spi: &mut SPI, cs: CS, diff --git a/src/epd5in65f/command.rs b/src/epd5in65f/command.rs new file mode 100644 index 0000000..54c4ac2 --- /dev/null +++ b/src/epd5in65f/command.rs @@ -0,0 +1,151 @@ +//! SPI Commands for the Waveshare 7.5" E-Ink Display + +use crate::traits; + +/// EPD6in65f 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)] +#[allow(non_camel_case_types)] +#[derive(Copy, Clone)] +pub(crate) enum Command { + /// Set Resolution, LUT selection, BWR pixels, gate scan direction, source shift + /// direction, booster switch, soft reset. + PANEL_SETTING = 0x00, + + /// Selecting internal and external power + POWER_SETTING = 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. + POWER_OFF = 0x02, + + /// Setting Power OFF sequence + POWER_OFF_SEQUENCE_SETTING = 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". + POWER_ON = 0x04, + + /// Starting data transmission + BOOSTER_SOFT_START = 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. + DEEP_SLEEP = 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 + DATA_START_TRANSMISSION_1 = 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. + DATA_STOP = 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. + DISPLAY_REFRESH = 0x12, + + /// Image Process Command + IMAGE_PROCESS_COMMAND = 0x13, + + /// This command builds the VCOM Look-Up Table (LUTC). + LUT_FOR_VCOM = 0x20, + /// This command builds the Black Look-Up Table (LUTB). + LUT_BLACK = 0x21, + /// This command builds the White Look-Up Table (LUTW). + LUT_WHITE = 0x22, + /// This command builds the Gray1 Look-Up Table (LUTG1). + LUT_GRAY_1 = 0x23, + /// This command builds the Gray2 Look-Up Table (LUTG2). + LUT_GRAY_2 = 0x24, + /// This command builds the Red0 Look-Up Table (LUTR0). + LUT_RED_0 = 0x25, + /// This command builds the Red1 Look-Up Table (LUTR1). + LUT_RED_1 = 0x26, + /// This command builds the Red2 Look-Up Table (LUTR2). + LUT_RED_2 = 0x27, + /// This command builds the Red3 Look-Up Table (LUTR3). + LUT_RED_3 = 0x28, + /// This command builds the XON Look-Up Table (LUTXON). + LUT_XON = 0x29, + + /// The command controls the PLL clock frequency. + PLL_CONTROL = 0x30, + + /// This command reads the temperature sensed by the temperature sensor. + TEMPERATURE_SENSOR_COMMAND = 0x40, + /// This command selects the Internal or External temperature sensor. + TEMPERATURE_CALIBRATION = 0x41, + /// This command could write data to the external temperature sensor. + TEMPERATURE_SENSOR_WRITE = 0x42, + /// This command could read data from the external temperature sensor. + TEMPERATURE_SENSOR_READ = 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). + VCOM_AND_DATA_INTERVAL_SETTING = 0x50, + /// This command indicates the input power condition. Host can read this flag to learn + /// the battery condition. + LOW_POWER_DETECTION = 0x51, + + /// This command defines non-overlap period of Gate and Source. + TCON_SETTING = 0x60, + /// This command defines alternative resolution and this setting is of higher priority + /// than the RES\[1:0\] in R00H (PSR). + TCON_RESOLUTION = 0x61, + /// This command defines MCU host direct access external memory mode. + //SPI_FLASH_CONTROL = 0x65, + + /// The LUT_REV / Chip Revision is read from OTP address = 25001 and 25000. + //REVISION = 0x70, + /// This command reads the IC status. + GET_STATUS = 0x71, + + /// This command implements related VCOM sensing setting. + //AUTO_MEASUREMENT_VCOM = 0x80, + /// This command gets the VCOM value. + READ_VCOM_VALUE = 0x81, + /// This command sets `VCOM_DC` value. + VCM_DC_SETTING = 0x82, + // /// This is in all the Waveshare controllers for EPD6in65f, but it's not documented + // /// anywhere in the datasheet `¯\_(ツ)_/¯` + FLASH_MODE = 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::PANEL_SETTING.address(), 0x00); + assert_eq!(Command::DISPLAY_REFRESH.address(), 0x12); + } +} diff --git a/src/epd5in65f/graphics.rs b/src/epd5in65f/graphics.rs new file mode 100644 index 0000000..9ec124c --- /dev/null +++ b/src/epd5in65f/graphics.rs @@ -0,0 +1,173 @@ +use crate::epd5in65f::{DEFAULT_BACKGROUND_COLOR, HEIGHT, WIDTH}; +use crate::graphics::{OctDisplay, DisplayRotation}; +use embedded_graphics::prelude::*; +use crate::color::OctColor; + +/// Full size buffer for use with the 5in65f EPD +/// +/// Can also be manually constructed: +/// `buffer: [DEFAULT_BACKGROUND_COLOR.get_byte_value(); WIDTH / 2 * HEIGHT]` +pub struct Display5in65f { + buffer: [u8; WIDTH as usize * HEIGHT as usize / 2], + rotation: DisplayRotation, +} + +impl Default for Display5in65f { + fn default() -> Self { + Display5in65f { + buffer: [OctColor::colors_byte(DEFAULT_BACKGROUND_COLOR, DEFAULT_BACKGROUND_COLOR); + WIDTH as usize * HEIGHT as usize / 2], + rotation: DisplayRotation::default(), + } + } +} + +impl DrawTarget for Display5in65f { + type Error = core::convert::Infallible; + + fn draw_pixel(&mut self, pixel: Pixel) -> Result<(), Self::Error> { + self.draw_helper(WIDTH, HEIGHT, pixel) + } + + fn size(&self) -> Size { + Size::new(WIDTH, HEIGHT) + } +} + +impl OctDisplay for Display5in65f { + fn buffer(&self) -> &[u8] { + &self.buffer + } + + fn get_mut_buffer(&mut self) -> &mut [u8] { + &mut self.buffer + } + + fn set_rotation(&mut self, rotation: DisplayRotation) { + self.rotation = rotation; + } + + fn rotation(&self) -> DisplayRotation { + self.rotation + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::epd5in65f; + use crate::graphics::{OctDisplay, DisplayRotation}; + use embedded_graphics::{primitives::Line, style::PrimitiveStyle}; + + // test buffer length + #[test] + fn graphics_size() { + let display = Display5in65f::default(); + assert_eq!(display.buffer().len(), 448*600 / 2); + } + + // test default background color on all bytes + #[test] + fn graphics_default() { + let display = Display5in65f::default(); + for &byte in display.buffer() { + assert_eq!(byte, OctColor::colors_byte( + epd5in65f::DEFAULT_BACKGROUND_COLOR, + epd5in65f::DEFAULT_BACKGROUND_COLOR, + )); + } + } + + #[test] + fn graphics_rotation_0() { + let mut display = Display5in65f::default(); + + let _ = Line::new(Point::new(0, 0), Point::new(1, 0)) + .into_styled(PrimitiveStyle::with_stroke(OctColor::Black, 1)) + .draw(&mut display); + + let buffer = display.buffer(); + + for &byte in buffer.iter().take(1) { + assert_eq!(OctColor::split_byte(byte), Ok((OctColor::Black, OctColor::Black))); + } + + for &byte in buffer.iter().skip(1) { + assert_eq!( + OctColor::split_byte(byte), + Ok((epd5in65f::DEFAULT_BACKGROUND_COLOR, epd5in65f::DEFAULT_BACKGROUND_COLOR)) + ); + } + } + + #[test] + fn graphics_rotation_90() { + let mut display = Display5in65f::default(); + display.set_rotation(DisplayRotation::Rotate90); + + let _ = Line::new(Point::new(0, WIDTH as i32 - 2), Point::new(0, WIDTH as i32- 1)) + .into_styled(PrimitiveStyle::with_stroke(OctColor::Black, 1)) + .draw(&mut display); + + let buffer = display.buffer(); + + for &byte in buffer.iter().take(1) { + assert_eq!(OctColor::split_byte(byte), Ok((OctColor::Black, OctColor::Black))); + } + + for &byte in buffer.iter().skip(1) { + assert_eq!( + OctColor::split_byte(byte), + Ok((epd5in65f::DEFAULT_BACKGROUND_COLOR, epd5in65f::DEFAULT_BACKGROUND_COLOR)) + ); + } + } + + #[test] + fn graphics_rotation_180() { + let mut display = Display5in65f::default(); + display.set_rotation(DisplayRotation::Rotate180); + + let _ = Line::new(Point::new(WIDTH as i32 - 2, HEIGHT as i32 - 1), + Point::new(WIDTH as i32 - 1, HEIGHT as i32 - 1)) + .into_styled(PrimitiveStyle::with_stroke(OctColor::Black, 1)) + .draw(&mut display); + + let buffer = display.buffer(); + + for &byte in buffer.iter().take(1) { + assert_eq!(OctColor::split_byte(byte), Ok((OctColor::Black, OctColor::Black))); + } + + for &byte in buffer.iter().skip(1) { + assert_eq!( + OctColor::split_byte(byte), + Ok((epd5in65f::DEFAULT_BACKGROUND_COLOR, epd5in65f::DEFAULT_BACKGROUND_COLOR)) + ); + } + } + + #[test] + fn graphics_rotation_270() { + let mut display = Display5in65f::default(); + display.set_rotation(DisplayRotation::Rotate270); + + let _ = Line::new(Point::new(HEIGHT as i32 -1, 0), + Point::new(HEIGHT as i32 -1, 1)) + .into_styled(PrimitiveStyle::with_stroke(OctColor::Black, 1)) + .draw(&mut display); + + let buffer = display.buffer(); + + for &byte in buffer.iter().take(1) { + assert_eq!(OctColor::split_byte(byte), Ok((OctColor::Black, OctColor::Black))); + } + + for &byte in buffer.iter().skip(1) { + assert_eq!( + OctColor::split_byte(byte), + Ok((epd5in65f::DEFAULT_BACKGROUND_COLOR, epd5in65f::DEFAULT_BACKGROUND_COLOR)) + ); + } + } +} diff --git a/src/epd5in65f/mod.rs b/src/epd5in65f/mod.rs new file mode 100644 index 0000000..59fa103 --- /dev/null +++ b/src/epd5in65f/mod.rs @@ -0,0 +1,247 @@ +//! A simple Driver for the Waveshare 6.65 inch (F) E-Ink Display via SPI +//! +//! # References +//! +//! - [Datasheet](https://www.waveshare.com/wiki/5.65inch_e-Paper_Module_(F)) +//! - [Waveshare C driver](https://github.com/waveshare/e-Paper/blob/master/RaspberryPi%26JetsonNano/c/lib/e-Paper/EPD_5in65f.c) +//! - [Waveshare Python driver](https://github.com/waveshare/e-Paper/blob/master/RaspberryPi%26JetsonNano/python/lib/waveshare_epd/epd5in65f.py) + + +use embedded_hal::{ + blocking::{delay::*, spi::Write}, + digital::v2::{InputPin, OutputPin}, +}; + +use crate::color::OctColor; +use crate::interface::DisplayInterface; +use crate::traits::{InternalWiAdditions, RefreshLUT, WaveshareDisplay}; + +pub(crate) mod command; +use self::command::Command; + +#[cfg(feature = "graphics")] +mod graphics; +#[cfg(feature = "graphics")] +pub use self::graphics::Display5in65f; + +/// Width of the display +pub const WIDTH: u32 = 600; +/// Height of the display +pub const HEIGHT: u32 = 448; +/// Default Background Color +pub const DEFAULT_BACKGROUND_COLOR: OctColor = OctColor::White; +const IS_BUSY_LOW: bool = false; + +/// EPD5in65f driver +/// +pub struct EPD5in65f { + /// Connection Interface + interface: DisplayInterface, + /// Background Color + color: OctColor, +} + +impl InternalWiAdditions + for EPD5in65f +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, +{ + fn init>( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + ) -> Result<(), SPI::Error> { + // Reset the device + self.interface.reset(delay, 2); + + self.cmd_with_data(spi, Command::PANEL_SETTING, &[0xEF, 0x08])?; + self.cmd_with_data(spi, Command::POWER_SETTING, &[0x37, 0x00, 0x23, 0x23])?; + self.cmd_with_data(spi, Command::POWER_OFF_SEQUENCE_SETTING, &[0x00])?; + self.cmd_with_data(spi, Command::BOOSTER_SOFT_START, &[0xC7, 0xC7, 0x1D])?; + self.cmd_with_data(spi, Command::PLL_CONTROL, &[0x3C])?; + self.cmd_with_data(spi, Command::TEMPERATURE_SENSOR_COMMAND, &[0x00])?; + self.cmd_with_data(spi, Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x37])?; + self.cmd_with_data(spi, Command::TCON_SETTING, &[0x22])?; + self.send_resolution(spi)?; + + self.cmd_with_data(spi, Command::FLASH_MODE, &[0xAA])?; + + delay.delay_ms(100); + + self.cmd_with_data(spi, Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x37])?; + Ok(()) + } +} + +impl WaveshareDisplay + for EPD5in65f +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, +{ + type DisplayColor = OctColor; + fn new>( + spi: &mut SPI, + cs: CS, + busy: BUSY, + dc: DC, + rst: RST, + delay: &mut DELAY, + ) -> Result { + let interface = DisplayInterface::new(cs, busy, dc, rst); + let color = DEFAULT_BACKGROUND_COLOR; + + let mut epd = EPD5in65f { 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) -> Result<(), SPI::Error> { + self.cmd_with_data(spi, Command::DEEP_SLEEP, &[0xA5])?; + Ok(()) + } + + fn update_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> { + self.cmd_with_data(spi, Command::DATA_START_TRANSMISSION_1, buffer)?; + Ok(()) + } + + fn update_partial_frame( + &mut self, + _spi: &mut SPI, + _buffer: &[u8], + _x: u32, + _y: u32, + _width: u32, + _height: u32, + ) -> Result<(), SPI::Error> { + unimplemented!(); + } + + fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + self.command(spi, Command::POWER_ON)?; + self.wait_busy_high(); + self.command(spi, Command::DISPLAY_REFRESH)?; + self.wait_busy_high(); + self.command(spi, Command::POWER_OFF)?; + self.wait_busy_low(); + Ok(()) + } + + fn update_and_display_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> { + self.update_frame(spi, buffer)?; + self.display_frame(spi)?; + Ok(()) + } + + fn clear_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + let bg = 0x77; /*clear frame */ //OctColor::colors_byte(self.color, self.color); + + self.command(spi, Command::DATA_START_TRANSMISSION_1)?; + self.interface.data_x_times(spi, bg, WIDTH * HEIGHT / 2)?; + + self.display_frame(spi)?; + 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, + _refresh_rate: Option, + ) -> Result<(), SPI::Error> { + unimplemented!(); + } + + fn is_busy(&self) -> bool { + self.interface.is_busy(IS_BUSY_LOW) + } +} + +impl EPD5in65f +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, +{ + 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_high(&mut self) { + self.interface.wait_until_idle(true) + } + fn wait_busy_low(&mut self) { + self.interface.wait_until_idle(false) + } + + fn send_resolution(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + let w = self.width(); + let h = self.height(); + + self.command(spi, Command::TCON_RESOLUTION)?; + 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]) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn epd_size() { + assert_eq!(WIDTH, 600); + assert_eq!(HEIGHT, 448); + assert_eq!(DEFAULT_BACKGROUND_COLOR, OctColor::White); + } +} diff --git a/src/epd7in5/mod.rs b/src/epd7in5/mod.rs index cdc142e..883deb3 100644 --- a/src/epd7in5/mod.rs +++ b/src/epd7in5/mod.rs @@ -108,6 +108,7 @@ where DC: OutputPin, RST: OutputPin, { + type DisplayColor = Color; fn new>( spi: &mut SPI, cs: CS, diff --git a/src/epd7in5_v2/mod.rs b/src/epd7in5_v2/mod.rs index 3c0fcc4..67f9dab 100644 --- a/src/epd7in5_v2/mod.rs +++ b/src/epd7in5_v2/mod.rs @@ -90,6 +90,7 @@ where DC: OutputPin, RST: OutputPin, { + type DisplayColor = Color; fn new>( spi: &mut SPI, cs: CS, diff --git a/src/graphics.rs b/src/graphics.rs index e378a64..524e550 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -1,7 +1,7 @@ //! Graphics Support for EPDs use crate::buffer_len; -use crate::color::Color; +use crate::color::{OctColor, Color}; use embedded_graphics::{pixelcolor::BinaryColor, prelude::*}; /// Displayrotation @@ -85,6 +85,64 @@ pub trait Display: DrawTarget { } } +/// Necessary traits for all displays to implement for drawing +/// +/// Adds support for: +/// - Drawing (With the help of DrawTarget/Embedded Graphics) +/// - Rotations +/// - Clearing +pub trait OctDisplay: DrawTarget { + /// Clears the buffer of the display with the chosen background color + fn clear_buffer(&mut self, background_color: Color) { + for elem in self.get_mut_buffer().iter_mut() { + *elem = background_color.get_byte_value(); + } + } + + /// Returns the buffer + fn buffer(&self) -> &[u8]; + + /// Returns a mutable buffer + fn get_mut_buffer(&mut self) -> &mut [u8]; + + /// Sets the rotation of the display + fn set_rotation(&mut self, rotation: DisplayRotation); + + /// Get the current rotation of the display + fn rotation(&self) -> DisplayRotation; + + /// Helperfunction for the Embedded Graphics draw trait + /// + /// Becomes uneccesary when const_generics become stablised + fn draw_helper( + &mut self, + width: u32, + height: u32, + pixel: Pixel, + ) -> Result<(), Self::Error> { + let rotation = self.rotation(); + let buffer = self.get_mut_buffer(); + + let Pixel(point, color) = pixel; + if outside_display(point, width, height, rotation) { + return Ok(()); + } + + // Give us index inside the buffer and the bit-position in that u8 which needs to be changed + let (index, upper) = find_oct_position(point.x as u32, point.y as u32, width, height, rotation); + let index = index as usize; + + // "Draw" the Pixel on that bit + let (mask, color_nibble) = if upper { + (0x0f, color.get_nibble() << 4) + } else { + (0xf0, color.get_nibble()) + }; + buffer[index] = (buffer[index] & mask) | color_nibble; + Ok(()) + } +} + /// A variable Display without a predefined buffer /// /// The buffer can be created as following: @@ -186,9 +244,7 @@ fn outside_display(p: Point, width: u32, height: u32, rotation: DisplayRotation) false } -#[rustfmt::skip] -//returns index position in the u8-slice and the bit-position inside that u8 -fn find_position(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRotation) -> (u32, u8) { +fn find_rotation(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRotation) -> (u32, u32) { let nx; let ny; match rotation { @@ -209,7 +265,23 @@ fn find_position(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRotat ny = height - 1 - x; }, } + (nx, ny) +} +#[rustfmt::skip] +//returns index position in the u8-slice and the bit-position inside that u8 +fn find_oct_position(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRotation) -> (u32, bool) { + let (nx, ny) = find_rotation(x, y, width, height, rotation); + ( + nx / 2 + (width / 2) * ny, + (nx & 0x1) == 0, + ) +} + +#[rustfmt::skip] +//returns index position in the u8-slice and the bit-position inside that u8 +fn find_position(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRotation) -> (u32, u8) { + let (nx, ny) = find_rotation(x, y, width, height, rotation); ( nx / 8 + ((width + 7) / 8) * ny, 0x80 >> (nx % 8), diff --git a/src/lib.rs b/src/lib.rs index 6c4b94b..4685308 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,6 +80,8 @@ pub mod epd2in9bc; pub mod epd4in2; pub mod epd7in5; pub mod epd7in5_v2; +pub mod epd5in65f; + pub(crate) mod type_a; /// Includes everything important besides the chosen Display diff --git a/src/traits.rs b/src/traits.rs index 360e63c..9b5aa01 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,4 +1,3 @@ -use crate::color::Color; use core::marker::Sized; use embedded_hal::{ blocking::{delay::*, spi::Write}, @@ -136,6 +135,8 @@ where DC: OutputPin, RST: OutputPin, { + /// The Color Type used by the Display + type DisplayColor; /// Creates a new driver from a SPI peripheral, CS Pin, Busy InputPin, DC /// /// This already initialises the device. @@ -165,10 +166,10 @@ where ) -> Result<(), SPI::Error>; /// Sets the backgroundcolor for various commands like [clear_frame](WaveshareDisplay::clear_frame) - fn set_background_color(&mut self, color: Color); + fn set_background_color(&mut self, color: Self::DisplayColor); /// Get current background color - fn background_color(&self) -> &Color; + fn background_color(&self) -> &Self::DisplayColor; /// Get the width of the display fn width(&self) -> u32; From c7d32ca86ea4d2557929146108184c84d4872ba3 Mon Sep 17 00:00:00 2001 From: Mitch Souders Date: Sun, 15 Nov 2020 05:32:17 -0800 Subject: [PATCH 2/6] Added some busy checks and seem to be avoiding missing pictures --- src/epd5in65f/mod.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/epd5in65f/mod.rs b/src/epd5in65f/mod.rs index 59fa103..1ccb4ff 100644 --- a/src/epd5in65f/mod.rs +++ b/src/epd5in65f/mod.rs @@ -30,7 +30,7 @@ pub const WIDTH: u32 = 600; pub const HEIGHT: u32 = 448; /// Default Background Color pub const DEFAULT_BACKGROUND_COLOR: OctColor = OctColor::White; -const IS_BUSY_LOW: bool = false; +const IS_BUSY_LOW: bool = true; /// EPD5in65f driver /// @@ -119,6 +119,8 @@ where } fn update_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> { + self.wait_busy_high(); + self.send_resolution(spi)?; self.cmd_with_data(spi, Command::DATA_START_TRANSMISSION_1, buffer)?; Ok(()) } @@ -136,6 +138,7 @@ where } fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + self.wait_busy_high(); self.command(spi, Command::POWER_ON)?; self.wait_busy_high(); self.command(spi, Command::DISPLAY_REFRESH)?; @@ -153,10 +156,10 @@ where fn clear_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { let bg = 0x77; /*clear frame */ //OctColor::colors_byte(self.color, self.color); - + self.wait_busy_high(); + self.send_resolution(spi)?; self.command(spi, Command::DATA_START_TRANSMISSION_1)?; - self.interface.data_x_times(spi, bg, WIDTH * HEIGHT / 2)?; - + self.interface.data_x_times(spi, bg, WIDTH * HEIGHT / 2)?; self.display_frame(spi)?; Ok(()) } @@ -221,7 +224,6 @@ where fn wait_busy_low(&mut self) { self.interface.wait_until_idle(false) } - fn send_resolution(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { let w = self.width(); let h = self.height(); From 231cc63009b7383b24e26e9f478cfdeef7d63a7e Mon Sep 17 00:00:00 2001 From: Mitch Souders Date: Sun, 15 Nov 2020 17:57:40 -0800 Subject: [PATCH 3/6] Minor cleanup, added HiZ as color --- src/color.rs | 6 +++++- src/epd5in65f/mod.rs | 2 +- src/lib.rs | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/color.rs b/src/color.rs index ee446da..c8bd818 100644 --- a/src/color.rs +++ b/src/color.rs @@ -30,7 +30,7 @@ pub enum TriColor { Chromatic, } -/// For the 5in65 8 Color Display +/// For the 5in65 7 Color Display #[derive(Clone, Copy, PartialEq, Debug)] pub enum OctColor { /// Black Color @@ -47,6 +47,8 @@ pub enum OctColor { Yellow = 0x05, /// Orange Color Orange = 0x06, + /// HiZ / Clean Color + HiZ = 0x07, } impl From<()> for OctColor { @@ -80,6 +82,7 @@ impl OctColor { 0x04 => Ok(OctColor::Red), 0x05 => Ok(OctColor::Yellow), 0x06 => Ok(OctColor::Orange), + 0x07 => Ok(OctColor::HiZ), _ => Err(()) } } @@ -99,6 +102,7 @@ impl OctColor { OctColor::Red => (0xff, 0x00, 0x00), OctColor::Yellow => (0xff, 0xff, 0x00), OctColor::Orange => (0xff, 0x80, 0x00), + OctColor::HiZ => (0x80, 0x80, 0x80), /* looks greyish */ } } } diff --git a/src/epd5in65f/mod.rs b/src/epd5in65f/mod.rs index 1ccb4ff..fb80fb7 100644 --- a/src/epd5in65f/mod.rs +++ b/src/epd5in65f/mod.rs @@ -155,7 +155,7 @@ where } fn clear_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { - let bg = 0x77; /*clear frame */ //OctColor::colors_byte(self.color, self.color); + let bg = OctColor::colors_byte(self.color, self.color); self.wait_busy_high(); self.send_resolution(spi)?; self.command(spi, Command::DATA_START_TRANSMISSION_1)?; diff --git a/src/lib.rs b/src/lib.rs index 4685308..94f275e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,13 +86,13 @@ pub(crate) mod type_a; /// Includes everything important besides the chosen Display pub mod prelude { - pub use crate::color::{Color, TriColor}; + pub use crate::color::{Color, TriColor, OctColor}; pub use crate::traits::{RefreshLUT, WaveshareDisplay, WaveshareThreeColorDisplay}; pub use crate::SPI_MODE; #[cfg(feature = "graphics")] - pub use crate::graphics::{Display, DisplayRotation}; + pub use crate::graphics::{OctDisplay, Display, DisplayRotation}; } /// Computes the needed buffer length. Takes care of rounding up in case width From 147eaf655d3a1ba2b819bb7816134e9303dfe12d Mon Sep 17 00:00:00 2001 From: Mitch Souders Date: Sun, 15 Nov 2020 18:01:45 -0800 Subject: [PATCH 4/6] Updated README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0ce9a23..93e177d 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,8 @@ epd.update_and_display_frame(&mut spi, &display.buffer())?; | [2.9 Inch B/W (A)](https://www.waveshare.com/product/2.9inch-e-paper-module.htm) | Black, White | ✕ | ✔ | ✔ | ✔ | | [1.54 Inch B/W/R (B)](https://www.waveshare.com/product/modules/oleds-lcds/e-paper/1.54inch-e-paper-module-b.htm) | Black, White, Red | ✕ | ✕ | ✔ | ✔ | | [2.9 Inch B/W/R (B/C)](https://www.waveshare.com/product/displays/e-paper/epaper-2/2.9inch-e-paper-module-b.htm) | Black, White, Red | ✕ | ✕ | ✔ | ✔ | +| [5.65 Inch 7 Color (F)](https://www.waveshare.com/5.65inch-e-paper-module-f.htm) | Black, White, Red, Green, Blue, Yellow, Orange | ✕ | ✕ | ✔ | ✔ | + ### [1]: 7.5 Inch B/W V2 (A) From e225464b62809ade7edd301d6daa30a557b5ae37 Mon Sep 17 00:00:00 2001 From: Mitch Souders Date: Sun, 15 Nov 2020 18:11:58 -0800 Subject: [PATCH 5/6] Cargo fmt --- src/color.rs | 4 +- src/epd5in65f/command.rs | 2 +- src/epd5in65f/graphics.rs | 92 +++++++++++++++++++++++++++------------ src/epd5in65f/mod.rs | 7 ++- src/graphics.rs | 13 +++--- src/lib.rs | 6 +-- 6 files changed, 79 insertions(+), 45 deletions(-) diff --git a/src/color.rs b/src/color.rs index c8bd818..b9388c7 100644 --- a/src/color.rs +++ b/src/color.rs @@ -2,7 +2,7 @@ #[cfg(feature = "graphics")] use embedded_graphics::pixelcolor::BinaryColor; -#[cfg(feature = "graphics")] +#[cfg(feature = "graphics")] use embedded_graphics::pixelcolor::PixelColor; #[cfg(feature = "graphics")] @@ -83,7 +83,7 @@ impl OctColor { 0x05 => Ok(OctColor::Yellow), 0x06 => Ok(OctColor::Orange), 0x07 => Ok(OctColor::HiZ), - _ => Err(()) + _ => Err(()), } } ///Split the nibbles of a single byte and convert both to an OctColor if possible diff --git a/src/epd5in65f/command.rs b/src/epd5in65f/command.rs index 54c4ac2..5035bb8 100644 --- a/src/epd5in65f/command.rs +++ b/src/epd5in65f/command.rs @@ -66,7 +66,7 @@ pub(crate) enum Command { /// Image Process Command IMAGE_PROCESS_COMMAND = 0x13, - + /// This command builds the VCOM Look-Up Table (LUTC). LUT_FOR_VCOM = 0x20, /// This command builds the Black Look-Up Table (LUTB). diff --git a/src/epd5in65f/graphics.rs b/src/epd5in65f/graphics.rs index 9ec124c..2a52b15 100644 --- a/src/epd5in65f/graphics.rs +++ b/src/epd5in65f/graphics.rs @@ -1,7 +1,7 @@ +use crate::color::OctColor; use crate::epd5in65f::{DEFAULT_BACKGROUND_COLOR, HEIGHT, WIDTH}; -use crate::graphics::{OctDisplay, DisplayRotation}; +use crate::graphics::{DisplayRotation, OctDisplay}; use embedded_graphics::prelude::*; -use crate::color::OctColor; /// Full size buffer for use with the 5in65f EPD /// @@ -56,14 +56,14 @@ impl OctDisplay for Display5in65f { mod tests { use super::*; use crate::epd5in65f; - use crate::graphics::{OctDisplay, DisplayRotation}; + use crate::graphics::{DisplayRotation, OctDisplay}; use embedded_graphics::{primitives::Line, style::PrimitiveStyle}; // test buffer length #[test] fn graphics_size() { let display = Display5in65f::default(); - assert_eq!(display.buffer().len(), 448*600 / 2); + assert_eq!(display.buffer().len(), 448 * 600 / 2); } // test default background color on all bytes @@ -71,10 +71,13 @@ mod tests { fn graphics_default() { let display = Display5in65f::default(); for &byte in display.buffer() { - assert_eq!(byte, OctColor::colors_byte( - epd5in65f::DEFAULT_BACKGROUND_COLOR, - epd5in65f::DEFAULT_BACKGROUND_COLOR, - )); + assert_eq!( + byte, + OctColor::colors_byte( + epd5in65f::DEFAULT_BACKGROUND_COLOR, + epd5in65f::DEFAULT_BACKGROUND_COLOR, + ) + ); } } @@ -87,15 +90,21 @@ mod tests { .draw(&mut display); let buffer = display.buffer(); - + for &byte in buffer.iter().take(1) { - assert_eq!(OctColor::split_byte(byte), Ok((OctColor::Black, OctColor::Black))); + assert_eq!( + OctColor::split_byte(byte), + Ok((OctColor::Black, OctColor::Black)) + ); } for &byte in buffer.iter().skip(1) { assert_eq!( OctColor::split_byte(byte), - Ok((epd5in65f::DEFAULT_BACKGROUND_COLOR, epd5in65f::DEFAULT_BACKGROUND_COLOR)) + Ok(( + epd5in65f::DEFAULT_BACKGROUND_COLOR, + epd5in65f::DEFAULT_BACKGROUND_COLOR + )) ); } } @@ -105,20 +114,29 @@ mod tests { let mut display = Display5in65f::default(); display.set_rotation(DisplayRotation::Rotate90); - let _ = Line::new(Point::new(0, WIDTH as i32 - 2), Point::new(0, WIDTH as i32- 1)) - .into_styled(PrimitiveStyle::with_stroke(OctColor::Black, 1)) - .draw(&mut display); + let _ = Line::new( + Point::new(0, WIDTH as i32 - 2), + Point::new(0, WIDTH as i32 - 1), + ) + .into_styled(PrimitiveStyle::with_stroke(OctColor::Black, 1)) + .draw(&mut display); let buffer = display.buffer(); for &byte in buffer.iter().take(1) { - assert_eq!(OctColor::split_byte(byte), Ok((OctColor::Black, OctColor::Black))); + assert_eq!( + OctColor::split_byte(byte), + Ok((OctColor::Black, OctColor::Black)) + ); } for &byte in buffer.iter().skip(1) { assert_eq!( OctColor::split_byte(byte), - Ok((epd5in65f::DEFAULT_BACKGROUND_COLOR, epd5in65f::DEFAULT_BACKGROUND_COLOR)) + Ok(( + epd5in65f::DEFAULT_BACKGROUND_COLOR, + epd5in65f::DEFAULT_BACKGROUND_COLOR + )) ); } } @@ -128,21 +146,29 @@ mod tests { let mut display = Display5in65f::default(); display.set_rotation(DisplayRotation::Rotate180); - let _ = Line::new(Point::new(WIDTH as i32 - 2, HEIGHT as i32 - 1), - Point::new(WIDTH as i32 - 1, HEIGHT as i32 - 1)) - .into_styled(PrimitiveStyle::with_stroke(OctColor::Black, 1)) - .draw(&mut display); + let _ = Line::new( + Point::new(WIDTH as i32 - 2, HEIGHT as i32 - 1), + Point::new(WIDTH as i32 - 1, HEIGHT as i32 - 1), + ) + .into_styled(PrimitiveStyle::with_stroke(OctColor::Black, 1)) + .draw(&mut display); let buffer = display.buffer(); - + for &byte in buffer.iter().take(1) { - assert_eq!(OctColor::split_byte(byte), Ok((OctColor::Black, OctColor::Black))); + assert_eq!( + OctColor::split_byte(byte), + Ok((OctColor::Black, OctColor::Black)) + ); } for &byte in buffer.iter().skip(1) { assert_eq!( OctColor::split_byte(byte), - Ok((epd5in65f::DEFAULT_BACKGROUND_COLOR, epd5in65f::DEFAULT_BACKGROUND_COLOR)) + Ok(( + epd5in65f::DEFAULT_BACKGROUND_COLOR, + epd5in65f::DEFAULT_BACKGROUND_COLOR + )) ); } } @@ -152,21 +178,29 @@ mod tests { let mut display = Display5in65f::default(); display.set_rotation(DisplayRotation::Rotate270); - let _ = Line::new(Point::new(HEIGHT as i32 -1, 0), - Point::new(HEIGHT as i32 -1, 1)) - .into_styled(PrimitiveStyle::with_stroke(OctColor::Black, 1)) - .draw(&mut display); + let _ = Line::new( + Point::new(HEIGHT as i32 - 1, 0), + Point::new(HEIGHT as i32 - 1, 1), + ) + .into_styled(PrimitiveStyle::with_stroke(OctColor::Black, 1)) + .draw(&mut display); let buffer = display.buffer(); for &byte in buffer.iter().take(1) { - assert_eq!(OctColor::split_byte(byte), Ok((OctColor::Black, OctColor::Black))); + assert_eq!( + OctColor::split_byte(byte), + Ok((OctColor::Black, OctColor::Black)) + ); } for &byte in buffer.iter().skip(1) { assert_eq!( OctColor::split_byte(byte), - Ok((epd5in65f::DEFAULT_BACKGROUND_COLOR, epd5in65f::DEFAULT_BACKGROUND_COLOR)) + Ok(( + epd5in65f::DEFAULT_BACKGROUND_COLOR, + epd5in65f::DEFAULT_BACKGROUND_COLOR + )) ); } } diff --git a/src/epd5in65f/mod.rs b/src/epd5in65f/mod.rs index fb80fb7..d10dddc 100644 --- a/src/epd5in65f/mod.rs +++ b/src/epd5in65f/mod.rs @@ -6,7 +6,6 @@ //! - [Waveshare C driver](https://github.com/waveshare/e-Paper/blob/master/RaspberryPi%26JetsonNano/c/lib/e-Paper/EPD_5in65f.c) //! - [Waveshare Python driver](https://github.com/waveshare/e-Paper/blob/master/RaspberryPi%26JetsonNano/python/lib/waveshare_epd/epd5in65f.py) - use embedded_hal::{ blocking::{delay::*, spi::Write}, digital::v2::{InputPin, OutputPin}, @@ -69,9 +68,9 @@ where self.send_resolution(spi)?; self.cmd_with_data(spi, Command::FLASH_MODE, &[0xAA])?; - + delay.delay_ms(100); - + self.cmd_with_data(spi, Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x37])?; Ok(()) } @@ -159,7 +158,7 @@ where self.wait_busy_high(); self.send_resolution(spi)?; self.command(spi, Command::DATA_START_TRANSMISSION_1)?; - self.interface.data_x_times(spi, bg, WIDTH * HEIGHT / 2)?; + self.interface.data_x_times(spi, bg, WIDTH * HEIGHT / 2)?; self.display_frame(spi)?; Ok(()) } diff --git a/src/graphics.rs b/src/graphics.rs index 524e550..f9aac40 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -1,7 +1,7 @@ //! Graphics Support for EPDs use crate::buffer_len; -use crate::color::{OctColor, Color}; +use crate::color::{Color, OctColor}; use embedded_graphics::{pixelcolor::BinaryColor, prelude::*}; /// Displayrotation @@ -129,7 +129,8 @@ pub trait OctDisplay: DrawTarget { } // Give us index inside the buffer and the bit-position in that u8 which needs to be changed - let (index, upper) = find_oct_position(point.x as u32, point.y as u32, width, height, rotation); + let (index, upper) = + find_oct_position(point.x as u32, point.y as u32, width, height, rotation); let index = index as usize; // "Draw" the Pixel on that bit @@ -251,19 +252,19 @@ fn find_rotation(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRotat DisplayRotation::Rotate0 => { nx = x; ny = y; - }, + } DisplayRotation::Rotate90 => { nx = width - 1 - y; ny = x; - } , + } DisplayRotation::Rotate180 => { nx = width - 1 - x; ny = height - 1 - y; - }, + } DisplayRotation::Rotate270 => { nx = y; ny = height - 1 - x; - }, + } } (nx, ny) } diff --git a/src/lib.rs b/src/lib.rs index 94f275e..175dc01 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -78,21 +78,21 @@ pub mod epd2in13_v2; pub mod epd2in9; pub mod epd2in9bc; pub mod epd4in2; +pub mod epd5in65f; pub mod epd7in5; pub mod epd7in5_v2; -pub mod epd5in65f; pub(crate) mod type_a; /// Includes everything important besides the chosen Display pub mod prelude { - pub use crate::color::{Color, TriColor, OctColor}; + pub use crate::color::{Color, OctColor, TriColor}; pub use crate::traits::{RefreshLUT, WaveshareDisplay, WaveshareThreeColorDisplay}; pub use crate::SPI_MODE; #[cfg(feature = "graphics")] - pub use crate::graphics::{OctDisplay, Display, DisplayRotation}; + pub use crate::graphics::{Display, DisplayRotation, OctDisplay}; } /// Computes the needed buffer length. Takes care of rounding up in case width From e49e144a348c055c2b335dc275b821d0d8637a54 Mon Sep 17 00:00:00 2001 From: Mitch Souders Date: Thu, 19 Nov 2020 19:22:41 -0800 Subject: [PATCH 6/6] Minor fixups --- src/color.rs | 10 +++++++++ src/epd5in65f/graphics.rs | 44 +++++++++++++++++++++++++++++++++++++++ src/graphics.rs | 6 ++++-- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/color.rs b/src/color.rs index b9388c7..6767673 100644 --- a/src/color.rs +++ b/src/color.rs @@ -185,4 +185,14 @@ mod tests { assert_eq!(Color::from(Color::White.get_bit_value()), Color::White); assert_eq!(Color::from(1u8).get_bit_value(), 1u8); } + + #[test] + fn test_oct() { + let left = OctColor::Red; + let right = OctColor::Green; + assert_eq!( + OctColor::split_byte(OctColor::colors_byte(left, right)), + Ok((left, right)) + ); + } } diff --git a/src/epd5in65f/graphics.rs b/src/epd5in65f/graphics.rs index 2a52b15..d21f8e8 100644 --- a/src/epd5in65f/graphics.rs +++ b/src/epd5in65f/graphics.rs @@ -204,4 +204,48 @@ mod tests { ); } } + + #[test] + fn graphics_colors() { + let mut display = Display5in65f::default(); + + const COLORS: [OctColor; 8] = [ + OctColor::HiZ, + OctColor::White, + OctColor::Black, + OctColor::Red, + OctColor::Green, + OctColor::Orange, + OctColor::Blue, + OctColor::Yellow, + ]; + for c in &COLORS { + display.clear_buffer(*c); + for b in display.buffer() { + assert_eq!(OctColor::split_byte(*b), Ok((*c, *c))); + } + } + + for (w, c) in (0..WIDTH).zip(COLORS.iter().cycle()) { + let _ = Line::new( + Point::new(w as i32, 0), + Point::new(w as i32, HEIGHT as i32 - 1), + ) + .into_styled(PrimitiveStyle::with_stroke(*c, 1)) + .draw(&mut display); + } + + COLORS + .chunks(2) + .cycle() + .take(WIDTH as usize * 2) + .cycle() + .zip(display.buffer()) + .for_each(|(window, b)| match (window, b) { + (&[c1, c2], b) => { + assert_eq!(OctColor::split_byte(*b), Ok((c1, c2))); + } + _ => panic!("unexpected pattern"), + }) + } } diff --git a/src/graphics.rs b/src/graphics.rs index f9aac40..c7b752e 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -93,9 +93,9 @@ pub trait Display: DrawTarget { /// - Clearing pub trait OctDisplay: DrawTarget { /// Clears the buffer of the display with the chosen background color - fn clear_buffer(&mut self, background_color: Color) { + fn clear_buffer(&mut self, background_color: OctColor) { for elem in self.get_mut_buffer().iter_mut() { - *elem = background_color.get_byte_value(); + *elem = OctColor::colors_byte(background_color, background_color); } } @@ -274,7 +274,9 @@ fn find_rotation(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRotat fn find_oct_position(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRotation) -> (u32, bool) { let (nx, ny) = find_rotation(x, y, width, height, rotation); ( + /* what byte address is this? */ nx / 2 + (width / 2) * ny, + /* is this the lower nibble (within byte)? */ (nx & 0x1) == 0, ) }