Skip to content

Commit f98c121

Browse files
authored
Merge pull request #129 from mangelajo/epd-3in7
Add support for EPD 3in7
2 parents 1733a0e + 3b30736 commit f98c121

4 files changed

Lines changed: 384 additions & 0 deletions

File tree

src/epd3in7/command.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//! SPI Commands for the Waveshare 3.7" E-Ink Display
2+
3+
use crate::traits;
4+
5+
/// EPD3IN7 commands
6+
///
7+
/// Should rarely (never?) be needed directly.
8+
///
9+
/// For more infos about the addresses and what they are doing look into the pdfs
10+
///
11+
/// The description of the single commands is mostly taken from EDP3IN7 specification
12+
#[allow(dead_code)]
13+
#[derive(Copy, Clone)]
14+
pub(crate) enum Command {
15+
///
16+
GateSetting = 0x01,
17+
///
18+
PowerOff = 0x02,
19+
///
20+
Sleep2 = 0x07,
21+
///
22+
GateVoltage = 0x03,
23+
///
24+
GateVoltageSource = 0x04,
25+
///
26+
BoosterSoftStartControl = 0x0C,
27+
/// After this command initiated, the chip will enter Deep Sleep Mode,
28+
/// BUSY pad will keep output high.
29+
///
30+
/// Note: To exit Deep Sleep Mode, User required to send HWRESET to the driver.
31+
DeepSleep = 0x10,
32+
///
33+
DataEntrySequence = 0x11,
34+
/// This command resets commands and parameters to their S/W Reset default values,
35+
/// except Deep Sleep Mode.
36+
/// During this operation BUSY pad will keep output high.
37+
///
38+
/// Note: RAM is unaffected by this command.
39+
SwReset = 0x12,
40+
/// This command selects the Internal or External temperature sensor and offset
41+
TemperatureSensorSelection = 0x18,
42+
/// Write to temperature register
43+
TemperatureSensorWrite = 0x1A,
44+
/// Read from temperature register
45+
TemperatureSensorRead = 0x1B,
46+
/// This command activates Display Update sequence.
47+
/// The Display Update sequence option is located at R22h.
48+
///
49+
/// Note: BUSY pad will output high during operation. User **should not** interrupt this operation
50+
/// to avoid corruption of panel images.
51+
DisplayUpdateSequence = 0x20,
52+
/// This command sets a Display Update Sequence option.
53+
DisplayUpdateSequenceSetting = 0x22,
54+
/// This command will transfer its data to B/W RAM, until another command is written
55+
WriteRam = 0x24,
56+
/// This command writes VCOM register from MCU interface
57+
WriteVcomRegister = 0x2C,
58+
/// This command writes LUT register from MCU interface (105 bytes),
59+
/// which contains the content of VS [nx-LUT], TP #[nX], RP #[n]
60+
WriteLutRegister = 0x32,
61+
///
62+
DisplayOption = 0x37,
63+
///
64+
BorderWaveformControl = 0x3C,
65+
/// This command specifies the start/end positions of the window address in the X direction,
66+
/// by an address unit of RAM.
67+
SetRamXAddressStartEndPosition = 0x44,
68+
/// This command specifies the start/end positions of the window address in the Y direction,
69+
/// by an address unit of RAM.
70+
SetRamYAddressStartEndPosition = 0x45,
71+
///
72+
AutoWriteRedRamRegularPattern = 0x46,
73+
///
74+
AutoWriteBwRamRegularPattern = 0x47,
75+
/// This command makes the initial settings for the RAM X address in the address counter (AC)
76+
SetRamXAddressCounter = 0x4E,
77+
/// This command makes the initial settings for the RAM Y address in the address counter (AC)
78+
SetRamYAddressCounter = 0x4F,
79+
///
80+
Sleep = 0x50,
81+
}
82+
83+
impl traits::Command for Command {
84+
/// Returns the address of the command
85+
fn address(self) -> u8 {
86+
self as u8
87+
}
88+
}

src/epd3in7/constants.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// This LUT clears the whole display during updates.
2+
pub(crate) const LUT_1GRAY_GC: [u8; 105] = [
3+
0x2A, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //1
4+
0x05, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //2
5+
0x2A, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //3
6+
0x05, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //4
7+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //5
8+
0x00, 0x02, 0x03, 0x0A, 0x00, 0x02, 0x06, 0x0A, 0x05, 0x00, //6
9+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //7
10+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //8
11+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //9
12+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //10
13+
0x22, 0x22, 0x22, 0x22, 0x22,
14+
];
15+
16+
// This LUT updates only the pixels that have changed.
17+
pub(crate) const LUT_1GRAY_DU: [u8; 105] = [
18+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //1
19+
0x01, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //2
20+
0x0A, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //3
21+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //4
22+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //5
23+
0x00, 0x00, 0x05, 0x05, 0x00, 0x05, 0x03, 0x05, 0x05, 0x00, //6
24+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //7
25+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //8
26+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //9
27+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //10
28+
0x22, 0x22, 0x22, 0x22, 0x22,
29+
];

src/epd3in7/mod.rs

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
//! A simple Driver for the Waveshare 3.7" E-Ink Display via SPI
2+
//!
3+
//!
4+
//! Build with the help of documentation/code from [Waveshare](https://www.waveshare.com/wiki/3.7inch_e-Paper_HAT),
5+
use embedded_hal::{
6+
blocking::{delay::DelayUs, spi::Write},
7+
digital::v2::{InputPin, OutputPin},
8+
};
9+
10+
pub(crate) mod command;
11+
mod constants;
12+
13+
use self::command::Command;
14+
use self::constants::*;
15+
16+
use crate::buffer_len;
17+
use crate::color::Color;
18+
use crate::interface::DisplayInterface;
19+
use crate::traits::{InternalWiAdditions, RefreshLut, WaveshareDisplay};
20+
21+
/// Width of the display.
22+
pub const WIDTH: u32 = 280;
23+
24+
/// Height of the display
25+
pub const HEIGHT: u32 = 480;
26+
27+
/// Default Background Color
28+
pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White;
29+
30+
const IS_BUSY_LOW: bool = false;
31+
32+
/// Display with Fullsize buffer for use with the 3in7 EPD
33+
#[cfg(feature = "graphics")]
34+
pub type Display3in7 = crate::graphics::Display<
35+
WIDTH,
36+
HEIGHT,
37+
false,
38+
{ buffer_len(WIDTH as usize, HEIGHT as usize) },
39+
Color,
40+
>;
41+
42+
/// EPD3in7 driver
43+
pub struct EPD3in7<SPI, CS, BUSY, DC, RST, DELAY> {
44+
/// Connection Interface
45+
interface: DisplayInterface<SPI, CS, BUSY, DC, RST, DELAY>,
46+
/// Background Color
47+
background_color: Color,
48+
}
49+
50+
impl<SPI, CS, BUSY, DC, RST, DELAY> InternalWiAdditions<SPI, CS, BUSY, DC, RST, DELAY>
51+
for EPD3in7<SPI, CS, BUSY, DC, RST, DELAY>
52+
where
53+
SPI: Write<u8>,
54+
CS: OutputPin,
55+
BUSY: InputPin,
56+
DC: OutputPin,
57+
RST: OutputPin,
58+
DELAY: DelayUs<u32>,
59+
{
60+
fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
61+
// reset the device
62+
self.interface.reset(delay, 30, 10);
63+
64+
self.interface.cmd(spi, Command::SwReset)?;
65+
delay.delay_us(300000u32);
66+
67+
self.interface
68+
.cmd_with_data(spi, Command::AutoWriteRedRamRegularPattern, &[0xF7])?;
69+
self.interface.wait_until_idle(delay, IS_BUSY_LOW);
70+
self.interface
71+
.cmd_with_data(spi, Command::AutoWriteBwRamRegularPattern, &[0xF7])?;
72+
self.interface.wait_until_idle(delay, IS_BUSY_LOW);
73+
74+
self.interface
75+
.cmd_with_data(spi, Command::GateSetting, &[0xDF, 0x01, 0x00])?;
76+
self.interface
77+
.cmd_with_data(spi, Command::GateVoltage, &[0x00])?;
78+
self.interface
79+
.cmd_with_data(spi, Command::GateVoltageSource, &[0x41, 0xA8, 0x32])?;
80+
81+
self.interface
82+
.cmd_with_data(spi, Command::DataEntrySequence, &[0x03])?;
83+
84+
self.interface
85+
.cmd_with_data(spi, Command::BorderWaveformControl, &[0x03])?;
86+
87+
self.interface.cmd_with_data(
88+
spi,
89+
Command::BoosterSoftStartControl,
90+
&[0xAE, 0xC7, 0xC3, 0xC0, 0xC0],
91+
)?;
92+
93+
self.interface
94+
.cmd_with_data(spi, Command::TemperatureSensorSelection, &[0x80])?;
95+
96+
self.interface
97+
.cmd_with_data(spi, Command::WriteVcomRegister, &[0x44])?;
98+
99+
self.interface.cmd_with_data(
100+
spi,
101+
Command::DisplayOption,
102+
&[0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x4F, 0xFF, 0xFF, 0xFF, 0xFF],
103+
)?;
104+
105+
self.interface.cmd_with_data(
106+
spi,
107+
Command::SetRamXAddressStartEndPosition,
108+
&[0x00, 0x00, 0x17, 0x01],
109+
)?;
110+
self.interface.cmd_with_data(
111+
spi,
112+
Command::SetRamYAddressStartEndPosition,
113+
&[0x00, 0x00, 0xDF, 0x01],
114+
)?;
115+
116+
self.interface
117+
.cmd_with_data(spi, Command::DisplayUpdateSequenceSetting, &[0xCF])?;
118+
119+
self.set_lut(spi, delay, Some(RefreshLut::Full))?;
120+
Ok(())
121+
}
122+
}
123+
124+
impl<SPI, CS, BUSY, DC, RST, DELAY> WaveshareDisplay<SPI, CS, BUSY, DC, RST, DELAY>
125+
for EPD3in7<SPI, CS, BUSY, DC, RST, DELAY>
126+
where
127+
SPI: Write<u8>,
128+
CS: OutputPin,
129+
BUSY: InputPin,
130+
DC: OutputPin,
131+
RST: OutputPin,
132+
DELAY: DelayUs<u32>,
133+
{
134+
type DisplayColor = Color;
135+
136+
fn new(
137+
spi: &mut SPI,
138+
cs: CS,
139+
busy: BUSY,
140+
dc: DC,
141+
rst: RST,
142+
delay: &mut DELAY,
143+
delay_us: Option<u32>,
144+
) -> Result<Self, SPI::Error> {
145+
let mut epd = EPD3in7 {
146+
interface: DisplayInterface::new(cs, busy, dc, rst, delay_us),
147+
background_color: DEFAULT_BACKGROUND_COLOR,
148+
};
149+
150+
epd.init(spi, delay)?;
151+
Ok(epd)
152+
}
153+
154+
fn wake_up(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
155+
self.init(spi, delay)
156+
}
157+
158+
fn sleep(&mut self, spi: &mut SPI, _delay: &mut DELAY) -> Result<(), SPI::Error> {
159+
self.interface.cmd_with_data(spi, Command::Sleep, &[0xF7])?;
160+
self.interface.cmd(spi, Command::PowerOff)?;
161+
self.interface
162+
.cmd_with_data(spi, Command::Sleep2, &[0xA5])?;
163+
Ok(())
164+
}
165+
166+
fn set_background_color(&mut self, color: Self::DisplayColor) {
167+
self.background_color = color;
168+
}
169+
170+
fn background_color(&self) -> &Self::DisplayColor {
171+
&self.background_color
172+
}
173+
174+
fn width(&self) -> u32 {
175+
WIDTH
176+
}
177+
178+
fn height(&self) -> u32 {
179+
HEIGHT
180+
}
181+
182+
fn update_frame(
183+
&mut self,
184+
spi: &mut SPI,
185+
buffer: &[u8],
186+
_delay: &mut DELAY,
187+
) -> Result<(), SPI::Error> {
188+
assert!(buffer.len() == buffer_len(WIDTH as usize, HEIGHT as usize));
189+
self.interface
190+
.cmd_with_data(spi, Command::SetRamXAddressCounter, &[0x00, 0x00])?;
191+
self.interface
192+
.cmd_with_data(spi, Command::SetRamYAddressCounter, &[0x00, 0x00])?;
193+
194+
self.interface
195+
.cmd_with_data(spi, Command::WriteRam, buffer)?;
196+
197+
Ok(())
198+
}
199+
200+
#[allow(unused)]
201+
fn update_partial_frame(
202+
&mut self,
203+
spi: &mut SPI,
204+
delay: &mut DELAY,
205+
buffer: &[u8],
206+
x: u32,
207+
y: u32,
208+
width: u32,
209+
height: u32,
210+
) -> Result<(), SPI::Error> {
211+
todo!()
212+
}
213+
214+
fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
215+
//self.interface
216+
// .cmd_with_data(spi, Command::WRITE_LUT_REGISTER, &LUT_1GRAY_GC)?;
217+
self.interface.cmd(spi, Command::DisplayUpdateSequence)?;
218+
self.interface.wait_until_idle(delay, IS_BUSY_LOW);
219+
Ok(())
220+
}
221+
222+
fn update_and_display_frame(
223+
&mut self,
224+
spi: &mut SPI,
225+
buffer: &[u8],
226+
delay: &mut DELAY,
227+
) -> Result<(), SPI::Error> {
228+
self.update_frame(spi, buffer, delay)?;
229+
self.display_frame(spi, delay)?;
230+
Ok(())
231+
}
232+
233+
fn clear_frame(&mut self, spi: &mut SPI, _delay: &mut DELAY) -> Result<(), SPI::Error> {
234+
self.interface
235+
.cmd_with_data(spi, Command::SetRamXAddressCounter, &[0x00, 0x00])?;
236+
self.interface
237+
.cmd_with_data(spi, Command::SetRamYAddressCounter, &[0x00, 0x00])?;
238+
239+
let color = self.background_color.get_byte_value();
240+
self.interface.cmd(spi, Command::WriteRam)?;
241+
self.interface.data_x_times(spi, color, WIDTH * HEIGHT)?;
242+
243+
Ok(())
244+
}
245+
246+
fn set_lut(
247+
&mut self,
248+
spi: &mut SPI,
249+
_delay: &mut DELAY,
250+
refresh_rate: Option<RefreshLut>,
251+
) -> Result<(), SPI::Error> {
252+
let buffer = match refresh_rate {
253+
Some(RefreshLut::Full) | None => &LUT_1GRAY_GC,
254+
Some(RefreshLut::Quick) => &LUT_1GRAY_DU,
255+
};
256+
257+
self.interface
258+
.cmd_with_data(spi, Command::WriteLutRegister, buffer)?;
259+
Ok(())
260+
}
261+
262+
fn wait_until_idle(&mut self, _spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
263+
self.interface.wait_until_idle(delay, IS_BUSY_LOW);
264+
Ok(())
265+
}
266+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ pub mod epd2in7b;
8383
pub mod epd2in9;
8484
pub mod epd2in9_v2;
8585
pub mod epd2in9bc;
86+
pub mod epd3in7;
8687
pub mod epd4in2;
8788
pub mod epd5in65f;
8889
pub mod epd5in83b_v2;

0 commit comments

Comments
 (0)