Compare commits

...

1 Commits

@ -34,4 +34,4 @@ version = "0.4.3"
[dependencies.embedded-hal]
features = ["unproven"]
version = "0.2.1"
version = "0.2.3"

@ -41,7 +41,7 @@ const IS_BUSY_LOW: bool = false;
use embedded_hal::{
blocking::{delay::*, spi::Write},
digital::*,
digital::v2::*,
};
use crate::type_a::{
@ -51,7 +51,7 @@ use crate::type_a::{
use crate::color::Color;
use crate::traits::{RefreshLUT, WaveshareDisplay};
use crate::traits::*;
use crate::interface::DisplayInterface;
@ -64,34 +64,35 @@ pub use crate::epd1in54::graphics::Display1in54;
///
pub struct EPD1in54<SPI, CS, BUSY, DC, RST> {
/// SPI
interface: DisplayInterface<SPI, CS, BUSY, DC, RST>,
di: DisplayInterface<SPI, CS, BUSY, DC, RST>,
/// Color
background_color: Color,
/// Refresh LUT
refresh: RefreshLUT,
}
impl<SPI, CS, BUSY, DC, RST> EPD1in54<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST, SpiE, PinRE, PinWE> InternalWiAdditions<SPI, CS, BUSY, DC, RST, SpiE, PinRE, PinWE>
for EPD1in54<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
SPI: Write<u8, Error = SpiE>,
CS: OutputPin<Error = PinWE>,
BUSY: InputPin<Error = PinRE>,
DC: OutputPin<Error = PinWE>,
RST: OutputPin<Error = PinWE>,
{
fn init<DELAY: DelayMs<u8>>(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
) -> Result<(), SPI::Error> {
self.interface.reset(delay);
self.di.reset(delay);
// 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.cmd_with_data(
self.di.cmd_with_data(
spi,
Command::DRIVER_OUTPUT_CONTROL,
&[HEIGHT as u8, (HEIGHT >> 8) as u8, 0x00],
@ -102,27 +103,27 @@ where
// 1 .. B[6:0] = 0xCE | 0xD6
// 1 .. C[6:0] = 0x8D | 0x9D
//TODO: test
self.interface.cmd_with_data(
self.di.cmd_with_data(
spi,
Command::BOOSTER_SOFT_START_CONTROL,
&[0xD7, 0xD6, 0x9D],
)?;
// One Databyte with value 0xA8 for 7V VCOM
self.interface
self.di
.cmd_with_data(spi, Command::WRITE_VCOM_REGISTER, &[0xA8])?;
// One Databyte with default value 0x1A for 4 dummy lines per gate
self.interface
self.di
.cmd_with_data(spi, Command::SET_DUMMY_LINE_PERIOD, &[0x1A])?;
// One Databyte with default value 0x08 for 2us per line
self.interface
self.di
.cmd_with_data(spi, Command::SET_GATE_LINE_WIDTH, &[0x08])?;
// One Databyte with default value 0x03
// -> address: x increment, y increment, address counter is updated in x direction
self.interface
self.di
.cmd_with_data(spi, Command::DATA_ENTRY_MODE_SETTING, &[0x03])?;
self.set_lut(spi, None)?;
@ -132,14 +133,14 @@ where
}
}
impl<SPI, CS, BUSY, DC, RST, E> WaveshareDisplay<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST, SpiE, PinRE, PinWE> WaveshareDisplay<SPI, CS, BUSY, DC, RST, SpiE, PinRE, PinWE>
for EPD1in54<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8, Error = E>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
SPI: Write<u8, Error = SpiE>,
CS: OutputPin<Error = PinWE>,
BUSY: InputPin<Error = PinRE>,
DC: OutputPin<Error = PinWE>,
RST: OutputPin<Error = PinWE>,
{
fn width(&self) -> u32 {
WIDTH
@ -181,7 +182,7 @@ where
fn sleep(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
// 0x00 for Normal mode (Power on Reset), 0x01 for Deep Sleep Mode
//TODO: is 0x00 needed here or would 0x01 be even more efficient?
self.interface
self.di
.cmd_with_data(spi, Command::DEEP_SLEEP_MODE, &[0x00])?;
self.wait_until_idle();
@ -190,7 +191,7 @@ where
fn update_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> {
self.use_full_frame(spi)?;
self.interface
self.di
.cmd_with_data(spi, Command::WRITE_RAM, buffer)?;
self.wait_until_idle();
@ -210,7 +211,7 @@ where
self.set_ram_area(spi, x, y, x + width, y + height)?;
self.set_ram_counter(spi, x, y)?;
self.interface
self.di
.cmd_with_data(spi, Command::WRITE_RAM, buffer)?;
self.wait_until_idle();
@ -220,13 +221,13 @@ where
fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
// 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
self.di
.cmd_with_data(spi, Command::DISPLAY_UPDATE_CONTROL_2, &[0xC4])?;
self.interface.cmd(spi, Command::MASTER_ACTIVATION)?;
self.di.cmd(spi, Command::MASTER_ACTIVATION)?;
// MASTER Activation should not be interupted to avoid currption of panel images
// therefore a terminate command is send
self.interface.cmd(spi, Command::NOP)?;
self.di.cmd(spi, Command::NOP)?;
self.wait_until_idle();
Ok(())
@ -238,8 +239,8 @@ where
// clear the ram with the background color
let color = self.background_color.get_byte_value();
self.interface.cmd(spi, Command::WRITE_RAM)?;
self.interface
self.di.cmd(spi, Command::WRITE_RAM)?;
self.di
.data_x_times(spi, color, WIDTH / 8 * HEIGHT)?;
self.wait_until_idle();
@ -269,7 +270,7 @@ where
}
fn is_busy(&self) -> bool {
self.interface.is_busy(IS_BUSY_LOW)
self.di.is_busy(IS_BUSY_LOW)
}
}
@ -282,7 +283,7 @@ where
RST: OutputPin,
{
fn wait_until_idle(&mut self) {
self.interface.wait_until_idle(IS_BUSY_LOW);
self.di.wait_until_idle(IS_BUSY_LOW);
}
pub(crate) fn use_full_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
@ -306,14 +307,14 @@ where
// 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.cmd_with_data(
self.di.cmd_with_data(
spi,
Command::SET_RAM_X_ADDRESS_START_END_POSITION,
&[(start_x >> 3) as u8, (end_x >> 3) as u8],
)?;
// 2 Databytes: A[7:0] & 0..A[8] for each - start and end
self.interface.cmd_with_data(
self.di.cmd_with_data(
spi,
Command::SET_RAM_Y_ADDRESS_START_END_POSITION,
&[
@ -336,11 +337,11 @@ where
) -> Result<(), SPI::Error> {
// 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
self.di
.cmd_with_data(spi, Command::SET_RAM_X_ADDRESS_COUNTER, &[(x >> 3) as u8])?;
// 2 Databytes: A[7:0] & 0..A[8]
self.interface.cmd_with_data(
self.di.cmd_with_data(
spi,
Command::SET_RAM_Y_ADDRESS_COUNTER,
&[y as u8, (y >> 8) as u8],
@ -353,7 +354,7 @@ where
fn set_lut_helper(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> {
assert!(buffer.len() == 30);
self.interface
self.di
.cmd_with_data(spi, Command::WRITE_LUT_REGISTER, buffer)?;
self.wait_until_idle();

@ -42,7 +42,7 @@ const IS_BUSY_LOW: bool = false;
use embedded_hal::{
blocking::{delay::*, spi::Write},
digital::*,
digital::v2::*,
};
use crate::type_a::{
@ -65,34 +65,35 @@ pub use crate::epd2in9::graphics::Display2in9;
///
pub struct EPD2in9<SPI, CS, BUSY, DC, RST> {
/// SPI
interface: DisplayInterface<SPI, CS, BUSY, DC, RST>,
di: DisplayInterface<SPI, CS, BUSY, DC, RST>,
/// Color
background_color: Color,
/// Refresh LUT
refresh: RefreshLUT,
}
impl<SPI, CS, BUSY, DC, RST> EPD2in9<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST, SpiE, PinRE, PinWE> InternalWiAdditions<SPI, CS, BUSY, DC, RST, SpiE, PinRE, PinWE>
for EPD2in9<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
SPI: Write<u8, Error = SpiE>,
CS: OutputPin<Error = PinWE>,
BUSY: InputPin<Error = PinRE>,
DC: OutputPin<Error = PinWE>,
RST: OutputPin<Error = PinWE>,
{
fn init<DELAY: DelayMs<u8>>(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
) -> Result<(), SPI::Error> {
self.interface.reset(delay);
self.di.reset(delay);
// 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
self.di
.cmd_with_data(spi, Command::DRIVER_OUTPUT_CONTROL, &[0x27, 0x01, 0x00])?;
// 3 Databytes: (and default values from datasheet and arduino)
@ -100,41 +101,41 @@ where
// 1 .. B[6:0] = 0xCE | 0xD6
// 1 .. C[6:0] = 0x8D | 0x9D
//TODO: test
self.interface.cmd_with_data(
self.di.cmd_with_data(
spi,
Command::BOOSTER_SOFT_START_CONTROL,
&[0xD7, 0xD6, 0x9D],
)?;
// One Databyte with value 0xA8 for 7V VCOM
self.interface
self.di
.cmd_with_data(spi, Command::WRITE_VCOM_REGISTER, &[0xA8])?;
// One Databyte with default value 0x1A for 4 dummy lines per gate
self.interface
self.di
.cmd_with_data(spi, Command::SET_DUMMY_LINE_PERIOD, &[0x1A])?;
// One Databyte with default value 0x08 for 2us per line
self.interface
self.di
.cmd_with_data(spi, Command::SET_GATE_LINE_WIDTH, &[0x08])?;
// One Databyte with default value 0x03
// -> address: x increment, y increment, address counter is updated in x direction
self.interface
self.di
.cmd_with_data(spi, Command::DATA_ENTRY_MODE_SETTING, &[0x03])?;
self.set_lut(spi, None)
}
}
impl<SPI, CS, BUSY, DC, RST> WaveshareDisplay<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST, SpiE, PinRE, PinWE> WaveshareDisplay<SPI, CS, BUSY, DC, RST, SpiE, PinRE, PinWE>
for EPD2in9<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
SPI: Write<u8, Error = SpiE>,
CS: OutputPin<Error = PinWE>,
BUSY: InputPin<Error = PinRE>,
DC: OutputPin<Error = PinWE>,
RST: OutputPin<Error = PinWE>,
{
fn width(&self) -> u32 {
WIDTH
@ -152,10 +153,10 @@ where
rst: RST,
delay: &mut DELAY,
) -> Result<Self, SPI::Error> {
let interface = DisplayInterface::new(cs, busy, dc, rst);
let di = DisplayInterface::new(cs, busy, dc, rst);
let mut epd = EPD2in9 {
interface,
di,
background_color: DEFAULT_BACKGROUND_COLOR,
refresh: RefreshLUT::FULL,
};
@ -168,7 +169,7 @@ where
fn sleep(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
// 0x00 for Normal mode (Power on Reset), 0x01 for Deep Sleep Mode
//TODO: is 0x00 needed here? (see also epd1in54)
self.interface
self.di
.cmd_with_data(spi, Command::DEEP_SLEEP_MODE, &[0x00])?;
self.wait_until_idle();
@ -189,7 +190,7 @@ where
fn update_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> {
self.use_full_frame(spi)?;
self.interface
self.di
.cmd_with_data(spi, Command::WRITE_RAM, buffer)?;
self.wait_until_idle();
@ -209,7 +210,7 @@ where
self.set_ram_area(spi, x, y, x + width, y + height)?;
self.set_ram_counter(spi, x, y)?;
self.interface
self.di
.cmd_with_data(spi, Command::WRITE_RAM, buffer)?;
self.wait_until_idle();
@ -219,13 +220,13 @@ where
fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
// 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
self.di
.cmd_with_data(spi, Command::DISPLAY_UPDATE_CONTROL_2, &[0xC4])?;
self.interface.cmd(spi, Command::MASTER_ACTIVATION)?;
self.di.cmd(spi, Command::MASTER_ACTIVATION)?;
// MASTER Activation should not be interupted to avoid currption of panel images
// therefore a terminate command is send
self.interface.cmd(spi, Command::NOP)?;
self.di.cmd(spi, Command::NOP)?;
self.wait_until_idle();
Ok(())
@ -237,8 +238,8 @@ where
// clear the ram with the background color
let color = self.background_color.get_byte_value();
self.interface.cmd(spi, Command::WRITE_RAM)?;
self.interface
self.di.cmd(spi, Command::WRITE_RAM)?;
self.di
.data_x_times(spi, color, WIDTH / 8 * HEIGHT)?;
self.wait_until_idle();
@ -268,7 +269,7 @@ where
}
fn is_busy(&self) -> bool {
self.interface.is_busy(IS_BUSY_LOW)
self.di.is_busy(IS_BUSY_LOW)
}
}
@ -281,7 +282,7 @@ where
RST: OutputPin,
{
fn wait_until_idle(&mut self) {
self.interface.wait_until_idle(IS_BUSY_LOW);
self.di.wait_until_idle(IS_BUSY_LOW);
}
fn use_full_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
@ -305,14 +306,14 @@ where
// 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.cmd_with_data(
self.di.cmd_with_data(
spi,
Command::SET_RAM_X_ADDRESS_START_END_POSITION,
&[(start_x >> 3) as u8, (end_x >> 3) as u8],
)?;
// 2 Databytes: A[7:0] & 0..A[8] for each - start and end
self.interface.cmd_with_data(
self.di.cmd_with_data(
spi,
Command::SET_RAM_Y_ADDRESS_START_END_POSITION,
&[
@ -327,11 +328,11 @@ where
fn set_ram_counter(&mut self, spi: &mut SPI, x: u32, y: u32) -> Result<(), SPI::Error> {
// 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
self.di
.cmd_with_data(spi, Command::SET_RAM_X_ADDRESS_COUNTER, &[(x >> 3) as u8])?;
// 2 Databytes: A[7:0] & 0..A[8]
self.interface.cmd_with_data(
self.di.cmd_with_data(
spi,
Command::SET_RAM_Y_ADDRESS_COUNTER,
&[y as u8, (y >> 8) as u8],
@ -344,7 +345,7 @@ where
/// Set your own LUT, this function is also used internally for set_lut
fn set_lut_helper(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> {
assert!(buffer.len() == 30);
self.interface
self.di
.cmd_with_data(spi, Command::WRITE_LUT_REGISTER, buffer)?;
self.wait_until_idle();
Ok(())

@ -48,8 +48,9 @@
use embedded_hal::{
blocking::{delay::*, spi::Write},
digital::*,
digital::v2::*,
};
use crate::Error;
use crate::interface::DisplayInterface;
use crate::traits::{InternalWiAdditions, RefreshLUT, WaveshareDisplay};
@ -77,54 +78,56 @@ pub use self::graphics::Display4in2;
///
pub struct EPD4in2<SPI, CS, BUSY, DC, RST> {
/// Connection Interface
interface: DisplayInterface<SPI, CS, BUSY, DC, RST>,
di: DisplayInterface<SPI, CS, BUSY, DC, RST>,
/// Background Color
color: Color,
/// Refresh LUT
refresh: RefreshLUT,
}
impl<SPI, CS, BUSY, DC, RST> InternalWiAdditions<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST, SpiE, PinRE, PinWE> InternalWiAdditions<SPI, CS, BUSY, DC, RST, SpiE, PinRE, PinWE>
for EPD4in2<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
SPI: Write<u8, Error = SpiE>,
CS: OutputPin<Error = PinWE>,
BUSY: InputPin<Error = PinRE>,
DC: OutputPin<Error = PinWE>,
RST: OutputPin<Error = PinWE>,
{
type Error = Error<SpiE, PinRE, PinWE>;
fn init<DELAY: DelayMs<u8>>(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
) -> Result<(), SPI::Error> {
) -> Result<(), Self::Error> {
// reset the device
self.interface.reset(delay);
self.di.reset(delay);
// set the power settings
self.interface.cmd_with_data(
self.di.cmd_with_data(
spi,
Command::POWER_SETTING,
&[0x03, 0x00, 0x2b, 0x2b, 0xff],
)?;
// start the booster
self.interface
self.di
.cmd_with_data(spi, Command::BOOSTER_SOFT_START, &[0x17, 0x17, 0x17])?;
// power on
self.command(spi, Command::POWER_ON)?;
self.di.cmd(spi, Command::POWER_ON)?;
delay.delay_ms(5);
self.wait_until_idle();
// set the panel settings
self.cmd_with_data(spi, Command::PANEL_SETTING, &[0x3F])?;
self.di.cmd_with_data(spi, Command::PANEL_SETTING, &[0x3F])?;
// Set Frequency, 200 Hz didn't work on my board
// 150Hz and 171Hz wasn't tested yet
// TODO: Test these other frequencies
// 3A 100HZ 29 150Hz 39 200HZ 31 171HZ DEFAULT: 3c 50Hz
self.cmd_with_data(spi, Command::PLL_CONTROL, &[0x3A])?;
self.di.cmd_with_data(spi, Command::PLL_CONTROL, &[0x3A])?;
self.set_lut(spi, None)?;
@ -133,15 +136,17 @@ where
}
}
impl<SPI, CS, BUSY, DC, RST> WaveshareDisplay<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST, SpiE, PinRE, PinWE> WaveshareDisplay<SPI, CS, BUSY, DC, RST, SpiE, PinRE, PinWE>
for EPD4in2<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
SPI: Write<u8, Error = SpiE>,
CS: OutputPin<Error = PinWE>,
BUSY: InputPin<Error = PinRE>,
DC: OutputPin<Error = PinWE>,
RST: OutputPin<Error = PinWE>,
{
type Error = Error<SpiE, PinRE, PinWE>;
/// Creates a new driver from a SPI peripheral, CS Pin, Busy InputPin, DC
///
/// This already initialises the device. That means [init()](init()) isn't needed directly afterwards
@ -165,11 +170,11 @@ where
rst: RST,
delay: &mut DELAY,
) -> Result<Self, SPI::Error> {
let interface = DisplayInterface::new(cs, busy, dc, rst);
let di = DisplayInterface::new(cs, busy, dc, rst);
let color = DEFAULT_BACKGROUND_COLOR;
let mut epd = EPD4in2 {
interface,
di,
color,
refresh: RefreshLUT::FULL,
};
@ -188,19 +193,19 @@ where
}
fn sleep(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
self.interface
self.di
.cmd_with_data(spi, Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x17])?; //border floating
self.command(spi, Command::VCM_DC_SETTING)?; // VCOM to 0V
self.command(spi, Command::PANEL_SETTING)?;
self.di.cmd(spi, Command::VCM_DC_SETTING)?; // VCOM to 0V
self.di.cmd(spi, Command::PANEL_SETTING)?;
self.command(spi, Command::POWER_SETTING)?; //VG&VS to 0V fast
self.di.cmd(spi, Command::POWER_SETTING)?; //VG&VS to 0V fast
for _ in 0..4 {
self.send_data(spi, &[0x00])?;
self.di.data(spi, &[0x00])?;
}
self.command(spi, Command::POWER_OFF)?;
self.di.cmd(spi, Command::POWER_OFF)?;
self.wait_until_idle();
self.interface
self.di
.cmd_with_data(spi, Command::DEEP_SLEEP, &[0xA5])?;
self.wait_until_idle();
@ -212,19 +217,19 @@ where
self.send_resolution(spi)?;
self.interface
self.di
.cmd_with_data(spi, Command::VCM_DC_SETTING, &[0x12])?;
//VBDF 17|D7 VBDW 97 VBDB 57 VBDF F7 VBDW 77 VBDB 37 VBDR B7
self.interface
self.di
.cmd_with_data(spi, Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x97])?;
self.interface
self.di
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
self.interface
self.di
.data_x_times(spi, color_value, WIDTH / 8 * HEIGHT)?;
self.interface
self.di
.cmd_with_data(spi, Command::DATA_START_TRANSMISSION_2, buffer)?;
self.wait_until_idle();
@ -245,41 +250,41 @@ where
//return Err("Wrong buffersize");
}
self.command(spi, Command::PARTIAL_IN)?;
self.command(spi, Command::PARTIAL_WINDOW)?;
self.send_data(spi, &[(x >> 8) as u8])?;
self.di.cmd(spi, Command::PARTIAL_IN)?;
self.di.cmd(spi, Command::PARTIAL_WINDOW)?;
self.di.data(spi, &[(x >> 8) as u8])?;
let tmp = x & 0xf8;
self.send_data(spi, &[tmp as u8])?; // x should be the multiple of 8, the last 3 bit will always be ignored
self.di.data(spi, &[tmp as u8])?; // x should be the multiple of 8, the last 3 bit will always be ignored
let tmp = tmp + width - 1;
self.send_data(spi, &[(tmp >> 8) as u8])?;
self.send_data(spi, &[(tmp | 0x07) as u8])?;
self.di.data(spi, &[(tmp >> 8) as u8])?;
self.di.data(spi, &[(tmp | 0x07) as u8])?;
self.send_data(spi, &[(y >> 8) as u8])?;
self.send_data(spi, &[y as u8])?;
self.di.data(spi, &[(y >> 8) as u8])?;
self.di.data(spi, &[y as u8])?;
self.send_data(spi, &[((y + height - 1) >> 8) as u8])?;
self.send_data(spi, &[(y + height - 1) as u8])?;
self.di.data(spi, &[((y + height - 1) >> 8) as u8])?;
self.di.data(spi, &[(y + height - 1) as u8])?;
self.send_data(spi, &[0x01])?; // Gates scan both inside and outside of the partial window. (default)
self.di.data(spi, &[0x01])?; // Gates scan both inside and outside of the partial window. (default)
//TODO: handle dtm somehow
let is_dtm1 = false;
if is_dtm1 {
self.command(spi, Command::DATA_START_TRANSMISSION_1)? //TODO: check if data_start transmission 1 also needs "old"/background data here
self.di.cmd(spi, Command::DATA_START_TRANSMISSION_1)? //TODO: check if data_start transmission 1 also needs "old"/background data here
} else {
self.command(spi, Command::DATA_START_TRANSMISSION_2)?
self.di.cmd(spi, Command::DATA_START_TRANSMISSION_2)?
}
self.send_data(spi, buffer)?;
self.di.data(spi, buffer)?;
self.command(spi, Command::PARTIAL_OUT)?;
self.di.cmd(spi, Command::PARTIAL_OUT)?;
self.wait_until_idle();
Ok(())
}
fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
self.command(spi, Command::DISPLAY_REFRESH)?;
self.di.cmd(spi, Command::DISPLAY_REFRESH)?;
self.wait_until_idle();
Ok(())
@ -290,14 +295,14 @@ where
let color_value = self.color.get_byte_value();
self.interface
self.di
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
self.interface
self.di
.data_x_times(spi, color_value, WIDTH / 8 * HEIGHT)?;
self.interface
self.di
.cmd(spi, Command::DATA_START_TRANSMISSION_2)?;
self.interface
self.di
.data_x_times(spi, color_value, WIDTH / 8 * HEIGHT)?;
self.wait_until_idle();
@ -344,48 +349,33 @@ where
}
fn is_busy(&self) -> bool {
self.interface.is_busy(IS_BUSY_LOW)
self.di.is_busy(IS_BUSY_LOW)
}
}
impl<SPI, CS, BUSY, DC, RST> EPD4in2<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST, SpiE, PinRE, PinWE> EPD4in2<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
SPI: Write<u8, Error = SpiE>,
CS: OutputPin<Error = PinWE>,
BUSY: InputPin<Error = PinRE>,
DC: OutputPin<Error = PinWE>,
RST: OutputPin<Error = PinWE>,
{
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_until_idle(&mut self) {
self.interface.wait_until_idle(IS_BUSY_LOW)
self.di.wait_until_idle(IS_BUSY_LOW)
}
fn send_resolution(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
let w = self.width();
let h = self.height();
self.command(spi, Command::RESOLUTION_SETTING)?;
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])
self.di.cmd(spi, Command::RESOLUTION_SETTING)?;
self.di.data(spi, &[(w >> 8) as u8])?;
self.di.data(spi, &[w as u8])?;
self.di.data(spi, &[(h >> 8) as u8])?;
self.di.data(spi, &[h as u8])
}
fn set_lut_helper(
@ -398,21 +388,21 @@ where
lut_bb: &[u8],
) -> Result<(), SPI::Error> {
// LUT VCOM
self.cmd_with_data(spi, Command::LUT_FOR_VCOM, lut_vcom)?;
self.di.cmd_with_data(spi, Command::LUT_FOR_VCOM, lut_vcom)?;
// LUT WHITE to WHITE
self.cmd_with_data(spi, Command::LUT_WHITE_TO_WHITE, lut_ww)?;
self.di.cmd_with_data(spi, Command::LUT_WHITE_TO_WHITE, lut_ww)?;
// LUT BLACK to WHITE
self.cmd_with_data(spi, Command::LUT_BLACK_TO_WHITE, lut_bw)?;
self.di.cmd_with_data(spi, Command::LUT_BLACK_TO_WHITE, lut_bw)?;
// LUT WHITE to BLACK
self.cmd_with_data(spi, Command::LUT_WHITE_TO_BLACK, lut_wb)?;
self.di.cmd_with_data(spi, Command::LUT_WHITE_TO_BLACK, lut_wb)?;
// LUT BLACK to BLACK
self.cmd_with_data(spi, Command::LUT_BLACK_TO_BLACK, lut_bb)?;
self.di.cmd_with_data(spi, Command::LUT_BLACK_TO_BLACK, lut_bb)?;
self.wait_until_idle();
self.di.wait_until_idle(IS_BUSY_LOW);
Ok(())
}
}

@ -2,8 +2,9 @@ use crate::traits::Command;
use core::marker::PhantomData;
use embedded_hal::{
blocking::{delay::*, spi::Write},
digital::*,
digital::v2::*,
};
use crate::Error;
/// The Connection Interface of all (?) Waveshare EPD-Devices
///
@ -20,13 +21,13 @@ pub(crate) struct DisplayInterface<SPI, CS, BUSY, DC, RST> {
rst: RST,
}
impl<SPI, CS, BUSY, DC, RST> DisplayInterface<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST, SpiE, PinRE, PinWE> DisplayInterface<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
SPI: Write<u8, Error = SpiE>,
CS: OutputPin<Error = PinWE>,
BUSY: InputPin<Error = PinRE>,
DC: OutputPin<Error = PinWE>,
RST: OutputPin<Error = PinWE>,
{
pub fn new(cs: CS, busy: BUSY, dc: DC, rst: RST) -> Self {
DisplayInterface {
@ -151,7 +152,7 @@ where
/// Most likely there was a mistake with the 2in9 busy connection
/// //TODO: use the #cfg feature to make this compile the right way for the certain types
pub(crate) fn is_busy(&self, is_busy_low: bool) -> bool {
(is_busy_low && self.busy.is_low()) || (!is_busy_low && self.busy.is_high())
(is_busy_low && self.busy.is_low().ok().unwrap()) || (!is_busy_low && self.busy.is_high().ok().unwrap())
}
/// Resets the device.

@ -87,6 +87,44 @@ pub mod prelude {
use embedded_hal::spi::{Mode, Phase, Polarity};
use embedded_hal::{
blocking::{spi::Write},
digital::v2::*,
};
/// Errors in this crate
#[derive(Debug)]
pub enum Error<SpiE, PinRE, PinWE> {
/// Communication error
Spi(SpiE),
/// Pin reading error
PinRead(PinRE),
/// Pin writing error
PinWrite(PinWE),
}
impl<SpiE, PinRE, PinWE> From<SpiE> for Error<SpiE, PinRE, PinWE> {
fn from(error: SpiE) -> Self {
Error::Spi(error)
}
}
impl<SpiE, PinRE, PinWE> From<PinRE> for Error<SpiE, PinRE, PinWE>
where PinRE: OutputPin::Error{
fn from(error: PinRE) -> Self {
Error::PinRead(error)
}
}
impl<SpiE, PinRE, PinWE> From<PinWE> for Error<SpiE, PinRE, PinWE> {
fn from(error: PinWE) -> Self {
Error::Spi(error)
}
}
/// SPI mode -
/// For more infos see [Requirements: SPI](index.html#spi)
pub const SPI_MODE: Mode = Mode {

@ -2,8 +2,9 @@ use crate::color::Color;
use core::marker::Sized;
use embedded_hal::{
blocking::{delay::*, spi::Write},
digital::*,
digital::v2::{InputPin, OutputPin},
};
use crate::Error;
/// All commands need to have this trait which gives the address of the command
/// which needs to be send via SPI with activated CommandsPin (Data/Command Pin in CommandMode)
@ -27,14 +28,16 @@ impl Default for RefreshLUT {
}
}
pub(crate) trait InternalWiAdditions<SPI, CS, BUSY, DC, RST>
pub(crate) trait InternalWiAdditions<SPI, CS, BUSY, DC, RST, SpiE, PinRE, PinWE>
where
SPI: Write<u8>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
SPI: Write<u8, Error = SpiE>,
CS: OutputPin<Error = PinWE>,
BUSY: InputPin<Error = PinRE>,
DC: OutputPin<Error = PinWE>,
RST: OutputPin<Error = PinWE>,
{
type Error;
/// This initialises the EPD and powers it up
///
/// This function is already called from
@ -49,20 +52,22 @@ where
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
) -> Result<(), SPI::Error>;
) -> Result<(), Self::Error>;
}
/// All the functions to interact with the EPDs
///
/// This trait includes all public functions to use the EPDS
pub trait WaveshareDisplay<SPI, CS, BUSY, DC, RST>
pub trait WaveshareDisplay<SPI, CS, BUSY, DC, RST, SpiE, PinRE, PinWE>
where
SPI: Write<u8>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
SPI: Write<u8, Error = SpiE>,
CS: OutputPin<Error = PinWE>,
BUSY: InputPin<Error = PinRE>,
DC: OutputPin<Error = PinWE>,
RST: OutputPin<Error = PinWE>,
{
type Error;
/// Creates a new driver from a SPI peripheral, CS Pin, Busy InputPin, DC
///
/// This already initialises the device. That means [init()](WaveshareInterface::init()) isn't needed directly afterwards
@ -73,7 +78,7 @@ where
dc: DC,
rst: RST,
delay: &mut DELAY,
) -> Result<Self, SPI::Error>
) -> Result<Self, Self::Error>
where
Self: Sized;
@ -83,14 +88,14 @@ where
/// But you can also use [wake_up()](WaveshareInterface::wake_up()) to awaken.
/// But as you need to power it up once more anyway you can also just directly use [new()](WaveshareInterface::new()) for resetting
/// and initialising which already contains the reset
fn sleep(&mut self, spi: &mut SPI) -> Result<(), SPI::Error>;
fn sleep(&mut self, spi: &mut SPI) -> Result<(), Self::Error>;
/// Wakes the device up from sleep
fn wake_up<DELAY: DelayMs<u8>>(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
) -> Result<(), SPI::Error>;
) -> Result<(), Self::Error>;
/// Sets the backgroundcolor for various commands like [clear_frame()](WaveshareInterface::clear_frame())
fn set_background_color(&mut self, color: Color);
@ -105,7 +110,7 @@ where
fn height(&self) -> u32;
/// Transmit a full frame to the SRAM of the EPD
fn update_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error>;
fn update_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), Self::Error>;
/// Transmits partial data to the SRAM of the EPD
///
@ -120,17 +125,17 @@ where
y: u32,
width: u32,
height: u32,
) -> Result<(), SPI::Error>;
) -> Result<(), Self::Error>;
/// Displays the frame data from SRAM
///
/// This function waits until the device isn`t busy anymore
fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error>;
fn display_frame(&mut self, spi: &mut SPI) -> Result<(), Self::Error>;
/// Clears the frame buffer on the EPD with the declared background color
///
/// The background color can be changed with [`set_background_color`]
fn clear_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error>;
fn clear_frame(&mut self, spi: &mut SPI) -> Result<(), Self::Error>;
/// Trait for using various Waveforms from different LUTs
/// E.g. for partial refreshes
@ -144,12 +149,12 @@ where
&mut self,
spi: &mut SPI,
refresh_rate: Option<RefreshLUT>,
) -> Result<(), SPI::Error>;
) -> Result<(), Self::Error>;
/// Checks if the display is busy transmitting data
///
/// This is normally handled by the more complicated commands themselves,
/// but in the case you send data and commands directly you might need to check
/// if the device is still busy
fn is_busy(&self) -> bool;
fn is_busy(&self) -> Result<bool, Self::Error>;
}

Loading…
Cancel
Save