Add graphics module

This commit is contained in:
Wesley Moore 2018-11-14 18:02:33 +11:00
parent 071a014ac1
commit 7b1e0b15c6
No known key found for this signature in database
GPG key ID: BF67766C0BC2D0EE
4 changed files with 183 additions and 10 deletions

44
src/color.rs Normal file
View file

@ -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<u8> 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());
}
}
}

View file

@ -13,28 +13,43 @@ const DIGITAL_BLOCK_CONTROL_MAGIC: u8 = 0x3B;
struct Config {} struct Config {}
struct Dimensions { pub struct Dimensions {
rows: u16, pub rows: u16,
cols: u8, 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<I> where I: DisplayInterface { pub struct Display<I> where I: DisplayInterface {
interface: I, interface: I,
dimensions: Dimensions, dimensions: Dimensions,
rotation: u8, rotation: Rotation,
} }
impl<I> Display<I> where I: DisplayInterface { impl<I> Display<I> 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 } Self { interface, dimensions, rotation }
} }
/// Perform a hardware reset followed by software reset /// Perform a hardware reset followed by software reset
fn reset<D: hal::blocking::delay::DelayMs<u8>>(&mut self, delay: &mut D) -> Result<(), I::Error> { pub fn reset<D: hal::blocking::delay::DelayMs<u8>>(&mut self, delay: &mut D) -> Result<(), I::Error> {
self.interface.reset(delay); self.interface.reset(delay);
Command::SoftReset.execute(&mut self.interface)?; Command::SoftReset.execute(&mut self.interface)?;
self.interface.busy_wait(); self.interface.busy_wait();
Ok(())
self.init(Config {})
} }
/// Initialise the controller according to Section 9: Typical Operating Sequence /// Initialise the controller according to Section 9: Typical Operating Sequence
@ -65,7 +80,7 @@ impl<I> Display<I> where I: DisplayInterface {
Ok(()) Ok(())
} }
fn update<D: hal::blocking::delay::DelayMs<u8>>(&mut self, black: &[u8], red: &[u8], delay: &mut D) -> Result<(), I::Error> { pub fn update<D: hal::blocking::delay::DelayMs<u8>>(&mut self, black: &[u8], red: &[u8], delay: &mut D) -> Result<(), I::Error> {
// Write the B/W RAM // Write the B/W RAM
Command::XAddress(0).execute(&mut self.interface)?; Command::XAddress(0).execute(&mut self.interface)?;
Command::YAddress(0).execute(&mut self.interface)?; Command::YAddress(0).execute(&mut self.interface)?;
@ -79,7 +94,7 @@ impl<I> Display<I> where I: DisplayInterface {
// Kick off the display update // Kick off the display update
Command::UpdateDisplayOption2(0xC7).execute(&mut self.interface)?; Command::UpdateDisplayOption2(0xC7).execute(&mut self.interface)?;
Command::UpdateDisplay.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 // 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 // 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 // the interface like a smart pointer in which "acquiring" it would wait until it's not
@ -89,7 +104,19 @@ impl<I> Display<I> where I: DisplayInterface {
Ok(()) 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) 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
}
} }

96
src/graphics.rs Normal file
View file

@ -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<I>,
black_buffer: &'a mut [u8],
red_buffer: &'a mut [u8],
}
impl<'a, I> GraphicDisplay<'a, I> where I: DisplayInterface {
pub fn new(display: Display<I>, 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<I>;
fn deref(&self) -> &Display<I> {
&self.display
}
}
impl<'a, I> DerefMut for GraphicDisplay<'a, I> where I: DisplayInterface {
fn deref_mut(&mut self) -> &mut Display<I> {
&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<Color> for GraphicDisplay<'a, I>
where
I: DisplayInterface,
{
fn draw<T>(&mut self, item_pixels: T)
where
T: Iterator<Item = Pixel<Color>>,
{
for Pixel(UnsignedCoord(x, y), colour) in item_pixels {
self.set_pixel(x, y, colour);
}
}
}

View file

@ -5,8 +5,14 @@ extern crate embedded_hal as hal;
mod command; mod command;
mod interface; mod interface;
mod display; mod display;
mod graphics;
mod color;
pub use interface::DisplayInterface; pub use interface::DisplayInterface;
pub use interface::Interface;
pub use display::{Display, Dimensions, Rotation};
pub use graphics::GraphicDisplay;
pub use color::Color;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {