mirror of
https://github.com/Feliix42/ssd1675.git
synced 2024-11-21 18:36:30 +00:00
Add graphics module
This commit is contained in:
parent
071a014ac1
commit
7b1e0b15c6
4 changed files with 183 additions and 10 deletions
44
src/color.rs
Normal file
44
src/color.rs
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<I> where I: DisplayInterface {
|
||||
interface: I,
|
||||
dimensions: Dimensions,
|
||||
rotation: u8,
|
||||
rotation: Rotation,
|
||||
}
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
/// 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);
|
||||
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<I> Display<I> where I: DisplayInterface {
|
|||
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
|
||||
Command::XAddress(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
|
||||
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<I> Display<I> 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
|
||||
}
|
||||
}
|
||||
|
|
96
src/graphics.rs
Normal file
96
src/graphics.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue