use crate::{command::*, consts::*};
use display_interface::{DisplayError, WriteOnlyDataCommand};
pub struct ST7567S<DI, MODE> {
pub(crate) mode: MODE,
pub(crate) display_interface: DI,
}
pub struct DirectWriteMode;
pub struct BufferedMode {
buffer: [u8; BUFFER_SIZE],
}
impl BufferedMode {
pub(crate) fn new() -> Self {
BufferedMode {
buffer: [0; BUFFER_SIZE],
}
}
}
impl<DI: WriteOnlyDataCommand> ST7567S<DI, DirectWriteMode> {
pub fn new(display_interface: DI) -> Self {
ST7567S {
mode: DirectWriteMode,
display_interface,
}
}
pub fn into_buffered_graphics_mode(self) -> ST7567S<DI, BufferedMode> {
ST7567S {
mode: BufferedMode::new(),
display_interface: self.display_interface,
}
}
}
impl<DI: WriteOnlyDataCommand> ST7567S<DI, BufferedMode> {
pub fn clear(&mut self) {
self.mode.buffer = [0; BUFFER_SIZE];
}
pub fn set_pixel(&mut self, x: u8, y: u8, value: bool) -> Result<(), DisplayError> {
if x >= DISPLAY_WIDTH || y >= DISPLAY_HEIGHT {
return Err(DisplayError::OutOfBoundsError);
}
let column: usize = x as usize;
let page: usize = (y / 8) as usize;
let page_bit = y % 8;
let byte_idx = page * (DISPLAY_WIDTH as usize) + column;
let byte = self.mode.buffer[byte_idx];
let bit_value: u8 = value.into();
let byte = byte & !(1 << page_bit) | (bit_value << page_bit);
self.mode.buffer[byte_idx] = byte;
Ok(())
}
pub fn flush(&mut self) -> Result<(), DisplayError> {
Self::flush_buffer_chunks(
&mut self.display_interface,
self.mode.buffer.as_slice(),
(0, 0),
(DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1),
)
}
}
impl<DI: WriteOnlyDataCommand, MODE> ST7567S<DI, MODE> {
pub fn init(&mut self) -> Result<(), DisplayError> {
SetBiasCommand::Bias1_9.write(&mut self.display_interface)?;
SetSEGDirectionCommand::Normal.write(&mut self.display_interface)?;
SetCOMDirectionCommand::Reverse.write(&mut self.display_interface)?;
SetRegulationResistorRatioCommand::Ratio5_0.write(&mut self.display_interface)?;
SetElectronicVolumeCommand::new(40)
.unwrap()
.write(&mut self.display_interface)?;
SetPowerControlCommand::BoosterOn.write(&mut self.display_interface)?;
SetPowerControlCommand::VoltageRegulatorOn.write(&mut self.display_interface)?;
SetPowerControlCommand::VoltageFollowerOn.write(&mut self.display_interface)?;
SetStartLineCommand::new(0)
.unwrap()
.write(&mut self.display_interface)?;
self.draw([0; BUFFER_SIZE].as_slice())?;
DisplayOnCommand::On.write(&mut self.display_interface)?;
Ok(())
}
pub fn reset(&mut self) -> Result<(), DisplayError> {
ResetCommand.write(&mut self.display_interface)
}
pub fn draw(&mut self, buffer: &[u8]) -> Result<(), DisplayError> {
if buffer.len() != BUFFER_SIZE {
return Err(DisplayError::OutOfBoundsError);
}
Self::flush_buffer_chunks(
&mut self.display_interface,
buffer,
(0, 0),
(DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1),
)
}
pub fn bounded_draw(
&mut self,
buffer: &[u8],
top_left: (u8, u8),
bottom_right: (u8, u8),
) -> Result<(), DisplayError> {
Self::flush_buffer_chunks(&mut self.display_interface, buffer, top_left, bottom_right)
}
pub(crate) fn flush_buffer_chunks(
display_interface: &mut DI,
buffer: &[u8],
top_left: (u8, u8),
bottom_right: (u8, u8),
) -> Result<(), DisplayError> {
if top_left.0 >= DISPLAY_WIDTH || top_left.1 >= DISPLAY_HEIGHT {
return Err(DisplayError::OutOfBoundsError);
}
if bottom_right.0 >= DISPLAY_WIDTH || bottom_right.1 >= DISPLAY_HEIGHT {
return Err(DisplayError::OutOfBoundsError);
}
if top_left.0 > bottom_right.0 || top_left.1 > bottom_right.1 {
return Err(DisplayError::OutOfBoundsError);
}
let first_page: usize = (top_left.1 / 8) as usize;
let first_column: usize = top_left.0 as usize;
let last_page: usize = (bottom_right.1 / 8) as usize;
let last_column: usize = bottom_right.0 as usize;
buffer
.chunks(DISPLAY_WIDTH as usize)
.skip(first_page)
.take(last_page - first_page + 1)
.map(|page| &page[first_column..=last_column])
.enumerate()
.try_for_each(|(page_idx, page)| {
SetPageAddressCommand::new(first_page as u8 + page_idx as u8)
.unwrap()
.write(display_interface)?;
SetColumnAddressLSNibbleCommand::new(first_column as u8)
.unwrap()
.write(display_interface)?;
SetColumnAddressMSNibbleCommand::new(first_column as u8)
.unwrap()
.write(display_interface)?;
display_interface.send_data(display_interface::DataFormat::U8(page))
})
}
}