1#![no_implicit_prelude]
21
22extern crate core;
23extern crate rpsp;
24
25use core::convert::{From, Into};
26use core::fmt::{self, Debug, Formatter};
27use core::iter::IntoIterator;
28use core::marker::{Send, Sized};
29use core::ops::{Deref, DerefMut, FnOnce};
30use core::option::Option::{self, None, Some};
31use core::result::Result::{self, Err, Ok};
32
33use rpsp::Pico;
34use rpsp::pin::{Pin, PinID};
35use rpsp::spi::{Spi, SpiBus, SpiError};
36
37use self::tga::Pixel;
38
39mod color;
40mod display;
41mod shift;
42pub mod tga;
43
44#[cfg_attr(rustfmt, rustfmt_skip)]
45pub use self::color::*;
46pub use self::display::*;
47pub use self::shift::*;
48
49const DEFAULT_CLEAR: u8 = 0x11u8;
50
51pub enum InkyError {
52 Spi(SpiError),
53 NoMemory,
54 InvalidBusyPins,
55}
56#[repr(u8)]
57pub enum InkyRotation {
58 Rotate0 = 0u8,
60 Rotate90 = 1u8,
62 Rotate180 = 2u8,
64 Rotate270 = 3u8,
66}
67
68pub struct InkyPins {
69 pub tx: PinID,
70 pub sck: PinID,
71 pub cs: PinID,
72 pub rst: PinID,
73 pub data: PinID,
74 pub busy_pin: Option<PinID>,
75 pub sr_data: Option<PinID>,
76 pub sr_clock: Option<PinID>,
77 pub sr_latch: Option<PinID>,
78}
79#[repr(transparent)]
80pub struct Bytes<const N: usize>([u8; N]);
81pub struct Inky<'a, const B: usize, const W: u16, const H: u16, M: InkyMemory<B> = Bytes<B>> {
82 dis: Display<'a, W, H>,
83 buf: M,
84 rot: InkyRotation,
85}
86
87pub trait InkyMemory<const N: usize>: Sized + Deref<Target = [u8]> + DerefMut {
88 fn new() -> Option<Self>;
89}
90
91pub type Inky4<'a> = Inky<'a, 128_000, 640u16, 400u16>;
92pub type Inky5<'a> = Inky<'a, 134_400, 600u16, 448u16>;
93#[cfg(any(feature = "static", feature = "static_large"))]
97pub type Inky4Static<'a> = Inky<'a, 128_000, 640u16, 400u16, heaped::Static<128_000>>;
104#[cfg(feature = "static_large")]
105pub type Inky5Static<'a> = Inky<'a, 134_400, 600u16, 448u16, heaped::Static<134_400>>;
110
111impl InkyPins {
112 #[inline(always)]
113 pub const fn inky_frame4() -> InkyPins {
114 InkyPins {
115 tx: PinID::Pin19,
116 sck: PinID::Pin18,
117 cs: PinID::Pin17,
118 rst: PinID::Pin27,
119 data: PinID::Pin28,
120 busy_pin: None,
121 sr_data: Some(PinID::Pin10),
122 sr_clock: Some(PinID::Pin8),
123 sr_latch: Some(PinID::Pin9),
124 }
125 }
126
127 #[inline]
128 pub const fn reset(mut self, p: PinID) -> InkyPins {
129 self.rst = p;
130 self
131 }
132 #[inline]
133 pub const fn spi_tx(mut self, p: PinID) -> InkyPins {
134 self.tx = p;
135 self
136 }
137 #[inline]
138 pub const fn spi_cs(mut self, p: PinID) -> InkyPins {
139 self.cs = p;
140 self
141 }
142 #[inline]
143 pub const fn spi_sck(mut self, p: PinID) -> InkyPins {
144 self.sck = p;
145 self
146 }
147 #[inline]
148 pub const fn busy_pin(mut self, p: PinID) -> InkyPins {
149 self.busy_pin = Some(p);
150 self
151 }
152 #[inline]
153 pub const fn shift_register(mut self, clock: PinID, latch: PinID, data: PinID) -> InkyPins {
154 self.sr_data = Some(data);
155 self.sr_clock = Some(clock);
156 self.sr_latch = Some(latch);
157 self
158 }
159
160 #[inline]
161 fn signal(self, p: &Pico) -> Result<BusySignal, InkyError> {
162 if let Some(v) = self.busy_pin {
163 return Ok(BusySignal::Pin(Pin::get(p, v).into_input()));
164 }
165 match (self.sr_clock, self.sr_latch, self.sr_data) {
166 (Some(c), Some(l), Some(d)) => Ok(BusySignal::SR(ShiftRegister::new(p, c, l, d))),
167 _ => Err(InkyError::InvalidBusyPins),
168 }
169 }
170}
171impl<const B: usize, const W: u16, const H: u16, M: InkyMemory<B>> Inky<'_, B, W, H, M> {
172 #[inline]
173 pub fn create(p: &Pico, cfg: InkyPins) -> Result<Inky<B, W, H, M>, InkyError> {
174 Ok(Inky {
175 dis: Display::create(
176 p,
177 cfg.tx,
178 cfg.sck,
179 cfg.cs,
180 cfg.rst,
181 cfg.data,
182 cfg.signal(p)?,
183 )
184 .map_err(InkyError::Spi)?,
185 buf: M::new().ok_or(InkyError::NoMemory)?,
186 rot: InkyRotation::Rotate0,
187 })
188 }
189 #[inline]
190 pub fn new<'a>(p: &Pico, spi: impl Into<SpiBus<'a>>, cfg: InkyPins) -> Result<Inky<'a, B, W, H, M>, InkyError> {
191 Ok(Inky {
192 dis: Display::new(p, spi.into(), cfg.cs, cfg.rst, cfg.data, cfg.signal(p)?),
193 buf: M::new().ok_or(InkyError::NoMemory)?,
194 rot: InkyRotation::Rotate0,
195 })
196 }
197
198 #[inline(always)]
199 pub fn off(&mut self) {
200 self.dis.off();
201 }
202 #[inline]
203 pub fn clear(&mut self) {
204 self.buf.fill(DEFAULT_CLEAR);
205 self.dis.update(&self.buf);
206 }
207 #[inline(always)]
208 pub fn update(&mut self) {
209 self.dis.update(&self.buf)
210 }
211 #[inline(always)]
212 pub fn width(&self) -> u16 {
213 self.dis.width()
214 }
215 #[inline(always)]
216 pub fn height(&self) -> u16 {
217 self.dis.height()
218 }
219 #[inline(always)]
220 pub fn is_busy(&self) -> bool {
221 self.dis.is_busy()
222 }
223 #[inline(always)]
224 pub fn is_ready(&self) -> bool {
225 self.dis.is_ready()
226 }
227 #[inline(always)]
228 pub fn set_fill(&mut self, c: Color) {
229 self.buf.fill(c as u8);
230 }
231 #[inline(always)]
232 pub fn spi_bus(&mut self) -> &mut Spi {
233 self.dis.spi_bus()
234 }
235 #[inline(always)]
236 pub fn set_rotation(&mut self, r: InkyRotation) {
237 self.rot = r;
238 }
239 pub fn set_pixel(&mut self, x: u16, y: u16, c: Color) {
240 if !self.in_bounds(x, y) {
241 return;
242 }
243 let (i, v) = self.index(x, y);
244 if let Some(p) = self.buf.get_mut(i) {
245 *p = (*p & if v { 0xF } else { 0xF0 }) | if v { (c as u8) << 4 } else { c as u8 };
246 }
247 }
248 #[inline(always)]
249 pub fn shift_register(&self) -> Option<&ShiftRegister> {
250 self.dis.shift_register()
251 }
252 pub fn set_pixel_raw(&mut self, x: u16, y: u16, c: u32) {
253 if !self.in_bounds(x, y) {
254 return;
255 }
256 let d = dither(x, y, c);
257 let (i, v) = self.index(x, y);
258 if let Some(p) = self.buf.get_mut(i) {
259 *p = (*p & if v { 0xF } else { 0xF0 }) | if v { d << 4 } else { d };
260 }
261 }
262 #[inline(always)]
263 pub fn set_pixel_color(&mut self, x: u16, y: u16, c: RGB) {
264 self.set_pixel_raw(x, y, c.uint());
265 }
266 #[inline(always)]
267 pub fn set_with<E>(&mut self, func: impl FnOnce(&mut Inky<'_, B, W, H, M>) -> Result<(), E>) -> Result<(), E> {
268 func(self)
269 }
270 pub fn set_image<E>(&mut self, x: i32, y: i32, image: impl IntoIterator<Item = Result<Pixel, E>>) -> Result<(), E> {
271 for e in image {
275 let r = e?;
276 if r.is_transparent() {
277 continue;
278 }
279 let (j, k) = (x + r.x, y + r.y);
280 if (j < 0) || (k < 0) {
281 continue;
282 }
283 let (f, g) = (j as u16, k as u16);
284 if !self.in_bounds(f, g) {
286 continue;
287 }
288 let d = dither(f, g, r.color);
289 let (i, v) = self.index(f, g);
290 if let Some(p) = self.buf.get_mut(i) {
291 *p = (*p & if v { 0xF } else { 0xF0 }) | if v { d << 4 } else { d };
292 }
293 }
294 Ok(())
295 }
296
297 #[inline(always)]
301 pub unsafe fn update_async(&mut self) {
302 unsafe { self.dis.update_async(&self.buf) }
303 }
304
305 #[inline(always)]
306 fn in_bounds(&self, x: u16, y: u16) -> bool {
307 match self.rot {
308 InkyRotation::Rotate0 | InkyRotation::Rotate180 if x >= W || y >= H => false,
309 InkyRotation::Rotate90 | InkyRotation::Rotate270 if y >= W || x >= H => false,
310 _ => true,
311 }
312 }
313 #[inline]
314 fn index(&self, x: u16, y: u16) -> (usize, bool) {
315 let (q, w) = match self.rot {
316 InkyRotation::Rotate0 => (x, y),
317 InkyRotation::Rotate90 => (W - 1 - y, x),
318 InkyRotation::Rotate180 => (W - 1 - x, H - 1 - y),
319 InkyRotation::Rotate270 => (y, H - 1 - x),
320 };
321 (q as usize / 2 + (W as usize / 2) * w as usize, q & 0x1 != 0)
322 }
323}
324
325impl<const N: usize> Deref for Bytes<N> {
326 type Target = [u8];
327
328 #[inline(always)]
329 fn deref(&self) -> &[u8] {
330 &self.0
331 }
332}
333impl<const N: usize> DerefMut for Bytes<N> {
334 #[inline(always)]
335 fn deref_mut(&mut self) -> &mut [u8] {
336 &mut self.0
337 }
338}
339impl<const N: usize> InkyMemory<N> for Bytes<N> {
340 #[inline(always)]
341 fn new() -> Option<Bytes<N>> {
342 Some(Bytes([0u8; N]))
343 }
344}
345
346impl From<u8> for InkyRotation {
347 #[inline(always)]
348 fn from(v: u8) -> InkyRotation {
349 match v {
350 1 => InkyRotation::Rotate90,
351 2 => InkyRotation::Rotate180,
352 3 => InkyRotation::Rotate270,
353 _ => InkyRotation::Rotate0,
354 }
355 }
356}
357
358unsafe impl<const B: usize, const W: u16, const H: u16, M: InkyMemory<B>> Send for Inky<'_, B, W, H, M> {}
359
360#[cfg(feature = "debug")]
361impl Debug for InkyError {
362 #[inline]
363 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
364 match self {
365 InkyError::Spi(v) => f.debug_tuple("Spi").field(v).finish(),
366 InkyError::NoMemory => f.write_str("NoMemory"),
367 InkyError::InvalidBusyPins => f.write_str("InvalidBusyPins"),
368 }
369 }
370}
371#[cfg(not(feature = "debug"))]
372impl Debug for InkyError {
373 #[inline(always)]
374 fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result {
375 Ok(())
376 }
377}
378
379#[cfg(any(feature = "static", feature = "static_large"))]
380pub mod heaped {
381 extern crate core;
382 extern crate rpsp;
383
384 use core::cell::UnsafeCell;
385 use core::marker::Sync;
386 use core::mem::forget;
387 use core::ops::{Deref, DerefMut, Drop};
388 use core::option::Option;
389 use core::ptr::NonNull;
390 use core::slice::{from_raw_parts, from_raw_parts_mut};
391
392 use rpsp::locks::Spinlock27;
393
394 use crate::frame::InkyMemory;
395
396 static INSTANCE: Inner = Inner::new();
397
398 pub struct Static<const N: usize>(NonNull<u8>);
401
402 struct Inner(UnsafeCell<[u8; Inner::SIZE]>);
403
404 impl Inner {
405 const SIZE: usize = if cfg!(feature = "static_large") { 0x20D00usize } else { 0x1F400usize };
406
407 #[inline(always)]
408 const fn new() -> Inner {
409 Inner(UnsafeCell::new([0u8; Inner::SIZE]))
410 }
411 }
412
413 impl<const N: usize> Drop for Static<N> {
414 #[inline(always)]
415 fn drop(&mut self) {
416 unsafe { Spinlock27::free() }
417 }
418 }
419 impl<const N: usize> Deref for Static<N> {
420 type Target = [u8];
421
422 #[inline(always)]
423 fn deref(&self) -> &[u8] {
424 unsafe { from_raw_parts(self.0.as_ptr(), N) }
425 }
426 }
427 impl<const N: usize> DerefMut for Static<N> {
428 #[inline(always)]
429 fn deref_mut(&mut self) -> &mut [u8] {
430 unsafe { from_raw_parts_mut(self.0.as_ptr(), N) }
431 }
432 }
433 impl<const N: usize> InkyMemory<N> for Static<N> {
434 #[inline(always)]
435 fn new() -> Option<Static<N>> {
436 Spinlock27::try_claim().map(|v| {
437 let r = Static(unsafe { NonNull::new_unchecked(INSTANCE.0.get() as *mut u8) });
438 forget(v);
439 r
440 })
441 }
442 }
443
444 unsafe impl Sync for Inner {}
445}