1use core::time::Duration;
2use embedded_graphics::{
3 pixelcolor::BinaryColor,
4 prelude::{Dimensions, Point, Size},
5 primitives::Rectangle,
6};
7use embedded_hal::digital::{InputPin, OutputPin};
8use embedded_hal_async::{delay::DelayNs, digital::Wait, spi::SpiDevice};
9
10use crate::{
11 buffer::{binary_buffer_length, BinaryBuffer},
12 log::{debug, trace},
13 Epd, EpdHw, Error,
14};
15
16const LUT_FULL_UPDATE: [u8; 30] = [
20 0x50, 0xAA, 0x55, 0xAA, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
21 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
22];
23const LUT_PARTIAL_UPDATE: [u8; 30] = [
28 0x10, 0x18, 0x18, 0x08, 0x18, 0x18, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
29 0x00, 0x00, 0x00, 0x00, 0x13, 0x14, 0x44, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30];
31
32#[cfg_attr(feature = "defmt", derive(defmt::Format))]
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum RefreshMode {
36 Full,
41 Partial,
44}
45
46impl RefreshMode {
47 pub fn lut(&self) -> &[u8; 30] {
49 match self {
50 RefreshMode::Full => &LUT_FULL_UPDATE,
51 RefreshMode::Partial => &LUT_PARTIAL_UPDATE,
52 }
53 }
54}
55
56pub const DISPLAY_HEIGHT: u16 = 296;
58pub const DISPLAY_WIDTH: u16 = 128;
60pub const RECOMMENDED_MIN_FULL_REFRESH_INTERVAL: Duration = Duration::from_secs(180);
62pub const RECOMMENDED_MAX_FULL_REFRESH_INTERVAL: Duration = Duration::from_secs(24 * 60 * 60);
64
65#[cfg_attr(feature = "defmt", derive(defmt::Format))]
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67pub enum Command {
68 DriverOutputControl = 0x01,
70 BoosterSoftStartControl = 0x0C,
72 DeepSleepMode = 0x10,
74 DataEntryModeSetting = 0x11,
76 SwReset = 0x12,
78 TemperatureSensorControl = 0x1A,
80 MasterActivation = 0x20,
83 DisplayUpdateControl1 = 0x21,
85 DisplayUpdateControl2 = 0x22,
87 WriteRam = 0x24,
89 WriteVcom = 0x2C,
91 WriteLut = 0x32,
93 SetDummyLinePeriod = 0x3A,
95 SetGateLineWidth = 0x3B,
97 BorderWaveformControl = 0x3C,
100 SetRamXStartEnd = 0x44,
102 SetRamYStartEnd = 0x45,
104 SetRamX = 0x4E,
106 SetRamY = 0x4F,
108 Noop = 0xFF,
110}
111
112impl Command {
113 fn register(&self) -> u8 {
114 *self as u8
115 }
116}
117
118const DRIVER_OUTPUT_INIT_DATA: [u8; 3] = [0x27, 0x01, 0x00];
126const BOOSTER_SOFT_START_INIT_DATA: [u8; 3] = [0xD7, 0xD6, 0x9D];
129pub struct Epd2in9<HW>
140where
141 HW: EpdHw,
142{
143 hw: HW,
144}
145
146impl<HW> Epd2in9<HW>
147where
148 HW: EpdHw,
149{
150 pub fn new(hw: HW) -> Self {
151 Epd2in9 { hw }
152 }
153
154 pub async fn set_border(
157 &mut self,
158 spi: &mut HW::Spi,
159 color: BinaryColor,
160 ) -> Result<(), HW::Error> {
161 let border_setting: u8 = match color {
162 BinaryColor::Off => 0x00,
163 BinaryColor::On => 0x01,
164 };
165 self.send(spi, Command::BorderWaveformControl, &[border_setting])
166 .await
167 }
168}
169
170impl<HW> Epd<HW> for Epd2in9<HW>
171where
172 HW: EpdHw,
173{
174 type Command = Command;
175 type RefreshMode = RefreshMode;
176 type Buffer = BinaryBuffer<
177 { binary_buffer_length(Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32)) },
178 >;
179
180 fn new_buffer(&self) -> Self::Buffer {
181 BinaryBuffer::new(Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32))
182 }
183
184 fn width(&self) -> u32 {
185 DISPLAY_WIDTH as u32
186 }
187
188 fn height(&self) -> u32 {
189 DISPLAY_HEIGHT as u32
190 }
191
192 async fn init(&mut self, spi: &mut HW::Spi, mode: RefreshMode) -> Result<(), HW::Error> {
193 self.reset().await?;
195
196 self.send(spi, Command::SwReset, &[]).await?;
198
199 self.send(spi, Command::DriverOutputControl, &DRIVER_OUTPUT_INIT_DATA)
200 .await?;
201 self.send(
202 spi,
203 Command::BoosterSoftStartControl,
204 &BOOSTER_SOFT_START_INIT_DATA,
205 )
206 .await?;
207 self.send(spi, Command::DataEntryModeSetting, &[0b11])
209 .await?;
210
211 self.send(spi, Command::WriteVcom, &[0xA8]).await?;
214 self.send(spi, Command::SetDummyLinePeriod, &[0x1A]).await?;
216 self.send(spi, Command::SetGateLineWidth, &[0x08]).await?;
218
219 self.send(spi, Command::WriteLut, mode.lut()).await?;
220
221 Ok(())
222 }
223
224 async fn set_refresh_mode(
225 &mut self,
226 spi: &mut <HW as EpdHw>::Spi,
227 mode: Self::RefreshMode,
228 ) -> Result<(), <HW as EpdHw>::Error> {
229 debug!("Changing refresh mode to {:?}", mode);
230 self.send(spi, Command::WriteLut, mode.lut()).await
231 }
232
233 async fn reset(&mut self) -> Result<(), HW::Error> {
234 debug!("Resetting EPD");
235 self.hw.reset().set_low()?;
237 self.hw.delay().delay_ms(10).await;
238 self.hw.reset().set_high()?;
239 self.hw.delay().delay_ms(10).await;
240 Ok(())
241 }
242
243 async fn sleep(&mut self, spi: &mut HW::Spi) -> Result<(), <HW as EpdHw>::Error> {
244 debug!("Sleeping EPD");
245 self.send(spi, Command::DeepSleepMode, &[0x01]).await
246 }
247
248 async fn wake(&mut self, _spi: &mut HW::Spi) -> Result<(), <HW as EpdHw>::Error> {
249 debug!("Waking EPD");
250 self.reset().await
251
252 }
254
255 async fn display_buffer(
256 &mut self,
257 spi: &mut HW::Spi,
258 buffer: &Self::Buffer,
259 ) -> Result<(), <HW as EpdHw>::Error> {
260 debug!("Displaying buffer");
261 let buffer_bounds = buffer.bounding_box();
262 self.set_window(spi, buffer_bounds).await?;
263 self.set_cursor(spi, buffer_bounds.top_left).await?;
264 self.write_image(spi, buffer.data()).await?;
265
266 self.update_display(spi).await?;
267
268 Ok(())
269 }
270
271 async fn set_window(
275 &mut self,
276 spi: &mut HW::Spi,
277 shape: Rectangle,
278 ) -> Result<(), <HW as EpdHw>::Error> {
279 let x_start = shape.top_left.x;
280 let x_end = x_start + shape.size.width as i32 - 1;
281 if x_start % 8 != 0 || x_end % 8 != 7 {
282 Err(Error::InvalidArgument)?
283 }
284 let x_start_byte = ((x_start >> 3) & 0xFF) as u8;
285 let x_end_byte = ((x_end >> 3) & 0xFF) as u8;
286 self.send(spi, Command::SetRamXStartEnd, &[x_start_byte, x_end_byte])
287 .await?;
288
289 let (y_start_low, y_start_high) = split_low_and_high(shape.top_left.y as u16);
290 let (y_end_low, y_end_high) =
291 split_low_and_high((shape.top_left.y + shape.size.height as i32 - 1) as u16);
292 self.send(
293 spi,
294 Command::SetRamYStartEnd,
295 &[y_start_low, y_start_high, y_end_low, y_end_high],
296 )
297 .await?;
298
299 Ok(())
300 }
301
302 async fn set_cursor(
306 &mut self,
307 spi: &mut HW::Spi,
308 position: Point,
309 ) -> Result<(), <HW as EpdHw>::Error> {
310 if position.x % 8 != 0 {
311 Err(Error::InvalidArgument)?
312 }
313 self.send(spi, Command::SetRamX, &[(position.x >> 3) as u8])
314 .await?;
315 let (y_low, y_high) = split_low_and_high(position.y as u16);
316 self.send(spi, Command::SetRamY, &[y_low, y_high]).await?;
317 Ok(())
318 }
319
320 async fn update_display(&mut self, spi: &mut HW::Spi) -> Result<(), <HW as EpdHw>::Error> {
321 self.send(spi, Command::DisplayUpdateControl2, &[0xC4])
333 .await?;
334 self.send(spi, Command::MasterActivation, &[]).await?;
335 self.send(spi, Command::Noop, &[]).await?;
336 Ok(())
337 }
338
339 async fn write_image(
340 &mut self,
341 spi: &mut HW::Spi,
342 image: &[u8],
343 ) -> Result<(), <HW as EpdHw>::Error> {
344 self.send(spi, Command::WriteRam, image).await
345 }
346
347 async fn send(
348 &mut self,
349 spi: &mut HW::Spi,
350 command: Command,
351 data: &[u8],
352 ) -> Result<(), HW::Error> {
353 trace!("Sending EPD command: {:?}", command);
354 self.wait_if_busy().await?;
355
356 self.hw.dc().set_low()?;
357 spi.write(&[command.register()]).await?;
358
359 if !data.is_empty() {
360 self.hw.dc().set_high()?;
361 spi.write(data).await?;
362 }
363
364 Ok(())
365 }
366
367 async fn wait_if_busy(&mut self) -> Result<(), HW::Error> {
368 let busy = self.hw.busy();
369 if busy.is_high().unwrap() {
372 trace!("Waiting for busy EPD");
373 busy.wait_for_low().await?;
374 }
375 Ok(())
376 }
377}
378
379#[inline(always)]
380fn split_low_and_high(value: u16) -> (u8, u8) {
382 let low = (value & 0xFF) as u8;
383 let high = ((value >> 8) & 0xFF) as u8;
384 (low, high)
385}