use embedded_graphics::{draw_target::Clipped, framebuffer::buffer_size};
use oled_async::{display, mode::displaymode::DisplayModeTrait, prelude::*, properties::DisplayProperties};
use display_interface::{AsyncWriteOnlyDataCommand, DisplayError, DataFormat};
use core::cmp::{min, max};
use super::*;
mod commands;
use commands as i2c;
impl From<DisplayError> for RendererError {
fn from(_value: DisplayError) -> Self {
return RendererError::BackingError
}
}
pub struct Sh1107Render<'a, DI, const BUFFER_SIZE: usize>
where
DI: AsyncWriteOnlyDataCommand,
{
width: u32,
height: u32,
chunk_width: u32,
chunk_height: u32,
clip: BoundingBox,
buffer: [u8; BUFFER_SIZE],
display: &'a mut DI,
}
impl<'a, DI, const BUFFER_SIZE: usize> Sh1107Render<'a, DI, BUFFER_SIZE>
where
DI: AsyncWriteOnlyDataCommand,
{
const CHUNK_SIZE: usize = usize::isqrt(BUFFER_SIZE) * 8;
pub fn new(display: &'a mut DI, width: u32, height: u32, chunk_width: u32, chunk_height: u32) -> Self {
Self {
width,
height,
chunk_width,
chunk_height,
clip: BoundingBox::new(0, 0, chunk_width, chunk_height),
buffer: [0u8;BUFFER_SIZE],
display,
}
}
pub async fn init(
&mut self,
dimensions: (u8, u8),
) -> Result<(), RendererError>
{
let iface = &mut self.display;
let (_, display_height) = dimensions;
i2c::Command::DisplayOn(false).send(*iface).await?;
i2c::Command::DisplayClockDiv(0x8, 0x0).send(*iface).await?;
i2c::Command::Multiplex(display_height - 1).send(*iface).await?;
i2c::Command::StartLine(0).send(*iface).await?;
i2c::Command::ChargePump(true).send(*iface).await?;
i2c::Command::Contrast(0x80).send(*iface).await?;
i2c::Command::PreChargePeriod(0x1, 0xF).send(*iface).await?;
i2c::Command::VcomhDeselect(i2c::VcomhLevel::Auto).send(*iface).await?;
i2c::Command::AllOn(false).send(*iface).await?;
i2c::Command::Invert(false).send(*iface).await?;
i2c::Command::DisplayOn(true).send(*iface).await?;
i2c::Command::DisplayOffset(0).send(*iface).await?;
i2c::Command::ComPinConfig(true).send(*iface).await?;
for x in (0..self.width).step_by(self.chunk_width as usize) {
for y in (0..self.height).step_by(self.chunk_height as usize) {
self.set_chunk(x, y)?;
self.clear()?;
self.flush().await?;
}
}
Ok(())
}
}
impl<'a, DI, const BUFFER_SIZE: usize> Renderer for Sh1107Render<'a, DI, BUFFER_SIZE>
where
DI: AsyncWriteOnlyDataCommand,
{
fn width(&self) -> u32 {
self.width
}
fn height(&self) -> u32 {
self.height
}
fn chunk_size(&self) -> (u32, u32) {
(self.chunk_width, self.chunk_height)
}
fn set_chunk(&mut self, x: u32, y: u32) -> Result<(), RendererError> {
if (x % self.chunk_width != 0) || (y % self.chunk_height != 0) {
return Err(RendererError::InvalidChunkOffset{x, y})
}
self.clip = BoundingBox{
x1: x,
y1: y,
x2: x + self.chunk_width,
y2: y + self.chunk_height,
};
Ok(())
}
fn draw(&mut self, command: &Command) -> Result<(), RendererError> {
use CommandType::*;
let clip = self.clip;
match command.flavor {
Null => Ok(()),
Rect(rgb) => {
let color = rgb.r|rgb.g|rgb.b;
let x1 = max(command.bounds.x1, clip.x1);
let y1 = max(command.bounds.y1, clip.y1);
let x2 = min(command.bounds.x2, clip.x2);
let y2 = min(command.bounds.y2, clip.y2);
let x = x1 - clip.x1;
let x_end = x2 - clip.x1;
let y = y1 - clip.y1;
let y_end = y2 - clip.y1;
for x_i in x..x_end {
for y_i in y..y_end {
let index = x_i * (y_i/8 + 1);
let byte = &mut self.buffer[index as usize];
let bit = y_i % 8;
let set_bit = 1u8<<bit;
if color > 0 {
*byte |= set_bit;
} else {
*byte &= !set_bit;
}
}
}
Ok(())
}
}
}
fn clear(&mut self) -> Result<(), RendererError> {
self.buffer = [0x00u8;BUFFER_SIZE];
Ok(())
}
async fn flush(&mut self) -> Result<(), RendererError> {
let column_start = self.clip.x1 as u8;
let row_start = (self.clip.y1/8) as u8;
let row_end = row_start + (self.chunk_height as u8 /8);
for (index,row) in (row_start..row_end).enumerate() {
i2c::Command::PageAddress(row)
.send(self.display)
.await?;
i2c::Command::ColumnAddressLow(0xF & column_start)
.send(self.display)
.await?;
i2c::Command::ColumnAddressHigh(0xF & (column_start >> 4))
.send(self.display)
.await?;
let start = self.chunk_width as usize * index;
let end = start + self.chunk_width as usize;
let data: DataFormat<'_> = DataFormat::U8(&self.buffer[start..end]);
self.display.send_data(data).await?;
}
Ok(())
}
}