mirror of
https://github.com/Feliix42/ssd1675.git
synced 2024-11-25 12:16:29 +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 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
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 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 {
|
||||||
|
|
Loading…
Reference in a new issue