mirror of
https://github.com/Feliix42/ssd1675.git
synced 2024-11-22 02:46:30 +00:00
Document most public items
This commit is contained in:
parent
b6d8d7aabb
commit
ca8175ca3e
9 changed files with 232 additions and 19 deletions
16
Cargo.toml
16
Cargo.toml
|
@ -4,6 +4,7 @@ version = "0.1.0"
|
||||||
authors = ["Wesley Moore <wes@wezm.net>"]
|
authors = ["Wesley Moore <wes@wezm.net>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
libm = "0.1.2"
|
||||||
|
|
||||||
[dependencies.embedded-hal]
|
[dependencies.embedded-hal]
|
||||||
features = ["unproven"]
|
features = ["unproven"]
|
||||||
|
@ -13,10 +14,19 @@ version = "0.2.2"
|
||||||
optional = true
|
optional = true
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dependencies.linux-embedded-hal]
|
||||||
linux-embedded-hal = "0.2.1" # for examples
|
optional = true
|
||||||
profont = "0.1"
|
version = "0.2.1"
|
||||||
|
|
||||||
|
[dependencies.profont]
|
||||||
|
optional = true
|
||||||
|
version = "0.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["graphics"]
|
default = ["graphics"]
|
||||||
graphics = ["embedded-graphics"]
|
graphics = ["embedded-graphics"]
|
||||||
|
examples = ["linux-embedded-hal", "profont"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "raspberry_pi_inky_phat"
|
||||||
|
required-features = ["examples"]
|
||||||
|
|
11
README.md
11
README.md
|
@ -1,4 +1,4 @@
|
||||||
# SSD1675 EPD display driver
|
# SSD1675 ePaper Display Driver
|
||||||
|
|
||||||
Rust driver for the [Solomon Systech SSD1675][SSD1675] e-Paper display (EPD)
|
Rust driver for the [Solomon Systech SSD1675][SSD1675] e-Paper display (EPD)
|
||||||
controller, for use with [embedded-hal].
|
controller, for use with [embedded-hal].
|
||||||
|
@ -8,7 +8,6 @@ controller, for use with [embedded-hal].
|
||||||
|
|
||||||
<img src="https://raw.githubusercontent.com/wezm/ssd1675/master/IMG_2198.jpg" width="459" alt="Photo of Inky pHAT ePaper display on Raspberry Pi Zero W" />
|
<img src="https://raw.githubusercontent.com/wezm/ssd1675/master/IMG_2198.jpg" width="459" alt="Photo of Inky pHAT ePaper display on Raspberry Pi Zero W" />
|
||||||
|
|
||||||
|
|
||||||
## 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`
|
||||||
|
@ -21,6 +20,14 @@ The library has been tested and confirmed working on these devices:
|
||||||
|
|
||||||
* Red/Black/White [Inky pHAT] version 2 on Raspberry Pi Zero (pictured above)
|
* Red/Black/White [Inky pHAT] version 2 on Raspberry Pi Zero (pictured above)
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
**Note:** To build the examples the `examples` feature needs to be enabled. E.g.
|
||||||
|
|
||||||
|
cargo build --release --examples --features examples
|
||||||
|
|
||||||
|
* [Raspberry Pi Inky pHAT example](https://github.com/wezm/ssd1675/blob/master/examples/raspberry_pi_inky_phat.rs).
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
* [Waveshare EPD driver](https://github.com/caemor/epd-waveshare)
|
* [Waveshare EPD driver](https://github.com/caemor/epd-waveshare)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/// Represents the state of a pixel in the display
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
pub enum Color {
|
pub enum Color {
|
||||||
Black,
|
Black,
|
||||||
|
|
|
@ -125,7 +125,7 @@ pub enum Command {
|
||||||
// WriteDisplayOption,
|
// WriteDisplayOption,
|
||||||
// WriteUserId,
|
// WriteUserId,
|
||||||
// OTPProgramMode,
|
// OTPProgramMode,
|
||||||
/// Set the number dummy line period in terms of gate line width (TGate)
|
/// Set the number of dummy line period in terms of gate line width (TGate)
|
||||||
DummyLinePeriod(u8),
|
DummyLinePeriod(u8),
|
||||||
/// Set the gate line width (TGate)
|
/// Set the gate line width (TGate)
|
||||||
GateLineWidth(u8),
|
GateLineWidth(u8),
|
||||||
|
|
|
@ -1,6 +1,25 @@
|
||||||
use command::{BufCommand, Command, DataEntryMode, IncrementAxis};
|
use command::{BufCommand, Command, DataEntryMode, IncrementAxis};
|
||||||
use display::{Dimensions, Rotation};
|
use display::{self, Dimensions, Rotation};
|
||||||
|
|
||||||
|
/// Builder for constructing a display Config.
|
||||||
|
///
|
||||||
|
/// Dimensions must supplied, all other settings will use a default value if not supplied. However
|
||||||
|
/// it's likely that LUT values will need to be supplied to successfully use a display.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use ssd1675::{Builder, Dimensions, Rotation};
|
||||||
|
///
|
||||||
|
/// let config = Builder::new()
|
||||||
|
/// .dimensions(Dimensions {
|
||||||
|
/// rows: 212,
|
||||||
|
/// cols: 104,
|
||||||
|
/// })
|
||||||
|
/// .rotation(Rotation::Rotate270)
|
||||||
|
/// .build()
|
||||||
|
/// .expect("invalid configuration");
|
||||||
|
/// ```
|
||||||
pub struct Builder<'a> {
|
pub struct Builder<'a> {
|
||||||
dummy_line_period: Command,
|
dummy_line_period: Command,
|
||||||
gate_line_width: Command,
|
gate_line_width: Command,
|
||||||
|
@ -11,6 +30,9 @@ pub struct Builder<'a> {
|
||||||
rotation: Rotation,
|
rotation: Rotation,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error returned if Builder configuration is invalid.
|
||||||
|
///
|
||||||
|
/// Currently only returned if a configuration is built without dimensions.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BuilderError {}
|
pub struct BuilderError {}
|
||||||
|
|
||||||
|
@ -42,10 +64,14 @@ impl<'a> Default for Builder<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Builder<'a> {
|
impl<'a> Builder<'a> {
|
||||||
|
/// Create a new Builder.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the number of dummy line period in terms of gate line width (TGate).
|
||||||
|
///
|
||||||
|
/// Defaults to 0x07. Corresponds to command 0x3A.
|
||||||
pub fn dummy_line_period(self, dummy_line_period: u8) -> Self {
|
pub fn dummy_line_period(self, dummy_line_period: u8) -> Self {
|
||||||
Self {
|
Self {
|
||||||
dummy_line_period: Command::DummyLinePeriod(dummy_line_period),
|
dummy_line_period: Command::DummyLinePeriod(dummy_line_period),
|
||||||
|
@ -53,6 +79,9 @@ impl<'a> Builder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the gate line width (TGate).
|
||||||
|
///
|
||||||
|
/// Defaults to 0x04. Corresponds to command 0x3B.
|
||||||
pub fn gate_line_width(self, gate_line_width: u8) -> Self {
|
pub fn gate_line_width(self, gate_line_width: u8) -> Self {
|
||||||
Self {
|
Self {
|
||||||
gate_line_width: Command::GateLineWidth(gate_line_width),
|
gate_line_width: Command::GateLineWidth(gate_line_width),
|
||||||
|
@ -60,6 +89,9 @@ impl<'a> Builder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set VCOM register value.
|
||||||
|
///
|
||||||
|
/// Defaults to 0x3C. Corresponds to command 0x2C.
|
||||||
pub fn vcom(self, value: u8) -> Self {
|
pub fn vcom(self, value: u8) -> Self {
|
||||||
Self {
|
Self {
|
||||||
write_vcom: Command::WriteVCOM(value),
|
write_vcom: Command::WriteVCOM(value),
|
||||||
|
@ -67,6 +99,13 @@ impl<'a> Builder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set lookup table (70 bytes).
|
||||||
|
///
|
||||||
|
/// **Note:** The supplied slice must be exactly 70 bytes long.
|
||||||
|
///
|
||||||
|
/// There is no default for the lookup table. Corresponds to command 0x32. If not supplied then
|
||||||
|
/// the default in the controller is used. Apparently the display manufacturer will normally
|
||||||
|
/// supply the LUT values for a particular display batch.
|
||||||
pub fn lut(self, lut: &'a [u8]) -> Self {
|
pub fn lut(self, lut: &'a [u8]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
write_lut: Some(BufCommand::WriteLUT(lut)),
|
write_lut: Some(BufCommand::WriteLUT(lut)),
|
||||||
|
@ -74,6 +113,10 @@ impl<'a> Builder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Define data entry sequence.
|
||||||
|
///
|
||||||
|
/// Defaults to DataEntryMode::IncrementAxis, IncrementAxis::Horizontal. Corresponds to command
|
||||||
|
/// 0x11.
|
||||||
pub fn data_entry_mode(
|
pub fn data_entry_mode(
|
||||||
self,
|
self,
|
||||||
data_entry_mode: DataEntryMode,
|
data_entry_mode: DataEntryMode,
|
||||||
|
@ -85,17 +128,41 @@ impl<'a> Builder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the display dimensions.
|
||||||
|
///
|
||||||
|
/// There is no default for this setting. The dimensions must be set for the builder to
|
||||||
|
/// successfully build a Config.
|
||||||
pub fn dimensions(self, dimensions: Dimensions) -> Self {
|
pub fn dimensions(self, dimensions: Dimensions) -> Self {
|
||||||
|
assert!(
|
||||||
|
dimensions.cols % 8 == 0,
|
||||||
|
"columns must be evenly divisible by 8"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
dimensions.rows <= display::MAX_GATE_OUTPUTS,
|
||||||
|
"rows must be less than MAX_GATE_OUTPUTS"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
dimensions.cols <= display::MAX_SOURCE_OUTPUTS,
|
||||||
|
"cols must be less than MAX_SOURCE_OUTPUTS"
|
||||||
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
dimensions: Some(dimensions),
|
dimensions: Some(dimensions),
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the display rotation.
|
||||||
|
///
|
||||||
|
/// Defaults to no rotation (`Rotation::Rotate0`). Use this to translate between the physical
|
||||||
|
/// rotation of the display and how the data is displayed on the display.
|
||||||
pub fn rotation(self, rotation: Rotation) -> Self {
|
pub fn rotation(self, rotation: Rotation) -> Self {
|
||||||
Self { rotation, ..self }
|
Self { rotation, ..self }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build the display Config.
|
||||||
|
///
|
||||||
|
/// Will fail if dimensions are not set.
|
||||||
pub fn build(self) -> Result<Config<'a>, BuilderError> {
|
pub fn build(self) -> Result<Config<'a>, BuilderError> {
|
||||||
Ok(Config {
|
Ok(Config {
|
||||||
dummy_line_period: self.dummy_line_period,
|
dummy_line_period: self.dummy_line_period,
|
||||||
|
|
|
@ -1,22 +1,38 @@
|
||||||
|
extern crate libm;
|
||||||
|
|
||||||
use hal;
|
use hal;
|
||||||
|
|
||||||
use command::{BufCommand, Command, DataEntryMode, DeepSleepMode, IncrementAxis};
|
use command::{BufCommand, Command, DeepSleepMode};
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use interface::DisplayInterface;
|
use interface::DisplayInterface;
|
||||||
|
|
||||||
// Max display resolution is 160x296
|
// Max display resolution is 160x296
|
||||||
const MAX_SOURCE_OUTPUTS: usize = 160;
|
/// The maximum number of rows supported by the controller
|
||||||
const MAX_GATE_OUTPUTS: usize = 296;
|
pub const MAX_GATE_OUTPUTS: u16 = 296;
|
||||||
|
/// The maximum number of columns supported by the controller
|
||||||
|
pub const MAX_SOURCE_OUTPUTS: u8 = 160;
|
||||||
|
|
||||||
// Magic numbers from the data sheet
|
// Magic numbers from the data sheet
|
||||||
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;
|
||||||
|
|
||||||
|
/// Represents the dimensions of the display.
|
||||||
pub struct Dimensions {
|
pub struct Dimensions {
|
||||||
|
/// The number of rows the display has.
|
||||||
|
///
|
||||||
|
/// Must be less than or equal to MAX_GATE_OUTPUTS.
|
||||||
pub rows: u16,
|
pub rows: u16,
|
||||||
|
/// The number of columns the display has.
|
||||||
|
///
|
||||||
|
/// Must be less than or equal to MAX_SOURCE_OUTPUTS.
|
||||||
pub cols: u8,
|
pub cols: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents the physical rotation of the display relative to the native orientation.
|
||||||
|
///
|
||||||
|
/// For example the native orientation of the Inky pHAT display is a tall (portrait) 104x212
|
||||||
|
/// display. `Rotate270` can be used to make it the right way up when attached to a Raspberry Pi
|
||||||
|
/// Zero with the ports on the top.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum Rotation {
|
pub enum Rotation {
|
||||||
Rotate0,
|
Rotate0,
|
||||||
|
@ -26,11 +42,13 @@ pub enum Rotation {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Rotation {
|
impl Default for Rotation {
|
||||||
|
/// Default is no rotation (`Rotate0`).
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Rotation::Rotate0
|
Rotation::Rotate0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A configured display with a hardware interface.
|
||||||
pub struct Display<'a, I>
|
pub struct Display<'a, I>
|
||||||
where
|
where
|
||||||
I: DisplayInterface,
|
I: DisplayInterface,
|
||||||
|
@ -43,12 +61,16 @@ impl<'a, I> Display<'a, I>
|
||||||
where
|
where
|
||||||
I: DisplayInterface,
|
I: DisplayInterface,
|
||||||
{
|
{
|
||||||
|
/// Create a new display instance from a DisplayInterface and Config.
|
||||||
|
///
|
||||||
|
/// The `Config` is typically created with `config::Builder`.
|
||||||
pub fn new(interface: I, config: Config<'a>) -> Self {
|
pub fn new(interface: I, config: Config<'a>) -> Self {
|
||||||
// TODO: Assert dimensions are evenly divisible by 8
|
|
||||||
Self { interface, config }
|
Self { interface, config }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform a hardware reset followed by software reset
|
/// Perform a hardware reset followed by software reset.
|
||||||
|
///
|
||||||
|
/// This will wake a controller that has previously entered deep sleep.
|
||||||
pub fn reset<D: hal::blocking::delay::DelayMs<u8>>(
|
pub fn reset<D: hal::blocking::delay::DelayMs<u8>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
delay: &mut D,
|
delay: &mut D,
|
||||||
|
@ -79,7 +101,6 @@ where
|
||||||
// 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)?;
|
|
||||||
if let Some(ref write_lut) = self.config.write_lut {
|
if let Some(ref write_lut) = self.config.write_lut {
|
||||||
write_lut.execute(&mut self.interface)?;
|
write_lut.execute(&mut self.interface)?;
|
||||||
}
|
}
|
||||||
|
@ -93,6 +114,10 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update the display by writing the supplied B/W and Red buffers to the controller.
|
||||||
|
///
|
||||||
|
/// This method will write the two buffers to the controller then initiate the update
|
||||||
|
/// display command. Currently it will busy wait until the update has completed.
|
||||||
pub fn update<D: hal::blocking::delay::DelayMs<u8>>(
|
pub fn update<D: hal::blocking::delay::DelayMs<u8>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
black: &[u8],
|
black: &[u8],
|
||||||
|
@ -100,7 +125,7 @@ where
|
||||||
delay: &mut D,
|
delay: &mut D,
|
||||||
) -> Result<(), I::Error> {
|
) -> 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 = libm::ceilf((self.rows() * self.cols() as u16) as f32 / 8.) as usize;
|
||||||
Command::XAddress(0).execute(&mut self.interface)?;
|
Command::XAddress(0).execute(&mut self.interface)?;
|
||||||
Command::YAddress(0).execute(&mut self.interface)?;
|
Command::YAddress(0).execute(&mut self.interface)?;
|
||||||
BufCommand::WriteBlackData(&black[..buf_limit]).execute(&mut self.interface)?;
|
BufCommand::WriteBlackData(&black[..buf_limit]).execute(&mut self.interface)?;
|
||||||
|
@ -123,18 +148,25 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enter deep sleep mode.
|
||||||
|
///
|
||||||
|
/// This puts the display controller into a low power mode. `reset` must be called to wake it
|
||||||
|
/// from sleep.
|
||||||
pub fn deep_sleep(&mut self) -> Result<(), I::Error> {
|
pub fn deep_sleep(&mut self) -> Result<(), I::Error> {
|
||||||
Command::DeepSleepMode(DeepSleepMode::PreserveRAM).execute(&mut self.interface)
|
Command::DeepSleepMode(DeepSleepMode::PreserveRAM).execute(&mut self.interface)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the number of rows the display has.
|
||||||
pub fn rows(&self) -> u16 {
|
pub fn rows(&self) -> u16 {
|
||||||
self.config.dimensions.rows
|
self.config.dimensions.rows
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the number of columns the display has.
|
||||||
pub fn cols(&self) -> u8 {
|
pub fn cols(&self) -> u8 {
|
||||||
self.config.dimensions.cols
|
self.config.dimensions.cols
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the rotation the display was configured with.
|
||||||
pub fn rotation(&self) -> Rotation {
|
pub fn rotation(&self) -> Rotation {
|
||||||
self.config.rotation
|
self.config.rotation
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,11 @@ use display::{Display, Rotation};
|
||||||
use hal;
|
use hal;
|
||||||
use interface::DisplayInterface;
|
use interface::DisplayInterface;
|
||||||
|
|
||||||
|
/// A display that holds buffers for drawing into and updating the display from.
|
||||||
|
///
|
||||||
|
/// When the `graphics` feature is enabled `GraphicDisplay` implements the `Draw` trait from
|
||||||
|
/// [embedded-graphics](https://crates.io/crates/embedded-graphics). This allows basic shapes and
|
||||||
|
/// text to be drawn on the display.
|
||||||
pub struct GraphicDisplay<'a, I>
|
pub struct GraphicDisplay<'a, I>
|
||||||
where
|
where
|
||||||
I: DisplayInterface,
|
I: DisplayInterface,
|
||||||
|
@ -17,6 +22,10 @@ impl<'a, I> GraphicDisplay<'a, I>
|
||||||
where
|
where
|
||||||
I: DisplayInterface,
|
I: DisplayInterface,
|
||||||
{
|
{
|
||||||
|
/// Promote a `Display` to a `GraphicDisplay`.
|
||||||
|
///
|
||||||
|
/// B/W and Red buffers for drawing into must be supplied. These should be `rows` * `cols` in
|
||||||
|
/// length.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
display: Display<'a, I>,
|
display: Display<'a, I>,
|
||||||
black_buffer: &'a mut [u8],
|
black_buffer: &'a mut [u8],
|
||||||
|
@ -29,6 +38,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update the display by writing the buffers to the controller.
|
||||||
pub fn update<D: hal::blocking::delay::DelayMs<u8>>(
|
pub fn update<D: hal::blocking::delay::DelayMs<u8>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
delay: &mut D,
|
delay: &mut D,
|
||||||
|
@ -37,6 +47,7 @@ where
|
||||||
.update(self.black_buffer, self.red_buffer, delay)
|
.update(self.black_buffer, self.red_buffer, delay)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clear the buffers, filling them a single color.
|
||||||
pub fn clear(&mut self, color: Color) {
|
pub fn clear(&mut self, color: Color) {
|
||||||
let (black, red) = match color {
|
let (black, red) = match color {
|
||||||
Color::White => (0xFF, 0x00),
|
Color::White => (0xFF, 0x00),
|
||||||
|
|
|
@ -14,16 +14,71 @@ pub trait DisplayInterface {
|
||||||
fn busy_wait(&self);
|
fn busy_wait(&self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The hardware interface to a display.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
///
|
||||||
|
/// This example uses the Linux implementation of the embedded HAL traits to build a display
|
||||||
|
/// interface. For a complete example see [the Raspberry Pi Inky pHAT example](https://github.com/wezm/ssd1675/blob/master/examples/raspberry_pi_inky_phat.rs).
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// extern crate linux_embedded_hal;
|
||||||
|
/// use linux_embedded_hal::spidev::{self, SpidevOptions};
|
||||||
|
/// use linux_embedded_hal::sysfs_gpio::Direction;
|
||||||
|
/// use linux_embedded_hal::Delay;
|
||||||
|
/// use linux_embedded_hal::{Pin, Spidev};
|
||||||
|
///
|
||||||
|
/// extern crate ssd1675;
|
||||||
|
/// use ssd1675::{Builder, Color, Dimensions, Display, GraphicDisplay, Rotation};
|
||||||
|
///
|
||||||
|
/// // Configure SPI
|
||||||
|
/// let mut spi = Spidev::open("/dev/spidev0.0").expect("SPI device");
|
||||||
|
/// let options = SpidevOptions::new()
|
||||||
|
/// .bits_per_word(8)
|
||||||
|
/// .max_speed_hz(4_000_000)
|
||||||
|
/// .mode(spidev::SPI_MODE_0)
|
||||||
|
/// .build();
|
||||||
|
/// spi.configure(&options).expect("SPI configuration");
|
||||||
|
///
|
||||||
|
/// // 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() {}
|
||||||
|
/// cs.set_direction(Direction::Out).expect("CS Direction");
|
||||||
|
/// cs.set_value(1).expect("CS Value set to 1");
|
||||||
|
///
|
||||||
|
/// let busy = Pin::new(17); // BCM17
|
||||||
|
/// busy.export().expect("busy export");
|
||||||
|
/// while !busy.is_exported() {}
|
||||||
|
/// busy.set_direction(Direction::In).expect("busy Direction");
|
||||||
|
///
|
||||||
|
/// let dc = Pin::new(22); // BCM22
|
||||||
|
/// dc.export().expect("dc export");
|
||||||
|
/// while !dc.is_exported() {}
|
||||||
|
/// dc.set_direction(Direction::Out).expect("dc Direction");
|
||||||
|
/// dc.set_value(1).expect("dc Value set to 1");
|
||||||
|
///
|
||||||
|
/// 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_value(1).expect("reset Value set to 1");
|
||||||
|
///
|
||||||
|
/// // Build the interface from the pins and SPI device
|
||||||
|
/// let controller = ssd1675::Interface::new(spi, cs, busy, dc, reset);
|
||||||
pub struct Interface<SPI, CS, BUSY, DC, RESET> {
|
pub struct Interface<SPI, CS, BUSY, DC, RESET> {
|
||||||
/// SPI
|
/// SPI interface
|
||||||
spi: SPI,
|
spi: SPI,
|
||||||
/// CS for SPI
|
/// CS (chip select) for SPI (output)
|
||||||
cs: CS,
|
cs: CS,
|
||||||
/// Low for busy, Wait until display is ready!
|
/// Active low busy pin (input)
|
||||||
busy: BUSY,
|
busy: BUSY,
|
||||||
/// Data/Command Control Pin (High for data, Low for command)
|
/// Data/Command Control Pin (High for data, Low for command) (output)
|
||||||
dc: DC,
|
dc: DC,
|
||||||
/// Pin for Reseting
|
/// Pin for reseting the controller (output)
|
||||||
reset: RESET,
|
reset: RESET,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +90,7 @@ where
|
||||||
DC: hal::digital::OutputPin,
|
DC: hal::digital::OutputPin,
|
||||||
RESET: hal::digital::OutputPin,
|
RESET: hal::digital::OutputPin,
|
||||||
{
|
{
|
||||||
|
/// Create a new Interface from embedded hal traits.
|
||||||
pub fn new(spi: SPI, cs: CS, busy: BUSY, dc: DC, reset: RESET) -> Self {
|
pub fn new(spi: SPI, cs: CS, busy: BUSY, dc: DC, reset: RESET) -> Self {
|
||||||
Self {
|
Self {
|
||||||
spi,
|
spi,
|
||||||
|
|
29
src/lib.rs
29
src/lib.rs
|
@ -1,5 +1,34 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
//! SSD1675 ePaper Display Driver
|
||||||
|
//!
|
||||||
|
//! For a complete example see
|
||||||
|
//! [the Raspberry Pi Inky pHAT example](https://github.com/wezm/ssd1675/blob/master/examples/raspberry_pi_inky_phat.rs).
|
||||||
|
//!
|
||||||
|
//! ### Usage
|
||||||
|
//!
|
||||||
|
//! To control a display you will need:
|
||||||
|
//!
|
||||||
|
//! * An [Interface] to the controller
|
||||||
|
//! * A [display configuration][Config]
|
||||||
|
//! * A [Display]
|
||||||
|
//!
|
||||||
|
//! The `Interface` captures the details of the hardware connection to the SSD1675 controller. This
|
||||||
|
//! includes an SPI device and some GPIO pins. The SSD1675 can control many different displays that
|
||||||
|
//! vary in dimensions, rotation, and driving characteristics. The [Config] captures these details.
|
||||||
|
//! To aid in constructing the `Config` there is a [Builder] interface. Finally when you have an
|
||||||
|
//! interface and a Config a Display instance can be created. Optionally the Display can be
|
||||||
|
//! promoted to a [GraphicDisplay], which allows it to use the functionality from the
|
||||||
|
//! [embedded-graphics crate]. The plain display only provides the ability to update the display by
|
||||||
|
//! passing black/white and red buffers.
|
||||||
|
//!
|
||||||
|
//! To update the display you will typically follow this flow:
|
||||||
|
//!
|
||||||
|
//! * [reset]
|
||||||
|
//! * [clear]
|
||||||
|
//! * [update]
|
||||||
|
//! * [sleep]
|
||||||
|
|
||||||
extern crate embedded_hal as hal;
|
extern crate embedded_hal as hal;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
Loading…
Reference in a new issue