From 7b1e0b15c606dcc257f00835d48427aebf81fe4c Mon Sep 17 00:00:00 2001 From: Wesley Moore Date: Wed, 14 Nov 2018 18:02:33 +1100 Subject: [PATCH] Add graphics module --- src/color.rs | 44 +++++++++++++++++++++++ src/display.rs | 47 ++++++++++++++++++------ src/graphics.rs | 96 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 6 ++++ 4 files changed, 183 insertions(+), 10 deletions(-) create mode 100644 src/color.rs create mode 100644 src/graphics.rs diff --git a/src/color.rs b/src/color.rs new file mode 100644 index 0000000..6b556be --- /dev/null +++ b/src/color.rs @@ -0,0 +1,44 @@ +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum Color { + Black, + White, + Red, +} + +#[cfg(feature = "graphics")] +extern crate embedded_graphics; +#[cfg(feature = "graphics")] +use self::embedded_graphics::prelude::*; +#[cfg(feature = "graphics")] +impl PixelColor for Color {} + +impl From for Color { + fn from(value: u8) -> Self { + match value { + 0 => Color::Black, + 1 => Color::White, + 2 => Color::Red, + _ => panic!("invalid color value") + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn from_u8() { + assert_eq!(Color::Black, Color::from(0u8)); + assert_eq!(Color::White, Color::from(1u8)); + } + + #[test] + fn from_u8_panic() { + for val in 3..=u8::max_value() { + extern crate std; + let result = std::panic::catch_unwind(|| Color::from(val)); + assert!(result.is_err()); + } + } +} diff --git a/src/display.rs b/src/display.rs index 56d5749..0236331 100644 --- a/src/display.rs +++ b/src/display.rs @@ -13,28 +13,43 @@ const DIGITAL_BLOCK_CONTROL_MAGIC: u8 = 0x3B; struct Config {} -struct Dimensions { - rows: u16, - cols: u8, +pub struct Dimensions { + pub rows: u16, + pub cols: u8, +} + +#[derive(Clone, Copy)] +pub enum Rotation { + Rotate0, + Rotate90, + Rotate180, + Rotate270, +} + +impl Default for Rotation { + fn default() -> Self { + Rotation::Rotate0 + } } pub struct Display where I: DisplayInterface { interface: I, dimensions: Dimensions, - rotation: u8, + rotation: Rotation, } impl Display where I: DisplayInterface { - fn new(interface: I, dimensions: Dimensions, rotation: u8) -> Self { + pub fn new(interface: I, dimensions: Dimensions, rotation: Rotation) -> Self { Self { interface, dimensions, rotation } } /// Perform a hardware reset followed by software reset - fn reset>(&mut self, delay: &mut D) -> Result<(), I::Error> { + pub fn reset>(&mut self, delay: &mut D) -> Result<(), I::Error> { self.interface.reset(delay); Command::SoftReset.execute(&mut self.interface)?; self.interface.busy_wait(); - Ok(()) + + self.init(Config {}) } /// Initialise the controller according to Section 9: Typical Operating Sequence @@ -65,7 +80,7 @@ impl Display where I: DisplayInterface { Ok(()) } - fn update>(&mut self, black: &[u8], red: &[u8], delay: &mut D) -> Result<(), I::Error> { + pub fn update>(&mut self, black: &[u8], red: &[u8], delay: &mut D) -> Result<(), I::Error> { // Write the B/W RAM Command::XAddress(0).execute(&mut self.interface)?; Command::YAddress(0).execute(&mut self.interface)?; @@ -79,7 +94,7 @@ impl Display where I: DisplayInterface { // Kick off the display update Command::UpdateDisplayOption2(0xC7).execute(&mut self.interface)?; Command::UpdateDisplay.execute(&mut self.interface)?; - delay.delay_ms(5); // Needed? + delay.delay_ms(5); // TODO: We don't really need to wait here... the program can go off and do other things // and only busy wait if it wants to talk to the display again. Could possibly treat // the interface like a smart pointer in which "acquiring" it would wait until it's not @@ -89,7 +104,19 @@ impl Display where I: DisplayInterface { Ok(()) } - fn deep_sleep(&mut self) -> Result<(), I::Error> { + pub fn deep_sleep(&mut self) -> Result<(), I::Error> { Command::DeepSleepMode(DeepSleepMode::PreserveRAM).execute(&mut self.interface) } + + pub fn rows(&self) -> u16 { + self.dimensions.rows + } + + pub fn cols(&self) -> u8 { + self.dimensions.cols + } + + pub fn rotation(&self) -> Rotation { + self.rotation + } } diff --git a/src/graphics.rs b/src/graphics.rs new file mode 100644 index 0000000..a1ddc71 --- /dev/null +++ b/src/graphics.rs @@ -0,0 +1,96 @@ +use color::Color; +use display::{Display, Rotation}; +use interface::DisplayInterface; +use core::ops::{Deref, DerefMut}; + +pub struct GraphicDisplay<'a, I> where I: DisplayInterface { + display: Display, + black_buffer: &'a mut [u8], + red_buffer: &'a mut [u8], +} + +impl<'a, I> GraphicDisplay<'a, I> where I: DisplayInterface { + pub fn new(display: Display, black_buffer: &'a mut [u8], red_buffer: &'a mut [u8]) -> Self { + GraphicDisplay { display, black_buffer, red_buffer } + } + + pub fn update(&mut self) -> Result<(), ()> { + unimplemented!() + } + + fn set_pixel(&mut self, x: u32, y: u32, color: Color) { + // Give us index inside the buffer and the bit-position in that u8 which needs to be changed + let (index, bit) = rotation(x, y, self.cols() as u32, self.rows() as u32, self.rotation()); + let index = index as usize; + + match color { + Color::Black => { + self.black_buffer[index] &= !bit; + self.red_buffer[index] &= !bit; + } + Color::White => { + self.black_buffer[index] |= bit; + self.red_buffer[index] &= !bit; + } + Color::Red => { + self.black_buffer[index] &= !bit; + self.red_buffer[index] |= bit; + } + } + } +} + +impl<'a, I> Deref for GraphicDisplay<'a, I> where I: DisplayInterface { + type Target = Display; + + fn deref(&self) -> &Display { + &self.display + } +} + +impl<'a, I> DerefMut for GraphicDisplay<'a, I> where I: DisplayInterface { + fn deref_mut(&mut self) -> &mut Display { + &mut self.display + } +} + +fn rotation(x: u32, y: u32, width: u32, height: u32, rotation: Rotation) -> (u32, u8) { + match rotation { + Rotation::Rotate0 => ( + x / 8 + (width / 8) * y, + 0x80 >> (x % 8), + ), + Rotation::Rotate90 => ( + (width - 1 - y) / 8 + (width / 8) * x, + 0x01 << (y % 8), + ), + Rotation::Rotate180 => ( + ((width / 8) * height - 1) - (x / 8 + (width / 8) * y), + 0x01 << (x % 8), + ), + Rotation::Rotate270 => ( + y / 8 + (height - 1 - x) * (width / 8), + 0x80 >> (y % 8), + ), + } +} + +#[cfg(feature = "graphics")] +extern crate embedded_graphics; +#[cfg(feature = "graphics")] +use self::embedded_graphics::{drawable::Pixel, Drawing, prelude::UnsignedCoord}; + +#[cfg(feature = "graphics")] +impl<'a, I> Drawing for GraphicDisplay<'a, I> +where + I: DisplayInterface, +{ + fn draw(&mut self, item_pixels: T) + where + T: Iterator>, + { + for Pixel(UnsignedCoord(x, y), colour) in item_pixels { + self.set_pixel(x, y, colour); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index f8eba05..fdab6ff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,8 +5,14 @@ extern crate embedded_hal as hal; mod command; mod interface; mod display; +mod graphics; +mod color; pub use interface::DisplayInterface; +pub use interface::Interface; +pub use display::{Display, Dimensions, Rotation}; +pub use graphics::GraphicDisplay; +pub use color::Color; #[cfg(test)] mod tests {