#![no_std]
extern crate bitvec;
extern crate embedded_graphics;
extern crate embedded_hal as hal;
use bitvec::prelude::*;
use core::ops::{BitOr, Not};
use embedded_graphics::draw_target::DrawTarget;
use embedded_graphics::pixelcolor::BinaryColor;
use embedded_graphics::prelude::{OriginDimensions, Size};
use embedded_graphics::Pixel;
use hal::blocking::spi::Write;
use hal::digital::v2::OutputPin;
use hal::spi::Mode;
#[cfg(not(any(
feature = "ls027b7dh01",
feature = "ls012b7dd06",
feature = "ls010b7dh04",
feature = "ls013b7dh05",
feature = "ls011b7dh03",
)))]
compile_error!("Please specify a display type via the feature flag");
#[cfg_attr(feature = "ls027b7dh01", path = "ls027b7dh01.rs")]
#[cfg_attr(feature = "ls012b7dd06", path = "ls012b7dd06.rs")]
#[cfg_attr(feature = "ls010b7dh04", path = "ls010b7dh04.rs")]
#[cfg_attr(feature = "ls013b7dh05", path = "ls013b7dh05.rs")]
#[cfg_attr(feature = "ls011b7dh03", path = "ls011b7dh03.rs")]
mod display;
const DUMMY_DATA: u8 = 0x00;
#[derive(Clone, Copy, PartialEq, Eq)]
enum Vcom {
Lo = 0x00, Hi = 0x40, }
impl Not for Vcom {
type Output = Self;
fn not(self) -> Self::Output {
match self {
Vcom::Lo => Vcom::Hi,
Vcom::Hi => Vcom::Lo,
}
}
}
impl BitOr<Command> for Vcom {
type Output = u8;
fn bitor(self, rhs: Command) -> Self::Output {
(self as u8) | (rhs as u8)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum Command {
Nop = 0x00, ClearMemory = 0x20, WriteLine = 0x80, }
impl BitOr<Vcom> for Command {
type Output = u8;
fn bitor(self, rhs: Vcom) -> Self::Output {
(self as u8) | (rhs as u8)
}
}
pub const MODE: Mode = display::MODE;
const WRITE_BUFFER_SIZE: usize = (display::WIDTH / 8) + 2;
pub struct MemoryDisplay<SPI, CS, DISP> {
spi: SPI,
cs: CS,
disp: DISP,
buffer: [BitArr!(for display::WIDTH, in u8, Lsb0); display::HEIGHT],
touched: BitArr!(for display::HEIGHT, in u8, Lsb0),
vcom: Vcom,
clear_state: BinaryColor,
}
impl<SPI, CS, DISP> OriginDimensions for MemoryDisplay<SPI, CS, DISP> {
fn size(&self) -> Size {
Size::new(display::WIDTH as u32, display::HEIGHT as u32)
}
}
impl<SPI, CS, DISP, E> DrawTarget for MemoryDisplay<SPI, CS, DISP>
where
SPI: Write<u8, Error = E>,
CS: OutputPin,
DISP: OutputPin,
{
type Color = BinaryColor;
type Error = E;
fn draw_iter<T>(&mut self, item_pixels: T) -> Result<(), E>
where
T: IntoIterator<Item = Pixel<Self::Color>>,
{
for Pixel(coord, color) in item_pixels {
if coord.x < 0 || coord.x >= (display::WIDTH as i32) || coord.y < 0 || coord.y >= (display::HEIGHT as i32) {
continue
} else {
unsafe { self.set_pixel(coord.x as u32, coord.y as u32, color) };
}
}
Ok(())
}
}
impl<SPI, CS, DISP, E> MemoryDisplay<SPI, CS, DISP>
where
SPI: Write<u8, Error = E>,
CS: OutputPin,
DISP: OutputPin,
{
pub fn new(spi: SPI, mut cs: CS, mut disp: DISP) -> Self {
let _ = disp.set_low();
let _ = cs.set_low();
let buffer = [bitarr![u8, Lsb0; 0; display::WIDTH]; display::HEIGHT];
let touched = bitarr![u8, Lsb0; 0; display::HEIGHT];
Self {
spi,
cs,
disp,
buffer,
touched,
vcom: Vcom::Hi,
clear_state: BinaryColor::On,
}
}
pub fn set_clear_state(&mut self, clear_state: BinaryColor) {
self.clear_state = clear_state;
}
pub fn enable(&mut self) {
let _ = self.disp.set_high();
}
pub fn disable(&mut self) {
let _ = self.disp.set_low();
}
pub unsafe fn set_pixel(&mut self, x: u32, y: u32, val: BinaryColor) {
let line_buffer = &mut self.buffer[y as usize];
line_buffer.set(x as usize, val.is_on());
self.touched.set(y as usize, true);
}
pub fn flush_buffer(&mut self) {
let _ = self.cs.set_high();
self.vcom = !self.vcom;
let _ = self.spi.write(&[Command::WriteLine | self.vcom]);
for y in self.touched.iter_ones() {
if y >= self.buffer.len() {
break;
}
let line_no = (y + 1) as u8;
defmt::trace!("Writing line {}", line_no);
let line_no_bits_msb = BitSlice::<u8, Lsb0>::from_element(&line_no);
let line_no_bits = Self::swap(line_no_bits_msb);
let line_buffer_msb = self.buffer[y as usize];
let mut write_buffer = [0u8; WRITE_BUFFER_SIZE];
write_buffer[0] = line_no_bits;
let mut chunks = line_buffer_msb.chunks(8);
(1..(write_buffer.len() - 1)).for_each(|x| {
write_buffer[x] = Self::swap(chunks.next().unwrap());
});
write_buffer[write_buffer.len() - 1] = DUMMY_DATA;
let _ = self.spi.write(&write_buffer);
}
let _ = self.spi.write(&[DUMMY_DATA]);
let _ = self.cs.set_low();
self.touched.fill(false);
}
pub fn swap(byte: &BitSlice<u8, Lsb0>) -> u8 {
let mut local_buffer = bitarr!(u8, Msb0; 0; 8);
for (i, bit) in byte.iter().by_ref().enumerate() {
local_buffer.set(i, *bit);
}
local_buffer.load::<u8>()
}
pub fn clear_buffer(&mut self) {
for y in 0..(self.size().height as usize) {
let line_buffer = &mut self.buffer[y];
line_buffer.fill(self.clear_state.is_on());
}
self.touched.fill(true);
}
pub fn clear(&mut self) {
self.clear_buffer();
self.vcom = !self.vcom;
self.write_spi(&[Command::ClearMemory | self.vcom, DUMMY_DATA]);
}
pub fn display_mode(&mut self) {
self.vcom = !self.vcom;
self.write_spi(&[Command::Nop | self.vcom, DUMMY_DATA]);
}
fn write_spi(&mut self, data: &[u8]) {
let _ = self.cs.set_high();
let _ = self.spi.write(data);
let _ = self.cs.set_low();
}
}