Skip to content

Commit

Permalink
WIP: early support for 2in13 e-ink
Browse files Browse the repository at this point in the history
  • Loading branch information
dkm committed Aug 23, 2020
1 parent 22f16ea commit 5c21f8b
Show file tree
Hide file tree
Showing 5 changed files with 679 additions and 0 deletions.
121 changes: 121 additions & 0 deletions src/epd2in13_v2/command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//! SPI Commands for the Waveshare 2.13" v2
use crate::traits;

/// EPD2in13 v2
///
/// 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 {
/// Driver Output control
/// 3 Databytes:
/// A[7:0]
/// 0.. A[8]
/// 0.. B[2:0]
/// Default: Set A[8:0] = 0x127 and B[2:0] = 0x0
DRIVER_OUTPUT_CONTROL = 0x01,

GATE_DRIVING_VOLTAGE_CTRL = 0x03,
SOURCE_DRIVING_VOLTAGE_CTRL = 0x04,

/// Booster Soft start control
/// 3 Databytes:
/// 1.. A[6:0]
/// 1.. B[6:0]
/// 1.. C[6:0]
/// Default: A[7:0] = 0xCF, B[7:0] = 0xCE, C[7:0] = 0x8D
BOOSTER_SOFT_START_CONTROL = 0x0C,
GATE_SCAN_START_POSITION = 0x0F,
//TODO: useful?
// GATE_SCAN_START_POSITION = 0x0F,
/// Deep Sleep Mode Control
/// 1 Databyte:
/// 0.. A[0]
/// Values:
/// A[0] = 0: Normal Mode (POR)
/// A[0] = 1: Enter Deep Sleep Mode
DEEP_SLEEP_MODE = 0x10,
// /// Data Entry mode setting
DATA_ENTRY_MODE_SETTING = 0x11,

SW_RESET = 0x12,

TEMPERATURE_SENSOR_CONTROL_WRITE = 0x1A,
TEMPERATURE_SENSOR_CONTROL_READ = 0x1B,

MASTER_ACTIVATION = 0x20,

DISPLAY_UPDATE_CONTROL_1 = 0x21,

DISPLAY_UPDATE_CONTROL_2 = 0x22,

WRITE_RAM = 0x24,
READ_RAM = 0x27,

VCOM_SENSE = 0x28,

WRITE_VCOM_REGISTER = 0x2C,

WRITE_LUT_REGISTER = 0x32,

WRITE_REGISTER_TO_DISPLAY_OPT = 0x37,
WRITE_REGISTER_FOR_USER_ID = 0x38,

SET_DUMMY_LINE_PERIOD = 0x3A,

SET_GATE_LINE_WIDTH = 0x3B,

BORDER_WAVEFORM_CONTROL = 0x3C,

SET_RAM_X_ADDRESS_START_END_POSITION = 0x44,

SET_RAM_Y_ADDRESS_START_END_POSITION = 0x45,

SET_RAM_X_ADDRESS_COUNTER = 0x4E,

SET_RAM_Y_ADDRESS_COUNTER = 0x4F,

SET_ANALOG_BLOCK_CONTROL = 0x74,

SET_DIGITAL_BLOCK_CONTROL = 0x7E,

NOP = 0x7F,
}

pub(crate) enum DataEntryModeIncr {
X_DECR_Y_DECR = 0x0,
X_INCR_Y_DECR = 0x1,
X_DECR_Y_INCR = 0x2,
X_INCR_Y_INCR = 0x3
}

pub (crate) enum DataEntryModeDir {
X_DIR = 0x0,
Y_DIR = 0x4,
}

impl traits::Command for Command {
/// Returns the address of the command
fn address(self) -> u8 {
self as u8
}
}

#[cfg(test)]
mod tests {
use super::Command;
use crate::traits::Command as CommandTrait;

#[test]
fn command_addr() {
assert_eq!(Command::DRIVER_OUTPUT_CONTROL.address(), 0x01);

assert_eq!(Command::SET_RAM_X_ADDRESS_COUNTER.address(), 0x4E);

assert_eq!(Command::NOP.address(), 0xFF);
}
}
39 changes: 39 additions & 0 deletions src/epd2in13_v2/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#[rustfmt::skip]
// Original Waveforms from Waveshare
pub(crate) const LUT_FULL_UPDATE: [u8; 70] =[
0x80,0x60,0x40,0x00,0x00,0x00,0x00, // LUT0: BB: VS 0 ~7
0x10,0x60,0x20,0x00,0x00,0x00,0x00, // LUT1: BW: VS 0 ~7
0x80,0x60,0x40,0x00,0x00,0x00,0x00, // LUT2: WB: VS 0 ~7
0x10,0x60,0x20,0x00,0x00,0x00,0x00, // LUT3: WW: VS 0 ~7
0x00,0x00,0x00,0x00,0x00,0x00,0x00, // LUT4: VCOM: VS 0 ~7

0x03,0x03,0x00,0x00,0x02, // TP0 A~D RP0
0x09,0x09,0x00,0x00,0x02, // TP1 A~D RP1
0x03,0x03,0x00,0x00,0x02, // TP2 A~D RP2
0x00,0x00,0x00,0x00,0x00, // TP3 A~D RP3
0x00,0x00,0x00,0x00,0x00, // TP4 A~D RP4
0x00,0x00,0x00,0x00,0x00, // TP5 A~D RP5
0x00,0x00,0x00,0x00,0x00, // TP6 A~D RP6

// 0x15,0x41,0xA8,0x32,0x30,0x0A,
];

#[rustfmt::skip]
pub(crate) const LUT_PARTIAL_UPDATE: [u8; 70] =[
0x00,0x00,0x00,0x00,0x00,0x00,0x00, // LUT0: BB: VS 0 ~7
0x80,0x00,0x00,0x00,0x00,0x00,0x00, // LUT1: BW: VS 0 ~7
0x40,0x00,0x00,0x00,0x00,0x00,0x00, // LUT2: WB: VS 0 ~7
0x00,0x00,0x00,0x00,0x00,0x00,0x00, // LUT3: WW: VS 0 ~7
0x00,0x00,0x00,0x00,0x00,0x00,0x00, // LUT4: VCOM: VS 0 ~7

0x0A,0x00,0x00,0x00,0x00, // TP0 A~D RP0
0x00,0x00,0x00,0x00,0x00, // TP1 A~D RP1
0x00,0x00,0x00,0x00,0x00, // TP2 A~D RP2
0x00,0x00,0x00,0x00,0x00, // TP3 A~D RP3
0x00,0x00,0x00,0x00,0x00, // TP4 A~D RP4
0x00,0x00,0x00,0x00,0x00, // TP5 A~D RP5
0x00,0x00,0x00,0x00,0x00, // TP6 A~D RP6

// 0x15,0x41,0xA8,0x32,0x30,0x0A,
];

149 changes: 149 additions & 0 deletions src/epd2in13_v2/graphics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
use crate::epd2in13_v2::{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 2in13 v2 EPD
///
/// Can also be manually constructed:
/// `buffer: [DEFAULT_BACKGROUND_COLOR.get_byte_value(); WIDTH / 8 * HEIGHT]`
pub struct Display2in13 {
buffer: [u8; WIDTH as usize * HEIGHT as usize / 8],
rotation: DisplayRotation,
}

impl Default for Display2in13 {
fn default() -> Self {
Display2in13 {
buffer: [DEFAULT_BACKGROUND_COLOR.get_byte_value();
WIDTH as usize * HEIGHT as usize / 8],
rotation: DisplayRotation::default(),
}
}
}

impl DrawTarget<BinaryColor> for Display2in13 {
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 Display2in13 {
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, Color};
use crate::epd2in13_v2;
use crate::graphics::{Display, DisplayRotation};
use embedded_graphics::{primitives::Line, style::PrimitiveStyle};

// test buffer length
#[test]
fn graphics_size() {
let display = Display2in13::default();
assert_eq!(display.buffer().len(), 48000);
}

// test default background color on all bytes
#[test]
fn graphics_default() {
let display = Display2in13::default();
for &byte in display.buffer() {
assert_eq!(byte, epd2in13_v2::DEFAULT_BACKGROUND_COLOR.get_byte_value());
}
}

#[test]
fn graphics_rotation_0() {
let mut display = Display2in13::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, epd2in13_v2::DEFAULT_BACKGROUND_COLOR.get_byte_value());
}
}

#[test]
fn graphics_rotation_90() {
let mut display = Display2in13::default();
display.set_rotation(DisplayRotation::Rotate90);

let _ = Line::new(Point::new(0, 792), Point::new(0, 799))
.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, epd2in13_v2::DEFAULT_BACKGROUND_COLOR.get_byte_value());
}
}

#[test]
fn graphics_rotation_180() {
let mut display = Display2in13::default();
display.set_rotation(DisplayRotation::Rotate180);

let _ = Line::new(Point::new(792, 479), Point::new(799, 479))
.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, epd2in13_v2::DEFAULT_BACKGROUND_COLOR.get_byte_value());
}
}

#[test]
fn graphics_rotation_270() {
let mut display = Display2in13::default();
display.set_rotation(DisplayRotation::Rotate270);

let _ = Line::new(Point::new(479, 0), Point::new(479, 7))
.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, epd2in13_v2::DEFAULT_BACKGROUND_COLOR.get_byte_value());
}
}
}
Loading

0 comments on commit 5c21f8b

Please sign in to comment.