use embedded_hal_async::spi::SpiBus;
use super::{Display, DisplayPort, DisplayPortDeviceType, DisplayPortLayer, DisplayPortLayers, DisplayPortSeverity};
const SPI_BUFFER_SIZE: usize = 512;
#[derive(Debug, PartialEq)]
pub struct DisplayPortMax7456<SPI> {
display_layers: DisplayPortLayers,
shadow_buffer: [u8; DisplayPort::VIDEO_BUFFER_PAL_CHARACTER_COUNT],
buffer: [u8; 32],
spi_buffer: [u8; SPI_BUFFER_SIZE],
font_is_loading: bool,
pub display_memory_mode_reg: u8,
spi_device: SPI, }
#[allow(unused)]
impl<SPI: SpiBus> DisplayPortMax7456<SPI> {
const VIDEO_BUFFER_DISABLE: u8 = 0x01;
const MAX7456_RESET: u8 = 0x02;
const VERTICAL_SYNC_NEXT_VSYNC: u8 = 0x04;
const OSD_ENABLE: u8 = 0x08;
const MAX_SPI_CLOCK_FREQUENCY_HZ: u32 = 10_000_000;
const INITIAL_SPI_CLOCK_FREQUENCY_HZ: u32 = 5_000_000;
const MAX_7456ADD_DMM: u8 = 0x04;
const MAX_7456ADD_DMAH: u8 = 0x05;
const MAX_7456ADD_DMAL: u8 = 0x06;
const MAX_7456ADD_DMDI: u8 = 0x07;
const DMM_AUTO_INC: u8 = 0x02;
const END_STRING: u8 = 0xFF;
}
#[allow(unused)]
impl<SPI: SpiBus> DisplayPortMax7456<SPI> {
pub fn new(spi_device: SPI) -> Self {
Self {
display_layers: DisplayPortLayers::new(DisplayPortDeviceType::Max7456),
shadow_buffer: [0u8; DisplayPort::VIDEO_BUFFER_PAL_CHARACTER_COUNT],
buffer: [0u8; 32],
spi_buffer: [0u8; SPI_BUFFER_SIZE],
font_is_loading: false,
display_memory_mode_reg: 0,
spi_device,
}
}
}
#[allow(unused)]
impl<SPI: SpiBus> DisplayPortMax7456<SPI> {
pub fn clear_shadow_buffer(&mut self) {
self.shadow_buffer.fill(0);
}
pub async fn draw_screen(&mut self) -> Result<bool, SPI::Error> {
if self.font_is_loading {
return Ok(false);
}
let active_idx = self.display_layers.display_port.active_layer() as usize;
let Some(layer) = self.display_layers.display_layers.get_mut(active_idx) else {
return Ok(false);
};
let active_buffer = &mut layer.buffer;
let screen_size = self.display_layers.display_port.screen_size();
let mut spi_buffer_index = 0;
let mut set_address = true;
let mut auto_inc = false;
let mut data_was_sent = false;
let max_safe_index = SPI_BUFFER_SIZE.saturating_sub(8);
for pos in 0..screen_size {
let mut char_to_write = active_buffer[pos];
let shadow_char = self.shadow_buffer[pos];
if char_to_write != shadow_char {
if char_to_write == 0xFF {
char_to_write = b' ';
active_buffer[pos] = b' ';
}
self.shadow_buffer[pos] = char_to_write;
if spi_buffer_index >= max_safe_index {
Self::flush_dma_buffer(
&mut self.spi_device,
&mut self.spi_buffer,
self.display_memory_mode_reg,
spi_buffer_index,
auto_inc,
set_address,
)
.await?;
spi_buffer_index = 0;
auto_inc = false;
set_address = true;
data_was_sent = true;
}
if set_address || !auto_inc {
self.spi_buffer[spi_buffer_index] = Self::MAX_7456ADD_DMM;
let next_pos = pos + 1;
if next_pos < screen_size && active_buffer[next_pos] != self.shadow_buffer[next_pos] {
self.spi_buffer[spi_buffer_index + 1] = self.display_memory_mode_reg | Self::DMM_AUTO_INC;
auto_inc = true;
} else {
self.spi_buffer[spi_buffer_index + 1] = self.display_memory_mode_reg;
auto_inc = false;
}
spi_buffer_index += 2;
#[allow(clippy::cast_possible_truncation)]
let address = (pos as u16).to_be_bytes();
self.spi_buffer[spi_buffer_index] = Self::MAX_7456ADD_DMAH;
self.spi_buffer[spi_buffer_index + 1] = address[0];
self.spi_buffer[spi_buffer_index + 2] = Self::MAX_7456ADD_DMAL;
self.spi_buffer[spi_buffer_index + 3] = address[1];
spi_buffer_index += 4;
set_address = false;
}
self.spi_buffer[spi_buffer_index] = Self::MAX_7456ADD_DMDI;
self.spi_buffer[spi_buffer_index + 1] = char_to_write;
spi_buffer_index += 2;
} else if !set_address {
set_address = true;
if auto_inc {
self.spi_buffer[spi_buffer_index] = Self::MAX_7456ADD_DMDI;
self.spi_buffer[spi_buffer_index + 1] = Self::END_STRING;
spi_buffer_index += 2;
}
}
}
if auto_inc {
if !set_address {
self.spi_buffer[spi_buffer_index] = Self::MAX_7456ADD_DMDI;
self.spi_buffer[spi_buffer_index + 1] = Self::END_STRING;
spi_buffer_index += 2;
}
self.spi_buffer[spi_buffer_index] = Self::MAX_7456ADD_DMM;
self.spi_buffer[spi_buffer_index + 1] = self.display_memory_mode_reg;
spi_buffer_index += 2;
}
if spi_buffer_index > 0 {
let tx_slice = &self.spi_buffer[..spi_buffer_index];
self.spi_device.write(tx_slice).await?;
data_was_sent = true;
}
Ok(data_was_sent)
}
async fn flush_dma_buffer(
spi_device: &mut SPI,
spi_buffer: &mut [u8; SPI_BUFFER_SIZE],
display_memory_mode_reg: u8,
spi_buffer_index: usize,
auto_inc: bool,
set_address: bool,
) -> Result<(), SPI::Error> {
let mut spi_buf_idx = spi_buffer_index;
if auto_inc {
if !set_address {
spi_buffer[spi_buf_idx] = Self::MAX_7456ADD_DMDI;
spi_buffer[spi_buf_idx + 1] = Self::END_STRING;
spi_buf_idx += 2;
}
spi_buffer[spi_buf_idx] = Self::MAX_7456ADD_DMM;
spi_buffer[spi_buf_idx + 1] = display_memory_mode_reg;
spi_buf_idx += 2;
}
let tx_slice = &spi_buffer[..spi_buf_idx];
spi_device.write(tx_slice).await?;
Ok(())
}
}
impl<SPI: SpiBus> Display for DisplayPortMax7456<SPI> {
fn display_port(&self) -> &DisplayPort {
&self.display_layers.display_port
}
fn display_port_mut(&mut self) -> &mut DisplayPort {
&mut self.display_layers.display_port
}
fn heartbeat(&mut self) -> i32 {
0
}
fn write_byte(&mut self, x: u8, y: u8, byte: u8, attr: DisplayPortSeverity) -> usize {
self.display_layers.write_byte(x, y, byte, attr)
}
fn write_slice(&mut self, x: u8, y: u8, slice: &[u8], attr: DisplayPortSeverity) -> usize {
self.display_layers.write_slice(x, y, slice, attr)
}
fn layer_supported(&self, _layer: DisplayPortLayer) -> bool {
true
}
fn layer_select(&mut self, layer: DisplayPortLayer) {
self.display_port_mut().set_active_layer(layer);
}
fn layer_copy(&mut self, src: DisplayPortLayer, dst: DisplayPortLayer) {
self.display_layers.layer_copy(src, dst);
}
fn begin_transaction(&mut self, option: u8) {
if option == DisplayPort::DISPLAY_TRANSACTION_OPTION_RESET_DRAWING {
self.display_layers.clear_layer(DisplayPortLayer::Background);
self.display_layers.clear_layer(DisplayPortLayer::Foreground);
}
}
fn commit_transaction(&mut self) {}
fn is_transfer_in_progress(&self) -> bool {
false
}
fn check_ready(&self, _val: bool) -> bool {
true
}
async fn clear_screen(&mut self) {
}
async fn transfer_screen(&mut self) -> Result<bool, &'static str> {
match Self::draw_screen(self).await {
Ok(transferring) => Ok(transferring),
Err(_hardware_error) => Err("SPI Bus transfer failed"),
}
}
fn redraw(&self) {}
}