mirror of
https://github.com/Feliix42/ssd1675.git
synced 2024-11-22 02:46:30 +00:00
Refactor DisplayInterface into trait, implement Command::execute
This commit is contained in:
parent
a4f5520d5c
commit
92a277f65b
4 changed files with 277 additions and 33 deletions
203
src/command.rs
203
src/command.rs
|
@ -1,17 +1,26 @@
|
||||||
|
use core;
|
||||||
use interface::DisplayInterface;
|
use interface::DisplayInterface;
|
||||||
|
|
||||||
|
const MAX_GATES: u16 = 296;
|
||||||
|
const MAX_DUMMY_LINE_PERIOD: u8 = 127;
|
||||||
|
|
||||||
/// The address increment orientation when writing image data. This configures how the controller will
|
trait Contains<C> where C: Copy + PartialOrd {
|
||||||
/// auto-increment the row and column addresses when image data is written using the
|
fn contains(&self, item: C) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The address increment orientation when writing image data. This configures how the controller
|
||||||
|
/// will auto-increment the row and column addresses when image data is written using the
|
||||||
/// `WriteImageData` command.
|
/// `WriteImageData` command.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum IncrementAxis {
|
pub enum IncrementAxis {
|
||||||
|
/// X direction
|
||||||
Horizontal,
|
Horizontal,
|
||||||
|
/// Y direction
|
||||||
Vertical,
|
Vertical,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
enum DataEntryMode {
|
pub enum DataEntryMode {
|
||||||
DecrementXDecrementY,
|
DecrementXDecrementY,
|
||||||
IncrementXDecrementY,
|
IncrementXDecrementY,
|
||||||
DecrementXIncrementY,
|
DecrementXIncrementY,
|
||||||
|
@ -19,18 +28,25 @@ enum DataEntryMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
enum TemperatureSensor {
|
pub enum TemperatureSensor {
|
||||||
Internal,
|
Internal,
|
||||||
External,
|
External,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
enum RamOption {
|
pub enum RamOption {
|
||||||
Normal,
|
Normal,
|
||||||
Bypass,
|
Bypass,
|
||||||
Invert,
|
Invert,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum DeepSleepMode {
|
||||||
|
Mode1,
|
||||||
|
Mode2,
|
||||||
|
Normal,
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
|
@ -39,9 +55,10 @@ pub enum Command {
|
||||||
/// Set the gate driving voltage.
|
/// Set the gate driving voltage.
|
||||||
GateDrivingVoltage(u8),
|
GateDrivingVoltage(u8),
|
||||||
/// Set the source driving voltage.
|
/// Set the source driving voltage.
|
||||||
/// 0: VHS1/VSH2
|
/// 0: VSH1
|
||||||
/// 1: VSL
|
/// 1: VSH2
|
||||||
SourceDrivingVoltage(u16, u8),
|
/// 2: VSL
|
||||||
|
SourceDrivingVoltage(u8, u8, u8),
|
||||||
/// Booster enable with phases 1 to 3 for soft start current and duration setting
|
/// Booster enable with phases 1 to 3 for soft start current and duration setting
|
||||||
/// 0: Soft start setting for phase 1
|
/// 0: Soft start setting for phase 1
|
||||||
/// 1: Soft start setting for phase 2
|
/// 1: Soft start setting for phase 2
|
||||||
|
@ -51,7 +68,7 @@ pub enum Command {
|
||||||
/// Set the scanning start position of the gate driver
|
/// Set the scanning start position of the gate driver
|
||||||
GateScanStartPostion(u16),
|
GateScanStartPostion(u16),
|
||||||
/// Set deep sleep mode
|
/// Set deep sleep mode
|
||||||
DeepSleepMode(u8),
|
DeepSleepMode(DeepSleepMode),
|
||||||
/// Set the data entry mode and increament axis
|
/// Set the data entry mode and increament axis
|
||||||
DataEntryMode(DataEntryMode, IncrementAxis),
|
DataEntryMode(DataEntryMode, IncrementAxis),
|
||||||
/// Perform a soft reset, and reset all parameters to their default values
|
/// Perform a soft reset, and reset all parameters to their default values
|
||||||
|
@ -71,7 +88,7 @@ pub enum Command {
|
||||||
ReadTemperatureSensor(u16),
|
ReadTemperatureSensor(u16),
|
||||||
/// Write a command to the external temperature sensor
|
/// Write a command to the external temperature sensor
|
||||||
WriteExternalTemperatureSensor(u8, u8, u8),
|
WriteExternalTemperatureSensor(u8, u8, u8),
|
||||||
/// Activate dispay update sequence. BUSY will be high when in progress.
|
/// Activate display update sequence. BUSY will be high when in progress.
|
||||||
UpdateDisplay,
|
UpdateDisplay,
|
||||||
/// Set RAM content options for update display command.
|
/// Set RAM content options for update display command.
|
||||||
/// 0: Black/White RAM option
|
/// 0: Black/White RAM option
|
||||||
|
@ -115,7 +132,7 @@ pub enum Command {
|
||||||
/// Set the start/end positions of the window address in the Y direction
|
/// Set the start/end positions of the window address in the Y direction
|
||||||
/// 0: Start
|
/// 0: Start
|
||||||
/// 1: End
|
/// 1: End
|
||||||
StartEndYPosition(u8, u8),
|
StartEndYPosition(u16, u16),
|
||||||
/// Auto write red RAM for regular pattern
|
/// Auto write red RAM for regular pattern
|
||||||
AutoWriteRedPattern(u8),
|
AutoWriteRedPattern(u8),
|
||||||
/// Auto write red RAM for regular pattern
|
/// Auto write red RAM for regular pattern
|
||||||
|
@ -147,3 +164,167 @@ pub enum BufCommand<'buf> {
|
||||||
/// Write LUT register (70 bytes)
|
/// Write LUT register (70 bytes)
|
||||||
WriteLUT(&'buf [u8]),
|
WriteLUT(&'buf [u8]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
/// appropriately sized slice into populated buffer.
|
||||||
|
/// E.g.
|
||||||
|
///
|
||||||
|
/// let mut buf = [0u8; 4];
|
||||||
|
/// let (command, data) = pack!(buf, 0x3C, [0x12, 0x34]);
|
||||||
|
macro_rules! pack {
|
||||||
|
($buf:ident, $cmd:expr,[]) => {
|
||||||
|
($cmd, &$buf[..0])
|
||||||
|
};
|
||||||
|
($buf:ident, $cmd:expr,[$arg0:expr]) => {{
|
||||||
|
$buf[0] = $arg0;
|
||||||
|
($cmd, &$buf[..1])
|
||||||
|
}};
|
||||||
|
($buf:ident, $cmd:expr,[$arg0:expr, $arg1:expr]) => {{
|
||||||
|
$buf[0] = $arg0;
|
||||||
|
$buf[1] = $arg1;
|
||||||
|
($cmd, &$buf[..2])
|
||||||
|
}};
|
||||||
|
($buf:ident, $cmd:expr,[$arg0:expr, $arg1:expr, $arg2:expr]) => {{
|
||||||
|
$buf[0] = $arg0;
|
||||||
|
$buf[1] = $arg1;
|
||||||
|
$buf[2] = $arg2;
|
||||||
|
($cmd, &$buf[..3])
|
||||||
|
}};
|
||||||
|
($buf:ident, $cmd:expr,[$arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr]) => {{
|
||||||
|
$buf[0] = $arg0;
|
||||||
|
$buf[1] = $arg1;
|
||||||
|
$buf[2] = $arg2;
|
||||||
|
$buf[3] = $arg3;
|
||||||
|
($cmd, &$buf[..4])
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Command {
|
||||||
|
fn execute<I: DisplayInterface>(self, interface: &mut I) -> Result<(), I::Error> {
|
||||||
|
use self::Command::*;
|
||||||
|
|
||||||
|
let mut buf = [0u8; 4];
|
||||||
|
let (command, data) = match self {
|
||||||
|
DriverOutputControl(gate_lines, scanning_seq_and_dir) => {
|
||||||
|
pack!(buf, 0x01, [0xD3, 0x00, 0x00])
|
||||||
|
}
|
||||||
|
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])
|
||||||
|
}
|
||||||
|
GateScanStartPostion(position) => {
|
||||||
|
debug_assert!(Contains::contains(&(0..MAX_GATES), position));
|
||||||
|
let [upper, lower] = u16_as_u8(position);
|
||||||
|
pack!(buf, 0x0F, [lower, upper])
|
||||||
|
}
|
||||||
|
DeepSleepMode(mode) => {
|
||||||
|
let mode = match mode {
|
||||||
|
self::DeepSleepMode::Normal => 0b00,
|
||||||
|
self::DeepSleepMode::Mode1 => 0b01,
|
||||||
|
self::DeepSleepMode::Mode2 => 0b11,
|
||||||
|
};
|
||||||
|
|
||||||
|
pack!(buf, 0x10, [mode])
|
||||||
|
}
|
||||||
|
DataEntryMode(data_entry_mode, increment_axis) => {
|
||||||
|
let mode = match data_entry_mode {
|
||||||
|
self::DataEntryMode::DecrementXDecrementY => 0b00,
|
||||||
|
self::DataEntryMode::IncrementXDecrementY => 0b01,
|
||||||
|
self::DataEntryMode::DecrementXIncrementY => 0b10,
|
||||||
|
self::DataEntryMode::IncrementYIncrementX => 0b11,
|
||||||
|
};
|
||||||
|
let axis = match increment_axis {
|
||||||
|
IncrementAxis::Horizontal => 0b000,
|
||||||
|
IncrementAxis::Vertical => 0b100,
|
||||||
|
};
|
||||||
|
|
||||||
|
pack!(buf, 0x11, [axis | mode])
|
||||||
|
}
|
||||||
|
SoftReset => {
|
||||||
|
pack!(buf, 0x12, [])
|
||||||
|
}
|
||||||
|
// TemperatatSensorSelection(TemperatureSensor) => {
|
||||||
|
// }
|
||||||
|
// WriteTemperatureSensor(u16) => {
|
||||||
|
// }
|
||||||
|
// ReadTemperatureSensor(u16) => {
|
||||||
|
// }
|
||||||
|
// WriteExternalTemperatureSensor(u8, u8, u8) => {
|
||||||
|
// }
|
||||||
|
UpdateDisplay => {
|
||||||
|
pack!(buf, 0x20, [])
|
||||||
|
}
|
||||||
|
// UpdateDisplayOption1(RamOption, RamOption) => {
|
||||||
|
// }
|
||||||
|
// UpdateDisplayOption2(u8) => {
|
||||||
|
// }
|
||||||
|
// EnterVCOMSensing => {
|
||||||
|
// }
|
||||||
|
// VCOMSenseDuration(u8) => {
|
||||||
|
// }
|
||||||
|
// WriteVCOM(u8) => {
|
||||||
|
// }
|
||||||
|
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])
|
||||||
|
}
|
||||||
|
StartEndYPosition(start, end) => {
|
||||||
|
let [start_upper, start_lower] = u16_as_u8(start);
|
||||||
|
let [end_upper, end_lower] = u16_as_u8(end);
|
||||||
|
pack!(buf, 0x45, [start_lower, start_upper, end_lower, end_upper])
|
||||||
|
}
|
||||||
|
// AutoWriteRedPattern(u8) => {
|
||||||
|
// }
|
||||||
|
// AutoWriteBlackPattern(u8) => {
|
||||||
|
// }
|
||||||
|
// XAddress(u8) => {
|
||||||
|
// }
|
||||||
|
// YAddress(u8) => {
|
||||||
|
// }
|
||||||
|
AnalogBlockControl(value) => {
|
||||||
|
pack!(buf, 0x74, [value])
|
||||||
|
}
|
||||||
|
DigitalBlockControl(value) => {
|
||||||
|
pack!(buf, 0x7E, [value])
|
||||||
|
}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
interface.send_command(command)?;
|
||||||
|
if data.len() == 0 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
interface.send_data(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> Contains<C> for core::ops::Range<C> where C: Copy + PartialOrd {
|
||||||
|
fn contains(&self, item: C) -> bool {
|
||||||
|
item >= self.start && item < self.end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> Contains<C> for core::ops::RangeInclusive<C> where C: Copy + PartialOrd {
|
||||||
|
fn contains(&self, item: C) -> bool {
|
||||||
|
item >= *self.start() && item <= *self.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
39
src/display.rs
Normal file
39
src/display.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use hal;
|
||||||
|
|
||||||
|
use interface::DisplayInterface;
|
||||||
|
|
||||||
|
struct Config {}
|
||||||
|
|
||||||
|
struct Dimensions {
|
||||||
|
rows: usize,
|
||||||
|
cols: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Display<I> where I: DisplayInterface {
|
||||||
|
interface: I,
|
||||||
|
dimensions: Dimensions,
|
||||||
|
rotation: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> Display<I> where I: DisplayInterface {
|
||||||
|
fn new(interface: I, dimensions: Dimensions, rotation: u8) -> Self {
|
||||||
|
Self { interface, dimensions, rotation }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset<D: hal::blocking::delay::DelayMs<u8>>(&mut self, delay: &mut D) {
|
||||||
|
self.interface.reset(delay)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self, config: Config) -> Result<(), I::Error> {
|
||||||
|
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deep_sleep(&mut self) -> Result<(), I::Error> {
|
||||||
|
// TODO: Send DeepSleep command
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,15 @@ use hal;
|
||||||
// Section 15.2 of the HINK-E0213A07 data sheet says to hold for 10ms
|
// Section 15.2 of the HINK-E0213A07 data sheet says to hold for 10ms
|
||||||
const RESET_DELAY_MS: u8 = 10;
|
const RESET_DELAY_MS: u8 = 10;
|
||||||
|
|
||||||
pub struct DisplayInterface<SPI, CS, BUSY, DC, RESET> {
|
pub trait DisplayInterface {
|
||||||
|
type Error;
|
||||||
|
|
||||||
|
fn send_command(&mut self, command: u8) -> Result<(), Self::Error>;
|
||||||
|
fn send_data(&mut self, data: &[u8]) -> Result<(), Self::Error>;
|
||||||
|
fn reset<D: hal::blocking::delay::DelayMs<u8>>(&mut self, delay: &mut D);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Interface<SPI, CS, BUSY, DC, RESET> {
|
||||||
/// SPI
|
/// SPI
|
||||||
spi: SPI,
|
spi: SPI,
|
||||||
/// CS for SPI
|
/// CS for SPI
|
||||||
|
@ -16,7 +24,7 @@ pub struct DisplayInterface<SPI, CS, BUSY, DC, RESET> {
|
||||||
reset: RESET,
|
reset: RESET,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SPI, CS, BUSY, DC, RESET> DisplayInterface<SPI, CS, BUSY, DC, RESET>
|
impl<SPI, CS, BUSY, DC, RESET> Interface<SPI, CS, BUSY, DC, RESET>
|
||||||
where
|
where
|
||||||
SPI: hal::blocking::spi::Write<u8>,
|
SPI: hal::blocking::spi::Write<u8>,
|
||||||
CS: hal::digital::OutputPin,
|
CS: hal::digital::OutputPin,
|
||||||
|
@ -34,26 +42,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset<D: hal::blocking::delay::DelayMs<u8>>(&mut self, delay: &mut D) {
|
|
||||||
self.reset.set_low();
|
|
||||||
delay.delay_ms(RESET_DELAY_MS);
|
|
||||||
self.reset.set_high();
|
|
||||||
delay.delay_ms(RESET_DELAY_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_command(&mut self, command: u8) -> Result<(), SPI::Error> {
|
|
||||||
self.dc.set_low();
|
|
||||||
self.write(&[command])?;
|
|
||||||
self.dc.set_high();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_data(&mut self, data: &[u8]) -> Result<(), SPI::Error> {
|
|
||||||
self.dc.set_high();
|
|
||||||
self.write(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&mut self, data: &[u8]) -> Result<(), SPI::Error> {
|
fn write(&mut self, data: &[u8]) -> Result<(), SPI::Error> {
|
||||||
// Select the controller with chip select (CS)
|
// Select the controller with chip select (CS)
|
||||||
self.cs.set_low();
|
self.cs.set_low();
|
||||||
|
@ -79,3 +67,34 @@ where
|
||||||
while self.busy.is_high() {}
|
while self.busy.is_high() {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<SPI, CS, BUSY, DC, RESET> DisplayInterface for Interface<SPI, CS, BUSY, DC, RESET>
|
||||||
|
where
|
||||||
|
SPI: hal::blocking::spi::Write<u8>,
|
||||||
|
CS: hal::digital::OutputPin,
|
||||||
|
BUSY: hal::digital::InputPin,
|
||||||
|
DC: hal::digital::OutputPin,
|
||||||
|
RESET: hal::digital::OutputPin,
|
||||||
|
{
|
||||||
|
type Error = SPI::Error;
|
||||||
|
|
||||||
|
fn reset<D: hal::blocking::delay::DelayMs<u8>>(&mut self, delay: &mut D) {
|
||||||
|
self.reset.set_low();
|
||||||
|
delay.delay_ms(RESET_DELAY_MS);
|
||||||
|
self.reset.set_high();
|
||||||
|
delay.delay_ms(RESET_DELAY_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_command(&mut self, command: u8) -> Result<(), Self::Error> {
|
||||||
|
self.dc.set_low();
|
||||||
|
self.write(&[command])?;
|
||||||
|
self.dc.set_high();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_data(&mut self, data: &[u8]) -> Result<(), Self::Error> {
|
||||||
|
self.dc.set_high();
|
||||||
|
self.write(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
extern crate embedded_hal as hal;
|
extern crate embedded_hal as hal;
|
||||||
|
|
||||||
mod command;
|
mod command;
|
||||||
mod interface;
|
mod interface;
|
||||||
|
mod display;
|
||||||
|
|
||||||
|
pub use interface::DisplayInterface;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
Loading…
Reference in a new issue