-
Notifications
You must be signed in to change notification settings - Fork 138
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into add-heap-allocation-support
- Loading branch information
Showing
5 changed files
with
385 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
//! SPI Commands for the Waveshare 3.7" E-Ink Display | ||
use crate::traits; | ||
|
||
/// EPD3IN7 commands | ||
/// | ||
/// Should rarely (never?) be needed directly. | ||
/// | ||
/// For more infos about the addresses and what they are doing look into the pdfs | ||
/// | ||
/// The description of the single commands is mostly taken from EDP3IN7 specification | ||
#[allow(dead_code)] | ||
#[derive(Copy, Clone)] | ||
pub(crate) enum Command { | ||
/// | ||
GateSetting = 0x01, | ||
/// | ||
PowerOff = 0x02, | ||
/// | ||
Sleep2 = 0x07, | ||
/// | ||
GateVoltage = 0x03, | ||
/// | ||
GateVoltageSource = 0x04, | ||
/// | ||
BoosterSoftStartControl = 0x0C, | ||
/// After this command initiated, the chip will enter Deep Sleep Mode, | ||
/// BUSY pad will keep output high. | ||
/// | ||
/// Note: To exit Deep Sleep Mode, User required to send HWRESET to the driver. | ||
DeepSleep = 0x10, | ||
/// | ||
DataEntrySequence = 0x11, | ||
/// This command resets commands and parameters to their S/W Reset default values, | ||
/// except Deep Sleep Mode. | ||
/// During this operation BUSY pad will keep output high. | ||
/// | ||
/// Note: RAM is unaffected by this command. | ||
SwReset = 0x12, | ||
/// This command selects the Internal or External temperature sensor and offset | ||
TemperatureSensorSelection = 0x18, | ||
/// Write to temperature register | ||
TemperatureSensorWrite = 0x1A, | ||
/// Read from temperature register | ||
TemperatureSensorRead = 0x1B, | ||
/// This command activates Display Update sequence. | ||
/// The Display Update sequence option is located at R22h. | ||
/// | ||
/// Note: BUSY pad will output high during operation. User **should not** interrupt this operation | ||
/// to avoid corruption of panel images. | ||
DisplayUpdateSequence = 0x20, | ||
/// This command sets a Display Update Sequence option. | ||
DisplayUpdateSequenceSetting = 0x22, | ||
/// This command will transfer its data to B/W RAM, until another command is written | ||
WriteRam = 0x24, | ||
/// This command writes VCOM register from MCU interface | ||
WriteVcomRegister = 0x2C, | ||
/// This command writes LUT register from MCU interface (105 bytes), | ||
/// which contains the content of VS [nx-LUT], TP #[nX], RP #[n] | ||
WriteLutRegister = 0x32, | ||
/// | ||
DisplayOption = 0x37, | ||
/// | ||
BorderWaveformControl = 0x3C, | ||
/// This command specifies the start/end positions of the window address in the X direction, | ||
/// by an address unit of RAM. | ||
SetRamXAddressStartEndPosition = 0x44, | ||
/// This command specifies the start/end positions of the window address in the Y direction, | ||
/// by an address unit of RAM. | ||
SetRamYAddressStartEndPosition = 0x45, | ||
/// | ||
AutoWriteRedRamRegularPattern = 0x46, | ||
/// | ||
AutoWriteBwRamRegularPattern = 0x47, | ||
/// This command makes the initial settings for the RAM X address in the address counter (AC) | ||
SetRamXAddressCounter = 0x4E, | ||
/// This command makes the initial settings for the RAM Y address in the address counter (AC) | ||
SetRamYAddressCounter = 0x4F, | ||
/// | ||
Sleep = 0x50, | ||
} | ||
|
||
impl traits::Command for Command { | ||
/// Returns the address of the command | ||
fn address(self) -> u8 { | ||
self as u8 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// This LUT clears the whole display during updates. | ||
pub(crate) const LUT_1GRAY_GC: [u8; 105] = [ | ||
0x2A, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //1 | ||
0x05, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //2 | ||
0x2A, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //3 | ||
0x05, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //4 | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //5 | ||
0x00, 0x02, 0x03, 0x0A, 0x00, 0x02, 0x06, 0x0A, 0x05, 0x00, //6 | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //7 | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //8 | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //9 | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //10 | ||
0x22, 0x22, 0x22, 0x22, 0x22, | ||
]; | ||
|
||
// This LUT updates only the pixels that have changed. | ||
pub(crate) const LUT_1GRAY_DU: [u8; 105] = [ | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //1 | ||
0x01, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //2 | ||
0x0A, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //3 | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //4 | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //5 | ||
0x00, 0x00, 0x05, 0x05, 0x00, 0x05, 0x03, 0x05, 0x05, 0x00, //6 | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //7 | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //8 | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //9 | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //10 | ||
0x22, 0x22, 0x22, 0x22, 0x22, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,266 @@ | ||
//! A simple Driver for the Waveshare 3.7" E-Ink Display via SPI | ||
//! | ||
//! | ||
//! Build with the help of documentation/code from [Waveshare](https://www.waveshare.com/wiki/3.7inch_e-Paper_HAT), | ||
use embedded_hal::{ | ||
blocking::{delay::DelayUs, spi::Write}, | ||
digital::v2::{InputPin, OutputPin}, | ||
}; | ||
|
||
pub(crate) mod command; | ||
mod constants; | ||
|
||
use self::command::Command; | ||
use self::constants::*; | ||
|
||
use crate::buffer_len; | ||
use crate::color::Color; | ||
use crate::interface::DisplayInterface; | ||
use crate::traits::{InternalWiAdditions, RefreshLut, WaveshareDisplay}; | ||
|
||
/// Width of the display. | ||
pub const WIDTH: u32 = 280; | ||
|
||
/// Height of the display | ||
pub const HEIGHT: u32 = 480; | ||
|
||
/// Default Background Color | ||
pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White; | ||
|
||
const IS_BUSY_LOW: bool = false; | ||
|
||
/// Display with Fullsize buffer for use with the 3in7 EPD | ||
#[cfg(feature = "graphics")] | ||
pub type Display3in7 = crate::graphics::Display< | ||
WIDTH, | ||
HEIGHT, | ||
false, | ||
{ buffer_len(WIDTH as usize, HEIGHT as usize) }, | ||
Color, | ||
>; | ||
|
||
/// EPD3in7 driver | ||
pub struct EPD3in7<SPI, CS, BUSY, DC, RST, DELAY> { | ||
/// Connection Interface | ||
interface: DisplayInterface<SPI, CS, BUSY, DC, RST, DELAY>, | ||
/// Background Color | ||
background_color: Color, | ||
} | ||
|
||
impl<SPI, CS, BUSY, DC, RST, DELAY> InternalWiAdditions<SPI, CS, BUSY, DC, RST, DELAY> | ||
for EPD3in7<SPI, CS, BUSY, DC, RST, DELAY> | ||
where | ||
SPI: Write<u8>, | ||
CS: OutputPin, | ||
BUSY: InputPin, | ||
DC: OutputPin, | ||
RST: OutputPin, | ||
DELAY: DelayUs<u32>, | ||
{ | ||
fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { | ||
// reset the device | ||
self.interface.reset(delay, 30, 10); | ||
|
||
self.interface.cmd(spi, Command::SwReset)?; | ||
delay.delay_us(300000u32); | ||
|
||
self.interface | ||
.cmd_with_data(spi, Command::AutoWriteRedRamRegularPattern, &[0xF7])?; | ||
self.interface.wait_until_idle(delay, IS_BUSY_LOW); | ||
self.interface | ||
.cmd_with_data(spi, Command::AutoWriteBwRamRegularPattern, &[0xF7])?; | ||
self.interface.wait_until_idle(delay, IS_BUSY_LOW); | ||
|
||
self.interface | ||
.cmd_with_data(spi, Command::GateSetting, &[0xDF, 0x01, 0x00])?; | ||
self.interface | ||
.cmd_with_data(spi, Command::GateVoltage, &[0x00])?; | ||
self.interface | ||
.cmd_with_data(spi, Command::GateVoltageSource, &[0x41, 0xA8, 0x32])?; | ||
|
||
self.interface | ||
.cmd_with_data(spi, Command::DataEntrySequence, &[0x03])?; | ||
|
||
self.interface | ||
.cmd_with_data(spi, Command::BorderWaveformControl, &[0x03])?; | ||
|
||
self.interface.cmd_with_data( | ||
spi, | ||
Command::BoosterSoftStartControl, | ||
&[0xAE, 0xC7, 0xC3, 0xC0, 0xC0], | ||
)?; | ||
|
||
self.interface | ||
.cmd_with_data(spi, Command::TemperatureSensorSelection, &[0x80])?; | ||
|
||
self.interface | ||
.cmd_with_data(spi, Command::WriteVcomRegister, &[0x44])?; | ||
|
||
self.interface.cmd_with_data( | ||
spi, | ||
Command::DisplayOption, | ||
&[0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x4F, 0xFF, 0xFF, 0xFF, 0xFF], | ||
)?; | ||
|
||
self.interface.cmd_with_data( | ||
spi, | ||
Command::SetRamXAddressStartEndPosition, | ||
&[0x00, 0x00, 0x17, 0x01], | ||
)?; | ||
self.interface.cmd_with_data( | ||
spi, | ||
Command::SetRamYAddressStartEndPosition, | ||
&[0x00, 0x00, 0xDF, 0x01], | ||
)?; | ||
|
||
self.interface | ||
.cmd_with_data(spi, Command::DisplayUpdateSequenceSetting, &[0xCF])?; | ||
|
||
self.set_lut(spi, delay, Some(RefreshLut::Full))?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl<SPI, CS, BUSY, DC, RST, DELAY> WaveshareDisplay<SPI, CS, BUSY, DC, RST, DELAY> | ||
for EPD3in7<SPI, CS, BUSY, DC, RST, DELAY> | ||
where | ||
SPI: Write<u8>, | ||
CS: OutputPin, | ||
BUSY: InputPin, | ||
DC: OutputPin, | ||
RST: OutputPin, | ||
DELAY: DelayUs<u32>, | ||
{ | ||
type DisplayColor = Color; | ||
|
||
fn new( | ||
spi: &mut SPI, | ||
cs: CS, | ||
busy: BUSY, | ||
dc: DC, | ||
rst: RST, | ||
delay: &mut DELAY, | ||
delay_us: Option<u32>, | ||
) -> Result<Self, SPI::Error> { | ||
let mut epd = EPD3in7 { | ||
interface: DisplayInterface::new(cs, busy, dc, rst, delay_us), | ||
background_color: DEFAULT_BACKGROUND_COLOR, | ||
}; | ||
|
||
epd.init(spi, delay)?; | ||
Ok(epd) | ||
} | ||
|
||
fn wake_up(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { | ||
self.init(spi, delay) | ||
} | ||
|
||
fn sleep(&mut self, spi: &mut SPI, _delay: &mut DELAY) -> Result<(), SPI::Error> { | ||
self.interface.cmd_with_data(spi, Command::Sleep, &[0xF7])?; | ||
self.interface.cmd(spi, Command::PowerOff)?; | ||
self.interface | ||
.cmd_with_data(spi, Command::Sleep2, &[0xA5])?; | ||
Ok(()) | ||
} | ||
|
||
fn set_background_color(&mut self, color: Self::DisplayColor) { | ||
self.background_color = color; | ||
} | ||
|
||
fn background_color(&self) -> &Self::DisplayColor { | ||
&self.background_color | ||
} | ||
|
||
fn width(&self) -> u32 { | ||
WIDTH | ||
} | ||
|
||
fn height(&self) -> u32 { | ||
HEIGHT | ||
} | ||
|
||
fn update_frame( | ||
&mut self, | ||
spi: &mut SPI, | ||
buffer: &[u8], | ||
_delay: &mut DELAY, | ||
) -> Result<(), SPI::Error> { | ||
assert!(buffer.len() == buffer_len(WIDTH as usize, HEIGHT as usize)); | ||
self.interface | ||
.cmd_with_data(spi, Command::SetRamXAddressCounter, &[0x00, 0x00])?; | ||
self.interface | ||
.cmd_with_data(spi, Command::SetRamYAddressCounter, &[0x00, 0x00])?; | ||
|
||
self.interface | ||
.cmd_with_data(spi, Command::WriteRam, buffer)?; | ||
|
||
Ok(()) | ||
} | ||
|
||
#[allow(unused)] | ||
fn update_partial_frame( | ||
&mut self, | ||
spi: &mut SPI, | ||
delay: &mut DELAY, | ||
buffer: &[u8], | ||
x: u32, | ||
y: u32, | ||
width: u32, | ||
height: u32, | ||
) -> Result<(), SPI::Error> { | ||
todo!() | ||
} | ||
|
||
fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { | ||
//self.interface | ||
// .cmd_with_data(spi, Command::WRITE_LUT_REGISTER, &LUT_1GRAY_GC)?; | ||
self.interface.cmd(spi, Command::DisplayUpdateSequence)?; | ||
self.interface.wait_until_idle(delay, IS_BUSY_LOW); | ||
Ok(()) | ||
} | ||
|
||
fn update_and_display_frame( | ||
&mut self, | ||
spi: &mut SPI, | ||
buffer: &[u8], | ||
delay: &mut DELAY, | ||
) -> Result<(), SPI::Error> { | ||
self.update_frame(spi, buffer, delay)?; | ||
self.display_frame(spi, delay)?; | ||
Ok(()) | ||
} | ||
|
||
fn clear_frame(&mut self, spi: &mut SPI, _delay: &mut DELAY) -> Result<(), SPI::Error> { | ||
self.interface | ||
.cmd_with_data(spi, Command::SetRamXAddressCounter, &[0x00, 0x00])?; | ||
self.interface | ||
.cmd_with_data(spi, Command::SetRamYAddressCounter, &[0x00, 0x00])?; | ||
|
||
let color = self.background_color.get_byte_value(); | ||
self.interface.cmd(spi, Command::WriteRam)?; | ||
self.interface.data_x_times(spi, color, WIDTH * HEIGHT)?; | ||
|
||
Ok(()) | ||
} | ||
|
||
fn set_lut( | ||
&mut self, | ||
spi: &mut SPI, | ||
_delay: &mut DELAY, | ||
refresh_rate: Option<RefreshLut>, | ||
) -> Result<(), SPI::Error> { | ||
let buffer = match refresh_rate { | ||
Some(RefreshLut::Full) | None => &LUT_1GRAY_GC, | ||
Some(RefreshLut::Quick) => &LUT_1GRAY_DU, | ||
}; | ||
|
||
self.interface | ||
.cmd_with_data(spi, Command::WriteLutRegister, buffer)?; | ||
Ok(()) | ||
} | ||
|
||
fn wait_until_idle(&mut self, _spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { | ||
self.interface.wait_until_idle(delay, IS_BUSY_LOW); | ||
Ok(()) | ||
} | ||
} |
Oops, something went wrong.