1use core::time::Duration;
2use embedded_graphics::{
3 pixelcolor::BinaryColor,
4 prelude::{Dimensions, Point, Size},
5 primitives::Rectangle,
6};
7use embedded_hal::{
8 digital::{InputPin, OutputPin},
9 spi::{Phase, Polarity},
10};
11use embedded_hal_async::{delay::DelayNs, digital::Wait, spi::SpiDevice};
12
13use crate::{
14 buffer::{binary_buffer_length, BinaryBuffer},
15 log::{debug, trace},
16 Epd, EpdHw,
17};
18
19const LUT_FULL_UPDATE: [u8; 30] = [
23 0x50, 0xAA, 0x55, 0xAA, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
24 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
25];
26const LUT_PARTIAL_UPDATE: [u8; 30] = [
31 0x10, 0x18, 0x18, 0x08, 0x18, 0x18, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32 0x00, 0x00, 0x00, 0x00, 0x13, 0x14, 0x44, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
33];
34
35#[cfg_attr(feature = "defmt", derive(defmt::Format))]
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum RefreshMode {
39 Full,
44 Partial,
47}
48
49impl RefreshMode {
50 pub fn lut(&self) -> &[u8; 30] {
52 match self {
53 RefreshMode::Full => &LUT_FULL_UPDATE,
54 RefreshMode::Partial => &LUT_PARTIAL_UPDATE,
55 }
56 }
57}
58
59pub const DISPLAY_HEIGHT: u16 = 296;
61pub const DISPLAY_WIDTH: u16 = 128;
63pub const RECOMMENDED_MIN_FULL_REFRESH_INTERVAL: Duration = Duration::from_secs(180);
65pub const RECOMMENDED_MAX_FULL_REFRESH_INTERVAL: Duration = Duration::from_secs(24 * 60 * 60);
67pub const RECOMMENDED_SPI_HZ: u32 = 4_000_000; pub const RECOMMENDED_SPI_PHASE: Phase = Phase::CaptureOnFirstTransition;
71pub const RECOMMENDED_SPI_POLARITY: Polarity = Polarity::IdleLow;
74
75#[cfg_attr(feature = "defmt", derive(defmt::Format))]
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub enum Command {
78 DriverOutputControl = 0x01,
80 BoosterSoftStartControl = 0x0C,
82 DeepSleepMode = 0x10,
84 DataEntryModeSetting = 0x11,
86 SwReset = 0x12,
88 TemperatureSensorControl = 0x1A,
90 MasterActivation = 0x20,
93 DisplayUpdateControl1 = 0x21,
95 DisplayUpdateControl2 = 0x22,
97 WriteRam = 0x24,
99 WriteVcom = 0x2C,
101 WriteLut = 0x32,
103 SetDummyLinePeriod = 0x3A,
105 SetGateLineWidth = 0x3B,
107 BorderWaveformControl = 0x3C,
109 SetRamXStartEnd = 0x44,
112 SetRamYStartEnd = 0x45,
114 SetRamX = 0x4E,
117 SetRamY = 0x4F,
119 Noop = 0xFF,
121}
122
123impl Command {
124 fn register(&self) -> u8 {
126 *self as u8
127 }
128}
129
130const DRIVER_OUTPUT_INIT_DATA: [u8; 3] = [0x27, 0x01, 0x00];
138const BOOSTER_SOFT_START_INIT_DATA: [u8; 3] = [0xD7, 0xD6, 0x9D];
141pub struct Epd2In9<HW>
152where
153 HW: EpdHw,
154{
155 hw: HW,
156}
157
158impl<HW> Epd2In9<HW>
159where
160 HW: EpdHw,
161{
162 pub fn new(hw: HW) -> Self {
163 Epd2In9 { hw }
164 }
165
166 pub async fn set_border(
169 &mut self,
170 spi: &mut HW::Spi,
171 color: BinaryColor,
172 ) -> Result<(), HW::Error> {
173 let border_setting: u8 = match color {
174 BinaryColor::Off => 0x00,
175 BinaryColor::On => 0x01,
176 };
177 self.send(spi, Command::BorderWaveformControl, &[border_setting])
178 .await
179 }
180
181 async fn send(
183 &mut self,
184 spi: &mut HW::Spi,
185 command: Command,
186 data: &[u8],
187 ) -> Result<(), HW::Error> {
188 trace!("Sending EPD command: {:?}", command);
189 self.wait_if_busy().await?;
190
191 self.hw.dc().set_low()?;
192 spi.write(&[command.register()]).await?;
193
194 if !data.is_empty() {
195 self.hw.dc().set_high()?;
196 spi.write(data).await?;
197 }
198
199 Ok(())
200 }
201
202 async fn wait_if_busy(&mut self) -> Result<(), HW::Error> {
206 let busy = self.hw.busy();
207 if busy.is_high().unwrap() {
210 trace!("Waiting for busy EPD");
211 busy.wait_for_low().await?;
212 }
213 Ok(())
214 }
215}
216
217impl<HW> Epd<HW> for Epd2In9<HW>
218where
219 HW: EpdHw,
220{
221 type RefreshMode = RefreshMode;
222 type Buffer = BinaryBuffer<
223 { binary_buffer_length(Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32)) },
224 >;
225
226 fn new_buffer(&self) -> Self::Buffer {
227 BinaryBuffer::new(Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32))
228 }
229
230 fn width(&self) -> u32 {
231 DISPLAY_WIDTH as u32
232 }
233
234 fn height(&self) -> u32 {
235 DISPLAY_HEIGHT as u32
236 }
237
238 async fn init(&mut self, spi: &mut HW::Spi, mode: RefreshMode) -> Result<(), HW::Error> {
239 self.reset().await?;
241
242 self.send(spi, Command::SwReset, &[]).await?;
244
245 self.send(spi, Command::DriverOutputControl, &DRIVER_OUTPUT_INIT_DATA)
246 .await?;
247 self.send(
248 spi,
249 Command::BoosterSoftStartControl,
250 &BOOSTER_SOFT_START_INIT_DATA,
251 )
252 .await?;
253 self.send(spi, Command::DataEntryModeSetting, &[0b11])
255 .await?;
256
257 self.send(spi, Command::WriteVcom, &[0xA8]).await?;
260 self.send(spi, Command::SetDummyLinePeriod, &[0x1A]).await?;
262 self.send(spi, Command::SetGateLineWidth, &[0x08]).await?;
264
265 self.send(spi, Command::WriteLut, mode.lut()).await?;
266
267 Ok(())
268 }
269
270 async fn set_refresh_mode(
271 &mut self,
272 spi: &mut <HW as EpdHw>::Spi,
273 mode: Self::RefreshMode,
274 ) -> Result<(), <HW as EpdHw>::Error> {
275 debug!("Changing refresh mode to {:?}", mode);
276 self.send(spi, Command::WriteLut, mode.lut()).await
277 }
278
279 async fn reset(&mut self) -> Result<(), HW::Error> {
280 debug!("Resetting EPD");
281 self.hw.reset().set_low()?;
283 self.hw.delay().delay_ms(10).await;
284 self.hw.reset().set_high()?;
285 self.hw.delay().delay_ms(10).await;
286 Ok(())
287 }
288
289 async fn sleep(&mut self, spi: &mut HW::Spi) -> Result<(), <HW as EpdHw>::Error> {
290 debug!("Sleeping EPD");
291 self.send(spi, Command::DeepSleepMode, &[0x01]).await
292 }
293
294 async fn wake(&mut self, _spi: &mut HW::Spi) -> Result<(), <HW as EpdHw>::Error> {
295 debug!("Waking EPD");
296 self.reset().await
297
298 }
300
301 async fn display_buffer(
302 &mut self,
303 spi: &mut HW::Spi,
304 buffer: &Self::Buffer,
305 ) -> Result<(), <HW as EpdHw>::Error> {
306 debug!("Displaying buffer");
307 let buffer_bounds = buffer.bounding_box();
308 self.set_window(spi, buffer_bounds).await?;
309 self.set_cursor(spi, buffer_bounds.top_left).await?;
310 self.write_image(spi, buffer.data()).await?;
311
312 self.update_display(spi).await?;
313
314 Ok(())
315 }
316
317 async fn set_window(
322 &mut self,
323 spi: &mut HW::Spi,
324 shape: Rectangle,
325 ) -> Result<(), <HW as EpdHw>::Error> {
326 let x_start = shape.top_left.x;
329 let x_end = x_start + shape.size.width as i32 - 1;
330 #[cfg(feature = "defmt")]
331 defmt::debug_assert!(
332 x_start % 8 == 0 && x_end % 8 == 7,
333 "window's top_left.x and width must be 8-bit aligned"
334 );
335 #[cfg(not(feature = "defmt"))]
336 debug_assert!(
337 x_start % 8 == 0 && x_end % 8 == 7,
338 "window's top_left.x and width must be 8-bit aligned"
339 );
340 let x_start_byte = ((x_start >> 3) & 0xFF) as u8;
341 let x_end_byte = ((x_end >> 3) & 0xFF) as u8;
342 self.send(spi, Command::SetRamXStartEnd, &[x_start_byte, x_end_byte])
343 .await?;
344
345 let (y_start_low, y_start_high) = split_low_and_high(shape.top_left.y as u16);
346 let (y_end_low, y_end_high) =
347 split_low_and_high((shape.top_left.y + shape.size.height as i32 - 1) as u16);
348 self.send(
349 spi,
350 Command::SetRamYStartEnd,
351 &[y_start_low, y_start_high, y_end_low, y_end_high],
352 )
353 .await?;
354
355 Ok(())
356 }
357
358 async fn set_cursor(
363 &mut self,
364 spi: &mut HW::Spi,
365 position: Point,
366 ) -> Result<(), <HW as EpdHw>::Error> {
367 #[cfg(feature = "defmt")]
370 defmt::debug_assert_eq!(position.x % 8, 0, "position.x must be 8-bit aligned");
371 #[cfg(not(feature = "defmt"))]
372 debug_assert_eq!(position.x % 8, 0, "position.x must be 8-bit aligned");
373
374 self.send(spi, Command::SetRamX, &[(position.x >> 3) as u8])
375 .await?;
376 let (y_low, y_high) = split_low_and_high(position.y as u16);
377 self.send(spi, Command::SetRamY, &[y_low, y_high]).await?;
378 Ok(())
379 }
380
381 async fn update_display(&mut self, spi: &mut HW::Spi) -> Result<(), <HW as EpdHw>::Error> {
382 self.send(spi, Command::DisplayUpdateControl2, &[0xC4])
394 .await?;
395 self.send(spi, Command::MasterActivation, &[]).await?;
396 self.send(spi, Command::Noop, &[]).await?;
397 Ok(())
398 }
399
400 async fn write_image(
401 &mut self,
402 spi: &mut HW::Spi,
403 image: &[u8],
404 ) -> Result<(), <HW as EpdHw>::Error> {
405 self.send(spi, Command::WriteRam, image).await
406 }
407}
408
409#[inline(always)]
410fn split_low_and_high(value: u16) -> (u8, u8) {
412 let low = (value & 0xFF) as u8;
413 let high = ((value >> 8) & 0xFF) as u8;
414 (low, high)
415}