mirror of
https://github.com/Feliix42/ssd1675.git
synced 2024-11-22 02:46:30 +00:00
Implement basic initialisation and data writing
This commit is contained in:
parent
92a277f65b
commit
2b24889e83
3 changed files with 108 additions and 16 deletions
|
@ -42,9 +42,12 @@ pub enum RamOption {
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum DeepSleepMode {
|
pub enum DeepSleepMode {
|
||||||
Mode1,
|
/// Not sleeping
|
||||||
Mode2,
|
|
||||||
Normal,
|
Normal,
|
||||||
|
/// Deep sleep with RAM preserved
|
||||||
|
Mode1,
|
||||||
|
/// Deep sleep RAM not preserved
|
||||||
|
Mode2,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
|
@ -204,13 +207,14 @@ macro_rules! pack {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl Command {
|
||||||
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) => {
|
||||||
pack!(buf, 0x01, [0xD3, 0x00, 0x00])
|
let [upper, lower] = u16_as_u8(gate_lines);
|
||||||
|
pack!(buf, 0x01, [lower, upper, scanning_seq_and_dir])
|
||||||
}
|
}
|
||||||
GateDrivingVoltage(voltages) => {
|
GateDrivingVoltage(voltages) => {
|
||||||
pack!(buf, 0x03, [voltages])
|
pack!(buf, 0x03, [voltages])
|
||||||
|
@ -271,8 +275,9 @@ impl Command {
|
||||||
// }
|
// }
|
||||||
// VCOMSenseDuration(u8) => {
|
// VCOMSenseDuration(u8) => {
|
||||||
// }
|
// }
|
||||||
// WriteVCOM(u8) => {
|
WriteVCOM(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])
|
||||||
|
@ -317,6 +322,33 @@ impl Command {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'buf> BufCommand<'buf> {
|
||||||
|
pub(crate) fn execute<I: DisplayInterface>(self, interface: &mut I) -> Result<(), I::Error> {
|
||||||
|
use self::BufCommand::*;
|
||||||
|
|
||||||
|
let (command, data) = match self {
|
||||||
|
WriteBlackData(buffer) => {
|
||||||
|
// TODO: Handle rotation
|
||||||
|
(0x24, buffer)
|
||||||
|
}
|
||||||
|
WriteRedData(buffer) => {
|
||||||
|
// TODO: Handle rotation
|
||||||
|
(0x26, buffer)
|
||||||
|
}
|
||||||
|
WriteLUT(buffer) => {
|
||||||
|
(0x32, buffer)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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 {
|
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
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
use hal;
|
use hal;
|
||||||
|
|
||||||
|
use command::{BufCommand, Command, DataEntryMode, IncrementAxis};
|
||||||
use interface::DisplayInterface;
|
use interface::DisplayInterface;
|
||||||
|
|
||||||
|
// Max display resolution is 160x296
|
||||||
|
const MAX_SOURCE_OUTPUTS: usize = 160;
|
||||||
|
const MAX_GATE_OUTPUTS: usize = 296;
|
||||||
|
|
||||||
|
// Magic numbers from the data sheet
|
||||||
|
const ANALOG_BLOCK_CONTROL_MAGIC: u8 = 0x54;
|
||||||
|
const DIGITAL_BLOCK_CONTROL_MAGIC: u8 = 0x3B;
|
||||||
|
|
||||||
struct Config {}
|
struct Config {}
|
||||||
|
|
||||||
struct Dimensions {
|
struct Dimensions {
|
||||||
rows: usize,
|
rows: u16,
|
||||||
cols: usize,
|
cols: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Display<I> where I: DisplayInterface {
|
pub struct Display<I> where I: DisplayInterface {
|
||||||
|
@ -20,12 +29,62 @@ impl<I> Display<I> where I: DisplayInterface {
|
||||||
Self { interface, dimensions, rotation }
|
Self { interface, dimensions, rotation }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset<D: hal::blocking::delay::DelayMs<u8>>(&mut self, delay: &mut D) {
|
/// Perform a hardware reset followed by software reset
|
||||||
self.interface.reset(delay)
|
fn reset<D: hal::blocking::delay::DelayMs<u8>>(&mut self, delay: &mut D) -> Result<(), I::Error> {
|
||||||
|
self.interface.reset(delay);
|
||||||
|
Command::SoftReset.execute(&mut self.interface)?;
|
||||||
|
self.interface.busy_wait();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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, config: Config) -> 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::DummyLinePeriod(0x07).execute(&mut self.interface)?;
|
||||||
|
Command::GateLineWidth(0x04).execute(&mut self.interface)?;
|
||||||
|
|
||||||
|
Command::SourceDrivingVoltage(0x2D, 0xB2, 0x22).execute(&mut self.interface)?;
|
||||||
|
Command::WriteVCOM(0x3C).execute(&mut self.interface)?;
|
||||||
|
|
||||||
|
// POR is HiZ. Need pull from config
|
||||||
|
// Command::BorderWaveform(u8).execute(&mut self.interface)?;
|
||||||
|
|
||||||
|
// BufCommand::WriteLUT().execute(&mut self.interface)?;
|
||||||
|
|
||||||
|
Command::DataEntryMode(DataEntryMode::IncrementYIncrementX, IncrementAxis::Horizontal).execute(&mut self.interface)?;
|
||||||
|
|
||||||
|
let end = self.dimensions.cols / 8 - 1;
|
||||||
|
Command::StartEndXPosition(0, end).execute(&mut self.interface)?;
|
||||||
|
Command::StartEndYPosition(0, self.dimensions.rows).execute(&mut self.interface)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update<D: hal::blocking::delay::DelayMs<u8>>(&mut self, black: &[u8], red: &[u8], delay: &mut D) -> Result<(), I::Error> {
|
||||||
|
// Write the B/W RAM
|
||||||
|
Command::XAddress(0).execute(&mut self.interface)?;
|
||||||
|
Command::YAddress(0).execute(&mut self.interface)?;
|
||||||
|
BufCommand::WriteBlackData(&black).execute(&mut self.interface)?;
|
||||||
|
|
||||||
|
// Write the Red RAM
|
||||||
|
Command::XAddress(0).execute(&mut self.interface)?;
|
||||||
|
Command::YAddress(0).execute(&mut self.interface)?;
|
||||||
|
BufCommand::WriteRedData(&red).execute(&mut self.interface)?;
|
||||||
|
|
||||||
|
// Kick off the display update
|
||||||
|
Command::UpdateDisplayOption2(0xC7).execute(&mut self.interface)?;
|
||||||
|
Command::UpdateDisplay.execute(&mut self.interface)?;
|
||||||
|
delay.delay_ms(5); // Needed?
|
||||||
|
// 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
|
||||||
|
// busy.
|
||||||
|
self.interface.busy_wait();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -35,5 +94,3 @@ impl<I> Display<I> where I: DisplayInterface {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,12 +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;
|
||||||
|
|
||||||
|
const MAX_SPI_SPEED_HZ: u32 = 20_000_000;
|
||||||
|
|
||||||
pub trait DisplayInterface {
|
pub trait DisplayInterface {
|
||||||
type Error;
|
type Error;
|
||||||
|
|
||||||
fn send_command(&mut self, command: u8) -> Result<(), Self::Error>;
|
fn send_command(&mut self, command: u8) -> Result<(), Self::Error>;
|
||||||
fn send_data(&mut self, data: &[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);
|
fn reset<D: hal::blocking::delay::DelayMs<u8>>(&mut self, delay: &mut D);
|
||||||
|
fn busy_wait(&self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Interface<SPI, CS, BUSY, DC, RESET> {
|
pub struct Interface<SPI, CS, BUSY, DC, RESET> {
|
||||||
|
@ -62,10 +65,6 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn busy_wait(&self) {
|
|
||||||
while self.busy.is_high() {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
||||||
|
@ -97,4 +96,8 @@ where
|
||||||
self.dc.set_high();
|
self.dc.set_high();
|
||||||
self.write(data)
|
self.write(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn busy_wait(&self) {
|
||||||
|
while self.busy.is_high() {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue