Merge pull request #6 from Caemor/add_1in54-epd

Add support for  1in54 epd
digi-v2-tests
Chris 5 years ago committed by GitHub
commit e5a1e3bf25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -98,9 +98,9 @@ script:
- cargo build
- cargo build --release
- cargo test
- cargo test --release
- cargo doc --release
- cd examples/embedded_linux && cargo build && cd ../../
- cargo test --all-features --release
- cargo doc --all-features --release
- cd examples/embedded_linux_epd4in2 && cargo build && cd ../../
#- cd ../f3_stm32f30x && cargo build
after_success:

@ -5,7 +5,11 @@ authors = ["Christoph Groß <christoph.gross@student.uni-tuebingen.de>"]
[features]
default = []
default = ["epd1in54", "epd2in9", "epd4in2"]
epd1in54 = []
epd2in9 = []
epd4in2 = []
# Activates the fast LUT for EPD4in2
epd4in2_fast_update = []

@ -2,14 +2,16 @@
This library contains a driver for E-Paper Modules from Waveshare.
Support for more than the 4.2" EPD (especially the smaller and faster ones) is in the work.
Support for more than the 4.2in EPD (especially the smaller and faster ones) is in the work.
The 2.9in (A) and 1.54 (A) variant should both work but aren't tested yet.
## (Supported) Devices
| Device (with Link) | Colors | Flexible Display | Partial Refresh | Supported | Tested |
| :---: | --- | :---: | :---: | :---: | :---: |
| [4.2 Inch B/W (A)](https://www.waveshare.com/product/4.2inch-e-paper-module.htm) | Black, White | ✕ | Not officially [[1](#42-inch-e-ink-blackwhite)] | ✔ | ✔ |
| [1.54 Inch B/W (A)](https://www.waveshare.com/1.54inch-e-Paper-Module.htm) | Black, White | ✕ | ✔ | | |
| [1.54 Inch B/W (A)](https://www.waveshare.com/1.54inch-e-Paper-Module.htm) | Black, White | ✕ | ✔ | | |
| [2.13 Inch B/W (A)](https://www.waveshare.com/product/2.13inch-e-paper-hat.htm) | Black, White | ✕ | ✔ | | |
| [2.9 Inch B/W (A)](https://www.waveshare.com/product/2.9inch-e-paper-module.htm) | Black, White | ✕ | ✔ | ✔ | |
@ -34,6 +36,28 @@ It's possible with this driver but might lead to ghosting / burn-in effects ther
| RST | External reset pin (Low for reset) |
| BUSY | Busy state output pin (Low for busy) |
### Display Configs
There are two types of Display Configurations used in Wavedshare EPDs, which also needs to be set on the "new" E-Paper Driver HAT.
They are also called A and B, but you shouldn't get confused and mix it with the Type A,B,C and D of the various Displays, which just describe different types (colored variants) or new versions. In the Display Config the seperation is most likely due to included fast partial refresh of the displays. In a Tabular form:
| Type A | Tybe B |
| :---: | :---: |
| 1.54in (A) | 1.54in (B) |
| 2.13in (A) | 1.54in (C) |
| 2.13in (D) | 2.13in (B) |
| 2.9in (A) | 2.13in (C) |
| | 2.7in (A) |
| | 2.7in (B) |
| | 2.9in (B) |
| | 2.9in (C) |
| | 4.2in (A) |
| | 4.2in (B) |
| | 4.2in (C) |
| | 7.5in (A) |
| | 7.5in (B) |
| | 7.5in (C) |
## TODO's
- [ ] add more examples (e.g. for f3)

@ -5,7 +5,12 @@ extern crate linux_embedded_hal as lin_hal;
extern crate eink_waveshare_rs;
use eink_waveshare_rs::{epd4in2::EPD4in2, drawing::{Graphics, color::Color}, interface::WaveshareInterface};
use eink_waveshare_rs::{
EPD4in2,
drawing::{Graphics, color::Color},
WaveshareInterface,
ConnectionInterface
};
use lin_hal::spidev::{self, SpidevOptions};
use lin_hal::{Pin, Spidev};
@ -99,7 +104,8 @@ fn main() {
//TODO: wait for Digital::InputPin
//fixed currently with the HackInputPin, see further above
let mut epd4in2 = EPD4in2::new(spi, cs, busy_in, dc, rst, delay).expect("eink inialize error");
let connection_interface = ConnectionInterface::new(spi, cs, busy_in, dc, rst, delay);
let mut epd4in2 = EPD4in2::new(connection_interface).expect("eink inialize error");
//let mut buffer = [0u8, epd4in2.get_width() / 8 * epd4in2.get_height()];
let mut buffer = [0u8; 15000];

@ -29,9 +29,10 @@ impl Color {
/// remember: 1 is white, 0 is black
/// Color is the color you want to draw with in the foreground
pub(crate) fn get_color(input: u8, pos: u8, color: &Color) -> Color {
match Color::is_drawable_pixel(input, pos) {
true => Color::normal_color(color),
false => Color::inverse_color(color)
if Color::is_drawable_pixel(input, pos) {
Color::normal_color(color)
} else {
Color::inverse_color(color)
}
}
@ -65,9 +66,10 @@ impl Color {
// - black for pixel to draw
//
//foreground color is the color you want to have in the foreground
match Color::is_drawable_pixel(input, pos) {
true => Color::normal_color(foreground_color),
false => Color::inverse_color(foreground_color)
if Color::is_drawable_pixel(input, pos) {
Color::normal_color(foreground_color)
} else {
Color::inverse_color(foreground_color)
}
}
}

@ -141,7 +141,7 @@ impl<'a> Graphics<'a> {
let mut counter = 0;
for input_char in input.chars() {
self.draw_char(x0 + counter, y0, input_char, font, color);
counter += font.get_char_width(input_char) as u16;
counter += u16::from(font.get_char_width(input_char));
}
}
@ -163,7 +163,7 @@ impl<'a> Graphics<'a> {
for _ in 0..8 {
self.draw_pixel(
x0 + width_counter as u16,
x0 + u16::from(width_counter),
y0 + row_counter,
&Color::get_color(elem, width_counter % 8, color));
@ -183,9 +183,9 @@ impl<'a> Graphics<'a> {
pub fn draw_char_8x8(&mut self, x0: u16, y0: u16, input: char, color: &Color) {
let mut counter = 0;
// includes special draw_char instructions as this one is ordered columnwise and not rowwise (first byte == first 8 pixel columnwise)
for &elem in font::bitmap_8x8(input).iter() {
for &elem in (&font::bitmap_8x8(input)).iter() {
for i in 0..8u8 {
self.draw_pixel(x0 + counter, y0 + 7 - i as u16, &Color::convert_color(elem, i, color))
self.draw_pixel(x0 + counter, y0 + 7 - u16::from(i), &Color::convert_color(elem, i, color))
}
counter += 1;
}
@ -197,10 +197,8 @@ impl<'a> Graphics<'a> {
///
/// no autobreak line yet
pub fn draw_string_8x8(&mut self, x0: u16, y0: u16, input: &str, color: &Color) {
let mut counter = 0;
for input_char in input.chars() {
self.draw_char_8x8(x0 + counter*8, y0, input_char, color);
counter += 1;
for (counter, input_char) in input.chars().enumerate() {
self.draw_char_8x8(x0 + counter as u16*8, y0, input_char, color);
}
}

@ -0,0 +1,306 @@
//! A simple Driver for the Waveshare 1.54" E-Ink Display via SPI
//!
//!
//! # Examples from the 4.2" Display. It should work the same for the 1.54" one.
//!
//! ```ignore
//! let mut epd4in2 = EPD4in2::new(spi, cs, busy, dc, rst, delay).unwrap();
//!
//! let mut buffer = [0u8, epd4in2.get_width() / 8 * epd4in2.get_height()];
//!
//! // draw something into the buffer
//!
//! epd4in2.display_and_transfer_buffer(buffer, None);
//!
//! // wait and look at the image
//!
//! epd4in2.clear_frame(None);
//!
//! epd4in2.sleep();
//! ```
const WIDTH: u16 = 200;
const HEIGHT: u16 = 200;
//const DPI: u16 = 184;
const DEFAULT_BACKGROUND_COLOR: Color = Color::White;
use hal::{
blocking::{
spi::Write,
delay::*
},
digital::*
};
use type_a::{
LUT_FULL_UPDATE,
LUT_PARTIAL_UPDATE,
command::Command
};
use drawing::color::Color;
use interface::*;
use interface::connection_interface::ConnectionInterface;
/// EPD1in54 driver
///
pub struct EPD1in54<SPI, CS, BUSY, DataCommand, RST, Delay> {
/// SPI
interface: ConnectionInterface<SPI, CS, BUSY, DataCommand, RST, Delay>,
/// EPD (width, height)
//epd: EPD,
/// Color
background_color: Color,
}
impl<SPI, CS, BUSY, DataCommand, RST, Delay, E> EPD1in54<SPI, CS, BUSY, DataCommand, RST, Delay>
where
SPI: Write<u8, Error = E>,
CS: OutputPin,
BUSY: InputPin,
DataCommand: OutputPin,
RST: OutputPin,
Delay: DelayUs<u16> + DelayMs<u16>
{
}
impl<SPI, CS, BUSY, DataCommand, RST, Delay, E> WaveshareInterface<SPI, CS, BUSY, DataCommand, RST, Delay, E>
for EPD1in54<SPI, CS, BUSY, DataCommand, RST, Delay>
where
SPI: Write<u8, Error = E>,
CS: OutputPin,
BUSY: InputPin,
DataCommand: OutputPin,
RST: OutputPin,
Delay: DelayUs<u16> + DelayMs<u16>,
{
fn get_width(&self) -> u16 {
WIDTH
}
fn get_height(&self) -> u16 {
HEIGHT
}
fn new(
interface: ConnectionInterface<SPI, CS, BUSY, DataCommand, RST, Delay>
) -> Result<Self, E> {
let mut epd = EPD1in54 {interface, background_color: DEFAULT_BACKGROUND_COLOR};
epd.init()?;
Ok(epd)
}
fn init(&mut self) -> Result<(), E> {
self.reset();
// 3 Databytes:
// A[7:0]
// 0.. A[8]
// 0.. B[2:0]
// Default Values: A = Height of Screen (0x127), B = 0x00 (GD, SM and TB=0?)
self.interface.send_command(Command::DRIVER_OUTPUT_CONTROL)?;
self.interface.send_data(HEIGHT as u8)?;
self.interface.send_data((HEIGHT >> 8) as u8)?;
self.interface.send_data(0x00)?;
// 3 Databytes: (and default values from datasheet and arduino)
// 1 .. A[6:0] = 0xCF | 0xD7
// 1 .. B[6:0] = 0xCE | 0xD6
// 1 .. C[6:0] = 0x8D | 0x9D
//TODO: test
self.interface.send_command(Command::BOOSTER_SOFT_START_CONTROL)?;
self.interface.send_data(0xD7)?;
self.interface.send_data(0xD6)?;
self.interface.send_data(0x9D)?;
// One Databyte with value 0xA8 for 7V VCOM
self.interface.send_command(Command::WRITE_VCOM_REGISTER)?;
self.interface.send_data(0xA8)?;
// One Databyte with default value 0x1A for 4 dummy lines per gate
self.interface.send_command(Command::SET_DUMMY_LINE_PERIOD)?;
self.interface.send_data(0x1A)?;
// One Databyte with default value 0x08 for 2us per line
self.interface.send_command(Command::SET_GATE_LINE_WIDTH)?;
self.interface.send_data(0x08)?;
// One Databyte with default value 0x03
// -> address: x increment, y increment, address counter is updated in x direction
self.interface.send_command(Command::DATA_ENTRY_MODE_SETTING)?;
self.interface.send_data(0x03)?;
self.set_lut()
}
fn sleep(&mut self) -> Result<(), E> {
self.interface.send_command(Command::DEEP_SLEEP_MODE)?;
// 0x00 for Normal mode (Power on Reset), 0x01 for Deep Sleep Mode
//TODO: is 0x00 needed here?
self.interface.send_data(0x00)?;
self.wait_until_idle();
Ok(())
}
fn reset(&mut self) {
self.interface.reset()
}
fn delay_ms(&mut self, delay: u16) {
self.interface.delay_ms(delay)
}
fn update_frame(&mut self, buffer: &[u8]) -> Result<(), E>{
self.use_full_frame()?;
self.interface.send_command(Command::WRITE_RAM)?;
self.interface.send_multiple_data(buffer)
}
//TODO: update description: last 3 bits will be ignored for width and x_pos
fn update_partial_frame(&mut self, buffer: &[u8], x: u16, y: u16, width: u16, height: u16) -> Result<(), E>{
self.set_ram_area(x, y, x + width, y + height)?;
self.set_ram_counter(x, y)?;
self.interface.send_command(Command::WRITE_RAM)?;
self.interface.send_multiple_data(buffer)
}
fn display_frame(&mut self) -> Result<(), E>{
// enable clock signal, enable cp, display pattern -> 0xC4 (tested with the arduino version)
//TODO: test control_1 or control_2 with default value 0xFF (from the datasheet)
self.interface.send_command(Command::DISPLAY_UPDATE_CONTROL_2)?;
self.interface.send_data(0xC4)?;
self.interface.send_command(Command::MASTER_ACTIVATION)?;
// MASTER Activation should not be interupted to avoid currption of panel images
// therefore a terminate command is send
self.interface.send_command(Command::TERMINATE_COMMANDS_AND_FRAME_WRITE)
}
fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>{
self.update_frame(buffer)?;
self.display_frame()
}
fn clear_frame(&mut self) -> Result<(), E>{
self.use_full_frame()?;
// clear the ram with the background color
let color = self.background_color.get_byte_value();
self.interface.send_command(Command::WRITE_RAM)?;
self.interface.send_data_x_times(color, WIDTH / 8 * HEIGHT)
}
/// Sets the backgroundcolor for various commands like [WaveshareInterface::clear_frame()](clear_frame())
fn set_background_color(&mut self, background_color: Color){
self.background_color = background_color;
}
}
impl<SPI, CS, BUSY, DC, RST, D, E> EPD1in54<SPI, CS, BUSY, DC, RST, D>
where
SPI: Write<u8, Error = E>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
D: DelayUs<u16> + DelayMs<u16>,
{
fn wait_until_idle(&mut self) {
self.interface.wait_until_idle(false);
}
pub(crate) fn use_full_frame(&mut self) -> Result<(), E> {
// choose full frame/ram
self.set_ram_area(0, 0, WIDTH - 1, HEIGHT - 1)?;
// start from the beginning
self.set_ram_counter(0,0)
}
pub(crate) fn set_ram_area(&mut self, start_x: u16, start_y: u16, end_x: u16, end_y: u16) -> Result<(), E> {
assert!(start_x < end_x);
assert!(start_y < end_y);
// x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram
// aren't relevant
self.interface.send_command(Command::SET_RAM_X_ADDRESS_START_END_POSITION)?;
self.interface.send_data((start_x >> 3) as u8)?;
self.interface.send_data((end_x >> 3) as u8)?;
// 2 Databytes: A[7:0] & 0..A[8] for each - start and end
self.interface.send_command(Command::SET_RAM_Y_ADDRESS_START_END_POSITION)?;
self.interface.send_data(start_y as u8)?;
self.interface.send_data((start_y >> 8) as u8)?;
self.interface.send_data(end_y as u8)?;
self.interface.send_data((end_y >> 8) as u8)
}
pub(crate) fn set_ram_counter(&mut self, x: u16, y: u16) -> Result<(), E> {
// x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram
// aren't relevant
self.interface.send_command(Command::SET_RAM_X_ADDRESS_COUNTER)?;
self.interface.send_data((x >> 3) as u8)?;
// 2 Databytes: A[7:0] & 0..A[8]
self.interface.send_command(Command::SET_RAM_Y_ADDRESS_COUNTER)?;
self.interface.send_data(y as u8)?;
self.interface.send_data((y >> 8) as u8)?;
self.wait_until_idle();
Ok(())
}
/// Uses the slower full update
pub fn set_lut(&mut self) -> Result<(), E> {
self.set_lut_helper(&LUT_FULL_UPDATE)
}
/// Uses the quick partial refresh
pub fn set_lut_quick(&mut self) -> Result<(), E> {
self.set_lut_helper(&LUT_PARTIAL_UPDATE)
}
//TODO: assert length for LUT is exactly 30
//fn set_lut_manual(&mut self, buffer: &[u8]) -> Result<(), E> {
// self.set_lut_helper(buffer)
//}
fn set_lut_helper(&mut self, buffer: &[u8]) -> Result<(), E> {
assert!(buffer.len() == 30);
self.interface.send_command(Command::WRITE_LUT_REGISTER)?;
self.interface.send_multiple_data(buffer)
}
}

@ -19,6 +19,9 @@
//! epd4in2.sleep();
//! ```
const WIDTH: u16 = 128;
const HEIGHT: u16 = 296;
const DEFAULT_BACKGROUND_COLOR: Color = Color::White;
use hal::{
blocking::{
@ -28,13 +31,17 @@ use hal::{
digital::*
};
mod constants;
use self::constants::*;
use type_a::{
LUT_FULL_UPDATE,
LUT_PARTIAL_UPDATE,
command::Command
};
use drawing::color::Color;
pub mod command;
pub use self::command::Command;
use interface::*;
@ -42,67 +49,58 @@ use interface::connection_interface::ConnectionInterface;
/// EPD2in9 driver
///
pub struct EPD2in9<SPI, CS, BUSY, DC, RST, D> {
pub struct EPD2in9<SPI, CS, BUSY, DataCommand, RST, Delay> {
/// SPI
interface: ConnectionInterface<SPI, CS, BUSY, DC, RST, D>,
/// Width
width: u16,
/// Height
height: u16,
interface: ConnectionInterface<SPI, CS, BUSY, DataCommand, RST, Delay>,
/// EPD (width, height)
//epd: EPD,
/// Color
background_color: Color,
}
impl<SPI, CS, BUSY, DC, RST, D, E> EPD2in9<SPI, CS, BUSY, DC, RST, D>
impl<SPI, CS, BUSY, DataCommand, RST, Delay, E> EPD2in9<SPI, CS, BUSY, DataCommand, RST, Delay>
where
SPI: Write<u8, Error = E>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
DataCommand: OutputPin,
RST: OutputPin,
D: DelayUs<u16> + DelayMs<u16>
Delay: DelayUs<u16> + DelayMs<u16>
{
}
impl<SPI, CS, BUSY, DC, RST, D, E> WaveshareInterface<SPI, CS, BUSY, DC, RST, D, E> for EPD2in9<SPI, CS, BUSY, DC, RST, D>
impl<SPI, CS, BUSY, DataCommand, RST, Delay, E> WaveshareInterface<SPI, CS, BUSY, DataCommand, RST, Delay, E>
for EPD2in9<SPI, CS, BUSY, DataCommand, RST, Delay>
where
SPI: Write<u8, Error = E>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
DataCommand: OutputPin,
RST: OutputPin,
D: DelayUs<u16> + DelayMs<u16>,
Delay: DelayUs<u16> + DelayMs<u16>,
{
fn get_width(&self) -> u16 {
self.width
WIDTH
}
fn get_height(&self) -> u16 {
self.height
HEIGHT
}
fn new(
spi: SPI,
cs: CS,
busy: BUSY,
dc: DC,
rst: RST,
delay: D
) -> Result<Self, E> {
let width = WIDTH as u16;
let height = HEIGHT as u16;
interface: ConnectionInterface<SPI, CS, BUSY, DataCommand, RST, Delay>
) -> Result<Self, E> {
//let epd = EPD::new(WIDTH, HEIGHT);
//let background_color = Color::White;
let interface = ConnectionInterface::new(spi, cs, busy, dc, rst, delay);
let background_color = Color::White;
let mut epd = EPD2in9 {interface, width, height, background_color};
let mut epd = EPD2in9 {interface, background_color: DEFAULT_BACKGROUND_COLOR};
epd.init()?;
@ -297,9 +295,9 @@ where
}
//TODO: assert length for LUT is exactly 30
fn set_lut_manual(&mut self, buffer: &[u8]) -> Result<(), E> {
self.set_lut_helper(buffer)
}
//fn set_lut_manual(&mut self, buffer: &[u8]) -> Result<(), E> {
// self.set_lut_helper(buffer)
//}
fn set_lut_helper(&mut self, buffer: &[u8]) -> Result<(), E> {

@ -10,7 +10,7 @@ use interface;
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
pub enum Command {
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

@ -1,5 +1,5 @@
pub(crate) const WIDTH: usize = 400;
pub(crate) const HEIGHT: usize = 300;
pub(crate) const WIDTH: u16 = 400;
pub(crate) const HEIGHT: u16 = 300;
pub(crate) const LUT_VCOM0: [u8; 44] = [
0x00, 0x17, 0x00, 0x00, 0x00, 0x02,

@ -49,7 +49,6 @@
use hal::{
blocking::{delay::*, spi::Write},
digital::*,
spi::{Mode, Phase, Polarity},
};
use interface::{connection_interface::ConnectionInterface, WaveshareInterface};
@ -61,19 +60,13 @@ use self::constants::*;
use drawing::color::Color;
pub mod command;
pub use self::command::Command;
//TODO: test spi mode
/// SPI mode -
/// For more infos see [Requirements: SPI](index.html#spi)
pub const SPI_MODE: Mode = Mode {
phase: Phase::CaptureOnFirstTransition,
polarity: Polarity::IdleLow,
};
use self::command::Command;
/// EPD4in2 driver
///
pub struct EPD4in2<SPI, CS, BUSY, DC, RST, D> {
pub struct EPD4in2<SPI, CS, BUSY, DC, RST, D>
{
/// Connection Interface
interface: ConnectionInterface<SPI, CS, BUSY, DC, RST, D>,
/// Width
@ -84,15 +77,17 @@ pub struct EPD4in2<SPI, CS, BUSY, DC, RST, D> {
color: Color,
}
impl<SPI, CS, BUSY, DC, RST, D, E> WaveshareInterface<SPI, CS, BUSY, DC, RST, D, E>
for EPD4in2<SPI, CS, BUSY, DC, RST, D>
where
SPI: Write<u8, Error = E>,
impl<SPI, CS, BUSY, DataCommand, RST, Delay, SpiError> WaveshareInterface<SPI, CS, BUSY, DataCommand, RST, Delay, SpiError>
for EPD4in2<SPI, CS, BUSY, DataCommand, RST, Delay>
where
SPI: Write<u8, Error = SpiError>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
DataCommand: OutputPin,
RST: OutputPin,
D: DelayUs<u16> + DelayMs<u16>,
Delay: DelayUs<u16> + DelayMs<u16>,
{
fn get_width(&self) -> u16 {
self.width
@ -117,11 +112,11 @@ where
///
/// epd4in2.sleep();
/// ```
fn new(spi: SPI, cs: CS, busy: BUSY, dc: DC, rst: RST, delay: D) -> Result<Self, E> {
fn new(interface: ConnectionInterface<SPI, CS, BUSY, DataCommand, RST, Delay>) -> Result<Self, SpiError> {
let width = WIDTH as u16;
let height = HEIGHT as u16;
let interface = ConnectionInterface::new(spi, cs, busy, dc, rst, delay);
let color = Color::White;
let mut epd = EPD4in2 {
interface,
@ -135,7 +130,7 @@ where
Ok(epd)
}
fn init(&mut self) -> Result<(), E> {
fn init(&mut self) -> Result<(), SpiError> {
// reset the device
self.reset();
@ -182,7 +177,7 @@ where
Ok(())
}
fn sleep(&mut self) -> Result<(), E> {
fn sleep(&mut self) -> Result<(), SpiError> {
self.send_command(Command::VCOM_AND_DATA_INTERVAL_SETTING)?;
self.send_data(0x17)?; //border floating
self.send_command(Command::VCM_DC_SETTING)?; // VCOM to 0V
@ -211,7 +206,7 @@ where
self.interface.delay_ms(delay)
}
fn update_frame(&mut self, buffer: &[u8]) -> Result<(), E> {
fn update_frame(&mut self, buffer: &[u8]) -> Result<(), SpiError> {
let color_value = self.color.get_byte_value();
self.send_resolution()?;
@ -247,7 +242,8 @@ where
y: u16,
width: u16,
height: u16,
) -> Result<(), E> {
) -> Result<(), SpiError> {
if buffer.len() as u16 != width / 8 * height {
//TODO: panic!! or sth like that
//return Err("Wrong buffersize");
@ -283,13 +279,13 @@ where
self.send_command(Command::PARTIAL_OUT)
}
fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>{
fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), SpiError>{
self.update_frame(buffer)?;
self.display_frame()
}
fn display_frame(&mut self) -> Result<(), E> {
fn display_frame(&mut self) -> Result<(), SpiError> {
self.send_command(Command::DISPLAY_REFRESH)?;
self.wait_until_idle();
@ -299,7 +295,7 @@ where
// TODO: add this abstraction function
// fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>;
fn clear_frame(&mut self) -> Result<(), E> {
fn clear_frame(&mut self) -> Result<(), SpiError> {
self.send_resolution()?;
let size = self.width / 8 * self.height;
@ -327,24 +323,24 @@ where
}
}
impl<SPI, CS, BUSY, DC, RST, D, E> EPD4in2<SPI, CS, BUSY, DC, RST, D>
impl<SPI, CS, BUSY, DC, RST, D, SpiError> EPD4in2<SPI, CS, BUSY, DC, RST, D>
where
SPI: Write<u8, Error = E>,
SPI: Write<u8, Error = SpiError>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
D: DelayUs<u16> + DelayMs<u16>,
{
fn send_command(&mut self, command: Command) -> Result<(), E> {
fn send_command(&mut self, command: Command) -> Result<(), SpiError> {
self.interface.send_command(command)
}
fn send_data(&mut self, val: u8) -> Result<(), E> {
fn send_data(&mut self, val: u8) -> Result<(), SpiError> {
self.interface.send_data(val)
}
fn send_multiple_data(&mut self, data: &[u8]) -> Result<(), E> {
fn send_multiple_data(&mut self, data: &[u8]) -> Result<(), SpiError> {
self.interface.send_multiple_data(data)
}
@ -352,7 +348,7 @@ where
self.interface.wait_until_idle(true)
}
fn send_resolution(&mut self) -> Result<(), E> {
fn send_resolution(&mut self) -> Result<(), SpiError> {
let w = self.get_width();
let h = self.get_height();
@ -365,7 +361,7 @@ where
/// Fill the look-up table for the EPD
//TODO: make public?
fn set_lut(&mut self) -> Result<(), E> {
fn set_lut(&mut self) -> Result<(), SpiError> {
self.set_lut_helper(&LUT_VCOM0, &LUT_WW, &LUT_BW, &LUT_WB, &LUT_BB)
}
@ -374,7 +370,7 @@ where
/// Is automatically done by [EPD4in2::display_frame_quick()](EPD4in2::display_frame_quick())
/// //TODO: make public?
#[cfg(feature = "epd4in2_fast_update")]
fn set_lut_quick(&mut self) -> Result<(), E> {
fn set_lut_quick(&mut self) -> Result<(), SpiError> {
self.set_lut_helper(
&LUT_VCOM0_QUICK,
&LUT_WW_QUICK,
@ -391,7 +387,7 @@ where
lut_bw: &[u8],
lut_wb: &[u8],
lut_bb: &[u8],
) -> Result<(), E> {
) -> Result<(), SpiError> {
// LUT VCOM
self.send_command(Command::LUT_FOR_VCOM)?;
self.send_multiple_data(lut_vcom)?;

@ -10,7 +10,7 @@ use interface::Command;
/// The Connection Interface of all (?) Waveshare EPD-Devices
///
pub(crate) struct ConnectionInterface<SPI, CS, BUSY, DC, RST, D> {
pub struct ConnectionInterface<SPI, CS, BUSY, DC, RST, D> {
/// SPI
spi: SPI,
/// CS for SPI
@ -26,16 +26,16 @@ pub(crate) struct ConnectionInterface<SPI, CS, BUSY, DC, RST, D> {
}
impl<SPI, CS, BUSY, DC, RST, D, E> ConnectionInterface<SPI, CS, BUSY, DC, RST, D>
impl<SPI, CS, BUSY, DataCommand, RST, Delay, ErrorSpeziale> ConnectionInterface<SPI, CS, BUSY, DataCommand, RST, Delay>
where
SPI: Write<u8, Error = E>,
SPI: Write<u8, Error = ErrorSpeziale>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
DataCommand: OutputPin,
RST: OutputPin,
D: DelayUs<u16> + DelayMs<u16>,
Delay: DelayUs<u16> + DelayMs<u16>,
{
pub(crate) fn new(spi: SPI, cs: CS, busy: BUSY, dc: DC, rst: RST, delay: D) -> Self {
pub fn new(spi: SPI, cs: CS, busy: BUSY, dc: DataCommand, rst: RST, delay: Delay) -> Self {
ConnectionInterface {spi, cs, busy, dc, rst, delay }
}
@ -44,7 +44,7 @@ where
/// Enables direct interaction with the device with the help of [send_data()](ConnectionInterface::send_data())
/// Should rarely be needed!
/// //TODO: make public?
pub(crate) fn send_command<T: Command>(&mut self, command: T) -> Result<(), E> {
pub(crate) fn send_command<T: Command>(&mut self, command: T) -> Result<(), ErrorSpeziale> {
// low for commands
self.dc.set_low();
@ -60,7 +60,7 @@ where
///
/// Should rarely be needed!
/// //TODO: make public?
pub(crate) fn send_data(&mut self, val: u8) -> Result<(), E> {
pub(crate) fn send_data(&mut self, val: u8) -> Result<(), ErrorSpeziale> {
// high for data
self.dc.set_high();
@ -76,7 +76,7 @@ where
///
/// Should rarely be needed!
/// //TODO: make public?
pub(crate) fn send_data_x_times(&mut self, val: u8, repetitions: u16) -> Result<(), E> {
pub(crate) fn send_data_x_times(&mut self, val: u8, repetitions: u16) -> Result<(), ErrorSpeziale> {
// high for data
self.dc.set_high();
@ -95,7 +95,7 @@ where
///
/// Should rarely be needed!
/// //TODO: make public?
pub(crate) fn send_multiple_data(&mut self, data: &[u8]) -> Result<(), E> {
pub(crate) fn send_multiple_data(&mut self, data: &[u8]) -> Result<(), ErrorSpeziale> {
// high for data
self.dc.set_high();
@ -106,9 +106,9 @@ where
}
// spi write helper/abstraction function
pub(crate) fn with_cs<F>(&mut self, f: F) -> Result<(), E>
pub(crate) fn with_cs<F>(&mut self, f: F) -> Result<(), ErrorSpeziale>
where
F: FnOnce(&mut Self) -> Result<(), E>,
F: FnOnce(&mut Self) -> Result<(), ErrorSpeziale>,
{
// activate spi with cs low
self.cs.set_low();
@ -129,8 +129,8 @@ where
///
/// is_busy_low
///
/// - TRUE for epd4in2, epd1in54, epd2in13, epd2in7, epd5in83, epd7in5
/// - FALSE for epd2in9
/// - TRUE for epd4in2, epd2in13, epd2in7, epd5in83, epd7in5
/// - FALSE for epd2in9, epd1in54 (for all Display Type A ones?)
///
/// Most likely there was a mistake with the 2in9 busy connection
pub(crate) fn wait_until_idle(&mut self, is_busy_low: bool) {

@ -7,11 +7,11 @@ use hal::{
};
use core::marker::Sized;
use drawing::color::Color;
/// Interface for the physical connection between display and the controlling device
pub mod connection_interface;
use self::connection_interface::ConnectionInterface;
/// All commands need to have this trait which gives the address of the command
@ -21,6 +21,8 @@ pub(crate) trait Command {
}
//TODO: add LUT trait with set_fast_lut and set_manual_lut and set_normal_lut or sth like that?
// for partial updates
trait LUTSupport<Error> {
@ -30,14 +32,14 @@ trait LUTSupport<Error> {
}
pub trait WaveshareInterface<SPI, CS, BUSY, DC, RST, D, E>
pub trait WaveshareInterface<SPI, CS, BUSY, DataCommand, RST, Delay, Error>
where
SPI: Write<u8, Error = E>,
SPI: Write<u8>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
DataCommand: OutputPin,
RST: OutputPin,
D: DelayUs<u16> + DelayMs<u16>,
Delay: DelayUs<u16> + DelayMs<u16>,
{
/// Get the width of the display
fn get_width(&self) -> u16;
@ -49,13 +51,8 @@ pub trait WaveshareInterface<SPI, CS, BUSY, DC, RST, D, E>
///
/// This already initialises the device. That means [init()](WaveshareInterface::init()) isn't needed directly afterwards
fn new(
spi: SPI,
cs: CS,
busy: BUSY,
dc: DC,
rst: RST,
delay: D
) -> Result<Self, E>
interface: ConnectionInterface<SPI, CS, BUSY, DataCommand, RST, Delay>
) -> Result<Self, Error>
where Self: Sized;
/// This initialises the EPD and powers it up
@ -65,13 +62,13 @@ pub trait WaveshareInterface<SPI, CS, BUSY, DC, RST, D, E>
/// This function calls [reset()](WaveshareInterface::reset()),
/// so you don't need to call reset your self when trying to wake your device up
/// after setting it to sleep.
fn init(&mut self) -> Result<(), E>;
fn init(&mut self) -> Result<(), Error>;
// void DisplayFrame(const unsigned char* frame_buffer);
/// Transmit a full frame to the SRAM of the DPD
///
fn update_frame(&mut self, buffer: &[u8]) -> Result<(), E>;
fn update_frame(&mut self, buffer: &[u8]) -> Result<(), Error>;
//TODO: is dtm always used?
/// Transmit partial data to the SRAM of the EPD,
@ -81,18 +78,18 @@ pub trait WaveshareInterface<SPI, CS, BUSY, DC, RST, D, E>
/// Normally it should be dtm2, so use false
///
/// BUFFER needs to be of size: w / 8 * l !
fn update_partial_frame(&mut self, buffer: &[u8], x: u16, y: u16, width: u16, height: u16) -> Result<(), E>;
fn update_partial_frame(&mut self, buffer: &[u8], x: u16, y: u16, width: u16, height: u16) -> Result<(), Error>;
/// Displays the frame data from SRAM
fn display_frame(&mut self) -> Result<(), E>;
fn display_frame(&mut self) -> Result<(), Error>;
// TODO: add this abstraction function
fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>;
fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), Error>;
/// Clears the frame from the buffer
///
/// Uses the chosen background color
fn clear_frame(&mut self) -> Result<(), E>;
fn clear_frame(&mut self) -> Result<(), Error>;
/// Sets the backgroundcolor for various commands like [clear_frame()](WaveshareInterface::clear_frame())
fn set_background_color(&mut self, color: Color);
@ -104,7 +101,7 @@ pub trait WaveshareInterface<SPI, CS, BUSY, DC, RST, D, E>
/// But you can also use [reset()](WaveshareInterface::reset()) to awaken.
/// But as you need to power it up once more anyway you can also just directly use [init()](WaveshareInterface::init()) for resetting
/// and initialising which already contains the reset
fn sleep(&mut self) -> Result<(), E>;
fn sleep(&mut self) -> Result<(), Error>;
/// Resets the device.
///

@ -51,14 +51,32 @@ use hal::{
};
pub mod drawing;
pub mod epd4in2;
mod interface;
pub use interface::{
WaveshareInterface,
connection_interface::ConnectionInterface};
#[cfg(feature="epd4in2")]
mod epd4in2;
#[cfg(feature="epd4in2")]
pub use epd4in2::EPD4in2;
pub mod epd2in9;
#[cfg(feature="epd1in54")]
mod epd1in54;
#[cfg(feature="epd1in54")]
pub use epd1in54::EPD1in54;
pub mod interface;
#[cfg(feature="epd2in9")]
mod epd2in9;
///2in9 eink
#[cfg(feature="epd2in9")]
///2in9 eink
pub use epd2in9::EPD2in9;
#[cfg(any(feature="epd1in54", feature="epd2in9"))]
pub mod type_a;
//TODO: test spi mode

@ -1,19 +1,17 @@
//! SPI Commands for the Waveshare 2.9" E-Ink Display
//! SPI Commands for the Waveshare 2.9" and 1.54" E-Ink Display
use interface;
/// EPD2IN9 commands
/// EPD1in54 and EPD2IN9 commands
///
/// Should rarely (never?) be needed directly.
///
/// For more infos about the addresses and what they are doing look into the pdfs
///
/// The description of the single commands is mostly taken from IL0398.pdf
//#[allow(dead_code)]
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
pub enum Command {
pub(crate) enum Command {
/// Driver Output control
/// 3 Databytes:
/// A[7:0]
@ -28,6 +26,7 @@ pub enum Command {
/// 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
@ -92,8 +91,8 @@ mod tests {
fn command_addr() {
assert_eq!(Command::DRIVER_OUTPUT_CONTROL.address(), 0x01);
//assert_eq!(Command::PANEL_SETTING.addr(), 0x00);
assert_eq!(Command::SET_RAM_X_ADDRESS_COUNTER.address(), 0x4E);
//assert_eq!(Command::DISPLAY_REFRESH.addr(), 0x12);
assert_eq!(Command::TERMINATE_COMMANDS_AND_FRAME_WRITE.address(), 0xFF);
}
}

@ -1,5 +1,4 @@
pub(crate) const WIDTH: u16 = 128;
pub(crate) const HEIGHT: u16 = 296;
pub(crate) mod command;
// Original Waveforms from Waveshare
pub(crate) const LUT_FULL_UPDATE: [u8; 30] =[
@ -14,4 +13,4 @@ pub(crate) const LUT_PARTIAL_UPDATE: [u8; 30] =[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x13, 0x14, 0x44, 0x12,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
];
];
Loading…
Cancel
Save