Skip to content

Commit

Permalink
make everything async
Browse files Browse the repository at this point in the history
  • Loading branch information
vhdirk committed Nov 6, 2023
1 parent 2c58432 commit 83878fc
Show file tree
Hide file tree
Showing 23 changed files with 2,167 additions and 1,638 deletions.
6 changes: 2 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ edition = "2021"

[dependencies]
embedded-graphics-core = { version = "0.4", optional = true }

embedded-hal = { version = "1.0.0-rc.1" }
embedded-hal-bus = { version = "0.1.0-rc.1", git="https://github.com/rust-embedded/embedded-hal" }
embedded-hal-async = { optional = true, version = "1.0.0-rc.1", git="https://github.com/rust-embedded/embedded-hal" }

bit_field = "0.10.1"
Expand Down Expand Up @@ -58,13 +56,13 @@ required-features = ["linux-dev"]

[features]
# Remove the linux-dev feature to build the tests on non unix systems
default = ["async", "epd2in13_v3"] #["dep:embedded-hal", "graphics", "linux-dev", "epd2in13_v3"]
default = ["async", "graphics", "linux-dev", "epd2in13_v3"]

graphics = ["embedded-graphics-core"]
epd2in13_v2 = []
epd2in13_v3 = []
linux-dev = []
async = ["dep:embedded-hal-async", "embedded-hal-bus/async"]
async = ["dep:embedded-hal-async"]

# Offers an alternative fast full lut for type_a displays, but the refreshed screen isnt as clean looking
type_a_alternative_faster_lut = []
187 changes: 107 additions & 80 deletions src/epd1in54/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White;
const IS_BUSY_LOW: bool = false;
const SINGLE_BYTE_WRITE: bool = true;

use embedded_hal::{delay::*, digital::*, spi::SpiDevice};
use embedded_hal::digital::{InputPin, OutputPin};
use embedded_hal_async::{delay::DelayUs, spi::SpiDevice};

use crate::type_a::{
command::Command,
Expand Down Expand Up @@ -96,48 +97,55 @@ where
RST: OutputPin,
DELAY: DelayUs,
{
fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.interface.reset(delay, 10_000, 10_000);
async fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.interface.reset(delay, 10_000, 10_000).await;

// 3 Databytes:
// A[7:0]
// 0.. A[8]
// 0.. B[2:0]
// Default Values: A = Height of Screen (0x127), B = 0x00 (GD, SM and TB=0?)
self.interface.cmd_with_data(
spi,
Command::DriverOutputControl,
&[HEIGHT as u8, (HEIGHT >> 8) as u8, 0x00],
)?;
self.interface
.cmd_with_data(
spi,
Command::DriverOutputControl,
&[HEIGHT as u8, (HEIGHT >> 8) as u8, 0x00],
)
.await?;

// 3 Databytes: (and default values from datasheet and arduino)
// 1 .. A[6:0] = 0xCF | 0xD7
// 1 .. B[6:0] = 0xCE | 0xD6
// 1 .. C[6:0] = 0x8D | 0x9D
//TODO: test
self.interface
.cmd_with_data(spi, Command::BoosterSoftStartControl, &[0xD7, 0xD6, 0x9D])?;
.cmd_with_data(spi, Command::BoosterSoftStartControl, &[0xD7, 0xD6, 0x9D])
.await?;

// One Databyte with value 0xA8 for 7V VCOM
self.interface
.cmd_with_data(spi, Command::WriteVcomRegister, &[0xA8])?;
.cmd_with_data(spi, Command::WriteVcomRegister, &[0xA8])
.await?;

// One Databyte with default value 0x1A for 4 dummy lines per gate
self.interface
.cmd_with_data(spi, Command::SetDummyLinePeriod, &[0x1A])?;
.cmd_with_data(spi, Command::SetDummyLinePeriod, &[0x1A])
.await?;

// One Databyte with default value 0x08 for 2us per line
self.interface
.cmd_with_data(spi, Command::SetGateLineWidth, &[0x08])?;
.cmd_with_data(spi, Command::SetGateLineWidth, &[0x08])
.await?;

// One Databyte with default value 0x03
// -> address: x increment, y increment, address counter is updated in x direction
self.interface
.cmd_with_data(spi, Command::DataEntryModeSetting, &[0x03])?;
.cmd_with_data(spi, Command::DataEntryModeSetting, &[0x03])
.await?;

self.set_lut(spi, delay, None)?;
self.set_lut(spi, delay, None).await?;

self.wait_until_idle(spi, delay)?;
self.wait_until_idle(spi, delay).await?;
Ok(())
}
}
Expand All @@ -160,7 +168,7 @@ where
HEIGHT
}

fn new(
async fn new(
spi: &mut SPI,
busy: BUSY,
dc: DC,
Expand All @@ -176,39 +184,41 @@ where
refresh: RefreshLut::Full,
};

epd.init(spi, delay)?;
epd.init(spi, delay).await?;

Ok(epd)
}

fn wake_up(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.init(spi, delay)
async fn wake_up(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.init(spi, delay).await
}

fn sleep(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.wait_until_idle(spi, delay)?;
async fn sleep(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.wait_until_idle(spi, delay).await?;
// 0x00 for Normal mode (Power on Reset), 0x01 for Deep Sleep Mode
//TODO: is 0x00 needed here or would 0x01 be even more efficient?
self.interface
.cmd_with_data(spi, Command::DeepSleepMode, &[0x00])?;
.cmd_with_data(spi, Command::DeepSleepMode, &[0x00])
.await?;
Ok(())
}

fn update_frame(
async fn update_frame(
&mut self,
spi: &mut SPI,
buffer: &[u8],
delay: &mut DELAY,
) -> Result<(), SPI::Error> {
self.wait_until_idle(spi, delay)?;
self.use_full_frame(spi, delay)?;
self.wait_until_idle(spi, delay).await?;
self.use_full_frame(spi, delay).await?;
self.interface
.cmd_with_data(spi, Command::WriteRam, buffer)?;
.cmd_with_data(spi, Command::WriteRam, buffer)
.await?;
Ok(())
}

//TODO: update description: last 3 bits will be ignored for width and x_pos
fn update_partial_frame(
async fn update_partial_frame(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
Expand All @@ -218,50 +228,54 @@ where
width: u32,
height: u32,
) -> Result<(), SPI::Error> {
self.wait_until_idle(spi, delay)?;
self.set_ram_area(spi, delay, x, y, x + width, y + height)?;
self.set_ram_counter(spi, delay, x, y)?;
self.wait_until_idle(spi, delay).await?;
self.set_ram_area(spi, delay, x, y, x + width, y + height)
.await?;
self.set_ram_counter(spi, delay, x, y).await?;

self.interface
.cmd_with_data(spi, Command::WriteRam, buffer)?;
.cmd_with_data(spi, Command::WriteRam, buffer)
.await?;
Ok(())
}

fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.wait_until_idle(spi, delay)?;
async fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.wait_until_idle(spi, delay).await?;
// enable clock signal, enable cp, display pattern -> 0xC4 (tested with the arduino version)
//TODO: test control_1 or control_2 with default value 0xFF (from the datasheet)
self.interface
.cmd_with_data(spi, Command::DisplayUpdateControl2, &[0xC4])?;
.cmd_with_data(spi, Command::DisplayUpdateControl2, &[0xC4])
.await?;

self.interface.cmd(spi, Command::MasterActivation)?;
self.interface.cmd(spi, Command::MasterActivation).await?;
// MASTER Activation should not be interupted to avoid currption of panel images
// therefore a terminate command is send
self.interface.cmd(spi, Command::Nop)?;
self.interface.cmd(spi, Command::Nop).await?;
Ok(())
}

fn update_and_display_frame(
async fn update_and_display_frame(
&mut self,
spi: &mut SPI,
buffer: &[u8],
delay: &mut DELAY,
) -> Result<(), SPI::Error> {
self.update_frame(spi, buffer, delay)?;
self.display_frame(spi, delay)?;
self.update_frame(spi, buffer, delay).await?;
self.display_frame(spi, delay).await?;
Ok(())
}

fn clear_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.wait_until_idle(spi, delay)?;
self.use_full_frame(spi, delay)?;
async fn clear_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.wait_until_idle(spi, delay).await?;
self.use_full_frame(spi, delay).await?;

// clear the ram with the background color
let color = self.background_color.get_byte_value();

self.interface.cmd(spi, Command::WriteRam)?;
self.interface.cmd(spi, Command::WriteRam).await?;
self.interface
.data_x_times(spi, color, WIDTH / 8 * HEIGHT)?;
.data_x_times(spi, color, WIDTH / 8 * HEIGHT)
.await?;
Ok(())
}

Expand All @@ -273,7 +287,7 @@ where
&self.background_color
}

fn set_lut(
async fn set_lut(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
Expand All @@ -283,13 +297,17 @@ where
self.refresh = refresh_lut;
}
match self.refresh {
RefreshLut::Full => self.set_lut_helper(spi, delay, &LUT_FULL_UPDATE),
RefreshLut::Quick => self.set_lut_helper(spi, delay, &LUT_PARTIAL_UPDATE),
RefreshLut::Full => self.set_lut_helper(spi, delay, &LUT_FULL_UPDATE).await,
RefreshLut::Quick => self.set_lut_helper(spi, delay, &LUT_PARTIAL_UPDATE).await,
}
}

fn wait_until_idle(&mut self, _spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.interface.wait_until_idle(delay, IS_BUSY_LOW);
async fn wait_until_idle(
&mut self,
_spi: &mut SPI,
delay: &mut DELAY,
) -> Result<(), SPI::Error> {
let _ = self.interface.wait_until_idle(delay, IS_BUSY_LOW).await;
Ok(())
}
}
Expand All @@ -302,19 +320,20 @@ where
RST: OutputPin,
DELAY: DelayUs,
{
pub(crate) fn use_full_frame(
pub(crate) async fn use_full_frame(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
) -> Result<(), SPI::Error> {
// choose full frame/ram
self.set_ram_area(spi, delay, 0, 0, WIDTH - 1, HEIGHT - 1)?;
self.set_ram_area(spi, delay, 0, 0, WIDTH - 1, HEIGHT - 1)
.await?;

// start from the beginning
self.set_ram_counter(spi, delay, 0, 0)
self.set_ram_counter(spi, delay, 0, 0).await
}

pub(crate) fn set_ram_area(
pub(crate) async fn set_ram_area(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
Expand All @@ -323,65 +342,73 @@ where
end_x: u32,
end_y: u32,
) -> Result<(), SPI::Error> {
self.wait_until_idle(spi, delay)?;
let _ = self.wait_until_idle(spi, delay).await;
assert!(start_x < end_x);
assert!(start_y < end_y);

// x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram
// aren't relevant
self.interface.cmd_with_data(
spi,
Command::SetRamXAddressStartEndPosition,
&[(start_x >> 3) as u8, (end_x >> 3) as u8],
)?;
self.interface
.cmd_with_data(
spi,
Command::SetRamXAddressStartEndPosition,
&[(start_x >> 3) as u8, (end_x >> 3) as u8],
)
.await?;

// 2 Databytes: A[7:0] & 0..A[8] for each - start and end
self.interface.cmd_with_data(
spi,
Command::SetRamYAddressStartEndPosition,
&[
start_y as u8,
(start_y >> 8) as u8,
end_y as u8,
(end_y >> 8) as u8,
],
)?;
self.interface
.cmd_with_data(
spi,
Command::SetRamYAddressStartEndPosition,
&[
start_y as u8,
(start_y >> 8) as u8,
end_y as u8,
(end_y >> 8) as u8,
],
)
.await?;
Ok(())
}

pub(crate) fn set_ram_counter(
pub(crate) async fn set_ram_counter(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
x: u32,
y: u32,
) -> Result<(), SPI::Error> {
self.wait_until_idle(spi, delay)?;
self.wait_until_idle(spi, delay).await?;
// x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram
// aren't relevant
self.interface
.cmd_with_data(spi, Command::SetRamXAddressCounter, &[(x >> 3) as u8])?;
.cmd_with_data(spi, Command::SetRamXAddressCounter, &[(x >> 3) as u8])
.await?;

// 2 Databytes: A[7:0] & 0..A[8]
self.interface.cmd_with_data(
spi,
Command::SetRamYAddressCounter,
&[y as u8, (y >> 8) as u8],
)?;
self.interface
.cmd_with_data(
spi,
Command::SetRamYAddressCounter,
&[y as u8, (y >> 8) as u8],
)
.await?;
Ok(())
}

fn set_lut_helper(
async fn set_lut_helper(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
buffer: &[u8],
) -> Result<(), SPI::Error> {
self.wait_until_idle(spi, delay)?;
self.wait_until_idle(spi, delay).await?;
assert!(buffer.len() == 30);

self.interface
.cmd_with_data(spi, Command::WriteLutRegister, buffer)?;
.cmd_with_data(spi, Command::WriteLutRegister, buffer)
.await?;
Ok(())
}
}
Expand Down
Loading

0 comments on commit 83878fc

Please sign in to comment.