mirror of
https://github.com/Feliix42/ssd1675.git
synced 2024-11-24 11:46:30 +00:00
Add builder for constructing display
This commit is contained in:
parent
d6ab8df12c
commit
f7e8275b30
6 changed files with 142 additions and 33 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
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,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,11 +208,11 @@ 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])
|
||||||
|
@ -326,7 +327,7 @@ 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 {
|
||||||
|
|
105
src/config.rs
Normal file
105
src/config.rs
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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,5 +1,6 @@
|
||||||
use hal;
|
use hal;
|
||||||
|
|
||||||
|
use config::Config;
|
||||||
use command::{BufCommand, Command, DataEntryMode, DeepSleepMode, IncrementAxis};
|
use command::{BufCommand, Command, DataEntryMode, DeepSleepMode, IncrementAxis};
|
||||||
use interface::DisplayInterface;
|
use interface::DisplayInterface;
|
||||||
|
|
||||||
|
@ -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,16 +31,15 @@ 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> where I: DisplayInterface {
|
||||||
pub fn new(interface: I, dimensions: Dimensions, rotation: Rotation) -> Self {
|
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
|
||||||
|
@ -50,34 +48,37 @@ impl<I> Display<I> where I: DisplayInterface {
|
||||||
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(())
|
||||||
}
|
}
|
||||||
|
@ -112,15 +113,15 @@ 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,13 @@ use interface::DisplayInterface;
|
||||||
use core::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
pub struct GraphicDisplay<'a, I> where I: DisplayInterface {
|
pub struct GraphicDisplay<'a, I> where I: DisplayInterface {
|
||||||
display: Display<I>,
|
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> where I: DisplayInterface {
|
||||||
pub fn new(display: Display<I>, black_buffer: &'a mut [u8], red_buffer: &'a mut [u8]) -> Self {
|
pub fn new(display: Display<'a, I>, black_buffer: &'a mut [u8], red_buffer: &'a mut [u8]) -> Self {
|
||||||
GraphicDisplay { display, black_buffer, red_buffer }
|
GraphicDisplay { display, black_buffer, red_buffer }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,15 +58,15 @@ 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> where I: DisplayInterface {
|
||||||
type Target = Display<I>;
|
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> where I: DisplayInterface {
|
||||||
fn deref_mut(&mut self) -> &mut Display<I> {
|
fn deref_mut(&mut self) -> &mut Display<'a, I> {
|
||||||
&mut self.display
|
&mut self.display
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 interface::DisplayInterface;
|
pub use interface::DisplayInterface;
|
||||||
pub use interface::Interface;
|
pub use interface::Interface;
|
||||||
pub use display::{Display, Dimensions, Rotation};
|
pub use display::{Display, Dimensions, Rotation};
|
||||||
pub use graphics::GraphicDisplay;
|
pub use graphics::GraphicDisplay;
|
||||||
pub use color::Color;
|
pub use color::Color;
|
||||||
|
pub use config::Builder;
|
||||||
|
|
Loading…
Reference in a new issue