memory_lcd_spi/
lib.rs

1//! Memory LCD display driver in SPI mode
2
3#![no_std]
4#![allow(incomplete_features)]
5#![feature(generic_const_exprs)]
6
7use core::ops::{Deref, DerefMut};
8
9use crate::error::Error;
10use embedded_hal::{delay::DelayNs, digital::OutputPin, spi::SpiBus};
11use framebuffer::FramebufferType;
12
13pub mod displays;
14pub mod error;
15pub mod framebuffer;
16pub mod pixelcolor;
17
18/// Specification for displays.
19pub trait DisplaySpec {
20    const WIDTH: u16;
21    const HEIGHT: u16;
22
23    type Framebuffer: FramebufferType;
24}
25
26// NOTE: The commands are actually 6 bit, but the MSB of next 10 bit is always 0,
27// So it's a trick to use 8 bit to represent the command.
28pub const CMD_NO_UPDATE: u8 = 0x00;
29pub const CMD_BLINKING_BLACK: u8 = 0x10;
30pub const CMD_BLINKING_INVERSION: u8 = 0x14;
31pub const CMD_BLINKING_WHITE: u8 = 0x18;
32pub const CMD_ALL_CLEAR: u8 = 0x20;
33pub const CMD_VCOM: u8 = 0x40;
34// 0x90 = 0b100100_00
35// Use 4 bit data input
36pub const CMD_UPDATE_4BIT: u8 = 0x90;
37// Also apply to Sharp's update mode: 0b100_xxxxx
38pub const CMD_UPDATE_1BIT: u8 = 0x88;
39
40pub struct MemoryLCD<SPEC: DisplaySpec, SPI, CS> {
41    // cs is active high, so SpiBus is needed.
42    spi: SPI,
43    cs: CS,
44    framebuffer: SPEC::Framebuffer,
45}
46
47impl<SPEC, SPI, CS> MemoryLCD<SPEC, SPI, CS>
48where
49    SPI: SpiBus<u8>,
50    CS: OutputPin,
51    SPEC: DisplaySpec,
52{
53    pub fn new(spi: SPI, cs: CS) -> Self {
54        Self {
55            spi,
56            cs,
57            framebuffer: SPEC::Framebuffer::default(),
58        }
59    }
60
61    pub fn turn_on_display<DISP: OutputPin>(&mut self, disp: &mut DISP) -> Result<(), Error<SPI::Error>> {
62        disp.set_high().map_err(|_| Error::Gpio)?;
63        Ok(())
64    }
65
66    pub fn turn_off_display<DISP: OutputPin>(&mut self, disp: &mut DISP) -> Result<(), Error<SPI::Error>> {
67        disp.set_low().map_err(|_| Error::Gpio)?;
68        Ok(())
69    }
70
71    pub fn init(&mut self) -> Result<(), Error<SPI::Error>> {
72        self.cs.set_high().map_err(|_| Error::Gpio)?;
73        self.spi.write(&[CMD_ALL_CLEAR, 0x00]).map_err(Error::Spi)?;
74        self.cs.set_low().map_err(|_| Error::Gpio)?;
75        Ok(())
76    }
77
78    pub fn update<D: DelayNs>(&mut self, _delay: &mut D) -> Result<(), Error<SPI::Error>> {
79        use crate::framebuffer::sealed::FramebufferSpiUpdate;
80
81        self.cs.set_high().map_err(|_| Error::Gpio)?;
82        self.framebuffer.update(&mut self.spi).map_err(Error::Spi)?;
83        self.cs.set_low().map_err(|_| Error::Gpio)?;
84        Ok(())
85    }
86}
87
88impl<SPEC, SPI, CS> Deref for MemoryLCD<SPEC, SPI, CS>
89where
90    SPEC: DisplaySpec,
91{
92    type Target = SPEC::Framebuffer;
93
94    fn deref(&self) -> &Self::Target {
95        &self.framebuffer
96    }
97}
98
99impl<SPEC, SPI, CS> DerefMut for MemoryLCD<SPEC, SPI, CS>
100where
101    SPEC: DisplaySpec,
102{
103    fn deref_mut(&mut self) -> &mut Self::Target {
104        &mut self.framebuffer
105    }
106}