diff --git a/README.md b/README.md index 470d51c..ac44482 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ controller, for use with [embedded-hal]. ## Description This driver is intended to work on embedded platforms using the `embedded-hal` -trait library. It is `no_std` compatible, only uses safe Rust, and does not -require an allocator. It supports the 4-wire SPI interface. +trait library. It is `no_std` compatible, builds on stable Rust, and only uses +safe Rust. It supports the 4-wire SPI interface. ## Tested Devices diff --git a/examples/raspberry_pi_inky_phat.rs b/examples/raspberry_pi_inky_phat.rs index 6e9beb0..09b6520 100644 --- a/examples/raspberry_pi_inky_phat.rs +++ b/examples/raspberry_pi_inky_phat.rs @@ -5,7 +5,7 @@ use linux_embedded_hal::Delay; use linux_embedded_hal::{Pin, Spidev}; extern crate ssd1675; -use ssd1675::{Display, Dimensions, GraphicDisplay, Color, Rotation}; +use ssd1675::{Builder, Color, Dimensions, Display, GraphicDisplay, Rotation}; // Graphics extern crate embedded_graphics; @@ -15,12 +15,12 @@ use embedded_graphics::Drawing; // Font extern crate profont; -use profont::{ProFont9Point, ProFont12Point, ProFont14Point, ProFont24Point}; +use profont::{ProFont12Point, ProFont14Point, ProFont24Point, ProFont9Point}; use std::process::Command; -use std::{fs, io}; -use std::time::Duration; use std::thread::sleep; +use std::time::Duration; +use std::{fs, io}; // Activate SPI, GPIO in raspi-config needs to be run with sudo because of some sysfs_gpio // permission problems and follow-up timing problems @@ -29,6 +29,27 @@ use std::thread::sleep; const ROWS: u16 = 212; const COLS: u8 = 104; +#[rustfmt::skip] +const LUT: [u8; 70] = [ + // Phase 0 Phase 1 Phase 2 Phase 3 Phase 4 Phase 5 Phase 6 + // A B C D A B C D A B C D A B C D A B C D A B C D A B C D + 0b01001000, 0b10100000, 0b00010000, 0b00010000, 0b00010011, 0b00000000, 0b00000000, // LUT0 - Black + 0b01001000, 0b10100000, 0b10000000, 0b00000000, 0b00000011, 0b00000000, 0b00000000, // LUTT1 - White + 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, // IGNORE + 0b01001000, 0b10100101, 0b00000000, 0b10111011, 0b00000000, 0b00000000, 0b00000000, // LUT3 - Red + 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, // LUT4 - VCOM + + // Duration | Repeat + // A B C D | + 64, 12, 32, 12, 6, // 0 Flash + 16, 8, 4, 4, 6, // 1 clear + 4, 8, 8, 16, 16, // 2 bring in the black + 2, 2, 2, 64, 32, // 3 time for red + 2, 2, 2, 2, 2, // 4 final black sharpen phase + 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0 // 6 +]; + fn main() -> Result<(), std::io::Error> { // Configure SPI let mut spi = Spidev::open("/dev/spidev0.0").expect("SPI device"); @@ -39,8 +60,8 @@ fn main() -> Result<(), std::io::Error> { .build(); spi.configure(&options).expect("SPI configuration"); - // https://pinout.xyz/pinout/inky_phat# - // Configure Digital I/O Pin to be used as Chip Select for SPI + // https://pinout.xyz/pinout/inky_phat + // Configure Digital I/O Pins let cs = Pin::new(8); // BCM8 cs.export().expect("cs export"); while !cs.is_exported() {} @@ -61,20 +82,33 @@ fn main() -> Result<(), std::io::Error> { let reset = Pin::new(27); // BCM27 reset.export().expect("reset export"); while !reset.is_exported() {} - reset.set_direction(Direction::Out).expect("reset Direction"); + reset + .set_direction(Direction::Out) + .expect("reset Direction"); reset.set_value(1).expect("reset Value set to 1"); println!("Pins configured"); + // Initialise display controller let mut delay = Delay {}; let controller = ssd1675::Interface::new(spi, cs, busy, dc, reset); - let dimensions = Dimensions { rows: ROWS, cols: COLS }; let mut black_buffer = [0u8; ROWS as usize * COLS as usize / 8]; let mut red_buffer = [0u8; ROWS as usize * COLS as usize / 8]; - let display = Display::new(controller, dimensions, Rotation::Rotate270); + let config = Builder::new() + .dimensions(Dimensions { + rows: ROWS, + cols: COLS, + }) + .rotation(Rotation::Rotate270) + .lut(&LUT) + .build() + .expect("invalid configuration"); + let display = Display::new(controller, config); let mut display = GraphicDisplay::new(display, &mut black_buffer, &mut red_buffer); + // Main loop. Displays CPU temperature, uname, and uptime every minute with a red Raspberry Pi + // header. loop { display.reset(&mut delay).expect("error resetting display"); println!("Reset and initialised"); @@ -147,21 +181,29 @@ fn read_cpu_temp() -> Result { } fn read_uptime() -> Option { - Command::new("uptime").arg("-p").output().ok().and_then(|output| { - if output.status.success() { - String::from_utf8(output.stdout).ok() - } else { - None - } - }) + Command::new("uptime") + .arg("-p") + .output() + .ok() + .and_then(|output| { + if output.status.success() { + String::from_utf8(output.stdout).ok() + } else { + None + } + }) } fn read_uname() -> Option { - Command::new("uname").arg("-smr").output().ok().and_then(|output| { - if output.status.success() { - String::from_utf8(output.stdout).ok() - } else { - None - } - }) + Command::new("uname") + .arg("-smr") + .output() + .ok() + .and_then(|output| { + if output.status.success() { + String::from_utf8(output.stdout).ok() + } else { + None + } + }) } diff --git a/src/color.rs b/src/color.rs index 6b556be..70451d1 100644 --- a/src/color.rs +++ b/src/color.rs @@ -18,7 +18,7 @@ impl From for Color { 0 => Color::Black, 1 => Color::White, 2 => Color::Red, - _ => panic!("invalid color value") + _ => panic!("invalid color value"), } } } diff --git a/src/command.rs b/src/command.rs index 1c9c7c4..4e99b2d 100644 --- a/src/command.rs +++ b/src/command.rs @@ -4,7 +4,10 @@ use interface::DisplayInterface; const MAX_GATES: u16 = 296; const MAX_DUMMY_LINE_PERIOD: u8 = 127; -trait Contains where C: Copy + PartialOrd { +trait Contains +where + C: Copy + PartialOrd, +{ fn contains(&self, item: C) -> bool; } @@ -50,6 +53,7 @@ pub enum DeepSleepMode { DiscardRAM, } +#[derive(Clone, Copy)] pub enum Command { /// Set the MUX of gate lines, scanning sequence and direction /// 0: MAX gate lines @@ -172,7 +176,7 @@ fn u16_as_u8(val: u16) -> [u8; 2] { [(val & 0xFF00 >> 8) as u8, (val & 0xFF) as u8] } -/// Populates data buffer (array) and returns a pair (tuple) with command and +/// Populates data buffer (array) and returns a pair (tuple) with command and /// appropriately sized slice into populated buffer. /// E.g. /// @@ -207,21 +211,17 @@ macro_rules! pack { } impl Command { - pub(crate) fn execute(self, interface: &mut I) -> Result<(), I::Error> { + pub(crate) fn execute(&self, interface: &mut I) -> Result<(), I::Error> { use self::Command::*; let mut buf = [0u8; 4]; - let (command, data) = match self { + let (command, data) = match *self { DriverOutputControl(gate_lines, scanning_seq_and_dir) => { let [upper, lower] = u16_as_u8(gate_lines); pack!(buf, 0x01, [lower, upper, scanning_seq_and_dir]) } - GateDrivingVoltage(voltages) => { - pack!(buf, 0x03, [voltages]) - } - SourceDrivingVoltage(vsh1, vsh2, vsl) => { - pack!(buf, 0x04, [vsh1, vsh2, vsl]) - } + GateDrivingVoltage(voltages) => pack!(buf, 0x03, [voltages]), + SourceDrivingVoltage(vsh1, vsh2, vsl) => pack!(buf, 0x04, [vsh1, vsh2, vsl]), BoosterEnable(phase1, phase2, phase3, duration) => { pack!(buf, 0x0C, [phase1, phase2, phase3, duration]) } @@ -253,9 +253,7 @@ impl Command { pack!(buf, 0x11, [axis | mode]) } - SoftReset => { - pack!(buf, 0x12, []) - } + SoftReset => pack!(buf, 0x12, []), // TemperatatSensorSelection(TemperatureSensor) => { // } // WriteTemperatureSensor(u16) => { @@ -264,34 +262,22 @@ impl Command { // } // WriteExternalTemperatureSensor(u8, u8, u8) => { // } - UpdateDisplay => { - pack!(buf, 0x20, []) - } + UpdateDisplay => pack!(buf, 0x20, []), // UpdateDisplayOption1(RamOption, RamOption) => { // } - UpdateDisplayOption2(value) => { - pack!(buf, 0x22, [value]) - } + UpdateDisplayOption2(value) => pack!(buf, 0x22, [value]), // EnterVCOMSensing => { // } // VCOMSenseDuration(u8) => { // } - WriteVCOM(value) => { - pack!(buf, 0x2C, [value]) - } + WriteVCOM(value) => pack!(buf, 0x2C, [value]), DummyLinePeriod(period) => { debug_assert!(Contains::contains(&(0..=MAX_DUMMY_LINE_PERIOD), period)); pack!(buf, 0x3A, [period]) } - GateLineWidth(tgate) => { - pack!(buf, 0x3B, [tgate]) - } - BorderWaveform(border_waveform) => { - pack!(buf, 0x3C, [border_waveform]) - } - StartEndXPosition(start, end) => { - pack!(buf, 0x44, [start, end]) - } + GateLineWidth(tgate) => pack!(buf, 0x3B, [tgate]), + BorderWaveform(border_waveform) => pack!(buf, 0x3C, [border_waveform]), + StartEndXPosition(start, end) => pack!(buf, 0x44, [start, end]), StartEndYPosition(start, end) => { let [start_upper, start_lower] = u16_as_u8(start); let [end_upper, end_lower] = u16_as_u8(end); @@ -301,19 +287,11 @@ impl Command { // } // AutoWriteBlackPattern(u8) => { // } - XAddress(address) => { - pack!(buf, 0x4E, [address]) - } - YAddress(address) => { - pack!(buf, 0x4F, [address]) - } - AnalogBlockControl(value) => { - pack!(buf, 0x74, [value]) - } - DigitalBlockControl(value) => { - pack!(buf, 0x7E, [value]) - } - _ => unimplemented!() + XAddress(address) => pack!(buf, 0x4E, [address]), + YAddress(address) => pack!(buf, 0x4F, [address]), + AnalogBlockControl(value) => pack!(buf, 0x74, [value]), + DigitalBlockControl(value) => pack!(buf, 0x7E, [value]), + _ => unimplemented!(), }; interface.send_command(command)?; @@ -326,19 +304,13 @@ impl Command { } impl<'buf> BufCommand<'buf> { - pub(crate) fn execute(self, interface: &mut I) -> Result<(), I::Error> { + pub(crate) fn execute(&self, interface: &mut I) -> Result<(), I::Error> { use self::BufCommand::*; let (command, data) = match self { - WriteBlackData(buffer) => { - (0x24, buffer) - } - WriteRedData(buffer) => { - (0x26, buffer) - } - WriteLUT(buffer) => { - (0x32, buffer) - } + WriteBlackData(buffer) => (0x24, buffer), + WriteRedData(buffer) => (0x26, buffer), + WriteLUT(buffer) => (0x32, buffer), }; interface.send_command(command)?; @@ -350,13 +322,19 @@ impl<'buf> BufCommand<'buf> { } } -impl Contains for core::ops::Range where C: Copy + PartialOrd { +impl Contains for core::ops::Range +where + C: Copy + PartialOrd, +{ fn contains(&self, item: C) -> bool { item >= self.start && item < self.end } } -impl Contains for core::ops::RangeInclusive where C: Copy + PartialOrd { +impl Contains for core::ops::RangeInclusive +where + C: Copy + PartialOrd, +{ fn contains(&self, item: C) -> bool { item >= *self.start() && item <= *self.end() } diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..0979b94 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,110 @@ +use command::{BufCommand, Command, DataEntryMode, IncrementAxis}; +use display::{Dimensions, Rotation}; + +pub struct Builder<'a> { + dummy_line_period: Command, + gate_line_width: Command, + write_vcom: Command, + write_lut: Option>, + data_entry_mode: Command, + dimensions: Option, + rotation: Rotation, +} + +#[derive(Debug)] +pub struct BuilderError {} + +pub struct Config<'a> { + pub(crate) dummy_line_period: Command, + pub(crate) gate_line_width: Command, + pub(crate) write_vcom: Command, + pub(crate) write_lut: Option>, + pub(crate) data_entry_mode: Command, + pub(crate) dimensions: Dimensions, + pub(crate) rotation: Rotation, +} + +impl<'a> Default for Builder<'a> { + fn default() -> Self { + Builder { + dummy_line_period: Command::DummyLinePeriod(0x07), + gate_line_width: Command::GateLineWidth(0x04), + write_vcom: Command::WriteVCOM(0x3C), + write_lut: None, + data_entry_mode: Command::DataEntryMode( + DataEntryMode::IncrementYIncrementX, + IncrementAxis::Horizontal, + ), + dimensions: None, + rotation: Rotation::default(), + } + } +} + +impl<'a> Builder<'a> { + pub fn new() -> Self { + Self::default() + } + + pub fn dummy_line_period(self, dummy_line_period: u8) -> Self { + Self { + dummy_line_period: Command::DummyLinePeriod(dummy_line_period), + ..self + } + } + + pub fn gate_line_width(self, gate_line_width: u8) -> Self { + Self { + gate_line_width: Command::GateLineWidth(gate_line_width), + ..self + } + } + + pub fn vcom(self, value: u8) -> Self { + Self { + write_vcom: Command::WriteVCOM(value), + ..self + } + } + + pub fn lut(self, lut: &'a [u8]) -> Self { + Self { + write_lut: Some(BufCommand::WriteLUT(lut)), + ..self + } + } + + pub fn data_entry_mode( + self, + data_entry_mode: DataEntryMode, + increment_axis: IncrementAxis, + ) -> Self { + Self { + data_entry_mode: Command::DataEntryMode(data_entry_mode, increment_axis), + ..self + } + } + + pub fn dimensions(self, dimensions: Dimensions) -> Self { + Self { + dimensions: Some(dimensions), + ..self + } + } + + pub fn rotation(self, rotation: Rotation) -> Self { + Self { rotation, ..self } + } + + pub fn build(self) -> Result, BuilderError> { + Ok(Config { + dummy_line_period: self.dummy_line_period, + gate_line_width: self.gate_line_width, + write_vcom: self.write_vcom, + write_lut: self.write_lut, + data_entry_mode: self.data_entry_mode, + dimensions: self.dimensions.ok_or_else(|| BuilderError {})?, + rotation: self.rotation, + }) + } +} diff --git a/src/display.rs b/src/display.rs index 949fd20..0c3eab9 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,6 +1,7 @@ use hal; use command::{BufCommand, Command, DataEntryMode, DeepSleepMode, IncrementAxis}; +use config::Config; use interface::DisplayInterface; // Max display resolution is 160x296 @@ -11,8 +12,6 @@ const MAX_GATE_OUTPUTS: usize = 296; const ANALOG_BLOCK_CONTROL_MAGIC: u8 = 0x54; const DIGITAL_BLOCK_CONTROL_MAGIC: u8 = 0x3B; -struct Config {} - pub struct Dimensions { pub rows: u16, pub cols: u8, @@ -32,57 +31,74 @@ impl Default for Rotation { } } -pub struct Display where I: DisplayInterface { +pub struct Display<'a, I> +where + I: DisplayInterface, +{ interface: I, - dimensions: Dimensions, - rotation: Rotation, + config: Config<'a>, } -impl Display where I: DisplayInterface { - pub fn new(interface: I, dimensions: Dimensions, rotation: Rotation) -> Self { +impl<'a, I> Display<'a, I> +where + I: DisplayInterface, +{ + pub fn new(interface: I, config: Config<'a>) -> Self { // TODO: Assert dimensions are evenly divisible by 8 - Self { interface, dimensions, rotation } + Self { interface, config } } /// Perform a hardware reset followed by software reset - pub 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(); - self.init(Config {}) + self.init() } /// Initialise the controller according to Section 9: Typical Operating Sequence /// from the data sheet - fn init(&mut self, config: Config) -> Result<(), I::Error> { + fn init(&mut self) -> Result<(), I::Error> { Command::AnalogBlockControl(ANALOG_BLOCK_CONTROL_MAGIC).execute(&mut self.interface)?; Command::DigitalBlockControl(DIGITAL_BLOCK_CONTROL_MAGIC).execute(&mut self.interface)?; - Command::DriverOutputControl(self.dimensions.rows, 0x00).execute(&mut self.interface)?; + Command::DriverOutputControl(self.config.dimensions.rows, 0x00) + .execute(&mut self.interface)?; - Command::DummyLinePeriod(0x07).execute(&mut self.interface)?; - Command::GateLineWidth(0x04).execute(&mut self.interface)?; + self.config.dummy_line_period.execute(&mut self.interface)?; + self.config.gate_line_width.execute(&mut self.interface)?; // Command::GateDrivingVoltage(0b10000 | 0b0001); // Command::SourceDrivingVoltage(0x2D, 0xB2, 0x22).execute(&mut self.interface)?; - Command::WriteVCOM(0x3C).execute(&mut self.interface)?; + self.config.write_vcom.execute(&mut self.interface)?; // POR is HiZ. Need pull from config // Command::BorderWaveform(u8).execute(&mut self.interface)?; - BufCommand::WriteLUT(&LUT_RED).execute(&mut self.interface)?; + // BufCommand::WriteLUT(&LUT_RED).execute(&mut self.interface)?; + if let Some(ref write_lut) = self.config.write_lut { + write_lut.execute(&mut self.interface)?; + } - Command::DataEntryMode(DataEntryMode::IncrementYIncrementX, IncrementAxis::Horizontal).execute(&mut self.interface)?; + self.config.data_entry_mode.execute(&mut self.interface)?; - let end = self.dimensions.cols / 8 - 1; + let end = self.config.dimensions.cols / 8 - 1; Command::StartEndXPosition(0, end).execute(&mut self.interface)?; - Command::StartEndYPosition(0, self.dimensions.rows).execute(&mut self.interface)?; + Command::StartEndYPosition(0, self.config.dimensions.rows).execute(&mut self.interface)?; Ok(()) } - pub 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 let buf_limit = ((self.rows() * self.cols() as u16) as f32 / 8.).ceil() as usize; Command::XAddress(0).execute(&mut self.interface)?; @@ -100,7 +116,7 @@ impl Display where I: DisplayInterface { delay.delay_ms(50); // 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 + // the interface like a smart pointer in which deref would wait until it's not // busy. self.interface.busy_wait(); @@ -112,34 +128,14 @@ impl Display where I: DisplayInterface { } pub fn rows(&self) -> u16 { - self.dimensions.rows + self.config.dimensions.rows } pub fn cols(&self) -> u8 { - self.dimensions.cols + self.config.dimensions.cols } pub fn rotation(&self) -> Rotation { - self.rotation + self.config.rotation } } - -const LUT_RED: [u8; 70] = [ - // Phase 0 Phase 1 Phase 2 Phase 3 Phase 4 Phase 5 Phase 6 - // A B C D A B C D A B C D A B C D A B C D A B C D A B C D - 0b01001000, 0b10100000, 0b00010000, 0b00010000, 0b00010011, 0b00000000, 0b00000000, // LUT0 - Black - 0b01001000, 0b10100000, 0b10000000, 0b00000000, 0b00000011, 0b00000000, 0b00000000, // LUTT1 - White - 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, // IGNORE - 0b01001000, 0b10100101, 0b00000000, 0b10111011, 0b00000000, 0b00000000, 0b00000000, // LUT3 - Red - 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, // LUT4 - VCOM - - // Duration | Repeat - // A B C D | - 64, 12, 32, 12, 6, // 0 Flash - 16, 8, 4, 4, 6, // 1 clear - 4, 8, 8, 16, 16, // 2 bring in the black - 2, 2, 2, 64, 32, // 3 time for red - 2, 2, 2, 2, 2, // 4 final black sharpen phase - 0, 0, 0, 0, 0, // 5 - 0, 0, 0, 0, 0 // 6 -]; diff --git a/src/graphics.rs b/src/graphics.rs index 33a878f..482fc94 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -1,22 +1,40 @@ -use hal; use color::Color; -use display::{Display, Rotation}; -use interface::DisplayInterface; use core::ops::{Deref, DerefMut}; +use display::{Display, Rotation}; +use hal; +use interface::DisplayInterface; -pub struct GraphicDisplay<'a, I> where I: DisplayInterface { - display: Display, +pub struct GraphicDisplay<'a, I> +where + I: DisplayInterface, +{ + display: Display<'a, 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, black_buffer: &'a mut [u8], red_buffer: &'a mut [u8]) -> Self { - GraphicDisplay { display, black_buffer, red_buffer } +impl<'a, I> GraphicDisplay<'a, I> +where + I: DisplayInterface, +{ + pub fn new( + display: Display<'a, I>, + black_buffer: &'a mut [u8], + red_buffer: &'a mut [u8], + ) -> Self { + GraphicDisplay { + display, + black_buffer, + red_buffer, + } } - pub fn update>(&mut self, delay: &mut D) -> Result<(), I::Error> { - self.display.update(self.black_buffer, self.red_buffer, delay) + pub fn update>( + &mut self, + delay: &mut D, + ) -> Result<(), I::Error> { + self.display + .update(self.black_buffer, self.red_buffer, delay) } pub fn clear(&mut self, color: Color) { @@ -37,7 +55,13 @@ impl<'a, I> GraphicDisplay<'a, I> where I: DisplayInterface { } fn set_pixel(&mut self, x: u32, y: u32, color: Color) { - let (index, bit) = rotation(x, y, self.cols() as u32, self.rows() as u32, self.rotation()); + let (index, bit) = rotation( + x, + y, + self.cols() as u32, + self.rows() as u32, + self.rotation(), + ); let index = index as usize; match color { @@ -57,38 +81,35 @@ impl<'a, I> GraphicDisplay<'a, I> where I: DisplayInterface { } } -impl<'a, I> Deref for GraphicDisplay<'a, I> where I: DisplayInterface { - type Target = Display; +impl<'a, I> Deref for GraphicDisplay<'a, I> +where + I: DisplayInterface, +{ + type Target = Display<'a, I>; - fn deref(&self) -> &Display { + fn deref(&self) -> &Display<'a, I> { &self.display } } -impl<'a, I> DerefMut for GraphicDisplay<'a, I> where I: DisplayInterface { - fn deref_mut(&mut self) -> &mut Display { +impl<'a, I> DerefMut for GraphicDisplay<'a, I> +where + I: DisplayInterface, +{ + fn deref_mut(&mut self) -> &mut Display<'a, 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::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), - ), + Rotation::Rotate270 => (y / 8 + (height - 1 - x) * (width / 8), 0x80 >> (y % 8)), } } @@ -112,7 +133,7 @@ fn outside_display(x: u32, y: u32, width: u32, height: u32, rotation: Rotation) #[cfg(feature = "graphics")] extern crate embedded_graphics; #[cfg(feature = "graphics")] -use self::embedded_graphics::{drawable::Pixel, Drawing, prelude::UnsignedCoord}; +use self::embedded_graphics::{drawable::Pixel, prelude::UnsignedCoord, Drawing}; #[cfg(feature = "graphics")] impl<'a, I> Drawing for GraphicDisplay<'a, I> @@ -124,7 +145,13 @@ where T: Iterator>, { for Pixel(UnsignedCoord(x, y), colour) in item_pixels { - if outside_display(x, y, self.cols() as u32, self.rows() as u32, self.rotation()) { + if outside_display( + x, + y, + self.cols() as u32, + self.rows() as u32, + self.rotation(), + ) { continue; } @@ -135,13 +162,13 @@ where #[cfg(test)] mod tests { - use super::*; - use ::{Display, DisplayInterface, Dimensions, GraphicDisplay, Color, Rotation}; use self::embedded_graphics::coord::Coord; use self::embedded_graphics::fonts::{Font12x16, Font6x8}; use self::embedded_graphics::prelude::*; use self::embedded_graphics::primitives::{Circle, Line, Rect}; use self::embedded_graphics::Drawing; + use super::*; + use {Color, Dimensions, Display, DisplayInterface, GraphicDisplay, Rotation}; const ROWS: u16 = 3; const COLS: u8 = 8; @@ -191,15 +218,18 @@ mod tests { #[test] fn clear_white() { let interface = MockInterface::new(); - let dimensions = Dimensions { rows: ROWS, cols: COLS }; + let dimensions = Dimensions { + rows: ROWS, + cols: COLS, + }; let mut black_buffer = [0u8; BUFFER_SIZE]; let mut red_buffer = [0u8; BUFFER_SIZE]; { - let display = Display::new(interface, dimensions, Rotation::Rotate270); - let mut display = GraphicDisplay::new(display, &mut black_buffer, &mut red_buffer); + let display = Display::new(interface, dimensions, Rotation::Rotate270); + let mut display = GraphicDisplay::new(display, &mut black_buffer, &mut red_buffer); - display.clear(Color::White); + display.clear(Color::White); } assert_eq!(black_buffer, [0xFF, 0xFF, 0xFF]); @@ -209,15 +239,18 @@ mod tests { #[test] fn clear_black() { let interface = MockInterface::new(); - let dimensions = Dimensions { rows: ROWS, cols: COLS }; + let dimensions = Dimensions { + rows: ROWS, + cols: COLS, + }; let mut black_buffer = [0u8; BUFFER_SIZE]; let mut red_buffer = [0u8; BUFFER_SIZE]; { - let display = Display::new(interface, dimensions, Rotation::Rotate270); - let mut display = GraphicDisplay::new(display, &mut black_buffer, &mut red_buffer); + let display = Display::new(interface, dimensions, Rotation::Rotate270); + let mut display = GraphicDisplay::new(display, &mut black_buffer, &mut red_buffer); - display.clear(Color::Black); + display.clear(Color::Black); } assert_eq!(black_buffer, [0x00, 0x00, 0x00]); @@ -227,15 +260,18 @@ mod tests { #[test] fn clear_red() { let interface = MockInterface::new(); - let dimensions = Dimensions { rows: ROWS, cols: COLS }; + let dimensions = Dimensions { + rows: ROWS, + cols: COLS, + }; let mut black_buffer = [0u8; BUFFER_SIZE]; let mut red_buffer = [0u8; BUFFER_SIZE]; { - let display = Display::new(interface, dimensions, Rotation::Rotate270); - let mut display = GraphicDisplay::new(display, &mut black_buffer, &mut red_buffer); + let display = Display::new(interface, dimensions, Rotation::Rotate270); + let mut display = GraphicDisplay::new(display, &mut black_buffer, &mut red_buffer); - display.clear(Color::Red); + display.clear(Color::Red); } assert_eq!(black_buffer, [0xFF, 0xFF, 0xFF]); @@ -245,45 +281,50 @@ mod tests { #[test] fn draw_rect_white() { let interface = MockInterface::new(); - let dimensions = Dimensions { rows: ROWS, cols: COLS }; + let dimensions = Dimensions { + rows: ROWS, + cols: COLS, + }; let mut black_buffer = [0u8; BUFFER_SIZE]; let mut red_buffer = [0u8; BUFFER_SIZE]; { - let display = Display::new(interface, dimensions, Rotation::Rotate0); - let mut display = GraphicDisplay::new(display, &mut black_buffer, &mut red_buffer); + let display = Display::new(interface, dimensions, Rotation::Rotate0); + let mut display = GraphicDisplay::new(display, &mut black_buffer, &mut red_buffer); - display.draw(Rect::new(Coord::new(0,0), Coord::new(2, 2)).with_stroke(Some(Color::White)).into_iter()); + display.draw( + Rect::new(Coord::new(0, 0), Coord::new(2, 2)) + .with_stroke(Some(Color::White)) + .into_iter(), + ); } - assert_eq!(black_buffer, [ - 0b11100000, - 0b10100000, - 0b11100000]); + assert_eq!(black_buffer, [0b11100000, 0b10100000, 0b11100000]); assert_eq!(red_buffer, [0b00000000, 0b00000000, 0b00000000]); } #[test] fn draw_rect_red() { let interface = MockInterface::new(); - let dimensions = Dimensions { rows: ROWS, cols: COLS }; + let dimensions = Dimensions { + rows: ROWS, + cols: COLS, + }; let mut black_buffer = [0u8; BUFFER_SIZE]; let mut red_buffer = [0u8; BUFFER_SIZE]; { - let display = Display::new(interface, dimensions, Rotation::Rotate0); - let mut display = GraphicDisplay::new(display, &mut black_buffer, &mut red_buffer); + let display = Display::new(interface, dimensions, Rotation::Rotate0); + let mut display = GraphicDisplay::new(display, &mut black_buffer, &mut red_buffer); - display.draw(Rect::new(Coord::new(1,0), Coord::new(3, 2)).with_stroke(Some(Color::Red)).into_iter()); + display.draw( + Rect::new(Coord::new(1, 0), Coord::new(3, 2)) + .with_stroke(Some(Color::Red)) + .into_iter(), + ); } - assert_eq!(black_buffer, [ - 0b01110000, - 0b01010000, - 0b01110000]); - assert_eq!(red_buffer, [ - 0b01110000, - 0b01010000, - 0b01110000]); + assert_eq!(black_buffer, [0b01110000, 0b01010000, 0b01110000]); + assert_eq!(red_buffer, [0b01110000, 0b01010000, 0b01110000]); } } diff --git a/src/interface.rs b/src/interface.rs index 9bc4278..beae4b2 100644 --- a/src/interface.rs +++ b/src/interface.rs @@ -64,10 +64,9 @@ where Ok(()) } - } -impl DisplayInterface for Interface +impl DisplayInterface for Interface where SPI: hal::blocking::spi::Write, CS: hal::digital::OutputPin, diff --git a/src/lib.rs b/src/lib.rs index fbbbdb7..6d31d7a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,14 +6,16 @@ extern crate embedded_hal as hal; #[macro_use] extern crate std; +mod color; mod command; -mod interface; +mod config; mod display; mod graphics; -mod color; +mod interface; +pub use color::Color; +pub use config::Builder; +pub use display::{Dimensions, Display, Rotation}; +pub use graphics::GraphicDisplay; pub use interface::DisplayInterface; pub use interface::Interface; -pub use display::{Display, Dimensions, Rotation}; -pub use graphics::GraphicDisplay; -pub use color::Color;