This commit is contained in:
pi
2021-04-14 15:22:36 +01:00
30 changed files with 844 additions and 876 deletions
+32 -35
View File
@@ -20,7 +20,7 @@
//!# let mut delay = delay::MockNoop::new();
//!
//!// Setup EPD
//!let mut epd = EPD1in54::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?;
//!let mut epd = Epd1in54::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?;
//!
//!// Use display graphics from embedded-graphics
//!let mut display = Display1in54::default();
@@ -61,7 +61,7 @@ use crate::type_a::{
use crate::color::Color;
use crate::traits::{RefreshLUT, WaveshareDisplay};
use crate::traits::{RefreshLut, WaveshareDisplay};
use crate::interface::DisplayInterface;
@@ -70,18 +70,18 @@ mod graphics;
#[cfg(feature = "graphics")]
pub use crate::epd1in54::graphics::Display1in54;
/// EPD1in54 driver
/// Epd1in54 driver
///
pub struct EPD1in54<SPI, CS, BUSY, DC, RST> {
pub struct Epd1in54<SPI, CS, BUSY, DC, RST> {
/// SPI
interface: DisplayInterface<SPI, CS, BUSY, DC, RST>,
/// Color
background_color: Color,
/// Refresh LUT
refresh: RefreshLUT,
refresh: RefreshLut,
}
impl<SPI, CS, BUSY, DC, RST> EPD1in54<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST> Epd1in54<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -103,7 +103,7 @@ where
// Default Values: A = Height of Screen (0x127), B = 0x00 (GD, SM and TB=0?)
self.interface.cmd_with_data(
spi,
Command::DRIVER_OUTPUT_CONTROL,
Command::DriverOutputControl,
&[HEIGHT as u8, (HEIGHT >> 8) as u8, 0x00],
)?;
@@ -112,28 +112,25 @@ where
// 1 .. B[6:0] = 0xCE | 0xD6
// 1 .. C[6:0] = 0x8D | 0x9D
//TODO: test
self.interface.cmd_with_data(
spi,
Command::BOOSTER_SOFT_START_CONTROL,
&[0xD7, 0xD6, 0x9D],
)?;
self.interface
.cmd_with_data(spi, Command::BoosterSoftStartControl, &[0xD7, 0xD6, 0x9D])?;
// One Databyte with value 0xA8 for 7V VCOM
self.interface
.cmd_with_data(spi, Command::WRITE_VCOM_REGISTER, &[0xA8])?;
.cmd_with_data(spi, Command::WriteVcomRegister, &[0xA8])?;
// One Databyte with default value 0x1A for 4 dummy lines per gate
self.interface
.cmd_with_data(spi, Command::SET_DUMMY_LINE_PERIOD, &[0x1A])?;
.cmd_with_data(spi, Command::SetDummyLinePeriod, &[0x1A])?;
// One Databyte with default value 0x08 for 2us per line
self.interface
.cmd_with_data(spi, Command::SET_GATE_LINE_WIDTH, &[0x08])?;
.cmd_with_data(spi, Command::SetGateLineWidth, &[0x08])?;
// One Databyte with default value 0x03
// -> address: x increment, y increment, address counter is updated in x direction
self.interface
.cmd_with_data(spi, Command::DATA_ENTRY_MODE_SETTING, &[0x03])?;
.cmd_with_data(spi, Command::DataEntryModeSetting, &[0x03])?;
self.set_lut(spi, None)?;
@@ -143,7 +140,7 @@ where
}
impl<SPI, CS, BUSY, DC, RST, E> WaveshareDisplay<SPI, CS, BUSY, DC, RST>
for EPD1in54<SPI, CS, BUSY, DC, RST>
for Epd1in54<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8, Error = E>,
CS: OutputPin,
@@ -170,10 +167,10 @@ where
) -> Result<Self, SPI::Error> {
let interface = DisplayInterface::new(cs, busy, dc, rst);
let mut epd = EPD1in54 {
let mut epd = Epd1in54 {
interface,
background_color: DEFAULT_BACKGROUND_COLOR,
refresh: RefreshLUT::FULL,
refresh: RefreshLut::Full,
};
epd.init(spi, delay)?;
@@ -194,7 +191,7 @@ where
// 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
.cmd_with_data(spi, Command::DEEP_SLEEP_MODE, &[0x00])?;
.cmd_with_data(spi, Command::DeepSleepMode, &[0x00])?;
Ok(())
}
@@ -202,7 +199,7 @@ where
self.wait_until_idle();
self.use_full_frame(spi)?;
self.interface
.cmd_with_data(spi, Command::WRITE_RAM, buffer)?;
.cmd_with_data(spi, Command::WriteRam, buffer)?;
Ok(())
}
@@ -221,7 +218,7 @@ where
self.set_ram_counter(spi, x, y)?;
self.interface
.cmd_with_data(spi, Command::WRITE_RAM, buffer)?;
.cmd_with_data(spi, Command::WriteRam, buffer)?;
Ok(())
}
@@ -230,12 +227,12 @@ where
// 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
.cmd_with_data(spi, Command::DISPLAY_UPDATE_CONTROL_2, &[0xC4])?;
.cmd_with_data(spi, Command::DisplayUpdateControl2, &[0xC4])?;
self.interface.cmd(spi, Command::MASTER_ACTIVATION)?;
self.interface.cmd(spi, Command::MasterActivation)?;
// 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.interface.cmd(spi, Command::Nop)?;
Ok(())
}
@@ -252,7 +249,7 @@ 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.cmd(spi, Command::WriteRam)?;
self.interface
.data_x_times(spi, color, WIDTH / 8 * HEIGHT)?;
Ok(())
@@ -269,14 +266,14 @@ where
fn set_lut(
&mut self,
spi: &mut SPI,
refresh_rate: Option<RefreshLUT>,
refresh_rate: Option<RefreshLut>,
) -> Result<(), SPI::Error> {
if let Some(refresh_lut) = refresh_rate {
self.refresh = refresh_lut;
}
match self.refresh {
RefreshLUT::FULL => self.set_lut_helper(spi, &LUT_FULL_UPDATE),
RefreshLUT::QUICK => self.set_lut_helper(spi, &LUT_PARTIAL_UPDATE),
RefreshLut::Full => self.set_lut_helper(spi, &LUT_FULL_UPDATE),
RefreshLut::Quick => self.set_lut_helper(spi, &LUT_PARTIAL_UPDATE),
}
}
@@ -285,7 +282,7 @@ where
}
}
impl<SPI, CS, BUSY, DC, RST> EPD1in54<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST> Epd1in54<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -321,14 +318,14 @@ where
// aren't relevant
self.interface.cmd_with_data(
spi,
Command::SET_RAM_X_ADDRESS_START_END_POSITION,
Command::SetRamXAddressStartEndPosition,
&[(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(
spi,
Command::SET_RAM_Y_ADDRESS_START_END_POSITION,
Command::SetRamYAddressStartEndPosition,
&[
start_y as u8,
(start_y >> 8) as u8,
@@ -349,12 +346,12 @@ 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(spi, Command::SET_RAM_X_ADDRESS_COUNTER, &[(x >> 3) as u8])?;
.cmd_with_data(spi, Command::SetRamXAddressCounter, &[(x >> 3) as u8])?;
// 2 Databytes: A[7:0] & 0..A[8]
self.interface.cmd_with_data(
spi,
Command::SET_RAM_Y_ADDRESS_COUNTER,
Command::SetRamYAddressCounter,
&[y as u8, (y >> 8) as u8],
)?;
Ok(())
@@ -365,7 +362,7 @@ where
assert!(buffer.len() == 30);
self.interface
.cmd_with_data(spi, Command::WRITE_LUT_REGISTER, buffer)?;
.cmd_with_data(spi, Command::WriteLutRegister, buffer)?;
Ok(())
}
}
+23 -24
View File
@@ -2,35 +2,34 @@
use crate::traits;
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
pub(crate) enum Command {
PANEL_SETTING = 0x00,
PanelSetting = 0x00,
POWER_SETTING = 0x01,
POWER_OFF = 0x02,
POWER_ON = 0x04,
BOOSTER_SOFT_START = 0x06,
DATA_START_TRANSMISSION_1 = 0x10,
DISPLAY_REFRESH = 0x12,
DATA_START_TRANSMISSION_2 = 0x13,
PowerSetting = 0x01,
PowerOff = 0x02,
PowerOn = 0x04,
BoosterSoftStart = 0x06,
DataStartTransmission1 = 0x10,
DisplayRefresh = 0x12,
DataStartTransmission2 = 0x13,
LUT_FOR_VCOM = 0x20,
LUT_WHITE_TO_WHITE = 0x21,
LUT_BLACK_TO_WHITE = 0x22,
LUT_G0 = 0x23,
LUT_G1 = 0x24,
LUT_RED_VCOM = 0x25,
LUT_RED0 = 0x26,
LUT_RED1 = 0x27,
LutForVcom = 0x20,
LutWhiteToWhite = 0x21,
LutBlackToWhite = 0x22,
LutG0 = 0x23,
LutG1 = 0x24,
LutRedVcom = 0x25,
LutRed0 = 0x26,
LutRed1 = 0x27,
PLL_CONTROL = 0x30,
TEMPERATURE_SENSOR_COMMAND = 0x40,
TEMPERATURE_SENSOR_SELECTION = 0x41,
VCOM_AND_DATA_INTERVAL_SETTING = 0x50,
RESOLUTION_SETTING = 0x61,
VCM_DC_SETTING = 0x82,
POWER_SAVING = 0xE3,
PllControl = 0x30,
TemperatureSensor = 0x40,
TemperatureSensorSelection = 0x41,
VcomAndDataIntervalSetting = 0x50,
ResolutionSetting = 0x61,
VcmDcSetting = 0x82,
PowerSaving = 0xE3,
}
impl traits::Command for Command {
+36 -42
View File
@@ -7,7 +7,7 @@ use embedded_hal::{
use crate::interface::DisplayInterface;
use crate::traits::{
InternalWiAdditions, RefreshLUT, WaveshareDisplay, WaveshareThreeColorDisplay,
InternalWiAdditions, RefreshLut, WaveshareDisplay, WaveshareThreeColorDisplay,
};
//The Lookup Tables for the Display
@@ -32,14 +32,14 @@ mod graphics;
#[cfg(feature = "graphics")]
pub use self::graphics::Display1in54b;
/// EPD1in54b driver
pub struct EPD1in54b<SPI, CS, BUSY, DC, RST> {
/// Epd1in54b driver
pub struct Epd1in54b<SPI, CS, BUSY, DC, RST> {
interface: DisplayInterface<SPI, CS, BUSY, DC, RST>,
color: Color,
}
impl<SPI, CS, BUSY, DC, RST> InternalWiAdditions<SPI, CS, BUSY, DC, RST>
for EPD1in54b<SPI, CS, BUSY, DC, RST>
for Epd1in54b<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -56,29 +56,29 @@ where
// set the power settings
self.interface
.cmd_with_data(spi, Command::POWER_SETTING, &[0x07, 0x00, 0x08, 0x00])?;
.cmd_with_data(spi, Command::PowerSetting, &[0x07, 0x00, 0x08, 0x00])?;
// start the booster
self.interface
.cmd_with_data(spi, Command::BOOSTER_SOFT_START, &[0x07, 0x07, 0x07])?;
.cmd_with_data(spi, Command::BoosterSoftStart, &[0x07, 0x07, 0x07])?;
// power on
self.command(spi, Command::POWER_ON)?;
self.command(spi, Command::PowerOn)?;
delay.delay_ms(5);
self.wait_until_idle();
// set the panel settings
self.cmd_with_data(spi, Command::PANEL_SETTING, &[0xCF])?;
self.cmd_with_data(spi, Command::PanelSetting, &[0xCF])?;
self.cmd_with_data(spi, Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x37])?;
self.cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x37])?;
// PLL
self.cmd_with_data(spi, Command::PLL_CONTROL, &[0x39])?;
self.cmd_with_data(spi, Command::PllControl, &[0x39])?;
// set resolution
self.send_resolution(spi)?;
self.cmd_with_data(spi, Command::VCM_DC_SETTING, &[0x0E])?;
self.cmd_with_data(spi, Command::VcmDcSetting, &[0x0E])?;
self.set_lut(spi, None)?;
@@ -89,7 +89,7 @@ where
}
impl<SPI, CS, BUSY, DC, RST> WaveshareThreeColorDisplay<SPI, CS, BUSY, DC, RST>
for EPD1in54b<SPI, CS, BUSY, DC, RST>
for Epd1in54b<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -111,8 +111,7 @@ where
self.wait_until_idle();
self.send_resolution(spi)?;
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
self.interface.cmd(spi, Command::DataStartTransmission1)?;
for b in black {
let expanded = expand_bits(*b);
@@ -126,15 +125,14 @@ where
spi: &mut SPI,
chromatic: &[u8],
) -> Result<(), SPI::Error> {
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_2)?;
self.interface.cmd(spi, Command::DataStartTransmission2)?;
self.interface.data(spi, chromatic)?;
Ok(())
}
}
impl<SPI, CS, BUSY, DC, RST> WaveshareDisplay<SPI, CS, BUSY, DC, RST>
for EPD1in54b<SPI, CS, BUSY, DC, RST>
for Epd1in54b<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -154,7 +152,7 @@ where
let interface = DisplayInterface::new(cs, busy, dc, rst);
let color = DEFAULT_BACKGROUND_COLOR;
let mut epd = EPD1in54b { interface, color };
let mut epd = Epd1in54b { interface, color };
epd.init(spi, delay)?;
@@ -164,19 +162,19 @@ where
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, &[0x17])?; //border floating
.cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x17])?; //border floating
self.interface
.cmd_with_data(spi, Command::VCM_DC_SETTING, &[0x00])?; // VCOM to 0V
.cmd_with_data(spi, Command::VcmDcSetting, &[0x00])?; // Vcom to 0V
self.interface
.cmd_with_data(spi, Command::POWER_SETTING, &[0x02, 0x00, 0x00, 0x00])?; //VG&VS to 0V fast
.cmd_with_data(spi, Command::PowerSetting, &[0x02, 0x00, 0x00, 0x00])?; //VG&VS to 0V fast
self.wait_until_idle();
//NOTE: The example code has a 1s delay here
self.command(spi, Command::POWER_OFF)?;
self.command(spi, Command::PowerOff)?;
Ok(())
}
@@ -209,8 +207,7 @@ where
self.wait_until_idle();
self.send_resolution(spi)?;
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
self.interface.cmd(spi, Command::DataStartTransmission1)?;
for b in buffer {
// Two bits per pixel
@@ -224,8 +221,7 @@ where
let color = self.color.get_byte_value();
let nbits = WIDTH * (HEIGHT / 8);
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_2)?;
self.interface.cmd(spi, Command::DataStartTransmission2)?;
self.interface.data_x_times(spi, color, nbits)?;
//NOTE: Example code has a delay here
@@ -247,7 +243,7 @@ where
fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
self.wait_until_idle();
self.command(spi, Command::DISPLAY_REFRESH)?;
self.command(spi, Command::DisplayRefresh)?;
Ok(())
}
@@ -264,16 +260,14 @@ where
let color = DEFAULT_BACKGROUND_COLOR.get_byte_value();
// Clear the black
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
self.interface.cmd(spi, Command::DataStartTransmission1)?;
// Uses 2 bits per pixel
self.interface
.data_x_times(spi, color, 2 * (WIDTH * HEIGHT / 8))?;
// Clear the red
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_2)?;
self.interface.cmd(spi, Command::DataStartTransmission2)?;
self.interface
.data_x_times(spi, color, WIDTH * HEIGHT / 8)?;
Ok(())
@@ -282,22 +276,22 @@ where
fn set_lut(
&mut self,
spi: &mut SPI,
_refresh_rate: Option<RefreshLUT>,
_refresh_rate: Option<RefreshLut>,
) -> Result<(), SPI::Error> {
self.interface
.cmd_with_data(spi, Command::LUT_FOR_VCOM, LUT_VCOM0)?;
.cmd_with_data(spi, Command::LutForVcom, LUT_VCOM0)?;
self.interface
.cmd_with_data(spi, Command::LUT_WHITE_TO_WHITE, LUT_WHITE_TO_WHITE)?;
.cmd_with_data(spi, Command::LutWhiteToWhite, LUT_WHITE_TO_WHITE)?;
self.interface
.cmd_with_data(spi, Command::LUT_BLACK_TO_WHITE, LUT_BLACK_TO_WHITE)?;
self.interface.cmd_with_data(spi, Command::LUT_G0, LUT_G1)?;
self.interface.cmd_with_data(spi, Command::LUT_G1, LUT_G2)?;
.cmd_with_data(spi, Command::LutBlackToWhite, LUT_BLACK_TO_WHITE)?;
self.interface.cmd_with_data(spi, Command::LutG0, LUT_G1)?;
self.interface.cmd_with_data(spi, Command::LutG1, LUT_G2)?;
self.interface
.cmd_with_data(spi, Command::LUT_RED_VCOM, LUT_RED_VCOM)?;
.cmd_with_data(spi, Command::LutRedVcom, LUT_RED_VCOM)?;
self.interface
.cmd_with_data(spi, Command::LUT_RED0, LUT_RED0)?;
.cmd_with_data(spi, Command::LutRed0, LUT_RED0)?;
self.interface
.cmd_with_data(spi, Command::LUT_RED1, LUT_RED1)?;
.cmd_with_data(spi, Command::LutRed1, LUT_RED1)?;
Ok(())
}
@@ -307,7 +301,7 @@ where
}
}
impl<SPI, CS, BUSY, DC, RST> EPD1in54b<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST> Epd1in54b<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -340,7 +334,7 @@ where
let w = self.width();
let h = self.height();
self.command(spi, Command::RESOLUTION_SETTING)?;
self.command(spi, Command::ResolutionSetting)?;
self.send_data(spi, &[w as u8])?;
self.send_data(spi, &[(h >> 8) as u8])?;
+21 -22
View File
@@ -2,33 +2,32 @@
use crate::traits;
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
pub(crate) enum Command {
PANEL_SETTING = 0x00,
PanelSetting = 0x00,
POWER_SETTING = 0x01,
POWER_OFF = 0x02,
POWER_ON = 0x04,
BOOSTER_SOFT_START = 0x06,
DEEP_SLEEP = 0x07,
DATA_START_TRANSMISSION_1 = 0x10,
DISPLAY_REFRESH = 0x12,
DATA_START_TRANSMISSION_2 = 0x13,
PowerSetting = 0x01,
PowerOff = 0x02,
PowerOn = 0x04,
BoosterSoftStart = 0x06,
DeepSleep = 0x07,
DataStartTransmission1 = 0x10,
DisplayRefresh = 0x12,
DataStartTransmission2 = 0x13,
LUT_FOR_VCOM = 0x20,
LUT_WHITE_TO_WHITE = 0x21,
LUT_BLACK_TO_WHITE = 0x22,
LUT_WHITE_TO_BLACK = 0x23,
LUT_BLACK_TO_BLACK = 0x24,
LutForVcom = 0x20,
LutWhiteToWhite = 0x21,
LutBlackToWhite = 0x22,
LutWhiteToBlack = 0x23,
LutBlackToBlack = 0x24,
PLL_CONTROL = 0x30,
TEMPERATURE_SENSOR_COMMAND = 0x40,
TEMPERATURE_SENSOR_SELECTION = 0x41,
VCOM_AND_DATA_INTERVAL_SETTING = 0x50,
RESOLUTION_SETTING = 0x61,
VCM_DC_SETTING = 0x82,
POWER_SAVING = 0xE3,
PllControl = 0x30,
TemperatureSensor = 0x40,
TemperatureSensorSelection = 0x41,
VcomAndDataIntervalSetting = 0x50,
ResolutionSetting = 0x61,
VcmDcSetting = 0x82,
PowerSaving = 0xE3,
}
impl traits::Command for Command {
+22 -22
View File
@@ -7,7 +7,7 @@ use embedded_hal::{
use crate::interface::DisplayInterface;
use crate::traits::{
InternalWiAdditions, RefreshLUT, WaveshareDisplay, WaveshareThreeColorDisplay,
InternalWiAdditions, RefreshLut, WaveshareDisplay, WaveshareThreeColorDisplay,
};
/// Width of epd1in54 in pixels
@@ -30,14 +30,14 @@ mod graphics;
#[cfg(feature = "graphics")]
pub use self::graphics::Display1in54c;
/// EPD1in54c driver
pub struct EPD1in54c<SPI, CS, BUSY, DC, RST> {
/// Epd1in54c driver
pub struct Epd1in54c<SPI, CS, BUSY, DC, RST> {
interface: DisplayInterface<SPI, CS, BUSY, DC, RST>,
color: Color,
}
impl<SPI, CS, BUSY, DC, RST> InternalWiAdditions<SPI, CS, BUSY, DC, RST>
for EPD1in54c<SPI, CS, BUSY, DC, RST>
for Epd1in54c<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -57,27 +57,27 @@ where
self.interface.reset(delay, 2);
// start the booster
self.cmd_with_data(spi, Command::BOOSTER_SOFT_START, &[0x17, 0x17, 0x17])?;
self.cmd_with_data(spi, Command::BoosterSoftStart, &[0x17, 0x17, 0x17])?;
// power on
self.command(spi, Command::POWER_ON)?;
self.command(spi, Command::PowerOn)?;
delay.delay_ms(5);
self.wait_until_idle();
// set the panel settings
self.cmd_with_data(spi, Command::PANEL_SETTING, &[0x0f, 0x0d])?;
self.cmd_with_data(spi, Command::PanelSetting, &[0x0f, 0x0d])?;
// set resolution
self.send_resolution(spi)?;
self.cmd_with_data(spi, Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x77])?;
self.cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x77])?;
Ok(())
}
}
impl<SPI, CS, BUSY, DC, RST> WaveshareThreeColorDisplay<SPI, CS, BUSY, DC, RST>
for EPD1in54c<SPI, CS, BUSY, DC, RST>
for Epd1in54c<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -97,7 +97,7 @@ where
fn update_achromatic_frame(&mut self, spi: &mut SPI, black: &[u8]) -> Result<(), SPI::Error> {
self.wait_until_idle();
self.cmd_with_data(spi, Command::DATA_START_TRANSMISSION_1, black)?;
self.cmd_with_data(spi, Command::DataStartTransmission1, black)?;
Ok(())
}
@@ -108,14 +108,14 @@ where
chromatic: &[u8],
) -> Result<(), SPI::Error> {
self.wait_until_idle();
self.cmd_with_data(spi, Command::DATA_START_TRANSMISSION_2, chromatic)?;
self.cmd_with_data(spi, Command::DataStartTransmission2, chromatic)?;
Ok(())
}
}
impl<SPI, CS, BUSY, DC, RST> WaveshareDisplay<SPI, CS, BUSY, DC, RST>
for EPD1in54c<SPI, CS, BUSY, DC, RST>
for Epd1in54c<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -135,7 +135,7 @@ where
let interface = DisplayInterface::new(cs, busy, dc, rst);
let color = DEFAULT_BACKGROUND_COLOR;
let mut epd = EPD1in54c { interface, color };
let mut epd = Epd1in54c { interface, color };
epd.init(spi, delay)?;
@@ -145,9 +145,9 @@ where
fn sleep(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
self.wait_until_idle();
self.command(spi, Command::POWER_OFF)?;
self.command(spi, Command::PowerOff)?;
self.wait_until_idle();
self.cmd_with_data(spi, Command::DEEP_SLEEP, &[0xa5])?;
self.cmd_with_data(spi, Command::DeepSleep, &[0xa5])?;
Ok(())
}
@@ -182,7 +182,7 @@ where
// Clear the chromatic layer
let color = self.color.get_byte_value();
self.command(spi, Command::DATA_START_TRANSMISSION_2)?;
self.command(spi, Command::DataStartTransmission2)?;
self.interface.data_x_times(spi, color, NUM_DISPLAY_BITS)?;
Ok(())
@@ -202,7 +202,7 @@ where
}
fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
self.command(spi, Command::DISPLAY_REFRESH)?;
self.command(spi, Command::DisplayRefresh)?;
self.wait_until_idle();
Ok(())
@@ -220,11 +220,11 @@ where
let color = DEFAULT_BACKGROUND_COLOR.get_byte_value();
// Clear the black
self.command(spi, Command::DATA_START_TRANSMISSION_1)?;
self.command(spi, Command::DataStartTransmission1)?;
self.interface.data_x_times(spi, color, NUM_DISPLAY_BITS)?;
// Clear the chromatic
self.command(spi, Command::DATA_START_TRANSMISSION_2)?;
self.command(spi, Command::DataStartTransmission2)?;
self.interface.data_x_times(spi, color, NUM_DISPLAY_BITS)?;
Ok(())
@@ -233,7 +233,7 @@ where
fn set_lut(
&mut self,
_spi: &mut SPI,
_refresh_rate: Option<RefreshLUT>,
_refresh_rate: Option<RefreshLut>,
) -> Result<(), SPI::Error> {
Ok(())
}
@@ -243,7 +243,7 @@ where
}
}
impl<SPI, CS, BUSY, DC, RST> EPD1in54c<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST> Epd1in54c<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -276,7 +276,7 @@ where
let w = self.width();
let h = self.height();
self.command(spi, Command::RESOLUTION_SETTING)?;
self.command(spi, Command::ResolutionSetting)?;
// | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
// | HRES[7:3] | 0 | 0 | 0 |
+74 -79
View File
@@ -5,57 +5,56 @@ use crate::traits;
extern crate bit_field;
use bit_field::BitField;
/// EPD2in13 v2
/// Epd2in13 v2
///
/// 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 = 0x01,
GATE_DRIVING_VOLTAGE_CTRL = 0x03,
SOURCE_DRIVING_VOLTAGE_CTRL = 0x04,
BOOSTER_SOFT_START_CONTROL = 0x0C,
GATE_SCAN_START_POSITION = 0x0F,
DEEP_SLEEP_MODE = 0x10,
DATA_ENTRY_MODE_SETTING = 0x11,
SW_RESET = 0x12,
HV_READY_DETECTION = 0x14,
VCI_DETECTION = 0x15,
TEMPERATURE_SENSOR_CONTROL_WRITE = 0x1A,
TEMPERATURE_SENSOR_CONTROL_READ = 0x1B,
TEMPERATURE_SENSOR_EXT_CONTROL_WRITE = 0x1C,
MASTER_ACTIVATION = 0x20,
DISPLAY_UPDATE_CONTROL_1 = 0x21,
DISPLAY_UPDATE_CONTROL_2 = 0x22,
WRITE_RAM = 0x24,
WRITE_RAM_RED = 0x26,
READ_RAM = 0x27,
VCOM_SENSE = 0x28,
VCOM_SENSE_DURATION = 0x29,
PROGRAM_VCOM_OPT = 0x2A,
WRITE_VCOM_REGISTER = 0x2C,
OTP_REGISTER_READ = 0x2D,
STATUS_BIT_READ = 0x2F,
PROGRAM_WS_OTP = 0x30,
LOAD_WS_OTP = 0x31,
WRITE_LUT_REGISTER = 0x32,
PROGRAM_OTP_SELECTION = 0x36,
WRITE_OTP_SELECTION = 0x37,
SET_DUMMY_LINE_PERIOD = 0x3A,
SET_GATE_LINE_WIDTH = 0x3B,
BORDER_WAVEFORM_CONTROL = 0x3C,
READ_RAM_OPTION = 0x41,
SET_RAM_X_ADDRESS_START_END_POSITION = 0x44,
SET_RAM_Y_ADDRESS_START_END_POSITION = 0x45,
AUTO_WRITE_RED_RAM_REGULAR_PATTERN = 0x46,
AUTO_WRITE_BW_RAM_REGULAR_PATTERN = 0x47,
SET_RAM_X_ADDRESS_COUNTER = 0x4E,
SET_RAM_Y_ADDRESS_COUNTER = 0x4F,
SET_ANALOG_BLOCK_CONTROL = 0x74,
SET_DIGITAL_BLOCK_CONTROL = 0x7E,
DriverOutputControl = 0x01,
GateDrivingVoltageCtrl = 0x03,
SourceDrivingVoltageCtrl = 0x04,
BoosterSoftStartControl = 0x0C,
GateScanStartPosition = 0x0F,
DeepSleepMode = 0x10,
DataEntryModeSetting = 0x11,
SwReset = 0x12,
HvReadyDetection = 0x14,
VciDetection = 0x15,
TemperatureSensorControlWrite = 0x1A,
TemperatureSensorControlRead = 0x1B,
TemperatureSensorExtControlWrite = 0x1C,
MasterActivation = 0x20,
DisplayUpdateControl1 = 0x21,
DisplayUpdateControl2 = 0x22,
WriteRam = 0x24,
WriteRamRed = 0x26,
ReadRam = 0x27,
VcomSense = 0x28,
VcomSenseDuration = 0x29,
ProgramVcomOpt = 0x2A,
WriteVcomRegister = 0x2C,
OtpRegisterRead = 0x2D,
StatusBitRead = 0x2F,
ProgramWsOtp = 0x30,
LoadWsOtp = 0x31,
WriteLutRegister = 0x32,
ProgramOtpSelection = 0x36,
WriteOtpSelection = 0x37,
SetDummyLinePeriod = 0x3A,
SetGateLineWidth = 0x3B,
BorderWaveformControl = 0x3C,
ReadRamOption = 0x41,
SetRamXAddressStartEndPosition = 0x44,
SetRamYAddressStartEndPosition = 0x45,
AutoWriteRedRamRegularPattern = 0x46,
AutoWriteBwRamRegularPattern = 0x47,
SetRamXAddressCounter = 0x4E,
SetRamYAddressCounter = 0x4F,
SetAnalogBlockControl = 0x74,
SetDigitalBlockControl = 0x7E,
NOP = 0x7F,
Nop = 0x7F,
}
pub(crate) struct DriverOutput {
@@ -135,54 +134,51 @@ impl DisplayUpdateControl2 {
}
#[allow(dead_code)]
#[allow(non_camel_case_types)]
pub(crate) enum DataEntryModeIncr {
X_DECR_Y_DECR = 0x0,
X_INCR_Y_DECR = 0x1,
X_DECR_Y_INCR = 0x2,
X_INCR_Y_INCR = 0x3,
XDecrYDecr = 0x0,
XIncrYDecr = 0x1,
XDecrYIncr = 0x2,
XIncrYIncr = 0x3,
}
#[allow(dead_code)]
#[allow(non_camel_case_types)]
pub(crate) enum DataEntryModeDir {
X_DIR = 0x0,
Y_DIR = 0x4,
XDir = 0x0,
YDir = 0x4,
}
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
pub(crate) enum BorderWaveFormVBD {
GS = 0x0,
FIX_LEVEL = 0x1,
VCOM = 0x2,
pub(crate) enum BorderWaveFormVbd {
Gs = 0x0,
FixLevel = 0x1,
Vcom = 0x2,
}
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
pub(crate) enum BorderWaveFormFixLevel {
VSS = 0x0,
VSH1 = 0x1,
VSL = 0x2,
VSH2 = 0x3,
Vss = 0x0,
Vsh1 = 0x1,
Vsl = 0x2,
Vsh2 = 0x3,
}
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
pub(crate) enum BorderWaveFormGS {
LUT0 = 0x0,
LUT1 = 0x1,
LUT2 = 0x2,
LUT3 = 0x3,
pub(crate) enum BorderWaveFormGs {
Lut0 = 0x0,
Lut1 = 0x1,
Lut2 = 0x2,
Lut3 = 0x3,
}
pub(crate) struct BorderWaveForm {
pub vbd: BorderWaveFormVBD,
pub vbd: BorderWaveFormVbd,
pub fix_level: BorderWaveFormFixLevel,
pub gs_trans: BorderWaveFormGS,
pub gs_trans: BorderWaveFormGs,
}
impl BorderWaveForm {
@@ -194,25 +190,24 @@ impl BorderWaveForm {
}
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
pub enum DeepSleepMode {
// Sleeps and keeps access to RAM and controller
NORMAL = 0x00,
Normal = 0x00,
// Sleeps without access to RAM/controller but keeps RAM content
MODE_1 = 0x01,
Mode1 = 0x01,
// Same as MODE_1 but RAM content is not kept
MODE_2 = 0x11,
Mode2 = 0x11,
}
pub(crate) struct GateDrivingVoltage(pub u8);
pub(crate) struct SourceDrivingVoltage(pub u8);
pub(crate) struct VCOM(pub u8);
pub(crate) struct Vcom(pub u8);
pub(crate) trait I32Ext {
fn vcom(self) -> VCOM;
fn vcom(self) -> Vcom;
fn gate_driving_decivolt(self) -> GateDrivingVoltage;
fn source_driving_decivolt(self) -> SourceDrivingVoltage;
}
@@ -220,7 +215,7 @@ pub(crate) trait I32Ext {
impl I32Ext for i32 {
// This is really not very nice. Until I find something better, this will be
// a placeholder.
fn vcom(self) -> VCOM {
fn vcom(self) -> Vcom {
assert!((-30..=-2).contains(&self));
let u = match -self {
2 => 0x08,
@@ -254,7 +249,7 @@ impl I32Ext for i32 {
30 => 0x78,
_ => 0,
};
VCOM(u)
Vcom(u)
}
fn gate_driving_decivolt(self) -> GateDrivingVoltage {
+59 -63
View File
@@ -16,13 +16,13 @@ use embedded_hal::{
use crate::buffer_len;
use crate::color::Color;
use crate::interface::DisplayInterface;
use crate::traits::{InternalWiAdditions, RefreshLUT, WaveshareDisplay};
use crate::traits::{InternalWiAdditions, RefreshLut, WaveshareDisplay};
pub(crate) mod command;
use self::command::{
BorderWaveForm, BorderWaveFormFixLevel, BorderWaveFormGS, BorderWaveFormVBD, Command,
BorderWaveForm, BorderWaveFormFixLevel, BorderWaveFormGs, BorderWaveFormVbd, Command,
DataEntryModeDir, DataEntryModeIncr, DeepSleepMode, DisplayUpdateControl2, DriverOutput,
GateDrivingVoltage, I32Ext, SourceDrivingVoltage, VCOM,
GateDrivingVoltage, I32Ext, SourceDrivingVoltage, Vcom,
};
pub(crate) mod constants;
@@ -43,9 +43,9 @@ pub const HEIGHT: u32 = 250;
pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White;
const IS_BUSY_LOW: bool = false;
/// EPD2in13 (V2) driver
/// Epd2in13 (V2) driver
///
pub struct EPD2in13<SPI, CS, BUSY, DC, RST> {
pub struct Epd2in13<SPI, CS, BUSY, DC, RST> {
/// Connection Interface
interface: DisplayInterface<SPI, CS, BUSY, DC, RST>,
@@ -53,11 +53,11 @@ pub struct EPD2in13<SPI, CS, BUSY, DC, RST> {
/// Background Color
background_color: Color,
refresh: RefreshLUT,
refresh: RefreshLut,
}
impl<SPI, CS, BUSY, DC, RST> InternalWiAdditions<SPI, CS, BUSY, DC, RST>
for EPD2in13<SPI, CS, BUSY, DC, RST>
for Epd2in13<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -73,14 +73,14 @@ where
// HW reset
self.interface.reset(delay, 10);
if self.refresh == RefreshLUT::QUICK {
if self.refresh == RefreshLut::Quick {
self.set_vcom_register(spi, (-9).vcom())?;
self.wait_until_idle();
self.set_lut(spi, Some(self.refresh))?;
// Python code does this, not sure why
// self.cmd_with_data(spi, Command::WRITE_OTP_SELECTION, &[0, 0, 0, 0, 0x40, 0, 0])?;
// self.cmd_with_data(spi, Command::WriteOtpSelection, &[0, 0, 0, 0, 0x40, 0, 0])?;
// During partial update, clock/analog are not disabled between 2
// updates.
@@ -88,20 +88,20 @@ where
spi,
DisplayUpdateControl2::new().enable_analog().enable_clock(),
)?;
self.command(spi, Command::MASTER_ACTIVATION)?;
self.command(spi, Command::MasterActivation)?;
self.wait_until_idle();
self.set_border_waveform(
spi,
BorderWaveForm {
vbd: BorderWaveFormVBD::GS,
fix_level: BorderWaveFormFixLevel::VSS,
gs_trans: BorderWaveFormGS::LUT1,
vbd: BorderWaveFormVbd::Gs,
fix_level: BorderWaveFormFixLevel::Vss,
gs_trans: BorderWaveFormGs::Lut1,
},
)?;
} else {
self.wait_until_idle();
self.command(spi, Command::SW_RESET)?;
self.command(spi, Command::SwReset)?;
self.wait_until_idle();
self.set_driver_output(
@@ -118,11 +118,7 @@ where
self.set_dummy_line_period(spi, 0x30)?;
self.set_gate_scan_start_position(spi, 0)?;
self.set_data_entry_mode(
spi,
DataEntryModeIncr::X_INCR_Y_INCR,
DataEntryModeDir::X_DIR,
)?;
self.set_data_entry_mode(spi, DataEntryModeIncr::XIncrYIncr, DataEntryModeDir::XDir)?;
// Use simple X/Y auto increase
self.set_ram_area(spi, 0, 0, WIDTH - 1, HEIGHT - 1)?;
@@ -131,9 +127,9 @@ where
self.set_border_waveform(
spi,
BorderWaveForm {
vbd: BorderWaveFormVBD::GS,
fix_level: BorderWaveFormFixLevel::VSS,
gs_trans: BorderWaveFormGS::LUT3,
vbd: BorderWaveFormVbd::Gs,
fix_level: BorderWaveFormFixLevel::Vss,
gs_trans: BorderWaveFormGs::Lut3,
},
)?;
@@ -158,7 +154,7 @@ where
}
impl<SPI, CS, BUSY, DC, RST> WaveshareDisplay<SPI, CS, BUSY, DC, RST>
for EPD2in13<SPI, CS, BUSY, DC, RST>
for Epd2in13<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -175,11 +171,11 @@ where
rst: RST,
delay: &mut DELAY,
) -> Result<Self, SPI::Error> {
let mut epd = EPD2in13 {
let mut epd = Epd2in13 {
interface: DisplayInterface::new(cs, busy, dc, rst),
sleep_mode: DeepSleepMode::MODE_1,
sleep_mode: DeepSleepMode::Mode1,
background_color: DEFAULT_BACKGROUND_COLOR,
refresh: RefreshLUT::FULL,
refresh: RefreshLut::Full,
};
epd.init(spi, delay)?;
@@ -206,7 +202,7 @@ where
.disable_analog()
.disable_clock(),
)?;
self.command(spi, Command::MASTER_ACTIVATION)?;
self.command(spi, Command::MasterActivation)?;
self.set_sleep_mode(spi, self.sleep_mode)?;
Ok(())
@@ -217,14 +213,14 @@ where
self.set_ram_area(spi, 0, 0, WIDTH - 1, HEIGHT - 1)?;
self.set_ram_address_counters(spi, 0, 0)?;
self.cmd_with_data(spi, Command::WRITE_RAM, buffer)?;
self.cmd_with_data(spi, Command::WriteRam, buffer)?;
if self.refresh == RefreshLUT::FULL {
if self.refresh == RefreshLut::Full {
// Always keep the base buffer equal to current if not doing partial refresh.
self.set_ram_area(spi, 0, 0, WIDTH - 1, HEIGHT - 1)?;
self.set_ram_address_counters(spi, 0, 0)?;
self.cmd_with_data(spi, Command::WRITE_RAM_RED, buffer)?;
self.cmd_with_data(spi, Command::WriteRamRed, buffer)?;
}
Ok(())
}
@@ -249,19 +245,19 @@ where
// RAM content). Using this function will most probably make the actual
// display incorrect as the controler will compare with something
// incorrect.
assert!(self.refresh == RefreshLUT::FULL);
assert!(self.refresh == RefreshLut::Full);
self.set_ram_area(spi, x, y, x + width, y + height)?;
self.set_ram_address_counters(spi, x, y)?;
self.cmd_with_data(spi, Command::WRITE_RAM, buffer)?;
self.cmd_with_data(spi, Command::WriteRam, buffer)?;
if self.refresh == RefreshLUT::FULL {
if self.refresh == RefreshLut::Full {
// Always keep the base buffer equals to current if not doing partial refresh.
self.set_ram_area(spi, x, y, x + width, y + height)?;
self.set_ram_address_counters(spi, x, y)?;
self.cmd_with_data(spi, Command::WRITE_RAM_RED, buffer)?;
self.cmd_with_data(spi, Command::WriteRamRed, buffer)?;
}
Ok(())
@@ -270,7 +266,7 @@ where
/// Never use directly this function when using partial refresh, or also
/// keep the base buffer in syncd using `set_partial_base_buffer` function.
fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
if self.refresh == RefreshLUT::FULL {
if self.refresh == RefreshLut::Full {
self.set_display_update_control_2(
spi,
DisplayUpdateControl2::new()
@@ -283,7 +279,7 @@ where
} else {
self.set_display_update_control_2(spi, DisplayUpdateControl2::new().display())?;
}
self.command(spi, Command::MASTER_ACTIVATION)?;
self.command(spi, Command::MasterActivation)?;
self.wait_until_idle();
Ok(())
@@ -293,7 +289,7 @@ where
self.update_frame(spi, buffer)?;
self.display_frame(spi)?;
if self.refresh == RefreshLUT::QUICK {
if self.refresh == RefreshLut::Quick {
self.set_partial_base_buffer(spi, buffer)?;
}
Ok(())
@@ -305,7 +301,7 @@ where
self.set_ram_area(spi, 0, 0, WIDTH - 1, HEIGHT - 1)?;
self.set_ram_address_counters(spi, 0, 0)?;
self.command(spi, Command::WRITE_RAM)?;
self.command(spi, Command::WriteRam)?;
self.interface.data_x_times(
spi,
color,
@@ -313,11 +309,11 @@ where
)?;
// Always keep the base buffer equals to current if not doing partial refresh.
if self.refresh == RefreshLUT::FULL {
if self.refresh == RefreshLut::Full {
self.set_ram_area(spi, 0, 0, WIDTH - 1, HEIGHT - 1)?;
self.set_ram_address_counters(spi, 0, 0)?;
self.command(spi, Command::WRITE_RAM_RED)?;
self.command(spi, Command::WriteRamRed)?;
self.interface.data_x_times(
spi,
color,
@@ -346,14 +342,14 @@ where
fn set_lut(
&mut self,
spi: &mut SPI,
refresh_rate: Option<RefreshLUT>,
refresh_rate: Option<RefreshLut>,
) -> Result<(), SPI::Error> {
let buffer = match refresh_rate {
Some(RefreshLUT::FULL) | None => &LUT_FULL_UPDATE,
Some(RefreshLUT::QUICK) => &LUT_PARTIAL_UPDATE,
Some(RefreshLut::Full) | None => &LUT_FULL_UPDATE,
Some(RefreshLut::Quick) => &LUT_PARTIAL_UPDATE,
};
self.cmd_with_data(spi, Command::WRITE_LUT_REGISTER, buffer)
self.cmd_with_data(spi, Command::WriteLutRegister, buffer)
}
fn is_busy(&self) -> bool {
@@ -361,7 +357,7 @@ where
}
}
impl<SPI, CS, BUSY, DC, RST> EPD2in13<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST> Epd2in13<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -380,7 +376,7 @@ where
self.set_ram_area(spi, 0, 0, WIDTH - 1, HEIGHT - 1)?;
self.set_ram_address_counters(spi, 0, 0)?;
self.cmd_with_data(spi, Command::WRITE_RAM_RED, buffer)?;
self.cmd_with_data(spi, Command::WriteRamRed, buffer)?;
Ok(())
}
@@ -395,7 +391,7 @@ where
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
refresh: RefreshLUT,
refresh: RefreshLut,
) -> Result<(), SPI::Error> {
if self.refresh != refresh {
self.refresh = refresh;
@@ -412,7 +408,7 @@ where
assert!(start <= 295);
self.cmd_with_data(
spi,
Command::GATE_SCAN_START_POSITION,
Command::GateScanStartPosition,
&[(start & 0xFF) as u8, ((start >> 8) & 0x1) as u8],
)
}
@@ -424,13 +420,13 @@ where
) -> Result<(), SPI::Error> {
self.cmd_with_data(
spi,
Command::BORDER_WAVEFORM_CONTROL,
Command::BorderWaveformControl,
&[borderwaveform.to_u8()],
)
}
fn set_vcom_register(&mut self, spi: &mut SPI, vcom: VCOM) -> Result<(), SPI::Error> {
self.cmd_with_data(spi, Command::WRITE_VCOM_REGISTER, &[vcom.0])
fn set_vcom_register(&mut self, spi: &mut SPI, vcom: Vcom) -> Result<(), SPI::Error> {
self.cmd_with_data(spi, Command::WriteVcomRegister, &[vcom.0])
}
fn set_gate_driving_voltage(
@@ -438,7 +434,7 @@ where
spi: &mut SPI,
voltage: GateDrivingVoltage,
) -> Result<(), SPI::Error> {
self.cmd_with_data(spi, Command::GATE_DRIVING_VOLTAGE_CTRL, &[voltage.0])
self.cmd_with_data(spi, Command::GateDrivingVoltageCtrl, &[voltage.0])
}
fn set_dummy_line_period(
@@ -447,11 +443,11 @@ where
number_of_lines: u8,
) -> Result<(), SPI::Error> {
assert!(number_of_lines <= 127);
self.cmd_with_data(spi, Command::SET_DUMMY_LINE_PERIOD, &[number_of_lines])
self.cmd_with_data(spi, Command::SetDummyLinePeriod, &[number_of_lines])
}
fn set_gate_line_width(&mut self, spi: &mut SPI, width: u8) -> Result<(), SPI::Error> {
self.cmd_with_data(spi, Command::SET_GATE_LINE_WIDTH, &[width & 0x0F])
self.cmd_with_data(spi, Command::SetGateLineWidth, &[width & 0x0F])
}
/// Sets the source driving voltage value
@@ -464,7 +460,7 @@ where
) -> Result<(), SPI::Error> {
self.cmd_with_data(
spi,
Command::SOURCE_DRIVING_VOLTAGE_CTRL,
Command::SourceDrivingVoltageCtrl,
&[vsh1.0, vsh2.0, vsl.0],
)
}
@@ -476,16 +472,16 @@ where
spi: &mut SPI,
value: DisplayUpdateControl2,
) -> Result<(), SPI::Error> {
self.cmd_with_data(spi, Command::DISPLAY_UPDATE_CONTROL_2, &[value.0])
self.cmd_with_data(spi, Command::DisplayUpdateControl2, &[value.0])
}
/// Triggers the deep sleep mode
fn set_sleep_mode(&mut self, spi: &mut SPI, mode: DeepSleepMode) -> Result<(), SPI::Error> {
self.cmd_with_data(spi, Command::DEEP_SLEEP_MODE, &[mode as u8])
self.cmd_with_data(spi, Command::DeepSleepMode, &[mode as u8])
}
fn set_driver_output(&mut self, spi: &mut SPI, output: DriverOutput) -> Result<(), SPI::Error> {
self.cmd_with_data(spi, Command::DRIVER_OUTPUT_CONTROL, &output.to_bytes())
self.cmd_with_data(spi, Command::DriverOutputControl, &output.to_bytes())
}
/// Sets the data entry mode (ie. how X and Y positions changes when writing
@@ -497,7 +493,7 @@ where
counter_direction: DataEntryModeDir,
) -> Result<(), SPI::Error> {
let mode = counter_incr_mode as u8 | counter_direction as u8;
self.cmd_with_data(spi, Command::DATA_ENTRY_MODE_SETTING, &[mode])
self.cmd_with_data(spi, Command::DataEntryModeSetting, &[mode])
}
/// Sets both X and Y pixels ranges
@@ -511,13 +507,13 @@ where
) -> Result<(), SPI::Error> {
self.cmd_with_data(
spi,
Command::SET_RAM_X_ADDRESS_START_END_POSITION,
Command::SetRamXAddressStartEndPosition,
&[(start_x >> 3) as u8, (end_x >> 3) as u8],
)?;
self.cmd_with_data(
spi,
Command::SET_RAM_Y_ADDRESS_START_END_POSITION,
Command::SetRamYAddressStartEndPosition,
&[
start_y as u8,
(start_y >> 8) as u8,
@@ -535,11 +531,11 @@ where
y: u32,
) -> Result<(), SPI::Error> {
self.wait_until_idle();
self.cmd_with_data(spi, Command::SET_RAM_X_ADDRESS_COUNTER, &[(x >> 3) as u8])?;
self.cmd_with_data(spi, Command::SetRamXAddressCounter, &[(x >> 3) as u8])?;
self.cmd_with_data(
spi,
Command::SET_RAM_Y_ADDRESS_COUNTER,
Command::SetRamYAddressCounter,
&[y as u8, (y >> 8) as u8],
)?;
Ok(())
+40 -41
View File
@@ -5,117 +5,116 @@ use crate::traits;
///
/// 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,
PanelSetting = 0x00,
/// Selecting internal and external power
POWER_SETTING = 0x01,
POWER_OFF = 0x02,
PowerSetting = 0x01,
PowerOff = 0x02,
/// Setting Power OFF sequence
POWER_OFF_SEQUENCE_SETTING = 0x03,
POWER_ON = 0x04,
PowerOffSequenceSetting = 0x03,
PowerOn = 0x04,
/// This command enables the internal bandgap, which will be cleared by the next POF.
POWER_ON_MEASURE = 0x05,
PowerOnMeasure = 0x05,
/// Starting data transmission
///
/// ```ignore
/// self.send_data(&[0x07, 0x07, 0x17])?;
/// ```
BOOSTER_SOFT_START = 0x06,
BoosterSoftStart = 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,
DeepSleep = 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,
DataStartTransmission1 = 0x10,
/// Stopping data transmission
DATA_STOP = 0x11,
DataStop = 0x11,
/// After this command is issued, driver will refresh display (data/VCOM) according to SRAM data and LUT.
DISPLAY_REFRESH = 0x12,
DisplayRefresh = 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,
DataStartTransmission2 = 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,
PartialDataStartTransmission1 = 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,
PartialDataStartTransmission2 = 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,
PartialDisplayRefresh = 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,
LutForVcom = 0x20,
LutWhiteToWhite = 0x21,
LutBlackToWhite = 0x22,
LutWhiteToBlack = 0x23,
LutBlackToBlack = 0x24,
/// The command controls the PLL clock frequency.
PLL_CONTROL = 0x30,
PllControl = 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,
TemperatureSensor = 0x40,
/// This command selects Internal or External temperature sensor.
TEMPERATURE_SENSOR_CALIBRATION = 0x41,
TemperatureSensorCalibration = 0x41,
/// Write External Temperature Sensor
TEMPERATURE_SENSOR_WRITE = 0x42,
TemperatureSensorWrite = 0x42,
/// Read External Temperature Sensor
///
/// Doesn't work! Waveshare doesn't connect the read pin
TEMPERATURE_SENSOR_READ = 0x43,
TemperatureSensorRead = 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,
VcomAndDataIntervalSetting = 0x50,
/// This command indicates the input power condition. Host can read this flag to learn the battery condition.
LOW_POWER_DETECTION = 0x51,
LowPowerDetection = 0x51,
/// This command defines non-overlap period of Gate and Source.
TCON_SETTING = 0x60,
TconSetting = 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,
ResolutionSetting = 0x61,
SourceAndGateSetting = 0x62,
/// This command reads the IC status.
///
/// Doesn't work! Waveshare doesn't connect the read pin
GET_STATUS = 0x71,
GetStatus = 0x71,
/// Automatically measure VCOM. This command reads the IC status
AUTO_MEASUREMENT_VCOM = 0x80,
AutoMeasurementVcom = 0x80,
/// This command gets the VCOM value
///
/// Doesn't work! Waveshare doesn't connect the read pin
READ_VCOM_VALUE = 0x81,
ReadVcomValue = 0x81,
/// This command sets VCOM_DC value.
VCM_DC_SETTING = 0x82,
VcmDcSetting = 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,
ProgramMode = 0xA0,
/// After this command is issued, the chip would enter the program mode.
ACTIVE_PROGRAMMING = 0xA1,
ActiveProgramming = 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,
ReadOtp = 0xA2,
/// Not shown in commands table, but used in init sequence
POWER_OPTIMIZATION = 0xf8,
PowerOptimization = 0xf8,
}
impl traits::Command for Command {
@@ -132,7 +131,7 @@ mod tests {
#[test]
fn command_addr() {
assert_eq!(Command::PANEL_SETTING.address(), 0x00);
assert_eq!(Command::DISPLAY_REFRESH.address(), 0x12);
assert_eq!(Command::PanelSetting.address(), 0x00);
assert_eq!(Command::DisplayRefresh.address(), 0x12);
}
}
+48 -54
View File
@@ -9,7 +9,7 @@ use embedded_hal::{
use crate::interface::DisplayInterface;
use crate::traits::{
InternalWiAdditions, RefreshLUT, WaveshareDisplay, WaveshareThreeColorDisplay,
InternalWiAdditions, RefreshLut, WaveshareDisplay, WaveshareThreeColorDisplay,
};
// The Lookup Tables for the Display
@@ -34,8 +34,8 @@ mod graphics;
#[cfg(feature = "graphics")]
pub use self::graphics::Display2in7b;
/// EPD2in7b driver
pub struct EPD2in7b<SPI, CS, BUSY, DC, RST> {
/// Epd2in7b driver
pub struct Epd2in7b<SPI, CS, BUSY, DC, RST> {
/// Connection Interface
interface: DisplayInterface<SPI, CS, BUSY, DC, RST>,
/// Background Color
@@ -43,7 +43,7 @@ pub struct EPD2in7b<SPI, CS, BUSY, DC, RST> {
}
impl<SPI, CS, BUSY, DC, RST> InternalWiAdditions<SPI, CS, BUSY, DC, RST>
for EPD2in7b<SPI, CS, BUSY, DC, RST>
for Epd2in7b<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -60,51 +60,51 @@ where
self.interface.reset(delay, 2);
// power on
self.command(spi, Command::POWER_ON)?;
self.command(spi, Command::PowerOn)?;
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])?;
.cmd_with_data(spi, Command::PanelSetting, &[0xaf])?;
// pll control
self.interface
.cmd_with_data(spi, Command::PLL_CONTROL, &[0x3a])?;
.cmd_with_data(spi, Command::PllControl, &[0x3a])?;
// set the power settings
self.interface.cmd_with_data(
spi,
Command::POWER_SETTING,
Command::PowerSetting,
&[0x03, 0x00, 0x2b, 0x2b, 0x09],
)?;
// start the booster
self.interface
.cmd_with_data(spi, Command::BOOSTER_SOFT_START, &[0x07, 0x07, 0x17])?;
.cmd_with_data(spi, Command::BoosterSoftStart, &[0x07, 0x07, 0x17])?;
// power optimization
self.interface
.cmd_with_data(spi, Command::POWER_OPTIMIZATION, &[0x60, 0xa5])?;
.cmd_with_data(spi, Command::PowerOptimization, &[0x60, 0xa5])?;
self.interface
.cmd_with_data(spi, Command::POWER_OPTIMIZATION, &[0x89, 0xa5])?;
.cmd_with_data(spi, Command::PowerOptimization, &[0x89, 0xa5])?;
self.interface
.cmd_with_data(spi, Command::POWER_OPTIMIZATION, &[0x90, 0x00])?;
.cmd_with_data(spi, Command::PowerOptimization, &[0x90, 0x00])?;
self.interface
.cmd_with_data(spi, Command::POWER_OPTIMIZATION, &[0x93, 0x2a])?;
.cmd_with_data(spi, Command::PowerOptimization, &[0x93, 0x2a])?;
self.interface
.cmd_with_data(spi, Command::POWER_OPTIMIZATION, &[0x73, 0x41])?;
.cmd_with_data(spi, Command::PowerOptimization, &[0x73, 0x41])?;
self.interface
.cmd_with_data(spi, Command::VCM_DC_SETTING, &[0x12])?;
.cmd_with_data(spi, Command::VcmDcSetting, &[0x12])?;
self.interface
.cmd_with_data(spi, Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x87])?;
.cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x87])?;
self.set_lut(spi, None)?;
self.interface
.cmd_with_data(spi, Command::PARTIAL_DISPLAY_REFRESH, &[0x00])?;
.cmd_with_data(spi, Command::PartialDisplayRefresh, &[0x00])?;
self.wait_until_idle();
Ok(())
@@ -112,7 +112,7 @@ where
}
impl<SPI, CS, BUSY, DC, RST> WaveshareDisplay<SPI, CS, BUSY, DC, RST>
for EPD2in7b<SPI, CS, BUSY, DC, RST>
for Epd2in7b<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -132,7 +132,7 @@ where
let interface = DisplayInterface::new(cs, busy, dc, rst);
let color = DEFAULT_BACKGROUND_COLOR;
let mut epd = EPD2in7b { interface, color };
let mut epd = Epd2in7b { interface, color };
epd.init(spi, delay)?;
@@ -150,27 +150,25 @@ where
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])?;
.cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0xf7])?;
self.command(spi, Command::POWER_OFF)?;
self.command(spi, Command::PowerOff)?;
self.wait_until_idle();
self.interface
.cmd_with_data(spi, Command::DEEP_SLEEP, &[0xA5])?;
.cmd_with_data(spi, Command::DeepSleep, &[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.interface.cmd(spi, Command::DataStartTransmission1)?;
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.cmd(spi, Command::DataStartTransmission2)?;
self.interface
.data_x_times(spi, !self.color.get_byte_value(), WIDTH * HEIGHT / 8)?;
self.interface.cmd(spi, Command::DATA_STOP)?;
self.interface.cmd(spi, Command::DataStop)?;
Ok(())
}
@@ -184,7 +182,7 @@ where
height: u32,
) -> Result<(), SPI::Error> {
self.interface
.cmd(spi, Command::PARTIAL_DATA_START_TRANSMISSION_1)?;
.cmd(spi, Command::PartialDataStartTransmission1)?;
self.send_data(spi, &[(x >> 8) as u8])?;
self.send_data(spi, &[(x & 0xf8) as u8])?;
@@ -198,18 +196,18 @@ where
self.send_buffer_helper(spi, buffer)?;
self.interface.cmd(spi, Command::DATA_STOP)
self.interface.cmd(spi, Command::DataStop)
}
fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
self.command(spi, Command::DISPLAY_REFRESH)?;
self.command(spi, Command::DisplayRefresh)?;
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)?;
self.command(spi, Command::DisplayRefresh)?;
Ok(())
}
@@ -217,18 +215,16 @@ where
self.wait_until_idle();
let color_value = self.color.get_byte_value();
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
self.interface.cmd(spi, Command::DataStartTransmission1)?;
self.interface
.data_x_times(spi, color_value, WIDTH * HEIGHT / 8)?;
self.interface.cmd(spi, Command::DATA_STOP)?;
self.interface.cmd(spi, Command::DataStop)?;
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_2)?;
self.interface.cmd(spi, Command::DataStartTransmission2)?;
self.interface
.data_x_times(spi, color_value, WIDTH * HEIGHT / 8)?;
self.interface.cmd(spi, Command::DATA_STOP)?;
self.interface.cmd(spi, Command::DataStop)?;
Ok(())
}
@@ -251,14 +247,14 @@ where
fn set_lut(
&mut self,
spi: &mut SPI,
_refresh_rate: Option<RefreshLUT>,
_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)?;
self.cmd_with_data(spi, Command::LutForVcom, &LUT_VCOM_DC)?;
self.cmd_with_data(spi, Command::LutWhiteToWhite, &LUT_WW)?;
self.cmd_with_data(spi, Command::LutBlackToWhite, &LUT_BW)?;
self.cmd_with_data(spi, Command::LutWhiteToBlack, &LUT_WB)?;
self.cmd_with_data(spi, Command::LutBlackToBlack, &LUT_BB)?;
Ok(())
}
@@ -268,7 +264,7 @@ where
}
impl<SPI, CS, BUSY, DC, RST> WaveshareThreeColorDisplay<SPI, CS, BUSY, DC, RST>
for EPD2in7b<SPI, CS, BUSY, DC, RST>
for Epd2in7b<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -294,12 +290,11 @@ where
spi: &mut SPI,
achromatic: &[u8],
) -> Result<(), SPI::Error> {
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
self.interface.cmd(spi, Command::DataStartTransmission1)?;
self.send_buffer_helper(spi, achromatic)?;
self.interface.cmd(spi, Command::DATA_STOP)
self.interface.cmd(spi, Command::DataStop)
}
/// Update only chromatic data of the display.
@@ -310,19 +305,18 @@ where
spi: &mut SPI,
chromatic: &[u8],
) -> Result<(), SPI::Error> {
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_2)?;
self.interface.cmd(spi, Command::DataStartTransmission2)?;
self.send_buffer_helper(spi, chromatic)?;
self.interface.cmd(spi, Command::DATA_STOP)?;
self.interface.cmd(spi, Command::DataStop)?;
self.wait_until_idle();
Ok(())
}
}
impl<SPI, CS, BUSY, DC, RST> EPD2in7b<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST> Epd2in7b<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -369,7 +363,7 @@ where
width: u32,
height: u32,
) -> Result<(), SPI::Error> {
self.command(spi, Command::PARTIAL_DISPLAY_REFRESH)?;
self.command(spi, Command::PartialDisplayRefresh)?;
self.send_data(spi, &[(x >> 8) as u8])?;
self.send_data(spi, &[(x & 0xf8) as u8])?;
self.send_data(spi, &[(y >> 8) as u8])?;
@@ -393,7 +387,7 @@ where
height: u32,
) -> Result<(), SPI::Error> {
self.interface
.cmd(spi, Command::PARTIAL_DATA_START_TRANSMISSION_1)?;
.cmd(spi, Command::PartialDataStartTransmission1)?;
self.send_data(spi, &[(x >> 8) as u8])?;
self.send_data(spi, &[(x & 0xf8) as u8])?;
self.send_data(spi, &[(y >> 8) as u8])?;
@@ -423,7 +417,7 @@ where
height: u32,
) -> Result<(), SPI::Error> {
self.interface
.cmd(spi, Command::PARTIAL_DATA_START_TRANSMISSION_2)?;
.cmd(spi, Command::PartialDataStartTransmission2)?;
self.send_data(spi, &[(x >> 8) as u8])?;
self.send_data(spi, &[(x & 0xf8) as u8])?;
self.send_data(spi, &[(y >> 8) as u8])?;
+31 -34
View File
@@ -21,7 +21,7 @@
//!# let mut delay = delay::MockNoop::new();
//!
//!// Setup EPD
//!let mut epd = EPD2in9::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?;
//!let mut epd = Epd2in9::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?;
//!
//!// Use display graphics from embedded-graphics
//!let mut display = Display2in9::default();
@@ -70,18 +70,18 @@ mod graphics;
#[cfg(feature = "graphics")]
pub use crate::epd2in9::graphics::Display2in9;
/// EPD2in9 driver
/// Epd2in9 driver
///
pub struct EPD2in9<SPI, CS, BUSY, DC, RST> {
pub struct Epd2in9<SPI, CS, BUSY, DC, RST> {
/// SPI
interface: DisplayInterface<SPI, CS, BUSY, DC, RST>,
/// Color
background_color: Color,
/// Refresh LUT
refresh: RefreshLUT,
refresh: RefreshLut,
}
impl<SPI, CS, BUSY, DC, RST> EPD2in9<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST> Epd2in9<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -104,42 +104,39 @@ where
// 0.. B[2:0]
// Default Values: A = Height of Screen (0x127), B = 0x00 (GD, SM and TB=0?)
self.interface
.cmd_with_data(spi, Command::DRIVER_OUTPUT_CONTROL, &[0x27, 0x01, 0x00])?;
.cmd_with_data(spi, Command::DriverOutputControl, &[0x27, 0x01, 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.cmd_with_data(
spi,
Command::BOOSTER_SOFT_START_CONTROL,
&[0xD7, 0xD6, 0x9D],
)?;
self.interface
.cmd_with_data(spi, Command::BoosterSoftStartControl, &[0xD7, 0xD6, 0x9D])?;
// One Databyte with value 0xA8 for 7V VCOM
self.interface
.cmd_with_data(spi, Command::WRITE_VCOM_REGISTER, &[0xA8])?;
.cmd_with_data(spi, Command::WriteVcomRegister, &[0xA8])?;
// One Databyte with default value 0x1A for 4 dummy lines per gate
self.interface
.cmd_with_data(spi, Command::SET_DUMMY_LINE_PERIOD, &[0x1A])?;
.cmd_with_data(spi, Command::SetDummyLinePeriod, &[0x1A])?;
// One Databyte with default value 0x08 for 2us per line
self.interface
.cmd_with_data(spi, Command::SET_GATE_LINE_WIDTH, &[0x08])?;
.cmd_with_data(spi, Command::SetGateLineWidth, &[0x08])?;
// One Databyte with default value 0x03
// -> address: x increment, y increment, address counter is updated in x direction
self.interface
.cmd_with_data(spi, Command::DATA_ENTRY_MODE_SETTING, &[0x03])?;
.cmd_with_data(spi, Command::DataEntryModeSetting, &[0x03])?;
self.set_lut(spi, None)
}
}
impl<SPI, CS, BUSY, DC, RST> WaveshareDisplay<SPI, CS, BUSY, DC, RST>
for EPD2in9<SPI, CS, BUSY, DC, RST>
for Epd2in9<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -166,10 +163,10 @@ where
) -> Result<Self, SPI::Error> {
let interface = DisplayInterface::new(cs, busy, dc, rst);
let mut epd = EPD2in9 {
let mut epd = Epd2in9 {
interface,
background_color: DEFAULT_BACKGROUND_COLOR,
refresh: RefreshLUT::FULL,
refresh: RefreshLut::Full,
};
epd.init(spi, delay)?;
@@ -182,7 +179,7 @@ where
// 0x00 for Normal mode (Power on Reset), 0x01 for Deep Sleep Mode
//TODO: is 0x00 needed here? (see also epd1in54)
self.interface
.cmd_with_data(spi, Command::DEEP_SLEEP_MODE, &[0x00])?;
.cmd_with_data(spi, Command::DeepSleepMode, &[0x00])?;
Ok(())
}
@@ -201,7 +198,7 @@ where
self.use_full_frame(spi)?;
self.interface
.cmd_with_data(spi, Command::WRITE_RAM, buffer)?;
.cmd_with_data(spi, Command::WriteRam, buffer)?;
Ok(())
}
@@ -220,7 +217,7 @@ where
self.set_ram_counter(spi, x, y)?;
self.interface
.cmd_with_data(spi, Command::WRITE_RAM, buffer)?;
.cmd_with_data(spi, Command::WriteRam, buffer)?;
Ok(())
}
@@ -229,12 +226,12 @@ where
// 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
.cmd_with_data(spi, Command::DISPLAY_UPDATE_CONTROL_2, &[0xC4])?;
.cmd_with_data(spi, Command::DisplayUpdateControl2, &[0xC4])?;
self.interface.cmd(spi, Command::MASTER_ACTIVATION)?;
self.interface.cmd(spi, Command::MasterActivation)?;
// 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.interface.cmd(spi, Command::Nop)?;
Ok(())
}
@@ -251,7 +248,7 @@ 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.cmd(spi, Command::WriteRam)?;
self.interface
.data_x_times(spi, color, WIDTH / 8 * HEIGHT)?;
Ok(())
@@ -268,14 +265,14 @@ where
fn set_lut(
&mut self,
spi: &mut SPI,
refresh_rate: Option<RefreshLUT>,
refresh_rate: Option<RefreshLut>,
) -> Result<(), SPI::Error> {
if let Some(refresh_lut) = refresh_rate {
self.refresh = refresh_lut;
}
match self.refresh {
RefreshLUT::FULL => self.set_lut_helper(spi, &LUT_FULL_UPDATE),
RefreshLUT::QUICK => self.set_lut_helper(spi, &LUT_PARTIAL_UPDATE),
RefreshLut::Full => self.set_lut_helper(spi, &LUT_FULL_UPDATE),
RefreshLut::Quick => self.set_lut_helper(spi, &LUT_PARTIAL_UPDATE),
}
}
@@ -284,7 +281,7 @@ where
}
}
impl<SPI, CS, BUSY, DC, RST> EPD2in9<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST> Epd2in9<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -319,14 +316,14 @@ where
// aren't relevant
self.interface.cmd_with_data(
spi,
Command::SET_RAM_X_ADDRESS_START_END_POSITION,
Command::SetRamXAddressStartEndPosition,
&[(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(
spi,
Command::SET_RAM_Y_ADDRESS_START_END_POSITION,
Command::SetRamYAddressStartEndPosition,
&[
start_y as u8,
(start_y >> 8) as u8,
@@ -341,12 +338,12 @@ 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(spi, Command::SET_RAM_X_ADDRESS_COUNTER, &[(x >> 3) as u8])?;
.cmd_with_data(spi, Command::SetRamXAddressCounter, &[(x >> 3) as u8])?;
// 2 Databytes: A[7:0] & 0..A[8]
self.interface.cmd_with_data(
spi,
Command::SET_RAM_Y_ADDRESS_COUNTER,
Command::SetRamYAddressCounter,
&[y as u8, (y >> 8) as u8],
)?;
Ok(())
@@ -357,7 +354,7 @@ where
self.wait_until_idle();
assert!(buffer.len() == 30);
self.interface
.cmd_with_data(spi, Command::WRITE_LUT_REGISTER, buffer)?;
.cmd_with_data(spi, Command::WriteLutRegister, buffer)?;
Ok(())
}
}
+21 -22
View File
@@ -2,33 +2,32 @@
use crate::traits;
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
pub(crate) enum Command {
PANEL_SETTING = 0x00,
PanelSetting = 0x00,
POWER_SETTING = 0x01,
POWER_OFF = 0x02,
POWER_ON = 0x04,
BOOSTER_SOFT_START = 0x06,
DEEP_SLEEP = 0x07,
DATA_START_TRANSMISSION_1 = 0x10,
DISPLAY_REFRESH = 0x12,
DATA_START_TRANSMISSION_2 = 0x13,
PowerSetting = 0x01,
PowerOff = 0x02,
PowerOn = 0x04,
BoosterSoftStart = 0x06,
DeepSleep = 0x07,
DataStartTransmission1 = 0x10,
DisplayRefresh = 0x12,
DataStartTransmission2 = 0x13,
LUT_FOR_VCOM = 0x20,
LUT_WHITE_TO_WHITE = 0x21,
LUT_BLACK_TO_WHITE = 0x22,
LUT_WHITE_TO_BLACK = 0x23,
LUT_BLACK_TO_BLACK = 0x24,
LutForVcom = 0x20,
LutWhiteToWhite = 0x21,
LutBlackToWhite = 0x22,
LutWhiteToBlack = 0x23,
LutBlackToBlack = 0x24,
PLL_CONTROL = 0x30,
TEMPERATURE_SENSOR_COMMAND = 0x40,
TEMPERATURE_SENSOR_SELECTION = 0x41,
VCOM_AND_DATA_INTERVAL_SETTING = 0x50,
RESOLUTION_SETTING = 0x61,
VCM_DC_SETTING = 0x82,
POWER_SAVING = 0xE3,
PllControl = 0x30,
TemperatureSensor = 0x40,
TemperatureSensorSelection = 0x41,
VcomAndDataIntervalSetting = 0x50,
ResolutionSetting = 0x61,
VcmDcSetting = 0x82,
PowerSaving = 0xE3,
}
impl traits::Command for Command {
+28 -34
View File
@@ -20,7 +20,7 @@
//!# let mut delay = delay::MockNoop::new();
//!
//!// Setup EPD
//!let mut epd = EPD2in9bc::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?;
//!let mut epd = Epd2in9bc::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?;
//!
//!// Use display graphics from embedded-graphics
//!// This display is for the black/white pixels
@@ -60,7 +60,7 @@ use embedded_hal::{
use crate::interface::DisplayInterface;
use crate::traits::{
InternalWiAdditions, RefreshLUT, WaveshareDisplay, WaveshareThreeColorDisplay,
InternalWiAdditions, RefreshLut, WaveshareDisplay, WaveshareThreeColorDisplay,
};
/// Width of epd2in9bc in pixels
@@ -89,14 +89,14 @@ mod graphics;
#[cfg(feature = "graphics")]
pub use self::graphics::Display2in9bc;
/// EPD2in9bc driver
pub struct EPD2in9bc<SPI, CS, BUSY, DC, RST> {
/// Epd2in9bc driver
pub struct Epd2in9bc<SPI, CS, BUSY, DC, RST> {
interface: DisplayInterface<SPI, CS, BUSY, DC, RST>,
color: Color,
}
impl<SPI, CS, BUSY, DC, RST> InternalWiAdditions<SPI, CS, BUSY, DC, RST>
for EPD2in9bc<SPI, CS, BUSY, DC, RST>
for Epd2in9bc<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -115,26 +115,26 @@ where
// start the booster
self.interface
.cmd_with_data(spi, Command::BOOSTER_SOFT_START, &[0x17, 0x17, 0x17])?;
.cmd_with_data(spi, Command::BoosterSoftStart, &[0x17, 0x17, 0x17])?;
// power on
self.command(spi, Command::POWER_ON)?;
self.command(spi, Command::PowerOn)?;
delay.delay_ms(5);
self.wait_until_idle();
// set the panel settings
self.cmd_with_data(spi, Command::PANEL_SETTING, &[0x8F])?;
self.cmd_with_data(spi, Command::PanelSetting, &[0x8F])?;
self.cmd_with_data(
spi,
Command::VCOM_AND_DATA_INTERVAL_SETTING,
Command::VcomAndDataIntervalSetting,
&[WHITE_BORDER | VCOM_DATA_INTERVAL],
)?;
// set resolution
self.send_resolution(spi)?;
self.cmd_with_data(spi, Command::VCM_DC_SETTING, &[0x0A])?;
self.cmd_with_data(spi, Command::VcmDcSetting, &[0x0A])?;
self.wait_until_idle();
@@ -143,7 +143,7 @@ where
}
impl<SPI, CS, BUSY, DC, RST> WaveshareThreeColorDisplay<SPI, CS, BUSY, DC, RST>
for EPD2in9bc<SPI, CS, BUSY, DC, RST>
for Epd2in9bc<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -165,8 +165,7 @@ where
///
/// Finish by calling `update_chromatic_frame`.
fn update_achromatic_frame(&mut self, spi: &mut SPI, black: &[u8]) -> Result<(), SPI::Error> {
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
self.interface.cmd(spi, Command::DataStartTransmission1)?;
self.interface.data(spi, black)?;
Ok(())
}
@@ -179,8 +178,7 @@ where
spi: &mut SPI,
chromatic: &[u8],
) -> Result<(), SPI::Error> {
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_2)?;
self.interface.cmd(spi, Command::DataStartTransmission2)?;
self.interface.data(spi, chromatic)?;
self.wait_until_idle();
@@ -189,7 +187,7 @@ where
}
impl<SPI, CS, BUSY, DC, RST> WaveshareDisplay<SPI, CS, BUSY, DC, RST>
for EPD2in9bc<SPI, CS, BUSY, DC, RST>
for Epd2in9bc<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -209,7 +207,7 @@ where
let interface = DisplayInterface::new(cs, busy, dc, rst);
let color = DEFAULT_BACKGROUND_COLOR;
let mut epd = EPD2in9bc { interface, color };
let mut epd = Epd2in9bc { interface, color };
epd.init(spi, delay)?;
@@ -220,15 +218,15 @@ where
// Section 8.2 from datasheet
self.interface.cmd_with_data(
spi,
Command::VCOM_AND_DATA_INTERVAL_SETTING,
Command::VcomAndDataIntervalSetting,
&[FLOATING_BORDER | VCOM_DATA_INTERVAL],
)?;
self.command(spi, Command::POWER_OFF)?;
// The example STM code from Github has a wait after POWER_OFF
self.command(spi, Command::PowerOff)?;
// The example STM code from Github has a wait after PowerOff
self.wait_until_idle();
self.cmd_with_data(spi, Command::DEEP_SLEEP, &[0xA5])?;
self.cmd_with_data(spi, Command::DeepSleep, &[0xA5])?;
Ok(())
}
@@ -258,16 +256,14 @@ where
}
fn update_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> {
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
self.interface.cmd(spi, Command::DataStartTransmission1)?;
self.interface.data(spi, &buffer)?;
// Clear the chromatic layer
let color = self.color.get_byte_value();
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_2)?;
self.interface.cmd(spi, Command::DataStartTransmission2)?;
self.interface.data_x_times(spi, color, NUM_DISPLAY_BITS)?;
self.wait_until_idle();
@@ -288,7 +284,7 @@ where
}
fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
self.command(spi, Command::DISPLAY_REFRESH)?;
self.command(spi, Command::DisplayRefresh)?;
self.wait_until_idle();
Ok(())
@@ -306,14 +302,12 @@ where
let color = DEFAULT_BACKGROUND_COLOR.get_byte_value();
// Clear the black
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
self.interface.cmd(spi, Command::DataStartTransmission1)?;
self.interface.data_x_times(spi, color, NUM_DISPLAY_BITS)?;
// Clear the chromatic
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_2)?;
self.interface.cmd(spi, Command::DataStartTransmission2)?;
self.interface.data_x_times(spi, color, NUM_DISPLAY_BITS)?;
self.wait_until_idle();
@@ -323,7 +317,7 @@ where
fn set_lut(
&mut self,
_spi: &mut SPI,
_refresh_rate: Option<RefreshLUT>,
_refresh_rate: Option<RefreshLut>,
) -> Result<(), SPI::Error> {
Ok(())
}
@@ -333,7 +327,7 @@ where
}
}
impl<SPI, CS, BUSY, DC, RST> EPD2in9bc<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST> Epd2in9bc<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -366,7 +360,7 @@ where
let w = self.width();
let h = self.height();
self.command(spi, Command::RESOLUTION_SETTING)?;
self.command(spi, Command::ResolutionSetting)?;
self.send_data(spi, &[w as u8])?;
self.send_data(spi, &[(h >> 8) as u8])?;
@@ -382,7 +376,7 @@ where
};
self.cmd_with_data(
spi,
Command::VCOM_AND_DATA_INTERVAL_SETTING,
Command::VcomAndDataIntervalSetting,
&[border | VCOM_DATA_INTERVAL],
)
}
+42 -43
View File
@@ -8,7 +8,6 @@ use crate::traits;
///
/// The description of the single commands is mostly taken from IL0398.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
@@ -17,142 +16,142 @@ pub(crate) enum Command {
/// 0x1F B/W Mode, LUT from OTP
/// 0x2F Red Mode, LUT set by registers
/// 0x3F B/W Mode, LUT set by registers
PANEL_SETTING = 0x00,
PanelSetting = 0x00,
/// selecting internal and external power
/// self.send_data(0x03)?; //VDS_EN, VDG_EN
/// self.send_data(0x00)?; //VCOM_HV, VGHL_LV[1], VGHL_LV[0]
/// self.send_data(0x2b)?; //VDH
/// self.send_data(0x2b)?; //VDL
/// self.send_data(0xff)?; //VDHR
POWER_SETTING = 0x01,
PowerSetting = 0x01,
/// After the Power Off command, the driver will power off following the Power Off Sequence. This command will turn off charge
/// pump, T-con, source driver, gate driver, VCOM, and temperature sensor, but register data will be kept until VDD becomes OFF.
/// Source Driver output and Vcom will remain as previous condition, which may have 2 conditions: floating.
POWER_OFF = 0x02,
PowerOff = 0x02,
/// Setting Power OFF sequence
POWER_OFF_SEQUENCE_SETTING = 0x03,
PowerOffSequenceSetting = 0x03,
/// Turning On the Power
POWER_ON = 0x04,
PowerOn = 0x04,
/// This command enables the internal bandgap, which will be cleared by the next POF.
POWER_ON_MEASURE = 0x05,
PowerOnMeasure = 0x05,
/// Starting data transmission
/// 3-times: self.send_data(0x17)?; //07 0f 17 1f 27 2F 37 2f
BOOSTER_SOFT_START = 0x06,
BoosterSoftStart = 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,
DeepSleep = 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.
/// - In Program mode, this command writes “OTP” data to SRAM for programming.
DATA_START_TRANSMISSION_1 = 0x10,
DataStartTransmission1 = 0x10,
/// Stopping data transmission
DATA_STOP = 0x11,
DataStop = 0x11,
/// While user sent this command, driver will refresh display (data/VCOM) according to SRAM data and LUT.
///
/// After Display Refresh command, BUSY_N signal will become “0” and the refreshing of panel starts.
DISPLAY_REFRESH = 0x12,
DisplayRefresh = 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,
DataStartTransmission2 = 0x13,
/// This command stores VCOM Look-Up Table with 7 groups of data. Each group contains information for one state and is stored
/// with 6 bytes, while the sixth byte indicates how many times that phase will repeat.
///
/// from IL0373
LUT_FOR_VCOM = 0x20,
LutForVcom = 0x20,
/// This command stores White-to-White Look-Up Table with 7 groups of data. Each group contains information for one state and is
/// stored with 6 bytes, while the sixth byte indicates how many times that phase will repeat.
///
/// from IL0373
LUT_WHITE_TO_WHITE = 0x21,
LutWhiteToWhite = 0x21,
/// This command stores Black-to-White Look-Up Table with 7 groups of data. Each group contains information for one state and is
/// stored with 6 bytes, while the sixth byte indicates how many times that phase will repeat.
///
/// from IL0373
LUT_BLACK_TO_WHITE = 0x22,
LutBlackToWhite = 0x22,
/// This command stores White-to-Black Look-Up Table with 7 groups of data. Each group contains information for one state and is
/// stored with 6 bytes, while the sixth byte indicates how many times that phase will repeat.
///
/// from IL0373
LUT_WHITE_TO_BLACK = 0x23,
LutWhiteToBlack = 0x23,
/// This command stores Black-to-Black Look-Up Table with 7 groups of data. Each group contains information for one state and is
/// stored with 6 bytes, while the sixth byte indicates how many times that phase will repeat.
///
/// from IL0373
LUT_BLACK_TO_BLACK = 0x24,
LutBlackToBlack = 0x24,
/// The command controls the PLL clock frequency.
PLL_CONTROL = 0x30,
PllControl = 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,
TemperatureSensor = 0x40,
/// Selects the Internal or External temperature sensor and offset
TEMPERATURE_SENSOR_SELECTION = 0x41,
TemperatureSensorSelection = 0x41,
/// Write External Temperature Sensor
TEMPERATURE_SENSOR_WRITE = 0x42,
TemperatureSensorWrite = 0x42,
/// Read External Temperature Sensor
///
/// Doesn't work! Waveshare doesn't connect the read pin
TEMPERATURE_SENSOR_READ = 0x43,
TemperatureSensorRead = 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,
VcomAndDataIntervalSetting = 0x50,
/// This command indicates the input power condition. Host can read this flag to learn the battery condition.
LOW_POWER_DETECTION = 0x51,
LowPowerDetection = 0x51,
/// This command defines non-overlap period of Gate and Source.
TCON_SETTING = 0x60,
TconSetting = 0x60,
/// This command defines alternative resolution and this setting is of higher priority than the RES\[1:0\] in R00H (PSR).
RESOLUTION_SETTING = 0x61,
ResolutionSetting = 0x61,
/// This command defines the Fist Active Gate and First Active Source of active channels.
GSST_SETTING = 0x65,
GsstSetting = 0x65,
/// The LUT_REV / Chip Revision is read from OTP address = 0x001.
///
/// Doesn't work! Waveshare doesn't connect the read pin
REVISION = 0x70,
Revision = 0x70,
/// Read Flags. This command reads the IC status
/// PTL, I2C_ERR, I2C_BUSY, DATA, PON, POF, BUSY
///
/// Doesn't work! Waveshare doesn't connect the read pin
GET_STATUS = 0x71,
GetStatus = 0x71,
/// Automatically measure VCOM. This command reads the IC status
AUTO_MEASUREMENT_VCOM = 0x80,
AutoMeasurementVcom = 0x80,
/// This command gets the VCOM value
///
/// Doesn't work! Waveshare doesn't connect the read pin
READ_VCOM_VALUE = 0x81,
ReadVcomValue = 0x81,
/// Set VCM_DC
VCM_DC_SETTING = 0x82,
VcmDcSetting = 0x82,
/// This command sets partial window
PARTIAL_WINDOW = 0x90,
PartialWindow = 0x90,
/// This command makes the display enter partial mode
PARTIAL_IN = 0x91,
PartialIn = 0x91,
/// This command makes the display exit partial mode and enter normal mode
PARTIAL_OUT = 0x92,
PartialOut = 0x92,
/// 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,
ProgramMode = 0xA0,
/// After this command is transmitted, the programming state machine would be activated.
///
/// The BUSY flag would fall to 0 until the programming is completed.
ACTIVE_PROGRAMMING = 0xA1,
ActiveProgramming = 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,
ReadOtp = 0xA2,
/// This command is set for saving power during fresh period. If the output voltage of VCOM / Source is from negative to positive or
/// from positive to negative, the power saving mechanism will be activated. The active period width is defined by the following two
/// parameters.
POWER_SAVING = 0xE3,
PowerSaving = 0xE3,
}
impl traits::Command for Command {
@@ -169,10 +168,10 @@ mod tests {
#[test]
fn command_addr() {
assert_eq!(Command::POWER_SAVING.address(), 0xE3);
assert_eq!(Command::PowerSaving.address(), 0xE3);
assert_eq!(Command::PANEL_SETTING.address(), 0x00);
assert_eq!(Command::PanelSetting.address(), 0x00);
assert_eq!(Command::DISPLAY_REFRESH.address(), 0x12);
assert_eq!(Command::DisplayRefresh.address(), 0x12);
}
}
+56 -65
View File
@@ -25,7 +25,7 @@
//!# let mut delay = delay::MockNoop::new();
//!
//!// Setup EPD
//!let mut epd = EPD4in2::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?;
//!let mut epd = Epd4in2::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?;
//!
//!// Use display graphics from embedded-graphics
//!let mut display = Display4in2::default();
@@ -55,7 +55,7 @@ use embedded_hal::{
};
use crate::interface::DisplayInterface;
use crate::traits::{InternalWiAdditions, QuickRefresh, RefreshLUT, WaveshareDisplay};
use crate::traits::{InternalWiAdditions, QuickRefresh, RefreshLut, WaveshareDisplay};
//The Lookup Tables for the Display
mod constants;
@@ -79,19 +79,19 @@ mod graphics;
#[cfg(feature = "graphics")]
pub use self::graphics::Display4in2;
/// EPD4in2 driver
/// Epd4in2 driver
///
pub struct EPD4in2<SPI, CS, BUSY, DC, RST> {
pub struct Epd4in2<SPI, CS, BUSY, DC, RST> {
/// Connection Interface
interface: DisplayInterface<SPI, CS, BUSY, DC, RST>,
/// Background Color
color: Color,
/// Refresh LUT
refresh: RefreshLUT,
refresh: RefreshLut,
}
impl<SPI, CS, BUSY, DC, RST> InternalWiAdditions<SPI, CS, BUSY, DC, RST>
for EPD4in2<SPI, CS, BUSY, DC, RST>
for Epd4in2<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -110,36 +110,36 @@ where
// set the power settings
self.interface.cmd_with_data(
spi,
Command::POWER_SETTING,
Command::PowerSetting,
&[0x03, 0x00, 0x2b, 0x2b, 0xff],
)?;
// start the booster
self.interface
.cmd_with_data(spi, Command::BOOSTER_SOFT_START, &[0x17, 0x17, 0x17])?;
.cmd_with_data(spi, Command::BoosterSoftStart, &[0x17, 0x17, 0x17])?;
// power on
self.command(spi, Command::POWER_ON)?;
self.command(spi, Command::PowerOn)?;
delay.delay_ms(5);
self.wait_until_idle();
// set the panel settings
self.cmd_with_data(spi, Command::PANEL_SETTING, &[0x3F])?;
self.cmd_with_data(spi, Command::PanelSetting, &[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.cmd_with_data(spi, Command::PllControl, &[0x3A])?;
self.send_resolution(spi)?;
self.interface
.cmd_with_data(spi, Command::VCM_DC_SETTING, &[0x12])?;
.cmd_with_data(spi, Command::VcmDcSetting, &[0x12])?;
//VBDF 17|D7 VBDW 97 VBDB 57 VBDF F7 VBDW 77 VBDB 37 VBDR B7
self.interface
.cmd_with_data(spi, Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x97])?;
.cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x97])?;
self.set_lut(spi, None)?;
@@ -149,7 +149,7 @@ where
}
impl<SPI, CS, BUSY, DC, RST> WaveshareDisplay<SPI, CS, BUSY, DC, RST>
for EPD4in2<SPI, CS, BUSY, DC, RST>
for Epd4in2<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -169,10 +169,10 @@ where
let interface = DisplayInterface::new(cs, busy, dc, rst);
let color = DEFAULT_BACKGROUND_COLOR;
let mut epd = EPD4in2 {
let mut epd = Epd4in2 {
interface,
color,
refresh: RefreshLUT::FULL,
refresh: RefreshLut::Full,
};
epd.init(spi, delay)?;
@@ -191,19 +191,19 @@ where
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, &[0x17])?; //border floating
self.command(spi, Command::VCM_DC_SETTING)?; // VCOM to 0V
self.command(spi, Command::PANEL_SETTING)?;
.cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x17])?; //border floating
self.command(spi, Command::VcmDcSetting)?; // VCOM to 0V
self.command(spi, Command::PanelSetting)?;
self.command(spi, Command::POWER_SETTING)?; //VG&VS to 0V fast
self.command(spi, Command::PowerSetting)?; //VG&VS to 0V fast
for _ in 0..4 {
self.send_data(spi, &[0x00])?;
}
self.command(spi, Command::POWER_OFF)?;
self.command(spi, Command::PowerOff)?;
self.wait_until_idle();
self.interface
.cmd_with_data(spi, Command::DEEP_SLEEP, &[0xA5])?;
.cmd_with_data(spi, Command::DeepSleep, &[0xA5])?;
Ok(())
}
@@ -211,13 +211,12 @@ where
self.wait_until_idle();
let color_value = self.color.get_byte_value();
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
self.interface.cmd(spi, Command::DataStartTransmission1)?;
self.interface
.data_x_times(spi, color_value, WIDTH / 8 * HEIGHT)?;
self.interface
.cmd_with_data(spi, Command::DATA_START_TRANSMISSION_2, buffer)?;
.cmd_with_data(spi, Command::DataStartTransmission2, buffer)?;
Ok(())
}
@@ -236,8 +235,8 @@ where
//return Err("Wrong buffersize");
}
self.command(spi, Command::PARTIAL_IN)?;
self.command(spi, Command::PARTIAL_WINDOW)?;
self.command(spi, Command::PartialIn)?;
self.command(spi, Command::PartialWindow)?;
self.send_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
@@ -256,26 +255,26 @@ where
//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.command(spi, Command::DataStartTransmission1)? //TODO: check if data_start transmission 1 also needs "old"/background data here
} else {
self.command(spi, Command::DATA_START_TRANSMISSION_2)?
self.command(spi, Command::DataStartTransmission2)?
}
self.send_data(spi, buffer)?;
self.command(spi, Command::PARTIAL_OUT)?;
self.command(spi, Command::PartialOut)?;
Ok(())
}
fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
self.wait_until_idle();
self.command(spi, Command::DISPLAY_REFRESH)?;
self.command(spi, Command::DisplayRefresh)?;
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)?;
self.command(spi, Command::DisplayRefresh)?;
Ok(())
}
@@ -285,13 +284,11 @@ where
let color_value = self.color.get_byte_value();
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
self.interface.cmd(spi, Command::DataStartTransmission1)?;
self.interface
.data_x_times(spi, color_value, WIDTH / 8 * HEIGHT)?;
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_2)?;
self.interface.cmd(spi, Command::DataStartTransmission2)?;
self.interface
.data_x_times(spi, color_value, WIDTH / 8 * HEIGHT)?;
Ok(())
@@ -316,16 +313,16 @@ where
fn set_lut(
&mut self,
spi: &mut SPI,
refresh_rate: Option<RefreshLUT>,
refresh_rate: Option<RefreshLut>,
) -> Result<(), SPI::Error> {
if let Some(refresh_lut) = refresh_rate {
self.refresh = refresh_lut;
}
match self.refresh {
RefreshLUT::FULL => {
RefreshLut::Full => {
self.set_lut_helper(spi, &LUT_VCOM0, &LUT_WW, &LUT_BW, &LUT_WB, &LUT_BB)
}
RefreshLUT::QUICK => self.set_lut_helper(
RefreshLut::Quick => self.set_lut_helper(
spi,
&LUT_VCOM0_QUICK,
&LUT_WW_QUICK,
@@ -341,7 +338,7 @@ where
}
}
impl<SPI, CS, BUSY, DC, RST> EPD4in2<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST> Epd4in2<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -374,7 +371,7 @@ where
let w = self.width();
let h = self.height();
self.command(spi, Command::RESOLUTION_SETTING)?;
self.command(spi, Command::ResolutionSetting)?;
self.send_data(spi, &[(w >> 8) as u8])?;
self.send_data(spi, &[w as u8])?;
self.send_data(spi, &[(h >> 8) as u8])?;
@@ -392,19 +389,19 @@ where
) -> Result<(), SPI::Error> {
self.wait_until_idle();
// LUT VCOM
self.cmd_with_data(spi, Command::LUT_FOR_VCOM, lut_vcom)?;
self.cmd_with_data(spi, Command::LutForVcom, lut_vcom)?;
// LUT WHITE to WHITE
self.cmd_with_data(spi, Command::LUT_WHITE_TO_WHITE, lut_ww)?;
self.cmd_with_data(spi, Command::LutWhiteToWhite, lut_ww)?;
// LUT BLACK to WHITE
self.cmd_with_data(spi, Command::LUT_BLACK_TO_WHITE, lut_bw)?;
self.cmd_with_data(spi, Command::LutBlackToWhite, lut_bw)?;
// LUT WHITE to BLACK
self.cmd_with_data(spi, Command::LUT_WHITE_TO_BLACK, lut_wb)?;
self.cmd_with_data(spi, Command::LutWhiteToBlack, lut_wb)?;
// LUT BLACK to BLACK
self.cmd_with_data(spi, Command::LUT_BLACK_TO_BLACK, lut_bb)?;
self.cmd_with_data(spi, Command::LutBlackToBlack, lut_bb)?;
Ok(())
}
@@ -438,7 +435,7 @@ where
}
impl<SPI, CS, BUSY, DC, RST> QuickRefresh<SPI, CS, BUSY, DC, RST>
for EPD4in2<SPI, CS, BUSY, DC, RST>
for Epd4in2<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -450,8 +447,7 @@ where
fn update_old_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> {
self.wait_until_idle();
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
self.interface.cmd(spi, Command::DataStartTransmission1)?;
self.interface.data(spi, buffer)?;
@@ -463,8 +459,7 @@ where
self.wait_until_idle();
// self.send_resolution(spi)?;
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_2)?;
self.interface.cmd(spi, Command::DataStartTransmission2)?;
self.interface.data(spi, buffer)?;
@@ -487,13 +482,12 @@ where
//return Err("Wrong buffersize");
}
self.interface.cmd(spi, Command::PARTIAL_IN)?;
self.interface.cmd(spi, Command::PARTIAL_WINDOW)?;
self.interface.cmd(spi, Command::PartialIn)?;
self.interface.cmd(spi, Command::PartialWindow)?;
self.shift_display(spi, x, y, width, height)?;
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
self.interface.cmd(spi, Command::DataStartTransmission1)?;
self.interface.data(spi, buffer)?;
@@ -519,12 +513,11 @@ where
self.shift_display(spi, x, y, width, height)?;
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_2)?;
self.interface.cmd(spi, Command::DataStartTransmission2)?;
self.interface.data(spi, buffer)?;
self.interface.cmd(spi, Command::PARTIAL_OUT)?;
self.interface.cmd(spi, Command::PartialOut)?;
Ok(())
}
@@ -541,22 +534,20 @@ where
let color_value = self.color.get_byte_value();
self.interface.cmd(spi, Command::PARTIAL_IN)?;
self.interface.cmd(spi, Command::PARTIAL_WINDOW)?;
self.interface.cmd(spi, Command::PartialIn)?;
self.interface.cmd(spi, Command::PartialWindow)?;
self.shift_display(spi, x, y, width, height)?;
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
self.interface.cmd(spi, Command::DataStartTransmission1)?;
self.interface
.data_x_times(spi, color_value, width / 8 * height)?;
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_2)?;
self.interface.cmd(spi, Command::DataStartTransmission2)?;
self.interface
.data_x_times(spi, color_value, width / 8 * height)?;
self.interface.cmd(spi, Command::PARTIAL_OUT)?;
self.interface.cmd(spi, Command::PartialOut)?;
Ok(())
}
}
+39 -40
View File
@@ -8,127 +8,126 @@ use crate::traits;
///
/// 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 {
/// Set Resolution, LUT selection, BWR pixels, gate scan direction, source shift
/// direction, booster switch, soft reset.
PANEL_SETTING = 0x00,
PanelSetting = 0x00,
/// Selecting internal and external power
POWER_SETTING = 0x01,
PowerSetting = 0x01,
/// After the Power Off command, the driver will power off following the Power Off
/// Sequence; BUSY signal will become "0". This command will turn off charge pump,
/// T-con, source driver, gate driver, VCOM, and temperature sensor, but register
/// data will be kept until VDD becomes OFF. Source Driver output and Vcom will remain
/// as previous condition, which may have 2 conditions: 0V or floating.
POWER_OFF = 0x02,
PowerOff = 0x02,
/// Setting Power OFF sequence
POWER_OFF_SEQUENCE_SETTING = 0x03,
PowerOffSequenceSetting = 0x03,
/// Turning On the Power
///
/// After the Power ON command, the driver will power on following the Power ON
/// sequence. Once complete, the BUSY signal will become "1".
POWER_ON = 0x04,
PowerOn = 0x04,
/// Starting data transmission
BOOSTER_SOFT_START = 0x06,
BoosterSoftStart = 0x06,
/// This command makes the chip enter the deep-sleep mode to save power.
///
/// The deep sleep mode would return to stand-by by hardware reset.
///
/// The only one parameter is a check code, the command would be excuted if check code = 0xA5.
DEEP_SLEEP = 0x07,
DeepSleep = 0x07,
/// This command starts transmitting data and write them into SRAM. To complete data
/// transmission, command DSP (Data Stop) must be issued. Then the chip will start to
/// send data/VCOM for panel.
///
/// BLACK/WHITE or OLD_DATA
DATA_START_TRANSMISSION_1 = 0x10,
DataStartTransmission1 = 0x10,
/// To stop data transmission, this command must be issued to check the `data_flag`.
///
/// After this command, BUSY signal will become "0" until the display update is
/// finished.
DATA_STOP = 0x11,
DataStop = 0x11,
/// After this command is issued, driver will refresh display (data/VCOM) according to
/// SRAM data and LUT.
///
/// After Display Refresh command, BUSY signal will become "0" until the display
/// update is finished.
DISPLAY_REFRESH = 0x12,
DisplayRefresh = 0x12,
/// Image Process Command
IMAGE_PROCESS_COMMAND = 0x13,
ImageProcess = 0x13,
/// This command builds the VCOM Look-Up Table (LUTC).
LUT_FOR_VCOM = 0x20,
LutForVcom = 0x20,
/// This command builds the Black Look-Up Table (LUTB).
LUT_BLACK = 0x21,
LutBlack = 0x21,
/// This command builds the White Look-Up Table (LUTW).
LUT_WHITE = 0x22,
LutWhite = 0x22,
/// This command builds the Gray1 Look-Up Table (LUTG1).
LUT_GRAY_1 = 0x23,
LutGray1 = 0x23,
/// This command builds the Gray2 Look-Up Table (LUTG2).
LUT_GRAY_2 = 0x24,
LutGray2 = 0x24,
/// This command builds the Red0 Look-Up Table (LUTR0).
LUT_RED_0 = 0x25,
LutRed0 = 0x25,
/// This command builds the Red1 Look-Up Table (LUTR1).
LUT_RED_1 = 0x26,
LutRed1 = 0x26,
/// This command builds the Red2 Look-Up Table (LUTR2).
LUT_RED_2 = 0x27,
LutRed2 = 0x27,
/// This command builds the Red3 Look-Up Table (LUTR3).
LUT_RED_3 = 0x28,
LutRed3 = 0x28,
/// This command builds the XON Look-Up Table (LUTXON).
LUT_XON = 0x29,
LutXon = 0x29,
/// The command controls the PLL clock frequency.
PLL_CONTROL = 0x30,
PllControl = 0x30,
/// This command reads the temperature sensed by the temperature sensor.
TEMPERATURE_SENSOR_COMMAND = 0x40,
TemperatureSensor = 0x40,
/// This command selects the Internal or External temperature sensor.
TEMPERATURE_CALIBRATION = 0x41,
TemperatureCalibration = 0x41,
/// This command could write data to the external temperature sensor.
TEMPERATURE_SENSOR_WRITE = 0x42,
TemperatureSensorWrite = 0x42,
/// This command could read data from the external temperature sensor.
TEMPERATURE_SENSOR_READ = 0x43,
TemperatureSensorRead = 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,
VcomAndDataIntervalSetting = 0x50,
/// This command indicates the input power condition. Host can read this flag to learn
/// the battery condition.
LOW_POWER_DETECTION = 0x51,
LowPowerDetection = 0x51,
/// This command defines non-overlap period of Gate and Source.
TCON_SETTING = 0x60,
TconSetting = 0x60,
/// This command defines alternative resolution and this setting is of higher priority
/// than the RES\[1:0\] in R00H (PSR).
TCON_RESOLUTION = 0x61,
TconResolution = 0x61,
/// This command defines MCU host direct access external memory mode.
//SPI_FLASH_CONTROL = 0x65,
//SpiFlashControl = 0x65,
/// The LUT_REV / Chip Revision is read from OTP address = 25001 and 25000.
//REVISION = 0x70,
//Revision = 0x70,
/// This command reads the IC status.
GET_STATUS = 0x71,
GetStatus = 0x71,
/// This command implements related VCOM sensing setting.
//AUTO_MEASUREMENT_VCOM = 0x80,
//AutoMeasurementVcom = 0x80,
/// This command gets the VCOM value.
READ_VCOM_VALUE = 0x81,
ReadVcomValue = 0x81,
/// This command sets `VCOM_DC` value.
VCM_DC_SETTING = 0x82,
VcmDcSetting = 0x82,
// /// This is in all the Waveshare controllers for EPD6in65f, but it's not documented
// /// anywhere in the datasheet `¯\_(ツ)_/¯`
FLASH_MODE = 0xE3,
FlashMode = 0xE3,
}
impl traits::Command for Command {
@@ -145,7 +144,7 @@ mod tests {
#[test]
fn command_addr() {
assert_eq!(Command::PANEL_SETTING.address(), 0x00);
assert_eq!(Command::DISPLAY_REFRESH.address(), 0x12);
assert_eq!(Command::PanelSetting.address(), 0x00);
assert_eq!(Command::DisplayRefresh.address(), 0x12);
}
}
+25 -25
View File
@@ -13,7 +13,7 @@ use embedded_hal::{
use crate::color::OctColor;
use crate::interface::DisplayInterface;
use crate::traits::{InternalWiAdditions, RefreshLUT, WaveshareDisplay};
use crate::traits::{InternalWiAdditions, RefreshLut, WaveshareDisplay};
pub(crate) mod command;
use self::command::Command;
@@ -31,9 +31,9 @@ pub const HEIGHT: u32 = 448;
pub const DEFAULT_BACKGROUND_COLOR: OctColor = OctColor::White;
const IS_BUSY_LOW: bool = true;
/// EPD5in65f driver
/// Epd5in65f driver
///
pub struct EPD5in65f<SPI, CS, BUSY, DC, RST> {
pub struct Epd5in65f<SPI, CS, BUSY, DC, RST> {
/// Connection Interface
interface: DisplayInterface<SPI, CS, BUSY, DC, RST>,
/// Background Color
@@ -41,7 +41,7 @@ pub struct EPD5in65f<SPI, CS, BUSY, DC, RST> {
}
impl<SPI, CS, BUSY, DC, RST> InternalWiAdditions<SPI, CS, BUSY, DC, RST>
for EPD5in65f<SPI, CS, BUSY, DC, RST>
for Epd5in65f<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -57,27 +57,27 @@ where
// Reset the device
self.interface.reset(delay, 2);
self.cmd_with_data(spi, Command::PANEL_SETTING, &[0xEF, 0x08])?;
self.cmd_with_data(spi, Command::POWER_SETTING, &[0x37, 0x00, 0x23, 0x23])?;
self.cmd_with_data(spi, Command::POWER_OFF_SEQUENCE_SETTING, &[0x00])?;
self.cmd_with_data(spi, Command::BOOSTER_SOFT_START, &[0xC7, 0xC7, 0x1D])?;
self.cmd_with_data(spi, Command::PLL_CONTROL, &[0x3C])?;
self.cmd_with_data(spi, Command::TEMPERATURE_SENSOR_COMMAND, &[0x00])?;
self.cmd_with_data(spi, Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x37])?;
self.cmd_with_data(spi, Command::TCON_SETTING, &[0x22])?;
self.cmd_with_data(spi, Command::PanelSetting, &[0xEF, 0x08])?;
self.cmd_with_data(spi, Command::PowerSetting, &[0x37, 0x00, 0x23, 0x23])?;
self.cmd_with_data(spi, Command::PowerOffSequenceSetting, &[0x00])?;
self.cmd_with_data(spi, Command::BoosterSoftStart, &[0xC7, 0xC7, 0x1D])?;
self.cmd_with_data(spi, Command::PllControl, &[0x3C])?;
self.cmd_with_data(spi, Command::TemperatureSensor, &[0x00])?;
self.cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x37])?;
self.cmd_with_data(spi, Command::TconSetting, &[0x22])?;
self.send_resolution(spi)?;
self.cmd_with_data(spi, Command::FLASH_MODE, &[0xAA])?;
self.cmd_with_data(spi, Command::FlashMode, &[0xAA])?;
delay.delay_ms(100);
self.cmd_with_data(spi, Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x37])?;
self.cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x37])?;
Ok(())
}
}
impl<SPI, CS, BUSY, DC, RST> WaveshareDisplay<SPI, CS, BUSY, DC, RST>
for EPD5in65f<SPI, CS, BUSY, DC, RST>
for Epd5in65f<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -97,7 +97,7 @@ where
let interface = DisplayInterface::new(cs, busy, dc, rst);
let color = DEFAULT_BACKGROUND_COLOR;
let mut epd = EPD5in65f { interface, color };
let mut epd = Epd5in65f { interface, color };
epd.init(spi, delay)?;
@@ -113,14 +113,14 @@ where
}
fn sleep(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
self.cmd_with_data(spi, Command::DEEP_SLEEP, &[0xA5])?;
self.cmd_with_data(spi, Command::DeepSleep, &[0xA5])?;
Ok(())
}
fn update_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> {
self.wait_busy_high();
self.send_resolution(spi)?;
self.cmd_with_data(spi, Command::DATA_START_TRANSMISSION_1, buffer)?;
self.cmd_with_data(spi, Command::DataStartTransmission1, buffer)?;
Ok(())
}
@@ -138,11 +138,11 @@ where
fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
self.wait_busy_high();
self.command(spi, Command::POWER_ON)?;
self.command(spi, Command::PowerOn)?;
self.wait_busy_high();
self.command(spi, Command::DISPLAY_REFRESH)?;
self.command(spi, Command::DisplayRefresh)?;
self.wait_busy_high();
self.command(spi, Command::POWER_OFF)?;
self.command(spi, Command::PowerOff)?;
self.wait_busy_low();
Ok(())
}
@@ -157,7 +157,7 @@ where
let bg = OctColor::colors_byte(self.color, self.color);
self.wait_busy_high();
self.send_resolution(spi)?;
self.command(spi, Command::DATA_START_TRANSMISSION_1)?;
self.command(spi, Command::DataStartTransmission1)?;
self.interface.data_x_times(spi, bg, WIDTH * HEIGHT / 2)?;
self.display_frame(spi)?;
Ok(())
@@ -182,7 +182,7 @@ where
fn set_lut(
&mut self,
_spi: &mut SPI,
_refresh_rate: Option<RefreshLUT>,
_refresh_rate: Option<RefreshLut>,
) -> Result<(), SPI::Error> {
unimplemented!();
}
@@ -192,7 +192,7 @@ where
}
}
impl<SPI, CS, BUSY, DC, RST> EPD5in65f<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST> Epd5in65f<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -227,7 +227,7 @@ where
let w = self.width();
let h = self.height();
self.command(spi, Command::TCON_RESOLUTION)?;
self.command(spi, Command::TconResolution)?;
self.send_data(spi, &[(w >> 8) as u8])?;
self.send_data(spi, &[w as u8])?;
self.send_data(spi, &[(h >> 8) as u8])?;
+41 -42
View File
@@ -2,136 +2,135 @@
use crate::traits;
/// EPD7in5 commands
/// Epd7in5 commands
///
/// 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 {
/// Set Resolution, LUT selection, BWR pixels, gate scan direction, source shift
/// direction, booster switch, soft reset.
PANEL_SETTING = 0x00,
PanelSetting = 0x00,
/// Selecting internal and external power
POWER_SETTING = 0x01,
PowerSetting = 0x01,
/// After the Power Off command, the driver will power off following the Power Off
/// Sequence; BUSY signal will become "0". This command will turn off charge pump,
/// T-con, source driver, gate driver, VCOM, and temperature sensor, but register
/// data will be kept until VDD becomes OFF. Source Driver output and Vcom will remain
/// as previous condition, which may have 2 conditions: 0V or floating.
POWER_OFF = 0x02,
PowerOff = 0x02,
/// Setting Power OFF sequence
POWER_OFF_SEQUENCE_SETTING = 0x03,
PowerOffSequenceSetting = 0x03,
/// Turning On the Power
///
/// After the Power ON command, the driver will power on following the Power ON
/// sequence. Once complete, the BUSY signal will become "1".
POWER_ON = 0x04,
PowerOn = 0x04,
/// Starting data transmission
BOOSTER_SOFT_START = 0x06,
BoosterSoftStart = 0x06,
/// This command makes the chip enter the deep-sleep mode to save power.
///
/// The deep sleep mode would return to stand-by by hardware reset.
///
/// The only one parameter is a check code, the command would be excuted if check code = 0xA5.
DEEP_SLEEP = 0x07,
DeepSleep = 0x07,
/// This command starts transmitting data and write them into SRAM. To complete data
/// transmission, command DSP (Data Stop) must be issued. Then the chip will start to
/// send data/VCOM for panel.
DATA_START_TRANSMISSION_1 = 0x10,
DataStartTransmission1 = 0x10,
/// To stop data transmission, this command must be issued to check the `data_flag`.
///
/// After this command, BUSY signal will become "0" until the display update is
/// finished.
DATA_STOP = 0x11,
DataStop = 0x11,
/// After this command is issued, driver will refresh display (data/VCOM) according to
/// SRAM data and LUT.
///
/// After Display Refresh command, BUSY signal will become "0" until the display
/// update is finished.
DISPLAY_REFRESH = 0x12,
DisplayRefresh = 0x12,
/// After this command is issued, image process engine will find thin lines/pixels
/// from frame SRAM and update the frame SRAM for applying new gray level waveform.
///
/// After "Image Process Command", BUSY_N signal will become "0" until image process
/// is finished.
IMAGE_PROCESS = 0x13,
ImageProcess = 0x13,
/// This command builds the VCOM Look-Up Table (LUTC).
LUT_FOR_VCOM = 0x20,
LutForVcom = 0x20,
/// This command builds the Black Look-Up Table (LUTB).
LUT_BLACK = 0x21,
LutBlack = 0x21,
/// This command builds the White Look-Up Table (LUTW).
LUT_WHITE = 0x22,
LutWhite = 0x22,
/// This command builds the Gray1 Look-Up Table (LUTG1).
LUT_GRAY_1 = 0x23,
LutGray1 = 0x23,
/// This command builds the Gray2 Look-Up Table (LUTG2).
LUT_GRAY_2 = 0x24,
LutGray2 = 0x24,
/// This command builds the Red0 Look-Up Table (LUTR0).
LUT_RED_0 = 0x25,
LutRed0 = 0x25,
/// This command builds the Red1 Look-Up Table (LUTR1).
LUT_RED_1 = 0x26,
LutRed1 = 0x26,
/// This command builds the Red2 Look-Up Table (LUTR2).
LUT_RED_2 = 0x27,
LutRed2 = 0x27,
/// This command builds the Red3 Look-Up Table (LUTR3).
LUT_RED_3 = 0x28,
LutRed3 = 0x28,
/// This command builds the XON Look-Up Table (LUTXON).
LUT_XON = 0x29,
LutXon = 0x29,
/// The command controls the PLL clock frequency.
PLL_CONTROL = 0x30,
PllControl = 0x30,
/// This command reads the temperature sensed by the temperature sensor.
TEMPERATURE_SENSOR_COMMAND = 0x40,
TemperatureSensor = 0x40,
/// This command selects the Internal or External temperature sensor.
TEMPERATURE_CALIBRATION = 0x41,
TemperatureCalibration = 0x41,
/// This command could write data to the external temperature sensor.
TEMPERATURE_SENSOR_WRITE = 0x42,
TemperatureSensorWrite = 0x42,
/// This command could read data from the external temperature sensor.
TEMPERATURE_SENSOR_READ = 0x43,
TemperatureSensorRead = 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,
VcomAndDataIntervalSetting = 0x50,
/// This command indicates the input power condition. Host can read this flag to learn
/// the battery condition.
LOW_POWER_DETECTION = 0x51,
LowPowerDetection = 0x51,
/// This command defines non-overlap period of Gate and Source.
TCON_SETTING = 0x60,
TconSetting = 0x60,
/// This command defines alternative resolution and this setting is of higher priority
/// than the RES\[1:0\] in R00H (PSR).
TCON_RESOLUTION = 0x61,
TconResolution = 0x61,
/// This command defines MCU host direct access external memory mode.
SPI_FLASH_CONTROL = 0x65,
SpiFlashControl = 0x65,
/// The LUT_REV / Chip Revision is read from OTP address = 25001 and 25000.
REVISION = 0x70,
Revision = 0x70,
/// This command reads the IC status.
GET_STATUS = 0x71,
GetStatus = 0x71,
/// This command implements related VCOM sensing setting.
AUTO_MEASUREMENT_VCOM = 0x80,
AutoMeasurementVcom = 0x80,
/// This command gets the VCOM value.
READ_VCOM_VALUE = 0x81,
ReadVcomValue = 0x81,
/// This command sets `VCOM_DC` value.
VCM_DC_SETTING = 0x82,
VcmDcSetting = 0x82,
/// This is in all the Waveshare controllers for EPD7in5, but it's not documented
/// This is in all the Waveshare controllers for Epd7in5, but it's not documented
/// anywhere in the datasheet `¯\_(ツ)_/¯`
FLASH_MODE = 0xE5,
FlashMode = 0xE5,
}
impl traits::Command for Command {
@@ -148,7 +147,7 @@ mod tests {
#[test]
fn command_addr() {
assert_eq!(Command::PANEL_SETTING.address(), 0x00);
assert_eq!(Command::DISPLAY_REFRESH.address(), 0x12);
assert_eq!(Command::PanelSetting.address(), 0x00);
assert_eq!(Command::DisplayRefresh.address(), 0x12);
}
}
+26 -26
View File
@@ -13,7 +13,7 @@ use embedded_hal::{
use crate::color::Color;
use crate::interface::DisplayInterface;
use crate::traits::{InternalWiAdditions, RefreshLUT, WaveshareDisplay};
use crate::traits::{InternalWiAdditions, RefreshLut, WaveshareDisplay};
pub(crate) mod command;
use self::command::Command;
@@ -31,9 +31,9 @@ pub const HEIGHT: u32 = 384;
pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White;
const IS_BUSY_LOW: bool = true;
/// EPD7in5 driver
/// Epd7in5 driver
///
pub struct EPD7in5<SPI, CS, BUSY, DC, RST> {
pub struct Epd7in5<SPI, CS, BUSY, DC, RST> {
/// Connection Interface
interface: DisplayInterface<SPI, CS, BUSY, DC, RST>,
/// Background Color
@@ -41,7 +41,7 @@ pub struct EPD7in5<SPI, CS, BUSY, DC, RST> {
}
impl<SPI, CS, BUSY, DC, RST> InternalWiAdditions<SPI, CS, BUSY, DC, RST>
for EPD7in5<SPI, CS, BUSY, DC, RST>
for Epd7in5<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -58,41 +58,41 @@ where
self.interface.reset(delay, 10);
// Set the power settings
self.cmd_with_data(spi, Command::POWER_SETTING, &[0x37, 0x00])?;
self.cmd_with_data(spi, Command::PowerSetting, &[0x37, 0x00])?;
// Set the panel settings:
// - 600 x 448
// - Using LUT from external flash
self.cmd_with_data(spi, Command::PANEL_SETTING, &[0xCF, 0x08])?;
self.cmd_with_data(spi, Command::PanelSetting, &[0xCF, 0x08])?;
// Start the booster
self.cmd_with_data(spi, Command::BOOSTER_SOFT_START, &[0xC7, 0xCC, 0x28])?;
self.cmd_with_data(spi, Command::BoosterSoftStart, &[0xC7, 0xCC, 0x28])?;
// Power on
self.command(spi, Command::POWER_ON)?;
self.command(spi, Command::PowerOn)?;
delay.delay_ms(5);
self.wait_until_idle();
// Set the clock frequency to 50Hz (default)
self.cmd_with_data(spi, Command::PLL_CONTROL, &[0x3C])?;
self.cmd_with_data(spi, Command::PllControl, &[0x3C])?;
// Select internal temperature sensor (default)
self.cmd_with_data(spi, Command::TEMPERATURE_CALIBRATION, &[0x00])?;
self.cmd_with_data(spi, Command::TemperatureCalibration, &[0x00])?;
// Set Vcom and data interval to 10 (default), border output to white
self.cmd_with_data(spi, Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x77])?;
self.cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x77])?;
// Set S2G and G2S non-overlap periods to 12 (default)
self.cmd_with_data(spi, Command::TCON_SETTING, &[0x22])?;
self.cmd_with_data(spi, Command::TconSetting, &[0x22])?;
// Set the real resolution
self.send_resolution(spi)?;
// Set VCOM_DC to -1.5V
self.cmd_with_data(spi, Command::VCM_DC_SETTING, &[0x1E])?;
self.cmd_with_data(spi, Command::VcmDcSetting, &[0x1E])?;
// This is in all the Waveshare controllers for EPD7in5
self.cmd_with_data(spi, Command::FLASH_MODE, &[0x03])?;
// This is in all the Waveshare controllers for Epd7in5
self.cmd_with_data(spi, Command::FlashMode, &[0x03])?;
self.wait_until_idle();
Ok(())
@@ -100,7 +100,7 @@ where
}
impl<SPI, CS, BUSY, DC, RST> WaveshareDisplay<SPI, CS, BUSY, DC, RST>
for EPD7in5<SPI, CS, BUSY, DC, RST>
for Epd7in5<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -120,7 +120,7 @@ where
let interface = DisplayInterface::new(cs, busy, dc, rst);
let color = DEFAULT_BACKGROUND_COLOR;
let mut epd = EPD7in5 { interface, color };
let mut epd = Epd7in5 { interface, color };
epd.init(spi, delay)?;
@@ -137,15 +137,15 @@ where
fn sleep(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
self.wait_until_idle();
self.command(spi, Command::POWER_OFF)?;
self.command(spi, Command::PowerOff)?;
self.wait_until_idle();
self.cmd_with_data(spi, Command::DEEP_SLEEP, &[0xA5])?;
self.cmd_with_data(spi, Command::DeepSleep, &[0xA5])?;
Ok(())
}
fn update_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> {
self.wait_until_idle();
self.command(spi, Command::DATA_START_TRANSMISSION_1)?;
self.command(spi, Command::DataStartTransmission1)?;
for byte in buffer {
let mut temp = *byte;
for _ in 0..4 {
@@ -174,13 +174,13 @@ where
fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
self.wait_until_idle();
self.command(spi, Command::DISPLAY_REFRESH)?;
self.command(spi, Command::DisplayRefresh)?;
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)?;
self.command(spi, Command::DisplayRefresh)?;
Ok(())
}
@@ -189,7 +189,7 @@ where
self.send_resolution(spi)?;
// The Waveshare controllers all implement clear using 0x33
self.command(spi, Command::DATA_START_TRANSMISSION_1)?;
self.command(spi, Command::DataStartTransmission1)?;
self.interface
.data_x_times(spi, 0x33, WIDTH / 8 * HEIGHT * 4)?;
Ok(())
@@ -214,7 +214,7 @@ where
fn set_lut(
&mut self,
_spi: &mut SPI,
_refresh_rate: Option<RefreshLUT>,
_refresh_rate: Option<RefreshLut>,
) -> Result<(), SPI::Error> {
unimplemented!();
}
@@ -224,7 +224,7 @@ where
}
}
impl<SPI, CS, BUSY, DC, RST> EPD7in5<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST> Epd7in5<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -257,7 +257,7 @@ where
let w = self.width();
let h = self.height();
self.command(spi, Command::TCON_RESOLUTION)?;
self.command(spi, Command::TconResolution)?;
self.send_data(spi, &[(w >> 8) as u8])?;
self.send_data(spi, &[w as u8])?;
self.send_data(spi, &[(h >> 8) as u8])?;
+42 -43
View File
@@ -2,136 +2,135 @@
use crate::traits;
/// EPD7in5 commands
/// Epd7in5 commands
///
/// 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 {
/// Set Resolution, LUT selection, BWR pixels, gate scan direction, source shift
/// direction, booster switch, soft reset.
PANEL_SETTING = 0x00,
PanelSetting = 0x00,
/// Selecting internal and external power
POWER_SETTING = 0x01,
PowerSetting = 0x01,
/// After the Power Off command, the driver will power off following the Power Off
/// Sequence; BUSY signal will become "0". This command will turn off charge pump,
/// T-con, source driver, gate driver, VCOM, and temperature sensor, but register
/// data will be kept until VDD becomes OFF. Source Driver output and Vcom will remain
/// as previous condition, which may have 2 conditions: 0V or floating.
POWER_OFF = 0x02,
PowerOff = 0x02,
/// Setting Power OFF sequence
POWER_OFF_SEQUENCE_SETTING = 0x03,
PowerOffSequenceSetting = 0x03,
/// Turning On the Power
///
/// After the Power ON command, the driver will power on following the Power ON
/// sequence. Once complete, the BUSY signal will become "1".
POWER_ON = 0x04,
PowerOn = 0x04,
/// Starting data transmission
BOOSTER_SOFT_START = 0x06,
BoosterSoftStart = 0x06,
/// This command makes the chip enter the deep-sleep mode to save power.
///
/// The deep sleep mode would return to stand-by by hardware reset.
///
/// The only one parameter is a check code, the command would be excuted if check code = 0xA5.
DEEP_SLEEP = 0x07,
DeepSleep = 0x07,
/// This command starts transmitting data and write them into SRAM. To complete data
/// transmission, command DSP (Data Stop) must be issued. Then the chip will start to
/// send data/VCOM for panel.
///
/// BLACK/WHITE or OLD_DATA
DATA_START_TRANSMISSION_1 = 0x10,
DataStartTransmission1 = 0x10,
/// To stop data transmission, this command must be issued to check the `data_flag`.
///
/// After this command, BUSY signal will become "0" until the display update is
/// finished.
DATA_STOP = 0x11,
DataStop = 0x11,
/// After this command is issued, driver will refresh display (data/VCOM) according to
/// SRAM data and LUT.
///
/// After Display Refresh command, BUSY signal will become "0" until the display
/// update is finished.
DISPLAY_REFRESH = 0x12,
DisplayRefresh = 0x12,
/// RED or NEW_DATA
DATA_START_TRANSMISSION_2 = 0x13,
DataStartTransmission2 = 0x13,
/// Dual SPI - what for?
DUAL_SPI = 0x15,
DualSpi = 0x15,
/// This command builds the VCOM Look-Up Table (LUTC).
LUT_FOR_VCOM = 0x20,
LutForVcom = 0x20,
/// This command builds the Black Look-Up Table (LUTB).
LUT_BLACK = 0x21,
LutBlack = 0x21,
/// This command builds the White Look-Up Table (LUTW).
LUT_WHITE = 0x22,
LutWhite = 0x22,
/// This command builds the Gray1 Look-Up Table (LUTG1).
LUT_GRAY_1 = 0x23,
LutGray1 = 0x23,
/// This command builds the Gray2 Look-Up Table (LUTG2).
LUT_GRAY_2 = 0x24,
LutGray2 = 0x24,
/// This command builds the Red0 Look-Up Table (LUTR0).
LUT_RED_0 = 0x25,
LutRed0 = 0x25,
/// This command builds the Red1 Look-Up Table (LUTR1).
LUT_RED_1 = 0x26,
LutRed1 = 0x26,
/// This command builds the Red2 Look-Up Table (LUTR2).
LUT_RED_2 = 0x27,
LutRed2 = 0x27,
/// This command builds the Red3 Look-Up Table (LUTR3).
LUT_RED_3 = 0x28,
LutRed3 = 0x28,
/// This command builds the XON Look-Up Table (LUTXON).
LUT_XON = 0x29,
LutXon = 0x29,
/// The command controls the PLL clock frequency.
PLL_CONTROL = 0x30,
PllControl = 0x30,
/// This command reads the temperature sensed by the temperature sensor.
TEMPERATURE_SENSOR_COMMAND = 0x40,
TemperatureSensor = 0x40,
/// This command selects the Internal or External temperature sensor.
TEMPERATURE_CALIBRATION = 0x41,
TemperatureCalibration = 0x41,
/// This command could write data to the external temperature sensor.
TEMPERATURE_SENSOR_WRITE = 0x42,
TemperatureSensorWrite = 0x42,
/// This command could read data from the external temperature sensor.
TEMPERATURE_SENSOR_READ = 0x43,
TemperatureSensorRead = 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,
VcomAndDataIntervalSetting = 0x50,
/// This command indicates the input power condition. Host can read this flag to learn
/// the battery condition.
LOW_POWER_DETECTION = 0x51,
LowPowerDetection = 0x51,
/// This command defines non-overlap period of Gate and Source.
TCON_SETTING = 0x60,
TconSetting = 0x60,
/// This command defines alternative resolution and this setting is of higher priority
/// than the RES\[1:0\] in R00H (PSR).
TCON_RESOLUTION = 0x61,
TconResolution = 0x61,
/// This command defines MCU host direct access external memory mode.
SPI_FLASH_CONTROL = 0x65,
SpiFlashControl = 0x65,
/// The LUT_REV / Chip Revision is read from OTP address = 25001 and 25000.
REVISION = 0x70,
Revision = 0x70,
/// This command reads the IC status.
GET_STATUS = 0x71,
GetStatus = 0x71,
/// This command implements related VCOM sensing setting.
AUTO_MEASUREMENT_VCOM = 0x80,
AutoMeasurementVcom = 0x80,
/// This command gets the VCOM value.
READ_VCOM_VALUE = 0x81,
ReadVcomValue = 0x81,
/// This command sets `VCOM_DC` value.
VCM_DC_SETTING = 0x82,
// /// This is in all the Waveshare controllers for EPD7in5, but it's not documented
VcmDcSetting = 0x82,
// /// This is in all the Waveshare controllers for Epd7in5, but it's not documented
// /// anywhere in the datasheet `¯\_(ツ)_/¯`
// FLASH_MODE = 0xE5,
// FlashMode = 0xE5,
}
impl traits::Command for Command {
@@ -148,7 +147,7 @@ mod tests {
#[test]
fn command_addr() {
assert_eq!(Command::PANEL_SETTING.address(), 0x00);
assert_eq!(Command::DISPLAY_REFRESH.address(), 0x12);
assert_eq!(Command::PanelSetting.address(), 0x00);
assert_eq!(Command::DisplayRefresh.address(), 0x12);
}
}
+26 -26
View File
@@ -17,7 +17,7 @@ use embedded_hal::{
use crate::color::Color;
use crate::interface::DisplayInterface;
use crate::traits::{InternalWiAdditions, RefreshLUT, WaveshareDisplay};
use crate::traits::{InternalWiAdditions, RefreshLut, WaveshareDisplay};
pub(crate) mod command;
use self::command::Command;
@@ -35,9 +35,9 @@ pub const HEIGHT: u32 = 480;
pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White;
const IS_BUSY_LOW: bool = true;
/// EPD7in5 (V2) driver
/// Epd7in5 (V2) driver
///
pub struct EPD7in5<SPI, CS, BUSY, DC, RST> {
pub struct Epd7in5<SPI, CS, BUSY, DC, RST> {
/// Connection Interface
interface: DisplayInterface<SPI, CS, BUSY, DC, RST>,
/// Background Color
@@ -45,7 +45,7 @@ pub struct EPD7in5<SPI, CS, BUSY, DC, RST> {
}
impl<SPI, CS, BUSY, DC, RST> InternalWiAdditions<SPI, CS, BUSY, DC, RST>
for EPD7in5<SPI, CS, BUSY, DC, RST>
for Epd7in5<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -66,23 +66,23 @@ where
// and as per specs:
// https://www.waveshare.com/w/upload/6/60/7.5inch_e-Paper_V2_Specification.pdf
self.cmd_with_data(spi, Command::BOOSTER_SOFT_START, &[0x17, 0x17, 0x27, 0x17])?;
self.cmd_with_data(spi, Command::POWER_SETTING, &[0x07, 0x17, 0x3F, 0x3F])?;
self.command(spi, Command::POWER_ON)?;
self.cmd_with_data(spi, Command::BoosterSoftStart, &[0x17, 0x17, 0x27, 0x17])?;
self.cmd_with_data(spi, Command::PowerSetting, &[0x07, 0x17, 0x3F, 0x3F])?;
self.command(spi, Command::PowerOn)?;
self.wait_until_idle();
self.cmd_with_data(spi, Command::PANEL_SETTING, &[0x1F])?;
self.cmd_with_data(spi, Command::PLL_CONTROL, &[0x06])?;
self.cmd_with_data(spi, Command::TCON_RESOLUTION, &[0x03, 0x20, 0x01, 0xE0])?;
self.cmd_with_data(spi, Command::DUAL_SPI, &[0x00])?;
self.cmd_with_data(spi, Command::TCON_SETTING, &[0x22])?;
self.cmd_with_data(spi, Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x10, 0x07])?;
self.cmd_with_data(spi, Command::PanelSetting, &[0x1F])?;
self.cmd_with_data(spi, Command::PllControl, &[0x06])?;
self.cmd_with_data(spi, Command::TconResolution, &[0x03, 0x20, 0x01, 0xE0])?;
self.cmd_with_data(spi, Command::DualSpi, &[0x00])?;
self.cmd_with_data(spi, Command::TconSetting, &[0x22])?;
self.cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x10, 0x07])?;
self.wait_until_idle();
Ok(())
}
}
impl<SPI, CS, BUSY, DC, RST> WaveshareDisplay<SPI, CS, BUSY, DC, RST>
for EPD7in5<SPI, CS, BUSY, DC, RST>
for Epd7in5<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -102,7 +102,7 @@ where
let interface = DisplayInterface::new(cs, busy, dc, rst);
let color = DEFAULT_BACKGROUND_COLOR;
let mut epd = EPD7in5 { interface, color };
let mut epd = Epd7in5 { interface, color };
epd.init(spi, delay)?;
@@ -119,15 +119,15 @@ where
fn sleep(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
self.wait_until_idle();
self.command(spi, Command::POWER_OFF)?;
self.command(spi, Command::PowerOff)?;
self.wait_until_idle();
self.cmd_with_data(spi, Command::DEEP_SLEEP, &[0xA5])?;
self.cmd_with_data(spi, Command::DeepSleep, &[0xA5])?;
Ok(())
}
fn update_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> {
self.wait_until_idle();
self.cmd_with_data(spi, Command::DATA_START_TRANSMISSION_2, buffer)?;
self.cmd_with_data(spi, Command::DataStartTransmission2, buffer)?;
Ok(())
}
@@ -145,13 +145,13 @@ where
fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
self.wait_until_idle();
self.command(spi, Command::DISPLAY_REFRESH)?;
self.command(spi, Command::DisplayRefresh)?;
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)?;
self.command(spi, Command::DisplayRefresh)?;
Ok(())
}
@@ -159,13 +159,13 @@ where
self.wait_until_idle();
self.send_resolution(spi)?;
self.command(spi, Command::DATA_START_TRANSMISSION_1)?;
self.command(spi, Command::DataStartTransmission1)?;
self.interface.data_x_times(spi, 0x00, WIDTH * HEIGHT / 8)?;
self.command(spi, Command::DATA_START_TRANSMISSION_2)?;
self.command(spi, Command::DataStartTransmission2)?;
self.interface.data_x_times(spi, 0x00, WIDTH * HEIGHT / 8)?;
self.command(spi, Command::DISPLAY_REFRESH)?;
self.command(spi, Command::DisplayRefresh)?;
Ok(())
}
@@ -188,7 +188,7 @@ where
fn set_lut(
&mut self,
_spi: &mut SPI,
_refresh_rate: Option<RefreshLUT>,
_refresh_rate: Option<RefreshLut>,
) -> Result<(), SPI::Error> {
unimplemented!();
}
@@ -198,7 +198,7 @@ where
}
}
impl<SPI, CS, BUSY, DC, RST> EPD7in5<SPI, CS, BUSY, DC, RST>
impl<SPI, CS, BUSY, DC, RST> Epd7in5<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
@@ -231,7 +231,7 @@ where
let w = self.width();
let h = self.height();
self.command(spi, Command::TCON_RESOLUTION)?;
self.command(spi, Command::TconResolution)?;
self.send_data(spi, &[(w >> 8) as u8])?;
self.send_data(spi, &[w as u8])?;
self.send_data(spi, &[(h >> 8) as u8])?;
+2 -2
View File
@@ -51,7 +51,7 @@ where
/// Basic function for sending an array of u8-values of data over spi
///
/// Enables direct interaction with the device with the help of [command()](EPD4in2::command())
/// Enables direct interaction with the device with the help of [command()](Epd4in2::command())
pub(crate) fn data(&mut self, spi: &mut SPI, data: &[u8]) -> Result<(), SPI::Error> {
// high for data
let _ = self.dc.set_high();
@@ -157,7 +157,7 @@ where
/// Resets the device.
///
/// Often used to awake the module from deep sleep. See [EPD4in2::sleep()](EPD4in2::sleep())
/// Often used to awake the module from deep sleep. See [Epd4in2::sleep()](Epd4in2::sleep())
///
/// The timing of keeping the reset pin low seems to be important and different per device.
/// Most displays seem to require keeping it low for 10ms, but the 7in5_v2 only seems to reset
+2 -2
View File
@@ -28,7 +28,7 @@
//!# let mut delay = delay::MockNoop::new();
//!
//!// Setup EPD
//!let mut epd = EPD1in54::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?;
//!let mut epd = Epd1in54::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?;
//!
//!// Use display graphics from embedded-graphics
//!let mut display = Display1in54::default();
@@ -91,7 +91,7 @@ pub(crate) mod type_a;
pub mod prelude {
pub use crate::color::{Color, OctColor, TriColor};
pub use crate::traits::{
QuickRefresh, RefreshLUT, WaveshareDisplay, WaveshareThreeColorDisplay,
QuickRefresh, RefreshLut, WaveshareDisplay, WaveshareThreeColorDisplay,
};
pub use crate::SPI_MODE;
+8 -8
View File
@@ -12,17 +12,17 @@ pub(crate) trait Command {
/// Seperates the different LUT for the Display Refresh process
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub enum RefreshLUT {
pub enum RefreshLut {
/// The "normal" full Lookuptable for the Refresh-Sequence
FULL,
Full,
/// The quick LUT where not the full refresh sequence is followed.
/// This might lead to some
QUICK,
Quick,
}
impl Default for RefreshLUT {
impl Default for RefreshLut {
fn default() -> Self {
RefreshLUT::FULL
RefreshLut::Full
}
}
@@ -108,7 +108,7 @@ where
///# let mut delay = delay::MockNoop::new();
///
///// Setup EPD
///let mut epd = EPD4in2::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?;
///let mut epd = Epd4in2::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?;
///
///// Use display graphics from embedded-graphics
///let mut display = Display4in2::default();
@@ -219,7 +219,7 @@ where
fn set_lut(
&mut self,
spi: &mut SPI,
refresh_rate: Option<RefreshLUT>,
refresh_rate: Option<RefreshLut>,
) -> Result<(), SPI::Error>;
/// Checks if the display is busy transmitting data
@@ -258,7 +258,7 @@ where
///# let mut delay = delay::MockNoop::new();
///#
///# // Setup EPD
///# let mut epd = EPD4in2::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?;
///# let mut epd = Epd4in2::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?;
///let (x, y, frame_width, frame_height) = (20, 40, 80,80);
///
///let mut buffer = [DEFAULT_BACKGROUND_COLOR.get_byte_value(); 80 / 8 * 80];
+26 -27
View File
@@ -2,13 +2,12 @@
use crate::traits;
/// EPD1in54 and 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
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
pub(crate) enum Command {
/// Driver Output control
@@ -17,58 +16,58 @@ pub(crate) enum Command {
/// 0.. A[8]
/// 0.. B[2:0]
/// Default: Set A[8:0] = 0x127 and B[2:0] = 0x0
DRIVER_OUTPUT_CONTROL = 0x01,
DriverOutputControl = 0x01,
/// 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,
BoosterSoftStartControl = 0x0C,
GateScanStartPosition = 0x0F,
//TODO: useful?
// GATE_SCAN_START_POSITION = 0x0F,
// GateScanStartPosition = 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,
DeepSleepMode = 0x10,
// /// Data Entry mode setting
DATA_ENTRY_MODE_SETTING = 0x11,
DataEntryModeSetting = 0x11,
SW_RESET = 0x12,
SwReset = 0x12,
TEMPERATURE_SENSOR_CONTROL = 0x1A,
TemperatureSensorControl = 0x1A,
MASTER_ACTIVATION = 0x20,
MasterActivation = 0x20,
DISPLAY_UPDATE_CONTROL_1 = 0x21,
DisplayUpdateControl1 = 0x21,
DISPLAY_UPDATE_CONTROL_2 = 0x22,
DisplayUpdateControl2 = 0x22,
WRITE_RAM = 0x24,
WriteRam = 0x24,
WRITE_VCOM_REGISTER = 0x2C,
WriteVcomRegister = 0x2C,
WRITE_LUT_REGISTER = 0x32,
WriteLutRegister = 0x32,
SET_DUMMY_LINE_PERIOD = 0x3A,
SetDummyLinePeriod = 0x3A,
SET_GATE_LINE_WIDTH = 0x3B,
SetGateLineWidth = 0x3B,
BORDER_WAVEFORM_CONTROL = 0x3C,
BorderWaveformControl = 0x3C,
SET_RAM_X_ADDRESS_START_END_POSITION = 0x44,
SetRamXAddressStartEndPosition = 0x44,
SET_RAM_Y_ADDRESS_START_END_POSITION = 0x45,
SetRamYAddressStartEndPosition = 0x45,
SET_RAM_X_ADDRESS_COUNTER = 0x4E,
SetRamXAddressCounter = 0x4E,
SET_RAM_Y_ADDRESS_COUNTER = 0x4F,
SetRamYAddressCounter = 0x4F,
NOP = 0xFF,
Nop = 0xFF,
}
impl traits::Command for Command {
@@ -85,10 +84,10 @@ mod tests {
#[test]
fn command_addr() {
assert_eq!(Command::DRIVER_OUTPUT_CONTROL.address(), 0x01);
assert_eq!(Command::DriverOutputControl.address(), 0x01);
assert_eq!(Command::SET_RAM_X_ADDRESS_COUNTER.address(), 0x4E);
assert_eq!(Command::SetRamXAddressCounter.address(), 0x4E);
assert_eq!(Command::NOP.address(), 0xFF);
assert_eq!(Command::Nop.address(), 0xFF);
}
}