1#![no_std]
26extern crate bitvec;
27extern crate embedded_graphics;
28extern crate embedded_hal as hal;
29
30use bitvec::prelude::*;
31use core::ops::{BitOr, Not};
32use embedded_graphics::draw_target::DrawTarget;
33use embedded_graphics::pixelcolor::BinaryColor;
34use embedded_graphics::prelude::{OriginDimensions, Size};
35use embedded_graphics::Pixel;
36use hal::blocking::spi::Write;
37use hal::digital::v2::OutputPin;
38use hal::spi::Mode;
39
40#[cfg(not(any(
41 feature = "ls027b7dh01",
42 feature = "ls012b7dd06",
43 feature = "ls010b7dh04",
44 feature = "ls013b7dh05",
45 feature = "ls011b7dh03",
46)))]
47compile_error!("Please specify a display type via the feature flag");
48
49#[cfg_attr(feature = "ls027b7dh01", path = "ls027b7dh01.rs")]
51#[cfg_attr(feature = "ls012b7dd06", path = "ls012b7dd06.rs")]
52#[cfg_attr(feature = "ls010b7dh04", path = "ls010b7dh04.rs")]
53#[cfg_attr(feature = "ls013b7dh05", path = "ls013b7dh05.rs")]
54#[cfg_attr(feature = "ls011b7dh03", path = "ls011b7dh03.rs")]
55mod display;
56
57const DUMMY_DATA: u8 = 0x00; #[derive(Clone, Copy, PartialEq, Eq)]
60enum Vcom {
61 Lo = 0x00, Hi = 0x40, }
65
66impl Not for Vcom {
67 type Output = Self;
68
69 fn not(self) -> Self::Output {
70 match self {
71 Vcom::Lo => Vcom::Hi,
72 Vcom::Hi => Vcom::Lo,
73 }
74 }
75}
76
77impl BitOr<Command> for Vcom {
78 type Output = u8;
79
80 fn bitor(self, rhs: Command) -> Self::Output {
81 (self as u8) | (rhs as u8)
82 }
83}
84
85#[derive(Clone, Copy, PartialEq, Eq)]
86enum Command {
87 Nop = 0x00, ClearMemory = 0x20, WriteLine = 0x80, }
92
93impl BitOr<Vcom> for Command {
94 type Output = u8;
95
96 fn bitor(self, rhs: Vcom) -> Self::Output {
97 (self as u8) | (rhs as u8)
98 }
99}
100
101pub const MODE: Mode = display::MODE;
103
104const WRITE_BUFFER_SIZE: usize = (display::WIDTH / 8) + 2;
106
107pub struct MemoryDisplay<SPI, CS, DISP> {
108 spi: SPI,
109 cs: CS,
110 disp: DISP,
111 buffer: [BitArr!(for display::WIDTH, in u8, Lsb0); display::HEIGHT],
112 touched: BitArr!(for display::HEIGHT, in u8, Lsb0),
113 vcom: Vcom,
114 clear_state: BinaryColor,
115}
116
117impl<SPI, CS, DISP> OriginDimensions for MemoryDisplay<SPI, CS, DISP> {
118 fn size(&self) -> Size {
119 Size::new(display::WIDTH as u32, display::HEIGHT as u32)
120 }
121}
122
123impl<SPI, CS, DISP, E> DrawTarget for MemoryDisplay<SPI, CS, DISP>
124where
125 SPI: Write<u8, Error = E>,
126 CS: OutputPin,
127 DISP: OutputPin,
128{
129 type Color = BinaryColor;
130 type Error = E;
131
132 fn draw_iter<T>(&mut self, item_pixels: T) -> Result<(), E>
133 where
134 T: IntoIterator<Item = Pixel<Self::Color>>,
135 {
136 for Pixel(coord, color) in item_pixels {
137 if coord.x < 0 || coord.x >= (display::WIDTH as i32) || coord.y < 0 || coord.y >= (display::HEIGHT as i32) {
138 continue
140 } else {
141 unsafe { self.set_pixel(coord.x as u32, coord.y as u32, color) };
142 }
143 }
144 Ok(())
145 }
146}
147
148impl<SPI, CS, DISP, E> MemoryDisplay<SPI, CS, DISP>
149where
150 SPI: Write<u8, Error = E>,
151 CS: OutputPin,
152 DISP: OutputPin,
153{
154 pub fn new(spi: SPI, mut cs: CS, mut disp: DISP) -> Self {
158 let _ = disp.set_low();
159 let _ = cs.set_low();
160
161 let buffer = [bitarr![u8, Lsb0; 0; display::WIDTH]; display::HEIGHT];
163 let touched = bitarr![u8, Lsb0; 0; display::HEIGHT];
164
165 Self {
166 spi,
167 cs,
168 disp,
169 buffer,
170 touched,
171 vcom: Vcom::Hi,
172 clear_state: BinaryColor::On,
173 }
174 }
175
176 pub fn set_clear_state(&mut self, clear_state: BinaryColor) {
181 self.clear_state = clear_state;
182 }
183
184 pub fn enable(&mut self) {
186 let _ = self.disp.set_high();
187 }
188
189 pub fn disable(&mut self) {
191 let _ = self.disp.set_low();
192 }
193
194 pub unsafe fn set_pixel(&mut self, x: u32, y: u32, val: BinaryColor) {
200 let line_buffer = &mut self.buffer[y as usize];
201 line_buffer.set(x as usize, val.is_on());
202 self.touched.set(y as usize, true);
203 }
204
205 pub fn flush_buffer(&mut self) {
208 let _ = self.cs.set_high();
209
210 self.vcom = !self.vcom;
211 let _ = self.spi.write(&[Command::WriteLine | self.vcom]);
212
213 for y in self.touched.iter_ones() {
215 if y >= self.buffer.len() {
219 break;
220 }
221 let line_no = (y + 1) as u8;
223 defmt::trace!("Writing line {}", line_no);
224 let line_no_bits_msb = BitSlice::<u8, Lsb0>::from_element(&line_no);
225 let line_no_bits = Self::swap(line_no_bits_msb);
226
227 let line_buffer_msb = self.buffer[y as usize];
228
229 let mut write_buffer = [0u8; WRITE_BUFFER_SIZE];
230 write_buffer[0] = line_no_bits;
231
232 let mut chunks = line_buffer_msb.chunks(8);
233 (1..(write_buffer.len() - 1)).for_each(|x| {
234 write_buffer[x] = Self::swap(chunks.next().unwrap());
235 });
236 write_buffer[write_buffer.len() - 1] = DUMMY_DATA;
238 let _ = self.spi.write(&write_buffer);
239 }
240
241 let _ = self.spi.write(&[DUMMY_DATA]);
243
244 let _ = self.cs.set_low();
245
246 self.touched.fill(false);
247 }
248
249 pub fn swap(byte: &BitSlice<u8, Lsb0>) -> u8 {
252 let mut local_buffer = bitarr!(u8, Msb0; 0; 8);
254 for (i, bit) in byte.iter().by_ref().enumerate() {
255 local_buffer.set(i, *bit);
256 }
257 local_buffer.load::<u8>()
258 }
259
260 pub fn clear_buffer(&mut self) {
262 for y in 0..(self.size().height as usize) {
263 let line_buffer = &mut self.buffer[y];
264 line_buffer.fill(self.clear_state.is_on());
265 }
266 self.touched.fill(true);
267 }
268
269 pub fn clear(&mut self) {
271 self.clear_buffer();
272 self.vcom = !self.vcom;
273 self.write_spi(&[Command::ClearMemory | self.vcom, DUMMY_DATA]);
274 }
275
276 pub fn display_mode(&mut self) {
280 self.vcom = !self.vcom;
281 self.write_spi(&[Command::Nop | self.vcom, DUMMY_DATA]);
282 }
283
284 fn write_spi(&mut self, data: &[u8]) {
286 let _ = self.cs.set_high();
287
288 let _ = self.spi.write(data);
289
290 let _ = self.cs.set_low();
291 }
292}