mirror of
https://github.com/Feliix42/ssd1675.git
synced 2024-11-24 03:36:30 +00:00
Merge pull request #1 from wezm/builder
Builder for constructing Display
This commit is contained in:
commit
a95bb3d01b
9 changed files with 365 additions and 197 deletions
|
@ -12,8 +12,8 @@ controller, for use with [embedded-hal].
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
This driver is intended to work on embedded platforms using the `embedded-hal`
|
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
|
trait library. It is `no_std` compatible, builds on stable Rust, and only uses
|
||||||
require an allocator. It supports the 4-wire SPI interface.
|
safe Rust. It supports the 4-wire SPI interface.
|
||||||
|
|
||||||
## Tested Devices
|
## Tested Devices
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use linux_embedded_hal::Delay;
|
||||||
use linux_embedded_hal::{Pin, Spidev};
|
use linux_embedded_hal::{Pin, Spidev};
|
||||||
|
|
||||||
extern crate ssd1675;
|
extern crate ssd1675;
|
||||||
use ssd1675::{Display, Dimensions, GraphicDisplay, Color, Rotation};
|
use ssd1675::{Builder, Color, Dimensions, Display, GraphicDisplay, Rotation};
|
||||||
|
|
||||||
// Graphics
|
// Graphics
|
||||||
extern crate embedded_graphics;
|
extern crate embedded_graphics;
|
||||||
|
@ -15,12 +15,12 @@ use embedded_graphics::Drawing;
|
||||||
|
|
||||||
// Font
|
// Font
|
||||||
extern crate profont;
|
extern crate profont;
|
||||||
use profont::{ProFont9Point, ProFont12Point, ProFont14Point, ProFont24Point};
|
use profont::{ProFont12Point, ProFont14Point, ProFont24Point, ProFont9Point};
|
||||||
|
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::{fs, io};
|
|
||||||
use std::time::Duration;
|
|
||||||
use std::thread::sleep;
|
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
|
// Activate SPI, GPIO in raspi-config needs to be run with sudo because of some sysfs_gpio
|
||||||
// permission problems and follow-up timing problems
|
// permission problems and follow-up timing problems
|
||||||
|
@ -29,6 +29,27 @@ use std::thread::sleep;
|
||||||
const ROWS: u16 = 212;
|
const ROWS: u16 = 212;
|
||||||
const COLS: u8 = 104;
|
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> {
|
fn main() -> Result<(), std::io::Error> {
|
||||||
// Configure SPI
|
// Configure SPI
|
||||||
let mut spi = Spidev::open("/dev/spidev0.0").expect("SPI device");
|
let mut spi = Spidev::open("/dev/spidev0.0").expect("SPI device");
|
||||||
|
@ -39,8 +60,8 @@ fn main() -> Result<(), std::io::Error> {
|
||||||
.build();
|
.build();
|
||||||
spi.configure(&options).expect("SPI configuration");
|
spi.configure(&options).expect("SPI configuration");
|
||||||
|
|
||||||
// https://pinout.xyz/pinout/inky_phat#
|
// https://pinout.xyz/pinout/inky_phat
|
||||||
// Configure Digital I/O Pin to be used as Chip Select for SPI
|
// Configure Digital I/O Pins
|
||||||
let cs = Pin::new(8); // BCM8
|
let cs = Pin::new(8); // BCM8
|
||||||
cs.export().expect("cs export");
|
cs.export().expect("cs export");
|
||||||
while !cs.is_exported() {}
|
while !cs.is_exported() {}
|
||||||
|
@ -61,20 +82,33 @@ fn main() -> Result<(), std::io::Error> {
|
||||||
let reset = Pin::new(27); // BCM27
|
let reset = Pin::new(27); // BCM27
|
||||||
reset.export().expect("reset export");
|
reset.export().expect("reset export");
|
||||||
while !reset.is_exported() {}
|
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");
|
reset.set_value(1).expect("reset Value set to 1");
|
||||||
println!("Pins configured");
|
println!("Pins configured");
|
||||||
|
|
||||||
|
// Initialise display controller
|
||||||
let mut delay = Delay {};
|
let mut delay = Delay {};
|
||||||
|
|
||||||
let controller = ssd1675::Interface::new(spi, cs, busy, dc, reset);
|
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 black_buffer = [0u8; ROWS as usize * COLS as usize / 8];
|
||||||
let mut red_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);
|
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 {
|
loop {
|
||||||
display.reset(&mut delay).expect("error resetting display");
|
display.reset(&mut delay).expect("error resetting display");
|
||||||
println!("Reset and initialised");
|
println!("Reset and initialised");
|
||||||
|
@ -147,21 +181,29 @@ fn read_cpu_temp() -> Result<f64, io::Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_uptime() -> Option<String> {
|
fn read_uptime() -> Option<String> {
|
||||||
Command::new("uptime").arg("-p").output().ok().and_then(|output| {
|
Command::new("uptime")
|
||||||
if output.status.success() {
|
.arg("-p")
|
||||||
String::from_utf8(output.stdout).ok()
|
.output()
|
||||||
} else {
|
.ok()
|
||||||
None
|
.and_then(|output| {
|
||||||
}
|
if output.status.success() {
|
||||||
})
|
String::from_utf8(output.stdout).ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_uname() -> Option<String> {
|
fn read_uname() -> Option<String> {
|
||||||
Command::new("uname").arg("-smr").output().ok().and_then(|output| {
|
Command::new("uname")
|
||||||
if output.status.success() {
|
.arg("-smr")
|
||||||
String::from_utf8(output.stdout).ok()
|
.output()
|
||||||
} else {
|
.ok()
|
||||||
None
|
.and_then(|output| {
|
||||||
}
|
if output.status.success() {
|
||||||
})
|
String::from_utf8(output.stdout).ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ impl From<u8> for Color {
|
||||||
0 => Color::Black,
|
0 => Color::Black,
|
||||||
1 => Color::White,
|
1 => Color::White,
|
||||||
2 => Color::Red,
|
2 => Color::Red,
|
||||||
_ => panic!("invalid color value")
|
_ => panic!("invalid color value"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,10 @@ use interface::DisplayInterface;
|
||||||
const MAX_GATES: u16 = 296;
|
const MAX_GATES: u16 = 296;
|
||||||
const MAX_DUMMY_LINE_PERIOD: u8 = 127;
|
const MAX_DUMMY_LINE_PERIOD: u8 = 127;
|
||||||
|
|
||||||
trait Contains<C> where C: Copy + PartialOrd {
|
trait Contains<C>
|
||||||
|
where
|
||||||
|
C: Copy + PartialOrd,
|
||||||
|
{
|
||||||
fn contains(&self, item: C) -> bool;
|
fn contains(&self, item: C) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +53,7 @@ pub enum DeepSleepMode {
|
||||||
DiscardRAM,
|
DiscardRAM,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
/// Set the MUX of gate lines, scanning sequence and direction
|
/// Set the MUX of gate lines, scanning sequence and direction
|
||||||
/// 0: MAX gate lines
|
/// 0: MAX gate lines
|
||||||
|
@ -207,21 +211,17 @@ macro_rules! pack {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl Command {
|
||||||
pub(crate) fn execute<I: DisplayInterface>(self, interface: &mut I) -> Result<(), I::Error> {
|
pub(crate) fn execute<I: DisplayInterface>(&self, interface: &mut I) -> Result<(), I::Error> {
|
||||||
use self::Command::*;
|
use self::Command::*;
|
||||||
|
|
||||||
let mut buf = [0u8; 4];
|
let mut buf = [0u8; 4];
|
||||||
let (command, data) = match self {
|
let (command, data) = match *self {
|
||||||
DriverOutputControl(gate_lines, scanning_seq_and_dir) => {
|
DriverOutputControl(gate_lines, scanning_seq_and_dir) => {
|
||||||
let [upper, lower] = u16_as_u8(gate_lines);
|
let [upper, lower] = u16_as_u8(gate_lines);
|
||||||
pack!(buf, 0x01, [lower, upper, scanning_seq_and_dir])
|
pack!(buf, 0x01, [lower, upper, scanning_seq_and_dir])
|
||||||
}
|
}
|
||||||
GateDrivingVoltage(voltages) => {
|
GateDrivingVoltage(voltages) => pack!(buf, 0x03, [voltages]),
|
||||||
pack!(buf, 0x03, [voltages])
|
SourceDrivingVoltage(vsh1, vsh2, vsl) => pack!(buf, 0x04, [vsh1, vsh2, vsl]),
|
||||||
}
|
|
||||||
SourceDrivingVoltage(vsh1, vsh2, vsl) => {
|
|
||||||
pack!(buf, 0x04, [vsh1, vsh2, vsl])
|
|
||||||
}
|
|
||||||
BoosterEnable(phase1, phase2, phase3, duration) => {
|
BoosterEnable(phase1, phase2, phase3, duration) => {
|
||||||
pack!(buf, 0x0C, [phase1, phase2, phase3, duration])
|
pack!(buf, 0x0C, [phase1, phase2, phase3, duration])
|
||||||
}
|
}
|
||||||
|
@ -253,9 +253,7 @@ impl Command {
|
||||||
|
|
||||||
pack!(buf, 0x11, [axis | mode])
|
pack!(buf, 0x11, [axis | mode])
|
||||||
}
|
}
|
||||||
SoftReset => {
|
SoftReset => pack!(buf, 0x12, []),
|
||||||
pack!(buf, 0x12, [])
|
|
||||||
}
|
|
||||||
// TemperatatSensorSelection(TemperatureSensor) => {
|
// TemperatatSensorSelection(TemperatureSensor) => {
|
||||||
// }
|
// }
|
||||||
// WriteTemperatureSensor(u16) => {
|
// WriteTemperatureSensor(u16) => {
|
||||||
|
@ -264,34 +262,22 @@ impl Command {
|
||||||
// }
|
// }
|
||||||
// WriteExternalTemperatureSensor(u8, u8, u8) => {
|
// WriteExternalTemperatureSensor(u8, u8, u8) => {
|
||||||
// }
|
// }
|
||||||
UpdateDisplay => {
|
UpdateDisplay => pack!(buf, 0x20, []),
|
||||||
pack!(buf, 0x20, [])
|
|
||||||
}
|
|
||||||
// UpdateDisplayOption1(RamOption, RamOption) => {
|
// UpdateDisplayOption1(RamOption, RamOption) => {
|
||||||
// }
|
// }
|
||||||
UpdateDisplayOption2(value) => {
|
UpdateDisplayOption2(value) => pack!(buf, 0x22, [value]),
|
||||||
pack!(buf, 0x22, [value])
|
|
||||||
}
|
|
||||||
// EnterVCOMSensing => {
|
// EnterVCOMSensing => {
|
||||||
// }
|
// }
|
||||||
// VCOMSenseDuration(u8) => {
|
// VCOMSenseDuration(u8) => {
|
||||||
// }
|
// }
|
||||||
WriteVCOM(value) => {
|
WriteVCOM(value) => pack!(buf, 0x2C, [value]),
|
||||||
pack!(buf, 0x2C, [value])
|
|
||||||
}
|
|
||||||
DummyLinePeriod(period) => {
|
DummyLinePeriod(period) => {
|
||||||
debug_assert!(Contains::contains(&(0..=MAX_DUMMY_LINE_PERIOD), period));
|
debug_assert!(Contains::contains(&(0..=MAX_DUMMY_LINE_PERIOD), period));
|
||||||
pack!(buf, 0x3A, [period])
|
pack!(buf, 0x3A, [period])
|
||||||
}
|
}
|
||||||
GateLineWidth(tgate) => {
|
GateLineWidth(tgate) => pack!(buf, 0x3B, [tgate]),
|
||||||
pack!(buf, 0x3B, [tgate])
|
BorderWaveform(border_waveform) => pack!(buf, 0x3C, [border_waveform]),
|
||||||
}
|
StartEndXPosition(start, end) => pack!(buf, 0x44, [start, end]),
|
||||||
BorderWaveform(border_waveform) => {
|
|
||||||
pack!(buf, 0x3C, [border_waveform])
|
|
||||||
}
|
|
||||||
StartEndXPosition(start, end) => {
|
|
||||||
pack!(buf, 0x44, [start, end])
|
|
||||||
}
|
|
||||||
StartEndYPosition(start, end) => {
|
StartEndYPosition(start, end) => {
|
||||||
let [start_upper, start_lower] = u16_as_u8(start);
|
let [start_upper, start_lower] = u16_as_u8(start);
|
||||||
let [end_upper, end_lower] = u16_as_u8(end);
|
let [end_upper, end_lower] = u16_as_u8(end);
|
||||||
|
@ -301,19 +287,11 @@ impl Command {
|
||||||
// }
|
// }
|
||||||
// AutoWriteBlackPattern(u8) => {
|
// AutoWriteBlackPattern(u8) => {
|
||||||
// }
|
// }
|
||||||
XAddress(address) => {
|
XAddress(address) => pack!(buf, 0x4E, [address]),
|
||||||
pack!(buf, 0x4E, [address])
|
YAddress(address) => pack!(buf, 0x4F, [address]),
|
||||||
}
|
AnalogBlockControl(value) => pack!(buf, 0x74, [value]),
|
||||||
YAddress(address) => {
|
DigitalBlockControl(value) => pack!(buf, 0x7E, [value]),
|
||||||
pack!(buf, 0x4F, [address])
|
_ => unimplemented!(),
|
||||||
}
|
|
||||||
AnalogBlockControl(value) => {
|
|
||||||
pack!(buf, 0x74, [value])
|
|
||||||
}
|
|
||||||
DigitalBlockControl(value) => {
|
|
||||||
pack!(buf, 0x7E, [value])
|
|
||||||
}
|
|
||||||
_ => unimplemented!()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface.send_command(command)?;
|
interface.send_command(command)?;
|
||||||
|
@ -326,19 +304,13 @@ impl Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'buf> BufCommand<'buf> {
|
impl<'buf> BufCommand<'buf> {
|
||||||
pub(crate) fn execute<I: DisplayInterface>(self, interface: &mut I) -> Result<(), I::Error> {
|
pub(crate) fn execute<I: DisplayInterface>(&self, interface: &mut I) -> Result<(), I::Error> {
|
||||||
use self::BufCommand::*;
|
use self::BufCommand::*;
|
||||||
|
|
||||||
let (command, data) = match self {
|
let (command, data) = match self {
|
||||||
WriteBlackData(buffer) => {
|
WriteBlackData(buffer) => (0x24, buffer),
|
||||||
(0x24, buffer)
|
WriteRedData(buffer) => (0x26, buffer),
|
||||||
}
|
WriteLUT(buffer) => (0x32, buffer),
|
||||||
WriteRedData(buffer) => {
|
|
||||||
(0x26, buffer)
|
|
||||||
}
|
|
||||||
WriteLUT(buffer) => {
|
|
||||||
(0x32, buffer)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface.send_command(command)?;
|
interface.send_command(command)?;
|
||||||
|
@ -350,13 +322,19 @@ impl<'buf> BufCommand<'buf> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> Contains<C> for core::ops::Range<C> where C: Copy + PartialOrd {
|
impl<C> Contains<C> for core::ops::Range<C>
|
||||||
|
where
|
||||||
|
C: Copy + PartialOrd,
|
||||||
|
{
|
||||||
fn contains(&self, item: C) -> bool {
|
fn contains(&self, item: C) -> bool {
|
||||||
item >= self.start && item < self.end
|
item >= self.start && item < self.end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> Contains<C> for core::ops::RangeInclusive<C> where C: Copy + PartialOrd {
|
impl<C> Contains<C> for core::ops::RangeInclusive<C>
|
||||||
|
where
|
||||||
|
C: Copy + PartialOrd,
|
||||||
|
{
|
||||||
fn contains(&self, item: C) -> bool {
|
fn contains(&self, item: C) -> bool {
|
||||||
item >= *self.start() && item <= *self.end()
|
item >= *self.start() && item <= *self.end()
|
||||||
}
|
}
|
||||||
|
|
110
src/config.rs
Normal file
110
src/config.rs
Normal file
|
@ -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<BufCommand<'a>>,
|
||||||
|
data_entry_mode: Command,
|
||||||
|
dimensions: Option<Dimensions>,
|
||||||
|
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<BufCommand<'a>>,
|
||||||
|
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<Config<'a>, 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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
use hal;
|
use hal;
|
||||||
|
|
||||||
use command::{BufCommand, Command, DataEntryMode, DeepSleepMode, IncrementAxis};
|
use command::{BufCommand, Command, DataEntryMode, DeepSleepMode, IncrementAxis};
|
||||||
|
use config::Config;
|
||||||
use interface::DisplayInterface;
|
use interface::DisplayInterface;
|
||||||
|
|
||||||
// Max display resolution is 160x296
|
// Max display resolution is 160x296
|
||||||
|
@ -11,8 +12,6 @@ const MAX_GATE_OUTPUTS: usize = 296;
|
||||||
const ANALOG_BLOCK_CONTROL_MAGIC: u8 = 0x54;
|
const ANALOG_BLOCK_CONTROL_MAGIC: u8 = 0x54;
|
||||||
const DIGITAL_BLOCK_CONTROL_MAGIC: u8 = 0x3B;
|
const DIGITAL_BLOCK_CONTROL_MAGIC: u8 = 0x3B;
|
||||||
|
|
||||||
struct Config {}
|
|
||||||
|
|
||||||
pub struct Dimensions {
|
pub struct Dimensions {
|
||||||
pub rows: u16,
|
pub rows: u16,
|
||||||
pub cols: u8,
|
pub cols: u8,
|
||||||
|
@ -32,57 +31,74 @@ impl Default for Rotation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Display<I> where I: DisplayInterface {
|
pub struct Display<'a, I>
|
||||||
|
where
|
||||||
|
I: DisplayInterface,
|
||||||
|
{
|
||||||
interface: I,
|
interface: I,
|
||||||
dimensions: Dimensions,
|
config: Config<'a>,
|
||||||
rotation: Rotation,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I> Display<I> where I: DisplayInterface {
|
impl<'a, I> Display<'a, I>
|
||||||
pub fn new(interface: I, dimensions: Dimensions, rotation: Rotation) -> Self {
|
where
|
||||||
|
I: DisplayInterface,
|
||||||
|
{
|
||||||
|
pub fn new(interface: I, config: Config<'a>) -> Self {
|
||||||
// TODO: Assert dimensions are evenly divisible by 8
|
// TODO: Assert dimensions are evenly divisible by 8
|
||||||
Self { interface, dimensions, rotation }
|
Self { interface, config }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform a hardware reset followed by software reset
|
/// Perform a hardware reset followed by software reset
|
||||||
pub 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();
|
||||||
|
|
||||||
self.init(Config {})
|
self.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialise the controller according to Section 9: Typical Operating Sequence
|
/// Initialise the controller according to Section 9: Typical Operating Sequence
|
||||||
/// from the data sheet
|
/// 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::AnalogBlockControl(ANALOG_BLOCK_CONTROL_MAGIC).execute(&mut self.interface)?;
|
||||||
Command::DigitalBlockControl(DIGITAL_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)?;
|
self.config.dummy_line_period.execute(&mut self.interface)?;
|
||||||
Command::GateLineWidth(0x04).execute(&mut self.interface)?;
|
self.config.gate_line_width.execute(&mut self.interface)?;
|
||||||
|
|
||||||
// Command::GateDrivingVoltage(0b10000 | 0b0001);
|
// Command::GateDrivingVoltage(0b10000 | 0b0001);
|
||||||
// Command::SourceDrivingVoltage(0x2D, 0xB2, 0x22).execute(&mut self.interface)?;
|
// 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
|
// POR is HiZ. Need pull from config
|
||||||
// Command::BorderWaveform(u8).execute(&mut self.interface)?;
|
// 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::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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub 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
|
||||||
let buf_limit = ((self.rows() * self.cols() as u16) as f32 / 8.).ceil() as usize;
|
let buf_limit = ((self.rows() * self.cols() as u16) as f32 / 8.).ceil() as usize;
|
||||||
Command::XAddress(0).execute(&mut self.interface)?;
|
Command::XAddress(0).execute(&mut self.interface)?;
|
||||||
|
@ -100,7 +116,7 @@ impl<I> Display<I> where I: DisplayInterface {
|
||||||
delay.delay_ms(50);
|
delay.delay_ms(50);
|
||||||
// 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 deref would wait until it's not
|
||||||
// busy.
|
// busy.
|
||||||
self.interface.busy_wait();
|
self.interface.busy_wait();
|
||||||
|
|
||||||
|
@ -112,34 +128,14 @@ impl<I> Display<I> where I: DisplayInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rows(&self) -> u16 {
|
pub fn rows(&self) -> u16 {
|
||||||
self.dimensions.rows
|
self.config.dimensions.rows
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cols(&self) -> u8 {
|
pub fn cols(&self) -> u8 {
|
||||||
self.dimensions.cols
|
self.config.dimensions.cols
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rotation(&self) -> Rotation {
|
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
|
|
||||||
];
|
|
||||||
|
|
169
src/graphics.rs
169
src/graphics.rs
|
@ -1,22 +1,40 @@
|
||||||
use hal;
|
|
||||||
use color::Color;
|
use color::Color;
|
||||||
use display::{Display, Rotation};
|
|
||||||
use interface::DisplayInterface;
|
|
||||||
use core::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
|
use display::{Display, Rotation};
|
||||||
|
use hal;
|
||||||
|
use interface::DisplayInterface;
|
||||||
|
|
||||||
pub struct GraphicDisplay<'a, I> where I: DisplayInterface {
|
pub struct GraphicDisplay<'a, I>
|
||||||
display: Display<I>,
|
where
|
||||||
|
I: DisplayInterface,
|
||||||
|
{
|
||||||
|
display: Display<'a, I>,
|
||||||
black_buffer: &'a mut [u8],
|
black_buffer: &'a mut [u8],
|
||||||
red_buffer: &'a mut [u8],
|
red_buffer: &'a mut [u8],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, I> GraphicDisplay<'a, I> where I: DisplayInterface {
|
impl<'a, I> GraphicDisplay<'a, I>
|
||||||
pub fn new(display: Display<I>, black_buffer: &'a mut [u8], red_buffer: &'a mut [u8]) -> Self {
|
where
|
||||||
GraphicDisplay { display, black_buffer, red_buffer }
|
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<D: hal::blocking::delay::DelayMs<u8>>(&mut self, delay: &mut D) -> Result<(), I::Error> {
|
pub fn update<D: hal::blocking::delay::DelayMs<u8>>(
|
||||||
self.display.update(self.black_buffer, self.red_buffer, delay)
|
&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) {
|
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) {
|
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;
|
let index = index as usize;
|
||||||
|
|
||||||
match color {
|
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 {
|
impl<'a, I> Deref for GraphicDisplay<'a, I>
|
||||||
type Target = Display<I>;
|
where
|
||||||
|
I: DisplayInterface,
|
||||||
|
{
|
||||||
|
type Target = Display<'a, I>;
|
||||||
|
|
||||||
fn deref(&self) -> &Display<I> {
|
fn deref(&self) -> &Display<'a, I> {
|
||||||
&self.display
|
&self.display
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, I> DerefMut for GraphicDisplay<'a, I> where I: DisplayInterface {
|
impl<'a, I> DerefMut for GraphicDisplay<'a, I>
|
||||||
fn deref_mut(&mut self) -> &mut Display<I> {
|
where
|
||||||
|
I: DisplayInterface,
|
||||||
|
{
|
||||||
|
fn deref_mut(&mut self) -> &mut Display<'a, I> {
|
||||||
&mut self.display
|
&mut self.display
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rotation(x: u32, y: u32, width: u32, height: u32, rotation: Rotation) -> (u32, u8) {
|
fn rotation(x: u32, y: u32, width: u32, height: u32, rotation: Rotation) -> (u32, u8) {
|
||||||
match rotation {
|
match rotation {
|
||||||
Rotation::Rotate0 => (
|
Rotation::Rotate0 => (x / 8 + (width / 8) * y, 0x80 >> (x % 8)),
|
||||||
x / 8 + (width / 8) * y,
|
Rotation::Rotate90 => ((width - 1 - y) / 8 + (width / 8) * x, 0x01 << (y % 8)),
|
||||||
0x80 >> (x % 8),
|
|
||||||
),
|
|
||||||
Rotation::Rotate90 => (
|
|
||||||
(width - 1 - y) / 8 + (width / 8) * x,
|
|
||||||
0x01 << (y % 8),
|
|
||||||
),
|
|
||||||
Rotation::Rotate180 => (
|
Rotation::Rotate180 => (
|
||||||
((width / 8) * height - 1) - (x / 8 + (width / 8) * y),
|
((width / 8) * height - 1) - (x / 8 + (width / 8) * y),
|
||||||
0x01 << (x % 8),
|
0x01 << (x % 8),
|
||||||
),
|
),
|
||||||
Rotation::Rotate270 => (
|
Rotation::Rotate270 => (y / 8 + (height - 1 - x) * (width / 8), 0x80 >> (y % 8)),
|
||||||
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")]
|
#[cfg(feature = "graphics")]
|
||||||
extern crate embedded_graphics;
|
extern crate embedded_graphics;
|
||||||
#[cfg(feature = "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")]
|
#[cfg(feature = "graphics")]
|
||||||
impl<'a, I> Drawing<Color> for GraphicDisplay<'a, I>
|
impl<'a, I> Drawing<Color> for GraphicDisplay<'a, I>
|
||||||
|
@ -124,7 +145,13 @@ where
|
||||||
T: Iterator<Item = Pixel<Color>>,
|
T: Iterator<Item = Pixel<Color>>,
|
||||||
{
|
{
|
||||||
for Pixel(UnsignedCoord(x, y), colour) in item_pixels {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,13 +162,13 @@ where
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
|
||||||
use ::{Display, DisplayInterface, Dimensions, GraphicDisplay, Color, Rotation};
|
|
||||||
use self::embedded_graphics::coord::Coord;
|
use self::embedded_graphics::coord::Coord;
|
||||||
use self::embedded_graphics::fonts::{Font12x16, Font6x8};
|
use self::embedded_graphics::fonts::{Font12x16, Font6x8};
|
||||||
use self::embedded_graphics::prelude::*;
|
use self::embedded_graphics::prelude::*;
|
||||||
use self::embedded_graphics::primitives::{Circle, Line, Rect};
|
use self::embedded_graphics::primitives::{Circle, Line, Rect};
|
||||||
use self::embedded_graphics::Drawing;
|
use self::embedded_graphics::Drawing;
|
||||||
|
use super::*;
|
||||||
|
use {Color, Dimensions, Display, DisplayInterface, GraphicDisplay, Rotation};
|
||||||
|
|
||||||
const ROWS: u16 = 3;
|
const ROWS: u16 = 3;
|
||||||
const COLS: u8 = 8;
|
const COLS: u8 = 8;
|
||||||
|
@ -191,15 +218,18 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn clear_white() {
|
fn clear_white() {
|
||||||
let interface = MockInterface::new();
|
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 black_buffer = [0u8; BUFFER_SIZE];
|
||||||
let mut red_buffer = [0u8; BUFFER_SIZE];
|
let mut red_buffer = [0u8; BUFFER_SIZE];
|
||||||
|
|
||||||
{
|
{
|
||||||
let display = Display::new(interface, dimensions, Rotation::Rotate270);
|
let display = Display::new(interface, dimensions, Rotation::Rotate270);
|
||||||
let mut display = GraphicDisplay::new(display, &mut black_buffer, &mut red_buffer);
|
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]);
|
assert_eq!(black_buffer, [0xFF, 0xFF, 0xFF]);
|
||||||
|
@ -209,15 +239,18 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn clear_black() {
|
fn clear_black() {
|
||||||
let interface = MockInterface::new();
|
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 black_buffer = [0u8; BUFFER_SIZE];
|
||||||
let mut red_buffer = [0u8; BUFFER_SIZE];
|
let mut red_buffer = [0u8; BUFFER_SIZE];
|
||||||
|
|
||||||
{
|
{
|
||||||
let display = Display::new(interface, dimensions, Rotation::Rotate270);
|
let display = Display::new(interface, dimensions, Rotation::Rotate270);
|
||||||
let mut display = GraphicDisplay::new(display, &mut black_buffer, &mut red_buffer);
|
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]);
|
assert_eq!(black_buffer, [0x00, 0x00, 0x00]);
|
||||||
|
@ -227,15 +260,18 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn clear_red() {
|
fn clear_red() {
|
||||||
let interface = MockInterface::new();
|
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 black_buffer = [0u8; BUFFER_SIZE];
|
||||||
let mut red_buffer = [0u8; BUFFER_SIZE];
|
let mut red_buffer = [0u8; BUFFER_SIZE];
|
||||||
|
|
||||||
{
|
{
|
||||||
let display = Display::new(interface, dimensions, Rotation::Rotate270);
|
let display = Display::new(interface, dimensions, Rotation::Rotate270);
|
||||||
let mut display = GraphicDisplay::new(display, &mut black_buffer, &mut red_buffer);
|
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]);
|
assert_eq!(black_buffer, [0xFF, 0xFF, 0xFF]);
|
||||||
|
@ -245,45 +281,50 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn draw_rect_white() {
|
fn draw_rect_white() {
|
||||||
let interface = MockInterface::new();
|
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 black_buffer = [0u8; BUFFER_SIZE];
|
||||||
let mut red_buffer = [0u8; BUFFER_SIZE];
|
let mut red_buffer = [0u8; BUFFER_SIZE];
|
||||||
|
|
||||||
{
|
{
|
||||||
let display = Display::new(interface, dimensions, Rotation::Rotate0);
|
let display = Display::new(interface, dimensions, Rotation::Rotate0);
|
||||||
let mut display = GraphicDisplay::new(display, &mut black_buffer, &mut red_buffer);
|
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, [
|
assert_eq!(black_buffer, [0b11100000, 0b10100000, 0b11100000]);
|
||||||
0b11100000,
|
|
||||||
0b10100000,
|
|
||||||
0b11100000]);
|
|
||||||
assert_eq!(red_buffer, [0b00000000, 0b00000000, 0b00000000]);
|
assert_eq!(red_buffer, [0b00000000, 0b00000000, 0b00000000]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn draw_rect_red() {
|
fn draw_rect_red() {
|
||||||
let interface = MockInterface::new();
|
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 black_buffer = [0u8; BUFFER_SIZE];
|
||||||
let mut red_buffer = [0u8; BUFFER_SIZE];
|
let mut red_buffer = [0u8; BUFFER_SIZE];
|
||||||
|
|
||||||
{
|
{
|
||||||
let display = Display::new(interface, dimensions, Rotation::Rotate0);
|
let display = Display::new(interface, dimensions, Rotation::Rotate0);
|
||||||
let mut display = GraphicDisplay::new(display, &mut black_buffer, &mut red_buffer);
|
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, [
|
assert_eq!(black_buffer, [0b01110000, 0b01010000, 0b01110000]);
|
||||||
0b01110000,
|
assert_eq!(red_buffer, [0b01110000, 0b01010000, 0b01110000]);
|
||||||
0b01010000,
|
|
||||||
0b01110000]);
|
|
||||||
assert_eq!(red_buffer, [
|
|
||||||
0b01110000,
|
|
||||||
0b01010000,
|
|
||||||
0b01110000]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,6 @@ where
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SPI, CS, BUSY, DC, RESET> DisplayInterface for Interface<SPI, CS, BUSY, DC, RESET>
|
impl<SPI, CS, BUSY, DC, RESET> DisplayInterface for Interface<SPI, CS, BUSY, DC, RESET>
|
||||||
|
|
12
src/lib.rs
12
src/lib.rs
|
@ -6,14 +6,16 @@ extern crate embedded_hal as hal;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate std;
|
extern crate std;
|
||||||
|
|
||||||
|
mod color;
|
||||||
mod command;
|
mod command;
|
||||||
mod interface;
|
mod config;
|
||||||
mod display;
|
mod display;
|
||||||
mod graphics;
|
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::DisplayInterface;
|
||||||
pub use interface::Interface;
|
pub use interface::Interface;
|
||||||
pub use display::{Display, Dimensions, Rotation};
|
|
||||||
pub use graphics::GraphicDisplay;
|
|
||||||
pub use color::Color;
|
|
||||||
|
|
Loading…
Reference in a new issue