Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 2in7 v2 EPD driver #206

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ epd4in2.sleep(&mut spi, &mut delay)
| [2.9 Inch B/W (A)](https://www.waveshare.com/product/2.9inch-e-paper-module.htm) | Black, White | ✕ | ✔ | ✔ | ✔ |
| [2.9 Inch B/W V2 (A)](https://www.waveshare.com/product/2.9inch-e-paper-module.htm) | Black, White | ✕ | ✔ | ✔ | ✔ |
| [2.7 Inch 3 Color (B)](https://www.waveshare.com/2.7inch-e-paper-b.htm) | Black, White, Red | ✕ | ✔ | ✔ | ✔ |
| [2.7 Inch B/W V2](https://www.waveshare.com/2.7inch-e-paper.htm) | Black, White | ✕ | ✔ | ✔ | ✔ |
| [2.66 Inch 3 Color (B)](https://www.waveshare.com/wiki/Pico-ePaper-2.66-B) | Black, White, Red | ✕ | ✕ | ✔ | ✔ |
| [1.54 Inch B/W/Y (C) (Discontinued)](https://www.waveshare.com/1.54inch-e-paper-module-c.htm) | Black, White, Yellow | ✕ | ✕ | ✔ | ✔ |
| [1.54 Inch B/W/R (B)](https://www.waveshare.com/1.54inch-e-Paper-B.htm) | Black, White, Red | ✕ | ✕ | ✔ | ✔ |
Expand Down
308 changes: 308 additions & 0 deletions src/epd2in7_v2/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
//! A simple Driver for the Waveshare 2.7inch v2 e-Paper HAT Display via SPI
//!
//! # References
//!
//! - [Datasheet](https://www.waveshare.com/wiki/2.7inch_e-Paper_HAT_Manual)
//! - [Waveshare C driver](https://github.com/waveshareteam/e-Paper/blob/master/RaspberryPi_JetsonNano/c/lib/e-Paper/EPD_2in7_V2.c)
//! - [Waveshare Python driver](https://github.com/waveshareteam/e-Paper/blob/master/RaspberryPi_JetsonNano/python/lib/waveshare_epd/epd2in7_V2.py)

use embedded_hal::{
delay::DelayNs,
digital::{InputPin, OutputPin},
spi::SpiDevice,
};

use crate::{
buffer_len,
color::Color,
interface::DisplayInterface,
traits::{InternalWiAdditions, RefreshLut, WaveshareDisplay},
type_a::command::Command,
};

/// 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 = false;
const SINGLE_BYTE_WRITE: bool = true;

/// Full size buffer for use with the 2in7B EPD
/// TODO this should be a TriColor, but let's keep it as is at first
#[cfg(feature = "graphics")]
pub type Display2in7 = crate::graphics::Display<
WIDTH,
HEIGHT,
false,
{ buffer_len(WIDTH as usize, HEIGHT as usize) },
Color,
>;

/// Epd2in7b driver
pub struct Epd2in7<SPI, BUSY, DC, RST, DELAY> {
/// Connection Interface
interface: DisplayInterface<SPI, BUSY, DC, RST, DELAY, SINGLE_BYTE_WRITE>,
/// Background Color
color: Color,
refresh: RefreshLut,
}

impl<SPI, BUSY, DC, RST, DELAY> InternalWiAdditions<SPI, BUSY, DC, RST, DELAY>
for Epd2in7<SPI, BUSY, DC, RST, DELAY>
where
SPI: SpiDevice,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
DELAY: DelayNs,
{
fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
// reset the device
self.interface.reset(delay, 200_000, 2_000);

self.wait_until_idle(spi, delay)?;
self.command(spi, Command::SwReset)?;
self.wait_until_idle(spi, delay)?;

self.use_full_frame(spi, delay)?;

self.interface
.cmd_with_data(spi, Command::DataEntryModeSetting, &[0x03])?;

Ok(())
}
}

impl<SPI, BUSY, DC, RST, DELAY> WaveshareDisplay<SPI, BUSY, DC, RST, DELAY>
for Epd2in7<SPI, BUSY, DC, RST, DELAY>
where
SPI: SpiDevice,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
DELAY: DelayNs,
{
type DisplayColor = Color;
fn new(
spi: &mut SPI,
busy: BUSY,
dc: DC,
rst: RST,
delay: &mut DELAY,
delay_us: Option<u32>,
) -> Result<Self, SPI::Error> {
let interface = DisplayInterface::new(busy, dc, rst, delay_us);
let color = DEFAULT_BACKGROUND_COLOR;

let mut epd = Epd2in7 {
interface,
color,
refresh: RefreshLut::Full,
};

epd.init(spi, delay)?;

Ok(epd)
}

fn wake_up(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.init(spi, delay)
}

fn sleep(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.wait_until_idle(spi, delay)?;
self.interface
.cmd_with_data(spi, Command::DeepSleepMode, &[0x01])?;
Ok(())
}

fn update_frame(
&mut self,
spi: &mut SPI,
buffer: &[u8],
delay: &mut DELAY,
) -> Result<(), SPI::Error> {
self.wait_until_idle(spi, delay)?;
self.use_full_frame(spi, delay)?;
self.interface
.cmd_with_data(spi, Command::WriteRam, buffer)?;
Ok(())
}

fn update_partial_frame(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
buffer: &[u8],
x: u32,
y: u32,
width: u32,
height: u32,
) -> Result<(), SPI::Error> {
self.wait_until_idle(spi, delay)?;
self.set_ram_area(spi, x, y, x + width, y + height)?;
self.set_ram_counter(spi, delay, x, y)?;

self.interface
.cmd_with_data(spi, Command::WriteRam, buffer)?;

Ok(())
}

fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.wait_until_idle(spi, delay)?;
if self.refresh == RefreshLut::Full {
self.interface
.cmd_with_data(spi, Command::DisplayUpdateControl2, &[0xF7])?;
} else if self.refresh == RefreshLut::Quick {
self.interface
.cmd_with_data(spi, Command::DisplayUpdateControl2, &[0xC7])?;
}

self.interface.cmd(spi, Command::MasterActivation)?;
self.wait_until_idle(spi, delay)?;
Ok(())
}

fn update_and_display_frame(
&mut self,
spi: &mut SPI,
buffer: &[u8],
delay: &mut DELAY,
) -> Result<(), SPI::Error> {
self.update_frame(spi, buffer, delay)?;
self.display_frame(spi, delay)?;
Ok(())
}

fn clear_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.wait_until_idle(spi, delay)?;
self.use_full_frame(spi, delay)?;

let color = self.color.get_byte_value();

self.interface.cmd(spi, Command::WriteRam)?;
self.interface
.data_x_times(spi, color, WIDTH / 8 * HEIGHT)?;

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,
_delay: &mut DELAY,
refresh_rate: Option<RefreshLut>,
) -> Result<(), SPI::Error> {
if let Some(refresh_lut) = refresh_rate {
self.refresh = refresh_lut;
}
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<SPI, BUSY, DC, RST, DELAY> Epd2in7<SPI, BUSY, DC, RST, DELAY>
where
SPI: SpiDevice,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
DELAY: DelayNs,
{
fn command(&mut self, spi: &mut SPI, command: Command) -> Result<(), SPI::Error> {
self.interface.cmd(spi, command)
}

fn set_ram_area(
&mut self,
spi: &mut SPI,
start_x: u32,
start_y: u32,
end_x: u32,
end_y: u32,
) -> Result<(), SPI::Error> {
assert!(start_x < end_x);
assert!(start_y < end_y);

self.interface.cmd_with_data(
spi,
Command::SetRamXAddressStartEndPosition,
&[(start_x >> 3) as u8, (end_x >> 3) as u8],
)?;

self.interface.cmd_with_data(
spi,
Command::SetRamYAddressStartEndPosition,
&[
(start_y & 0xFF) as u8,
((start_y >> 8) & 0x01) as u8,
(end_y & 0xFF) as u8,
((end_y >> 8) & 0x01) as u8,
],
)?;
Ok(())
}

fn set_ram_counter(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
x: u32,
y: u32,
) -> Result<(), SPI::Error> {
self.wait_until_idle(spi, delay)?;
self.interface
.cmd_with_data(spi, Command::SetRamXAddressCounter, &[(x & 0xFF) as u8])?;

self.interface.cmd_with_data(
spi,
Command::SetRamYAddressCounter,
&[(y & 0xFF) as u8, ((y >> 8) & 0x01) as u8],
)?;
Ok(())
}

fn use_full_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
// choose full frame/ram
self.set_ram_area(spi, 0, 0, WIDTH - 1, HEIGHT - 1)?;

// start from the beginning
self.set_ram_counter(spi, delay, 0, 0)
}
}

#[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 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ pub mod epd1in54c;
pub mod epd2in13_v2;
pub mod epd2in13bc;
pub mod epd2in66b;
pub mod epd2in7_v2;
pub mod epd2in7b;
pub mod epd2in9;
pub mod epd2in9_v2;
Expand Down
Loading