use super::brightness::Brightness;
use super::command::{Command, Logical};
use super::display::DisplayDefinition;
use super::mode::{BasicMode, BufferedGraphics};
use super::rotation::DisplayRotation;
use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand};
use embedded_hal::delay::DelayNs;
use embedded_hal::digital::OutputPin;
pub struct Gc9a01<I, D, M>
where
I: WriteOnlyDataCommand,
D: DisplayDefinition,
{
pub(crate) interface: I,
pub(crate) display: D,
pub(crate) mode: M,
pub(crate) display_rotation: DisplayRotation,
}
impl<I, D, M> Gc9a01<I, D, M>
where
I: WriteOnlyDataCommand,
D: DisplayDefinition,
{
#[allow(clippy::needless_pass_by_ref_mut)]
pub fn reset<RST, DELAY>(&mut self, rst: &mut RST, delay: &mut DELAY) -> Result<(), RST::Error>
where
RST: OutputPin,
DELAY: DelayNs,
{
fn inner_reset<RST, DELAY>(rst: &mut RST, delay: &mut DELAY) -> Result<(), RST::Error>
where
RST: OutputPin,
DELAY: DelayNs,
{
rst.set_high()?;
delay.delay_ms(50);
rst.set_low()?;
delay.delay_ms(50);
rst.set_high()?;
delay.delay_ms(50);
Ok(())
}
inner_reset(rst, delay)
}
}
impl<I, D> Gc9a01<I, D, BasicMode>
where
I: WriteOnlyDataCommand,
D: DisplayDefinition,
{
pub fn new(interface: I, screen: D, screen_rotation: DisplayRotation) -> Self {
Self {
interface,
display: screen,
mode: BasicMode::new(),
display_rotation: screen_rotation,
}
}
}
impl<I, D, M> Gc9a01<I, D, M>
where
I: WriteOnlyDataCommand,
D: DisplayDefinition,
{
fn into_mode<MODE>(self, mode: MODE) -> Gc9a01<I, D, MODE> {
Gc9a01 {
mode,
interface: self.interface,
display: self.display,
display_rotation: self.display_rotation,
}
}
pub fn into_buffered_graphics(self) -> Gc9a01<I, D, BufferedGraphics<D>> {
self.into_mode(BufferedGraphics::new())
}
pub fn init_with_addr_mode(&mut self, delay: &mut impl DelayNs) -> Result<(), DisplayError> {
let rotation = self.display_rotation;
self.display.configure(&mut self.interface, delay)?;
self.set_display_rotation(rotation)?;
self.set_brightness(Brightness::default())?;
Command::DisplayState(Logical::On).send(&mut self.interface)?;
delay.delay_ms(120);
Ok(())
}
pub fn draw(&mut self, buffer: &[u8]) -> Result<(), DisplayError> {
self.interface.send_data(DataFormat::U8(buffer))
}
pub fn clear_fit(&mut self) -> Result<(), DisplayError> {
self.interface
.send_data(DataFormat::U16(&[0, D::HEIGHT * D::WIDTH]))
}
#[allow(clippy::match_same_arms)]
pub fn set_display_rotation(&mut self, rotation: DisplayRotation) -> Result<(), DisplayError> {
self.display_rotation = rotation;
match self.display_rotation {
DisplayRotation::Rotate0 => Command::MemoryAccessControl(
Logical::Off,
Logical::Off,
Logical::Off,
Logical::On,
Logical::On,
Logical::Off,
)
.send(&mut self.interface)?,
DisplayRotation::Rotate90 => Command::MemoryAccessControl(
Logical::On,
Logical::Off,
Logical::Off,
Logical::On,
Logical::On,
Logical::Off,
)
.send(&mut self.interface)?,
DisplayRotation::Rotate180 => Command::MemoryAccessControl(
Logical::On,
Logical::On,
Logical::Off,
Logical::On,
Logical::On,
Logical::Off,
)
.send(&mut self.interface)?,
DisplayRotation::Rotate270 => Command::MemoryAccessControl(
Logical::Off,
Logical::On,
Logical::Off,
Logical::On,
Logical::On,
Logical::Off,
)
.send(&mut self.interface)?,
};
Ok(())
}
pub fn set_brightness(&mut self, brightness: Brightness) -> Result<(), DisplayError> {
Command::DisplayBrightness(brightness.brightness).send(&mut self.interface)
}
pub fn set_screen_state(&mut self, on: Logical) -> Result<(), DisplayError> {
Command::DisplayState(on).send(&mut self.interface)
}
pub fn set_invert_pixels(&mut self, value: bool) -> Result<(), DisplayError> {
Command::DisplayInversion(value.into()).send(&mut self.interface)
}
pub fn set_draw_area(
&mut self,
start: (u16, u16),
end: (u16, u16),
) -> Result<(), DisplayError> {
Command::ColumnAddressSet(start.0, end.0.saturating_sub(1)).send(&mut self.interface)?;
Command::RowAddressSet(start.1, end.1.saturating_sub(1)).send(&mut self.interface)?;
Ok(())
}
pub const fn get_screen_rotation(&self) -> DisplayRotation {
self.display_rotation
}
pub const fn dimensions(&self) -> (u16, u16) {
match self.display_rotation {
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => (D::WIDTH, D::HEIGHT),
DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => (D::HEIGHT, D::WIDTH),
}
}
pub const fn bounds(&self) -> (u16, u16) {
match self.display_rotation {
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => (D::WIDTH - 1, D::HEIGHT - 1),
DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => (D::HEIGHT - 1, D::WIDTH - 1),
}
}
pub(crate) fn flush_buffer_chunks(
interface: &mut I,
buffer: &[u16],
disp_width: usize,
upper_left: (u16, u16),
lower_right: (u16, u16),
) -> Result<(), DisplayError> {
Command::MemoryWrite.send(interface)?;
let num_pages = (lower_right.1 - upper_left.1) as usize + 1;
let starting_page = (upper_left.1) as usize;
let page_lower = upper_left.0 as usize;
let page_upper = lower_right.0 as usize;
buffer
.chunks(disp_width)
.skip(starting_page)
.take(num_pages)
.map(|s| &s[page_lower..page_upper])
.try_for_each(|c| interface.send_data(DataFormat::U16(c)))
}
}