use embedded_hal::delay::DelayNs;
use embedded_hal::digital::{InputPin, OutputPin};
use embedded_hal::spi::SpiDevice;
use crate::bus::{EpdBusError, SpiBusWrapper};
use crate::traits::{ColorChannel, EpdController};
pub mod cmd {
pub const DRIVER_CONTROL: u8 = 0x01;
pub const BOOSTER_SOFT_START: u8 = 0x0C;
pub const DEEP_SLEEP_MODE: u8 = 0x10;
pub const DATA_ENTRY_MODE: u8 = 0x11;
pub const SW_RESET: u8 = 0x12;
pub const TEMP_CONTROL: u8 = 0x18;
pub const WRITE_TEMP_REG: u8 = 0x1A;
pub const MASTER_ACTIVATE: u8 = 0x20;
pub const DISPLAY_UPDATE_CTRL1: u8 = 0x21;
pub const UPDATE_DISPLAY_CTRL2: u8 = 0x22;
pub const WRITE_BW_DATA: u8 = 0x24;
pub const WRITE_RED_DATA: u8 = 0x26;
pub const BORDER_WAVEFORM_CONTROL: u8 = 0x3C;
pub const SET_RAMXPOS: u8 = 0x44;
pub const SET_RAMYPOS: u8 = 0x45;
pub const SET_RAMXCNT: u8 = 0x4E;
pub const SET_RAMYCNT: u8 = 0x4F;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Ssd1677RefreshMode {
#[default]
Full,
FastFull,
Partial,
}
#[derive(Debug, Clone, Copy)]
pub struct Ssd1677Controller {
width: u32,
height: u32,
refresh_mode: Ssd1677RefreshMode,
}
impl Ssd1677Controller {
pub fn new(width: u32, height: u32) -> Self {
Self {
width,
height,
refresh_mode: Ssd1677RefreshMode::default(),
}
}
pub fn with_refresh_mode(mut self, mode: Ssd1677RefreshMode) -> Self {
self.refresh_mode = mode;
self
}
pub fn set_refresh_mode(&mut self, mode: Ssd1677RefreshMode) {
self.refresh_mode = mode;
}
pub fn refresh_mode(&self) -> Ssd1677RefreshMode {
self.refresh_mode
}
}
impl<SPI, DC, RST, BUSY> EpdController<SpiBusWrapper<SPI, DC, RST, BUSY>> for Ssd1677Controller
where
SPI: SpiDevice,
DC: OutputPin,
RST: OutputPin,
BUSY: InputPin,
{
type Error = EpdBusError<SPI::Error, DC::Error, RST::Error, BUSY::Error>;
fn init_sequence<DELAY: DelayNs>(
&mut self,
bus: &mut SpiBusWrapper<SPI, DC, RST, BUSY>,
delay: &mut DELAY,
) -> Result<(), Self::Error> {
bus.hard_reset(delay, 10)?;
bus.send_command(cmd::SW_RESET)?;
delay.delay_ms(10);
bus.send_command_with_data(cmd::TEMP_CONTROL, &[0x80])?;
bus.send_command_with_data(cmd::BOOSTER_SOFT_START, &[0xAE, 0xC7, 0xC3, 0xC0, 0x80])?;
let h_low = ((self.height - 1) & 0xFF) as u8;
let h_high = (((self.height - 1) >> 8) & 0xFF) as u8;
bus.send_command_with_data(cmd::DRIVER_CONTROL, &[h_low, h_high, 0x02])?;
bus.send_command_with_data(cmd::BORDER_WAVEFORM_CONTROL, &[0x01])?;
self.set_window(bus, 0, 0, self.width - 1, self.height - 1)?;
self.set_cursor(bus, 0, 0)?;
Ok(())
}
fn set_window(
&mut self,
bus: &mut SpiBusWrapper<SPI, DC, RST, BUSY>,
x_start: u32,
y_start: u32,
x_end: u32,
y_end: u32,
) -> Result<(), Self::Error> {
let xs = x_start & !7;
let xe = x_end | 7;
let h = y_end - y_start + 1;
let yy = self.height - y_start - h;
let yy_end = self.height - y_start - 1;
bus.send_command_with_data(cmd::DATA_ENTRY_MODE, &[0x01])?;
bus.send_command_with_data(
cmd::SET_RAMXPOS,
&[
(xs & 0xFF) as u8,
((xs >> 8) & 0xFF) as u8,
(xe & 0xFF) as u8,
((xe >> 8) & 0xFF) as u8,
],
)?;
bus.send_command_with_data(
cmd::SET_RAMYPOS,
&[
(yy_end & 0xFF) as u8,
((yy_end >> 8) & 0xFF) as u8,
(yy & 0xFF) as u8,
((yy >> 8) & 0xFF) as u8,
],
)
}
fn set_cursor(
&mut self,
bus: &mut SpiBusWrapper<SPI, DC, RST, BUSY>,
x: u32,
y: u32,
) -> Result<(), Self::Error> {
let xx = x & !7;
let yy_cursor = self.height - 1 - y;
bus.send_command_with_data(
cmd::SET_RAMXCNT,
&[(xx & 0xFF) as u8, ((xx >> 8) & 0xFF) as u8],
)?;
bus.send_command_with_data(
cmd::SET_RAMYCNT,
&[(yy_cursor & 0xFF) as u8, ((yy_cursor >> 8) & 0xFF) as u8],
)
}
fn write_frame(
&mut self,
bus: &mut SpiBusWrapper<SPI, DC, RST, BUSY>,
channel: ColorChannel,
data: &[u8],
) -> Result<(), Self::Error> {
let cmd = match channel {
ColorChannel::BlackWhite => cmd::WRITE_BW_DATA,
ColorChannel::RedYellow
| ColorChannel::Red
| ColorChannel::Yellow
| ColorChannel::Color7(_) => cmd::WRITE_RED_DATA,
};
bus.send_command_with_data(cmd, data)
}
fn write_frame_pattern(
&mut self,
bus: &mut SpiBusWrapper<SPI, DC, RST, BUSY>,
channel: ColorChannel,
byte: u8,
count: usize,
) -> Result<(), Self::Error> {
let cmd = match channel {
ColorChannel::BlackWhite => cmd::WRITE_BW_DATA,
ColorChannel::RedYellow
| ColorChannel::Red
| ColorChannel::Yellow
| ColorChannel::Color7(_) => cmd::WRITE_RED_DATA,
};
bus.send_command(cmd)?;
bus.send_data_repeated(byte, count)
}
fn trigger_refresh<DELAY: DelayNs>(
&mut self,
bus: &mut SpiBusWrapper<SPI, DC, RST, BUSY>,
_delay: &mut DELAY,
) -> Result<(), Self::Error> {
let bypass = match self.refresh_mode {
Ssd1677RefreshMode::Full | Ssd1677RefreshMode::FastFull => [0x40, 0x00],
Ssd1677RefreshMode::Partial => [0x00, 0x00],
};
bus.send_command_with_data(cmd::DISPLAY_UPDATE_CTRL1, &bypass)?;
if self.refresh_mode == Ssd1677RefreshMode::FastFull {
bus.send_command_with_data(cmd::WRITE_TEMP_REG, &[0x5A])?;
}
let mode_byte = match self.refresh_mode {
Ssd1677RefreshMode::Full => 0xF7,
Ssd1677RefreshMode::FastFull => 0xD7,
Ssd1677RefreshMode::Partial => 0xFC,
};
bus.send_command_with_data(cmd::UPDATE_DISPLAY_CTRL2, &[mode_byte])?;
bus.send_command(cmd::MASTER_ACTIVATE)?;
bus.wait_busy(true)
}
fn sleep<DELAY: DelayNs>(
&mut self,
bus: &mut SpiBusWrapper<SPI, DC, RST, BUSY>,
_delay: &mut DELAY,
) -> Result<(), Self::Error> {
bus.send_command_with_data(cmd::DEEP_SLEEP_MODE, &[0x01])
}
}