Browse Source

Merge pull request #60 from pjsier/feat/epd2in7b

Add support for epd2in7b
main
Chris 5 years ago committed by GitHub
parent
commit
87037c6f52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      README.md
  2. 138
      src/epd2in7b/command.rs
  3. 55
      src/epd2in7b/constants.rs
  4. 163
      src/epd2in7b/graphics.rs
  5. 456
      src/epd2in7b/mod.rs
  6. 1
      src/lib.rs

1
README.md

@ -47,6 +47,7 @@ epd.update_and_display_frame(&mut spi, &display.buffer())?;
| [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 | ✕ | ✕ | ✔ | ✔ |
| [2.7 Inch 3 Color (B)](https://www.waveshare.com/2.7inch-e-paper-b.htm) | Black, White, Red | ✕ | ✔ | ✔ | ✔ |
### [1]: 7.5 Inch B/W V2 (A)

138
src/epd2in7b/command.rs

@ -0,0 +1,138 @@
//! SPI Commands for the Waveshare 2.7" B 3 color E-Ink Display
use crate::traits;
/// EPD2IN7B commands
///
/// More information can be found in the [specification](https://www.waveshare.com/w/upload/d/d8/2.7inch-e-paper-b-specification.pdf)
#[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,
POWER_OFF = 0x02,
/// Setting Power OFF sequence
POWER_OFF_SEQUENCE_SETTING = 0x03,
POWER_ON = 0x04,
/// This command enables the internal bandgap, which will be cleared by the next POF.
POWER_ON_MEASURE = 0x05,
/// Starting data transmission
///
/// ```ignore
/// self.send_data(&[0x07, 0x07, 0x17])?;
/// ```
BOOSTER_SOFT_START = 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.
DEEP_SLEEP = 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.
DATA_START_TRANSMISSION_1 = 0x10,
/// Stopping data transmission
DATA_STOP = 0x11,
/// After this command is issued, driver will refresh display (data/VCOM) according to SRAM data and LUT.
DISPLAY_REFRESH = 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.
DATA_START_TRANSMISSION_2 = 0x13,
/// The command define as follows: The register is indicates that user start to transmit data, then write to SRAM. While data transmission
/// complete, user must send command DSP (Data transmission Stop). Then 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.
PARTIAL_DATA_START_TRANSMISSION_1 = 0x14,
/// The command define as follows: The register is indicates that user start to transmit data, then write to SRAM. While data transmission
/// complete, user must send command DSP (Data transmission Stop). Then 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.
PARTIAL_DATA_START_TRANSMISSION_2 = 0x15,
/// While user sent this command, driver will refresh display (data/VCOM) base on SRAM data and LUT.
///
/// Only the area (X,Y, W, L) would update, the others pixel output would follow VCOM LUT
PARTIAL_DISPLAY_REFRESH = 0x16,
/// This command builds the Look-up table for VCOM
LUT_FOR_VCOM = 0x20,
LUT_WHITE_TO_WHITE = 0x21,
LUT_BLACK_TO_WHITE = 0x22,
LUT_WHITE_TO_BLACK = 0x23,
LUT_BLACK_TO_BLACK = 0x24,
/// The command controls the PLL clock frequency.
PLL_CONTROL = 0x30,
/// This command reads the temperature sensed by the temperature sensor.
///
/// Doesn't work! Waveshare doesn't connect the read pin
TEMPERATURE_SENSOR_COMMAND = 0x40,
/// This command selects Internal or External temperature sensor.
TEMPERATURE_SENSOR_CALIBRATION = 0x41,
/// Write External Temperature Sensor
TEMPERATURE_SENSOR_WRITE = 0x42,
/// Read External Temperature Sensor
///
/// Doesn't work! Waveshare doesn't connect the read pin
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).
RESOLUTION_SETTING = 0x61,
SOURCE_AND_GATE_SETTING = 0x62,
/// This command reads the IC status.
///
/// Doesn't work! Waveshare doesn't connect the read pin
GET_STATUS = 0x71,
/// Automatically measure VCOM. This command reads the IC status
AUTO_MEASUREMENT_VCOM = 0x80,
/// This command gets the VCOM value
///
/// Doesn't work! Waveshare doesn't connect the read pin
READ_VCOM_VALUE = 0x81,
/// This command sets VCOM_DC value.
VCM_DC_SETTING = 0x82,
/// 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.
PROGRAM_MODE = 0xA0,
/// After this command is issued, the chip would enter the program mode.
ACTIVE_PROGRAMMING = 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.
READ_OTP = 0xA2,
/// Not shown in commands table, but used in init sequence
POWER_OPTIMIZATION = 0xf8,
}
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);
}
}

55
src/epd2in7b/constants.rs

@ -0,0 +1,55 @@
#[rustfmt::skip]
pub(crate) const LUT_VCOM_DC: [u8; 44] = [
0x00, 0x00,
0x00, 0x1A, 0x1A, 0x00, 0x00, 0x01,
0x00, 0x0A, 0x0A, 0x00, 0x00, 0x08,
0x00, 0x0E, 0x01, 0x0E, 0x01, 0x10,
0x00, 0x0A, 0x0A, 0x00, 0x00, 0x08,
0x00, 0x04, 0x10, 0x00, 0x00, 0x05,
0x00, 0x03, 0x0E, 0x00, 0x00, 0x0A,
0x00, 0x23, 0x00, 0x00, 0x00, 0x01,
];
#[rustfmt::skip]
pub(crate) const LUT_WW: [u8; 42] =[
0x90, 0x1A, 0x1A, 0x00, 0x00, 0x01,
0x40, 0x0A, 0x0A, 0x00, 0x00, 0x08,
0x84, 0x0E, 0x01, 0x0E, 0x01, 0x10,
0x80, 0x0A, 0x0A, 0x00, 0x00, 0x08,
0x00, 0x04, 0x10, 0x00, 0x00, 0x05,
0x00, 0x03, 0x0E, 0x00, 0x00, 0x0A,
0x00, 0x23, 0x00, 0x00, 0x00, 0x01,
];
#[rustfmt::skip]
pub(crate) const LUT_BW: [u8; 42] =[
0xA0, 0x1A, 0x1A, 0x00, 0x00, 0x01,
0x00, 0x0A, 0x0A, 0x00, 0x00, 0x08,
0x84, 0x0E, 0x01, 0x0E, 0x01, 0x10,
0x90, 0x0A, 0x0A, 0x00, 0x00, 0x08,
0xB0, 0x04, 0x10, 0x00, 0x00, 0x05,
0xB0, 0x03, 0x0E, 0x00, 0x00, 0x0A,
0xC0, 0x23, 0x00, 0x00, 0x00, 0x01,
];
#[rustfmt::skip]
pub(crate) const LUT_BB: [u8; 42] =[
0x90, 0x1A, 0x1A, 0x00, 0x00, 0x01,
0x40, 0x0A, 0x0A, 0x00, 0x00, 0x08,
0x84, 0x0E, 0x01, 0x0E, 0x01, 0x10,
0x80, 0x0A, 0x0A, 0x00, 0x00, 0x08,
0x00, 0x04, 0x10, 0x00, 0x00, 0x05,
0x00, 0x03, 0x0E, 0x00, 0x00, 0x0A,
0x00, 0x23, 0x00, 0x00, 0x00, 0x01,
];
#[rustfmt::skip]
pub(crate) const LUT_WB: [u8; 42] =[
0x90, 0x1A, 0x1A, 0x00, 0x00, 0x01,
0x20, 0x0A, 0x0A, 0x00, 0x00, 0x08,
0x84, 0x0E, 0x01, 0x0E, 0x01, 0x10,
0x10, 0x0A, 0x0A, 0x00, 0x00, 0x08,
0x00, 0x04, 0x10, 0x00, 0x00, 0x05,
0x00, 0x03, 0x0E, 0x00, 0x00, 0x0A,
0x00, 0x23, 0x00, 0x00, 0x00, 0x01,
];

163
src/epd2in7b/graphics.rs

@ -0,0 +1,163 @@
use crate::epd2in7b::{DEFAULT_BACKGROUND_COLOR, HEIGHT, WIDTH};
use crate::graphics::{Display, DisplayRotation};
use embedded_graphics::pixelcolor::BinaryColor;
use embedded_graphics::prelude::*;
/// Full size buffer for use with the 2in7B EPD
///
/// Can also be manuall constructed:
/// `buffer: [DEFAULT_BACKGROUND_COLOR.get_byte_value(); WIDTH * HEIGHT / 8]`
pub struct Display2in7b {
buffer: [u8; WIDTH as usize * HEIGHT as usize / 8],
rotation: DisplayRotation,
}
impl Default for Display2in7b {
fn default() -> Self {
Display2in7b {
buffer: [DEFAULT_BACKGROUND_COLOR.get_byte_value();
WIDTH as usize * HEIGHT as usize / 8],
rotation: DisplayRotation::default(),
}
}
}
impl DrawTarget<BinaryColor> for Display2in7b {
type Error = core::convert::Infallible;
fn draw_pixel(&mut self, pixel: Pixel<BinaryColor>) -> Result<(), Self::Error> {
self.draw_helper(WIDTH, HEIGHT, pixel)
}
fn size(&self) -> Size {
Size::new(WIDTH, HEIGHT)
}
}
impl Display for Display2in7b {
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::color::Black;
use crate::color::Color;
use crate::epd2in7b;
use crate::epd2in7b::{HEIGHT, WIDTH};
use crate::graphics::{Display, DisplayRotation};
use embedded_graphics::{primitives::Line, style::PrimitiveStyle};
// test buffer length
#[test]
fn graphics_size() {
let display = Display2in7b::default();
assert_eq!(display.buffer().len(), 5808);
}
// test default background color on all bytes
#[test]
fn graphics_default() {
let display = Display2in7b::default();
for &byte in display.buffer() {
assert_eq!(byte, epd2in7b::DEFAULT_BACKGROUND_COLOR.get_byte_value());
}
}
#[test]
fn graphics_rotation_0() {
let mut display = Display2in7b::default();
let _ = Line::new(Point::new(0, 0), Point::new(7, 0))
.into_styled(PrimitiveStyle::with_stroke(Black, 1))
.draw(&mut display);
let buffer = display.buffer();
assert_eq!(buffer[0], Color::Black.get_byte_value());
for &byte in buffer.iter().skip(1) {
assert_eq!(byte, epd2in7b::DEFAULT_BACKGROUND_COLOR.get_byte_value());
}
}
#[test]
fn graphics_rotation_90() {
let mut display = Display2in7b::default();
display.set_rotation(DisplayRotation::Rotate90);
let _ = Line::new(
Point::new(0, WIDTH as i32 - 8),
Point::new(0, WIDTH as i32 - 1),
)
.into_styled(PrimitiveStyle::with_stroke(Black, 1))
.draw(&mut display);
let buffer = display.buffer();
assert_eq!(buffer[0], Color::Black.get_byte_value());
for &byte in buffer.iter().skip(1) {
assert_eq!(byte, epd2in7b::DEFAULT_BACKGROUND_COLOR.get_byte_value());
}
}
#[test]
fn graphics_rotation_180() {
let mut display = Display2in7b::default();
display.set_rotation(DisplayRotation::Rotate180);
let _ = Line::new(
Point::new(WIDTH as i32 - 8, HEIGHT as i32 - 1),
Point::new(WIDTH as i32 - 1, HEIGHT as i32 - 1),
)
.into_styled(PrimitiveStyle::with_stroke(Black, 1))
.draw(&mut display);
let buffer = display.buffer();
extern crate std;
std::println!("{:?}", buffer);
assert_eq!(buffer[0], Color::Black.get_byte_value());
for &byte in buffer.iter().skip(1) {
assert_eq!(byte, epd2in7b::DEFAULT_BACKGROUND_COLOR.get_byte_value());
}
}
#[test]
fn graphics_rotation_270() {
let mut display = Display2in7b::default();
display.set_rotation(DisplayRotation::Rotate270);
let _ = Line::new(
Point::new(HEIGHT as i32 - 1, 0),
Point::new(HEIGHT as i32 - 1, 7),
)
.into_styled(PrimitiveStyle::with_stroke(Black, 1))
.draw(&mut display);
let buffer = display.buffer();
extern crate std;
std::println!("{:?}", buffer);
assert_eq!(buffer[0], Color::Black.get_byte_value());
for &byte in buffer.iter().skip(1) {
assert_eq!(byte, epd2in7b::DEFAULT_BACKGROUND_COLOR.get_byte_value());
}
}
}

456
src/epd2in7b/mod.rs

@ -0,0 +1,456 @@
//! A simple Driver for the Waveshare 2.7" B Tri-Color E-Ink Display via SPI
//!
//! [Documentation](https://www.waveshare.com/wiki/2.7inch_e-Paper_HAT_(B))
use embedded_hal::{
blocking::{delay::*, spi::Write},
digital::v2::*,
};
use crate::interface::DisplayInterface;
use crate::traits::{
InternalWiAdditions, RefreshLUT, WaveshareDisplay, WaveshareThreeColorDisplay,
};
// The Lookup Tables for the Display
mod constants;
use crate::epd2in7b::constants::*;
/// Width of the display
pub const WIDTH: u32 = 176;
/// Height of the display
pub const HEIGHT: u32 = 264;
/// Default Background Color
pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White;
const IS_BUSY_LOW: bool = true;
use crate::color::Color;
pub(crate) mod command;
use self::command::Command;
#[cfg(feature = "graphics")]
mod graphics;
#[cfg(feature = "graphics")]
pub use self::graphics::Display2in7b;
/// EPD2in7b driver
pub struct EPD2in7b<SPI, CS, BUSY, DC, RST> {
/// Connection Interface
interface: DisplayInterface<SPI, CS, BUSY, DC, RST>,
/// Background Color
color: Color,
}
impl<SPI, CS, BUSY, DC, RST> InternalWiAdditions<SPI, CS, BUSY, DC, RST>
for EPD2in7b<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
{
fn init<DELAY: DelayMs<u8>>(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
) -> Result<(), SPI::Error> {
// reset the device
self.interface.reset(delay, 2);
// power on
self.command(spi, Command::POWER_ON)?;
delay.delay_ms(5);
self.wait_until_idle();
// set panel settings, 0xbf is bw, 0xaf is multi-color
self.interface
.cmd_with_data(spi, Command::PANEL_SETTING, &[0xaf])?;
// pll control
self.interface
.cmd_with_data(spi, Command::PLL_CONTROL, &[0x3a])?;
// set the power settings
self.interface.cmd_with_data(
spi,
Command::POWER_SETTING,
&[0x03, 0x00, 0x2b, 0x2b, 0x09],
)?;
// start the booster
self.interface
.cmd_with_data(spi, Command::BOOSTER_SOFT_START, &[0x07, 0x07, 0x17])?;
// power optimization
self.interface
.cmd_with_data(spi, Command::POWER_OPTIMIZATION, &[0x60, 0xa5])?;
self.interface
.cmd_with_data(spi, Command::POWER_OPTIMIZATION, &[0x89, 0xa5])?;
self.interface
.cmd_with_data(spi, Command::POWER_OPTIMIZATION, &[0x90, 0x00])?;
self.interface
.cmd_with_data(spi, Command::POWER_OPTIMIZATION, &[0x93, 0x2a])?;
self.interface
.cmd_with_data(spi, Command::POWER_OPTIMIZATION, &[0x73, 0x41])?;
self.interface
.cmd_with_data(spi, Command::VCM_DC_SETTING, &[0x12])?;
self.interface
.cmd_with_data(spi, Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x87])?;
self.set_lut(spi, None)?;
self.interface
.cmd_with_data(spi, Command::PARTIAL_DISPLAY_REFRESH, &[0x00])?;
self.wait_until_idle();
Ok(())
}
}
impl<SPI, CS, BUSY, DC, RST> WaveshareDisplay<SPI, CS, BUSY, DC, RST>
for EPD2in7b<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
{
type DisplayColor = Color;
fn new<DELAY: DelayMs<u8>>(
spi: &mut SPI,
cs: CS,
busy: BUSY,
dc: DC,
rst: RST,
delay: &mut DELAY,
) -> Result<Self, SPI::Error> {
let interface = DisplayInterface::new(cs, busy, dc, rst);
let color = DEFAULT_BACKGROUND_COLOR;
let mut epd = EPD2in7b { interface, color };
epd.init(spi, delay)?;
Ok(epd)
}
fn wake_up<DELAY: DelayMs<u8>>(
&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.wait_until_idle();
self.interface
.cmd_with_data(spi, Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0xf7])?;
self.command(spi, Command::POWER_OFF)?;
self.wait_until_idle();
self.interface
.cmd_with_data(spi, Command::DEEP_SLEEP, &[0xA5])?;
Ok(())
}
fn update_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> {
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
self.send_buffer_helper(spi, buffer)?;
// Clear chromatic layer since we won't be using it here
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_2)?;
self.interface
.data_x_times(spi, !self.color.get_byte_value(), WIDTH * HEIGHT / 8)?;
self.interface.cmd(spi, Command::DATA_STOP)?;
Ok(())
}
fn update_partial_frame(
&mut self,
spi: &mut SPI,
buffer: &[u8],
x: u32,
y: u32,
width: u32,
height: u32,
) -> Result<(), SPI::Error> {
self.interface
.cmd(spi, Command::PARTIAL_DATA_START_TRANSMISSION_1)?;
self.send_data(spi, &[(x >> 8) as u8])?;
self.send_data(spi, &[(x & 0xf8) as u8])?;
self.send_data(spi, &[(y >> 8) as u8])?;
self.send_data(spi, &[(y & 0xff) as u8])?;
self.send_data(spi, &[(width >> 8) as u8])?;
self.send_data(spi, &[(width & 0xf8) as u8])?;
self.send_data(spi, &[(height >> 8) as u8])?;
self.send_data(spi, &[(height & 0xff) as u8])?;
self.wait_until_idle();
self.send_buffer_helper(spi, buffer)?;
self.interface.cmd(spi, Command::DATA_STOP)
}
fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
self.command(spi, Command::DISPLAY_REFRESH)?;
self.wait_until_idle();
Ok(())
}
fn update_and_display_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> {
self.update_frame(spi, buffer)?;
self.command(spi, Command::DISPLAY_REFRESH)?;
Ok(())
}
fn clear_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
self.wait_until_idle();
let color_value = self.color.get_byte_value();
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
self.interface
.data_x_times(spi, color_value, WIDTH * HEIGHT / 8)?;
self.interface.cmd(spi, Command::DATA_STOP)?;
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_2)?;
self.interface
.data_x_times(spi, color_value, WIDTH * HEIGHT / 8)?;
self.interface.cmd(spi, Command::DATA_STOP)?;
Ok(())
}
fn set_background_color(&mut self, color: Color) {
self.color = color;
}
fn background_color(&self) -> &Color {
&self.color
}
fn width(&self) -> u32 {
WIDTH
}
fn height(&self) -> u32 {
HEIGHT
}
fn set_lut(
&mut self,
spi: &mut SPI,
_refresh_rate: Option<RefreshLUT>,
) -> Result<(), SPI::Error> {
self.wait_until_idle();
self.cmd_with_data(spi, Command::LUT_FOR_VCOM, &LUT_VCOM_DC)?;
self.cmd_with_data(spi, Command::LUT_WHITE_TO_WHITE, &LUT_WW)?;
self.cmd_with_data(spi, Command::LUT_BLACK_TO_WHITE, &LUT_BW)?;
self.cmd_with_data(spi, Command::LUT_WHITE_TO_BLACK, &LUT_WB)?;
self.cmd_with_data(spi, Command::LUT_BLACK_TO_BLACK, &LUT_BB)?;
Ok(())
}
fn is_busy(&self) -> bool {
self.interface.is_busy(IS_BUSY_LOW)
}
}
impl<SPI, CS, BUSY, DC, RST> WaveshareThreeColorDisplay<SPI, CS, BUSY, DC, RST>
for EPD2in7b<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
{
fn update_color_frame(
&mut self,
spi: &mut SPI,
black: &[u8],
chromatic: &[u8],
) -> Result<(), SPI::Error> {
self.update_achromatic_frame(spi, black)?;
self.update_chromatic_frame(spi, chromatic)
}
/// Update only the black/white data of the display.
///
/// Finish by calling `update_chromatic_frame`.
fn update_achromatic_frame(
&mut self,
spi: &mut SPI,
achromatic: &[u8],
) -> Result<(), SPI::Error> {
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
self.send_buffer_helper(spi, achromatic)?;
self.interface.cmd(spi, Command::DATA_STOP)
}
/// Update only chromatic data of the display.
///
/// This data takes precedence over the black/white data.
fn update_chromatic_frame(
&mut self,
spi: &mut SPI,
chromatic: &[u8],
) -> Result<(), SPI::Error> {
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_2)?;
self.send_buffer_helper(spi, chromatic)?;
self.interface.cmd(spi, Command::DATA_STOP)?;
self.wait_until_idle();
Ok(())
}
}
impl<SPI, CS, BUSY, DC, RST> EPD2in7b<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
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 send_buffer_helper(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> {
// Based on the waveshare implementation, all data for color values is flipped. This helper
// method makes that transmission easier
for b in buffer.iter() {
self.send_data(spi, &[!b])?;
}
Ok(())
}
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_until_idle(&mut self) {
self.interface.wait_until_idle(IS_BUSY_LOW)
}
/// Refresh display for partial frame
pub fn display_partial_frame(
&mut self,
spi: &mut SPI,
x: u32,
y: u32,
width: u32,
height: u32,
) -> Result<(), SPI::Error> {
self.command(spi, Command::PARTIAL_DISPLAY_REFRESH)?;
self.send_data(spi, &[(x >> 8) as u8])?;
self.send_data(spi, &[(x & 0xf8) as u8])?;
self.send_data(spi, &[(y >> 8) as u8])?;
self.send_data(spi, &[(y & 0xff) as u8])?;
self.send_data(spi, &[(width >> 8) as u8])?;
self.send_data(spi, &[(width & 0xf8) as u8])?;
self.send_data(spi, &[(height >> 8) as u8])?;
self.send_data(spi, &[(height & 0xff) as u8])?;
self.wait_until_idle();
Ok(())
}
/// Update black/achromatic frame
pub fn update_partial_achromatic_frame(
&mut self,
spi: &mut SPI,
achromatic: &[u8],
x: u32,
y: u32,
width: u32,
height: u32,
) -> Result<(), SPI::Error> {
self.interface
.cmd(spi, Command::PARTIAL_DATA_START_TRANSMISSION_1)?;
self.send_data(spi, &[(x >> 8) as u8])?;
self.send_data(spi, &[(x & 0xf8) as u8])?;
self.send_data(spi, &[(y >> 8) as u8])?;
self.send_data(spi, &[(y & 0xff) as u8])?;
self.send_data(spi, &[(width >> 8) as u8])?;
self.send_data(spi, &[(width & 0xf8) as u8])?;
self.send_data(spi, &[(height >> 8) as u8])?;
self.send_data(spi, &[(height & 0xff) as u8])?;
self.wait_until_idle();
for b in achromatic.iter() {
// Flipping based on waveshare implementation
self.send_data(spi, &[!b])?;
}
Ok(())
}
/// Update partial chromatic/red frame
pub fn update_partial_chromatic_frame(
&mut self,
spi: &mut SPI,
chromatic: &[u8],
x: u32,
y: u32,
width: u32,
height: u32,
) -> Result<(), SPI::Error> {
self.interface
.cmd(spi, Command::PARTIAL_DATA_START_TRANSMISSION_2)?;
self.send_data(spi, &[(x >> 8) as u8])?;
self.send_data(spi, &[(x & 0xf8) as u8])?;
self.send_data(spi, &[(y >> 8) as u8])?;
self.send_data(spi, &[(y & 0xff) as u8])?;
self.send_data(spi, &[(width >> 8) as u8])?;
self.send_data(spi, &[(width & 0xf8) as u8])?;
self.send_data(spi, &[(height >> 8) as u8])?;
self.send_data(spi, &[(height & 0xff) as u8])?;
self.wait_until_idle();
for b in chromatic.iter() {
// Flipping based on waveshare implementation
self.send_data(spi, &[!b])?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn epd_size() {
assert_eq!(WIDTH, 176);
assert_eq!(HEIGHT, 264);
assert_eq!(DEFAULT_BACKGROUND_COLOR, Color::White);
}
}

1
src/lib.rs

@ -75,6 +75,7 @@ mod interface;
pub mod epd1in54;
pub mod epd1in54b;
pub mod epd2in13_v2;
pub mod epd2in7b;
pub mod epd2in9;
pub mod epd2in9bc;
pub mod epd4in2;

Loading…
Cancel
Save