use core::time::Duration;
use embedded_graphics::{
pixelcolor::BinaryColor,
prelude::{Dimensions, Point, Size},
primitives::Rectangle,
};
use embedded_hal::digital::{InputPin, OutputPin};
use embedded_hal_async::{delay::DelayNs, digital::Wait, spi::SpiBus};
use crate::{
buffer::{binary_buffer_length, BinaryBuffer},
Epd, EpdHw, Error,
};
pub const LUT_FULL_UPDATE: [u8; 30] = [
0x50, 0xAA, 0x55, 0xAA, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
pub const LUT_PARTIAL_UPDATE: [u8; 30] = [
0x10, 0x18, 0x18, 0x08, 0x18, 0x18, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x13, 0x14, 0x44, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
pub const DISPLAY_HEIGHT: u16 = 296;
pub const DISPLAY_WIDTH: u16 = 128;
pub const RECOMMENDED_MIN_FULL_REFRESH_INTERVAL: Duration = Duration::from_secs(180);
pub const RECOMMENDED_MAX_FULL_REFRESH_INTERVAL: Duration = Duration::from_secs(24 * 60 * 60);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Command {
DriverOutputControl = 0x01,
BoosterSoftStartControl = 0x0C,
DeepSleepMode = 0x10,
DataEntryModeSetting = 0x11,
SwReset = 0x12,
TemperatureSensorControl = 0x1A,
MasterActivation = 0x20,
DisplayUpdateControl1 = 0x21,
DisplayUpdateControl2 = 0x22,
WriteRam = 0x24,
WriteVcom = 0x2C,
WriteLut = 0x32,
SetDummyLinePeriod = 0x3A,
SetGateLineWidth = 0x3B,
BorderWaveformControl = 0x3C,
SetRamXStartEnd = 0x44,
SetRamYStartEnd = 0x45,
SetRamX = 0x4E,
SetRamY = 0x4F,
Noop = 0xFF,
}
const DRIVER_OUTPUT_INIT_DATA: [u8; 3] = [0x27, 0x01, 0x00];
const BOOSTER_SOFT_START_INIT_DATA: [u8; 3] = [0xCF, 0xCE, 0x8D];
impl Command {
fn register(&self) -> u8 {
*self as u8
}
}
pub struct Epd2in9<HW>
where
HW: EpdHw,
{
hw: HW,
}
impl<HW> Epd2in9<HW>
where
HW: EpdHw,
{
pub fn new(hw: HW) -> Self {
Epd2in9 { hw }
}
pub async fn set_border(&mut self, color: BinaryColor) -> Result<(), HW::Error> {
let border_setting: u8 = match color {
BinaryColor::Off => 0x40, BinaryColor::On => 0x50, };
self.send(Command::BorderWaveformControl, &[border_setting])
.await
}
}
impl<HW> Epd<HW> for Epd2in9<HW>
where
HW: EpdHw,
{
type Command = Command;
type Buffer = BinaryBuffer<
{ binary_buffer_length(Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32)) },
>;
async fn init(&mut self, lut: &[u8]) -> Result<(), HW::Error> {
if lut.len() != 30 {
Err(Error::InvalidArgument)?
}
self.hw.reset().set_high()?;
self.send(Command::SwReset, &[]).await?;
self.send(Command::DriverOutputControl, &DRIVER_OUTPUT_INIT_DATA)
.await?;
self.send(
Command::BoosterSoftStartControl,
&BOOSTER_SOFT_START_INIT_DATA,
)
.await?;
self.send(Command::DataEntryModeSetting, &[0b11]).await?;
self.send(Command::WriteVcom, &[0xA8]).await?;
self.send(Command::SetDummyLinePeriod, &[0x1A]).await?;
self.send(Command::SetGateLineWidth, &[0x08]).await?;
self.send(Command::WriteLut, lut).await?;
Ok(())
}
async fn clear(&mut self) -> Result<(), HW::Error> {
self.send(Command::DisplayUpdateControl1, &[0x90]).await?;
self.update_display().await?;
self.send(Command::DisplayUpdateControl1, &[0x01]).await?;
Ok(())
}
async fn reset(&mut self) -> Result<(), HW::Error> {
self.hw.reset().set_low()?;
self.hw.delay().delay_ms(10).await;
self.hw.reset().set_high()?;
self.hw.delay().delay_ms(10).await;
Ok(())
}
async fn sleep(&mut self) -> Result<(), <HW as EpdHw>::Error> {
self.send(Command::DeepSleepMode, &[0x01]).await
}
async fn wake(&mut self) -> Result<(), <HW as EpdHw>::Error> {
self.reset().await
}
async fn display_buffer(&mut self, buffer: &Self::Buffer) -> Result<(), <HW as EpdHw>::Error> {
let buffer_bounds = buffer.bounding_box();
self.set_window(buffer_bounds).await?;
self.set_cursor(buffer_bounds.top_left).await?;
self.write_image(buffer.data()).await?;
Ok(())
}
async fn set_window(&mut self, shape: Rectangle) -> Result<(), <HW as EpdHw>::Error> {
let x_start = shape.top_left.x;
let x_end = x_start + shape.size.width as i32;
if x_start % 8 != 0 || x_end % 8 != 0 {
Err(Error::InvalidArgument)?
}
let x_start_byte = (x_start >> 3) as u8;
let x_end_byte = (x_end >> 8) as u8;
self.send(Command::SetRamXStartEnd, &[x_start_byte, x_end_byte])
.await?;
let y_start = shape.top_left.y;
let y_end = y_start + shape.size.height as i32;
let y_start_low = (y_start & 0xFF) as u8;
let y_start_high = ((y_start >> 8) & 0xFF) as u8;
let y_end_low = (y_end & 0xFF) as u8;
let y_end_high = ((y_end >> 8) & 0xFF) as u8;
self.send(
Command::SetRamYStartEnd,
&[y_start_low, y_start_high, y_end_low, y_end_high],
)
.await?;
Ok(())
}
async fn set_cursor(&mut self, position: Point) -> Result<(), <HW as EpdHw>::Error> {
if position.x % 8 != 0 {
Err(Error::InvalidArgument)?
}
self.send(Command::SetRamX, &[(position.x >> 3) as u8])
.await?;
let y_low = (position.y & 0xFF) as u8;
let y_high = ((position.y >> 8) & 0xFF) as u8;
self.send(Command::SetRamY, &[y_low, y_high]).await?;
Ok(())
}
async fn update_display(&mut self) -> Result<(), <HW as EpdHw>::Error> {
self.send(Command::DisplayUpdateControl2, &[0x04]).await?;
self.send(Command::MasterActivation, &[]).await?;
self.send(Command::Noop, &[]).await?;
Ok(())
}
async fn write_image(&mut self, image: &[u8]) -> Result<(), <HW as EpdHw>::Error> {
self.send(Command::WriteRam, image).await
}
async fn send(&mut self, command: Command, data: &[u8]) -> Result<(), HW::Error> {
self.wait_if_busy().await?;
self.hw.cs().set_low()?;
self.hw.dc().set_low()?;
self.hw.spi().write(&[command.register()]).await?;
if !data.is_empty() {
self.hw.dc().set_high()?;
self.hw.spi().write(data).await?;
}
self.hw.cs().set_high()?;
Ok(())
}
async fn wait_if_busy(&mut self) -> Result<(), HW::Error> {
let busy = self.hw.busy();
if busy.is_low().unwrap() {
busy.wait_for_high().await?;
}
Ok(())
}
}