Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 126 additions & 48 deletions examples/epd7in5_v2.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// This example tests rotations and draws analog clock, tests default fonts of embedded-graphics crate and displays an image of Ferris from examples/assets/ directory.
use std::error::Error;

// This example tests rotations and draws analog clock, tests default fonts of embedded-graphics crate, displays an image of Ferris from examples/assets/ directory and showcases partial updating with a digital clock.
use embedded_graphics::{
image::Image,
image::ImageRaw,
mono_font::ascii::*,
mono_font::MonoTextStyleBuilder,
image::{Image, ImageRaw},
mono_font::{ascii::*, MonoTextStyleBuilder},
prelude::*,
primitives::{Circle, Line, PrimitiveStyleBuilder},
text::{Baseline, Text, TextStyleBuilder},
Expand All @@ -12,48 +12,54 @@ use embedded_hal::delay::DelayNs;
#[cfg(feature = "graphics")]
use epd_waveshare::{color::Color, epd7in5_v2::*, graphics::DisplayRotation, prelude::*};
use linux_embedded_hal::{
spidev::{self, SpidevOptions},
sysfs_gpio::Direction,
Delay, SPIError, SpidevDevice, SysfsPin,
gpio_cdev::{Chip, LineRequestFlags},
spidev::{SpiModeFlags, SpidevOptions},
CdevPin, Delay, SpidevDevice,
};

fn main() -> Result<(), SPIError> {
// GPIO pin definitions (BCM numbering - no offset needed for cdev)
const EPD_RST_PIN: u32 = 17;
const EPD_DC_PIN: u32 = 25;
const EPD_BUSY_PIN: u32 = 24;
const EPD_PWR_PIN: u32 = 18;

fn main() -> Result<(), Box<dyn Error>> {
// Set up the device
let mut spi = SpidevDevice::open("/dev/spidev0.0").expect("spidev directory");
// Open the GPIO chip (usually gpiochip0 on Raspberry Pi)
let mut chip = Chip::new("/dev/gpiochip0")?;

// Get GPIO lines and configure them
let rst_line = chip.get_line(EPD_RST_PIN)?;
let rst_handle = rst_line.request(LineRequestFlags::OUTPUT, 0, "epd-rst")?;
let rst_pin = CdevPin::new(rst_handle)?;

let dc_line = chip.get_line(EPD_DC_PIN)?;
let dc_handle = dc_line.request(LineRequestFlags::OUTPUT, 0, "epd-dc")?;
let dc_pin = CdevPin::new(dc_handle)?;

let busy_line = chip.get_line(EPD_BUSY_PIN)?;
let busy_handle = busy_line.request(LineRequestFlags::INPUT, 0, "epd-busy")?;
let busy_pin = CdevPin::new(busy_handle)?;

let pwr_line = chip.get_line(EPD_PWR_PIN)?;
let _ = pwr_line.request(LineRequestFlags::OUTPUT, 1, "epd-pwr")?;

// Initialize SPI
let mut spi = SpidevDevice::open("/dev/spidev0.0")?;
let options = SpidevOptions::new()
.bits_per_word(8)
.max_speed_hz(10_000_000)
.mode(spidev::SpiModeFlags::SPI_MODE_0)
.mode(SpiModeFlags::SPI_MODE_0)
.build();
spi.configure(&options).expect("spi configuration");

let cs = SysfsPin::new(26);
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 = SysfsPin::new(24);
busy.export().expect("busy export");
while !busy.is_exported() {}
busy.set_direction(Direction::In).expect("busy Direction");

let dc = SysfsPin::new(25);
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 rst = SysfsPin::new(17);
rst.export().expect("rst export");
while !rst.is_exported() {}
rst.set_direction(Direction::Out).expect("rst Direction");
rst.set_value(1).expect("rst Value set to 1");
spi.configure(&options)?;

let mut delay = Delay {};

let mut epd7in5 = Epd7in5::new(&mut spi, busy, dc, rst, &mut delay, None).expect("epd new");
let mut epd7in5 =
Epd7in5::new(&mut spi, busy_pin, dc_pin, rst_pin, &mut delay, None).expect("epd new");
epd7in5.set_lut(&mut spi, &mut delay, Some(RefreshLut::Quick))?;
let mut display = Display7in5::default();
display.clear(Color::White);
println!("Device successfully initialized!");

// Test graphics display
Expand All @@ -77,9 +83,9 @@ fn main() -> Result<(), SPIError> {

// Draw an analog clock
println!("Draw a clock");
display.clear(Color::White).ok();
display.clear(Color::Black).ok();
let style = PrimitiveStyleBuilder::new()
.stroke_color(Color::Black)
.stroke_color(Color::White)
.stroke_width(1)
.build();

Expand All @@ -98,7 +104,7 @@ fn main() -> Result<(), SPIError> {
// Draw some text
println!("Print text in all sizes");
// Color is inverted - black means white, white means black; the output will be black text on white background
display.clear(Color::Black).ok();
display.clear(Color::White).ok();
let fonts = [
&FONT_4X6,
&FONT_5X7,
Expand Down Expand Up @@ -126,8 +132,8 @@ fn main() -> Result<(), SPIError> {
for (n, font) in fonts.iter().enumerate() {
let style = MonoTextStyleBuilder::new()
.font(font)
.text_color(Color::White)
.background_color(Color::Black)
.text_color(Color::Black)
.background_color(Color::White)
.build();
let text_style = TextStyleBuilder::new().baseline(Baseline::Top).build();
let y = 10 + n * 30;
Expand All @@ -144,26 +150,98 @@ fn main() -> Result<(), SPIError> {

// Draw an image
println!("Draw Ferris");
display.clear(Color::Black).ok();
display.clear(Color::White).ok();
let data = include_bytes!("./assets/ferris.raw");
let raw_image = ImageRaw::<Color>::new(data, 460);
let image = Image::new(&raw_image, Point::zero());
image.draw(&mut display).unwrap();
epd7in5.update_and_display_frame(&mut spi, display.buffer(), &mut delay)?;

delay.delay_ms(5000);

println!("Clock Demo (partial update)");
epd7in5.clear_frame(&mut spi, &mut delay)?;

epd7in5.set_lut(&mut spi, &mut delay, Some(RefreshLut::PartialRefresh))?;

// Clock parameters - using FONT_6X10 (6 pixels wide, 10 pixels tall)
// "HH:MM:SS" = 8 characters
let char_width = 6;
let char_height = 10;
let clock_string_length = 8; // "HH:MM:SS"

// Create a buffer for the entire clock region (with padding)
let clock_buffer_width = (char_width * clock_string_length) as u32 + 1;
let clock_buffer_height = char_height as u32;

let mut partial_display =
display.get_partial_frame(299, 200, clock_buffer_width, clock_buffer_height);

// Time variables
let mut hours = 12u8;
let mut minutes = 34u8;
let mut seconds = 56u8;

println!("Updating clock every second for 10 iterations...");

for iteration in 0..10 {
// Format current time
let current_time = format!("{:02}:{:02}:{:02}", hours, minutes, seconds);

// Clear the clock buffer (white background)
partial_display.clear(Color::White).unwrap();

// Draw the entire time string on the clock buffer
draw_text(&mut partial_display, &current_time, 1, 0);

let params = partial_display.get_update_parameters();

epd7in5
.update_partial_frame(
&mut spi,
&mut delay,
params.buffer,
params.x,
params.y,
params.width,
params.height,
)
.unwrap();

epd7in5.display_frame(&mut spi, &mut delay)?;

// Increment time
seconds += 1;
if seconds >= 60 {
seconds = 0;
minutes += 1;
if minutes >= 60 {
minutes = 0;
hours += 1;
if hours >= 24 {
hours = 0;
}
}
}

println!("[{}] Time: {}", iteration, current_time);
delay.delay_ms(1000);
}

// Clear and sleep
println!("Clear the display");
display.clear(Color::Black).ok();
epd7in5.update_and_display_frame(&mut spi, display.buffer(), &mut delay)?;
epd7in5.set_lut(&mut spi, &mut delay, Some(RefreshLut::Full))?;
epd7in5.clear_frame(&mut spi, &mut delay)?;
println!("Finished tests - going to sleep");
epd7in5.sleep(&mut spi, &mut delay)
epd7in5.sleep(&mut spi, &mut delay)?;
Ok(())
}

fn draw_text(display: &mut Display7in5, text: &str, x: i32, y: i32) {
fn draw_text<D: DrawTarget<Color = Color>>(display: &mut D, text: &str, x: i32, y: i32) {
let style = MonoTextStyleBuilder::new()
.font(&embedded_graphics::mono_font::ascii::FONT_6X10)
.text_color(Color::White)
.background_color(Color::Black)
.text_color(Color::Black)
.background_color(Color::White)
.build();

let text_style = TextStyleBuilder::new().baseline(Baseline::Top).build();
Expand Down
4 changes: 3 additions & 1 deletion src/epd1in02/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,9 @@ where
) -> Result<(), SPI::Error> {
let (white_lut, black_lut) = match refresh_rate {
Some(RefreshLut::Full) => (&LUT_FULL_UPDATE_WHITE, &LUT_FULL_UPDATE_BLACK),
Some(RefreshLut::Quick) => (&LUT_PARTIAL_UPDATE_WHITE, &LUT_PARTIAL_UPDATE_BLACK),
Some(RefreshLut::Quick | RefreshLut::PartialRefresh) => {
(&LUT_PARTIAL_UPDATE_WHITE, &LUT_PARTIAL_UPDATE_BLACK)
}
None => return Ok(()),
};

Expand Down
4 changes: 3 additions & 1 deletion src/epd1in54/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,9 @@ where
}
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::Quick | RefreshLut::PartialRefresh => {
self.set_lut_helper(spi, delay, &LUT_PARTIAL_UPDATE)
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/epd1in54_v2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,9 @@ where
}
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::Quick | RefreshLut::PartialRefresh => {
self.set_lut_helper(spi, delay, &LUT_PARTIAL_UPDATE)
}
}?;

// Additional configuration required only for partial updates
Expand Down
2 changes: 1 addition & 1 deletion src/epd2in13_v2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ where
) -> Result<(), SPI::Error> {
let buffer = match refresh_rate {
Some(RefreshLut::Full) | None => &LUT_FULL_UPDATE,
Some(RefreshLut::Quick) => &LUT_PARTIAL_UPDATE,
Some(RefreshLut::Quick | RefreshLut::PartialRefresh) => &LUT_PARTIAL_UPDATE,
};

self.cmd_with_data(spi, Command::WriteLutRegister, buffer)
Expand Down
4 changes: 3 additions & 1 deletion src/epd2in9/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,9 @@ where
}
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::Quick | RefreshLut::PartialRefresh => {
self.set_lut_helper(spi, delay, &LUT_PARTIAL_UPDATE)
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/epd3in7/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ where
) -> Result<(), SPI::Error> {
let buffer = match refresh_rate {
Some(RefreshLut::Full) | None => &LUT_1GRAY_GC,
Some(RefreshLut::Quick) => &LUT_1GRAY_DU,
Some(RefreshLut::Quick | RefreshLut::PartialRefresh) => &LUT_1GRAY_DU,
};

self.interface
Expand Down
2 changes: 1 addition & 1 deletion src/epd4in2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ where
RefreshLut::Full => {
self.set_lut_helper(spi, delay, &LUT_VCOM0, &LUT_WW, &LUT_BW, &LUT_WB, &LUT_BB)
}
RefreshLut::Quick => self.set_lut_helper(
RefreshLut::Quick | RefreshLut::PartialRefresh => self.set_lut_helper(
spi,
delay,
&LUT_VCOM0_QUICK,
Expand Down
47 changes: 23 additions & 24 deletions src/epd7in5_v2/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,27 +69,6 @@ pub(crate) enum Command {
/// Dual SPI - what for?
DualSpi = 0x15,

/// This command builds the VCOM Look-Up Table (LUTC).
LutForVcom = 0x20,
/// This command builds the Black Look-Up Table (LUTB).
LutBlack = 0x21,
/// This command builds the White Look-Up Table (LUTW).
LutWhite = 0x22,
/// This command builds the Gray1 Look-Up Table (LUTG1).
LutGray1 = 0x23,
/// This command builds the Gray2 Look-Up Table (LUTG2).
LutGray2 = 0x24,
/// This command builds the Red0 Look-Up Table (LUTR0).
LutRed0 = 0x25,
/// This command builds the Red1 Look-Up Table (LUTR1).
LutRed1 = 0x26,
/// This command builds the Red2 Look-Up Table (LUTR2).
LutRed2 = 0x27,
/// This command builds the Red3 Look-Up Table (LUTR3).
LutRed3 = 0x28,
/// This command builds the XON Look-Up Table (LUTXON).
LutXon = 0x29,

/// The command controls the PLL clock frequency.
PllControl = 0x30,

Expand Down Expand Up @@ -128,9 +107,29 @@ pub(crate) enum Command {
ReadVcomValue = 0x81,
/// This command sets `VCOM_DC` value.
VcmDcSetting = 0x82,
// /// This is in all the Waveshare controllers for Epd7in5, but it's not documented
// /// anywhere in the datasheet `¯\_(ツ)_/¯`
// FlashMode = 0xE5,

/// Sets window size for the partial update
PartialWindow = 0x90,
/// Sets chip into partial update mode
PartialIn = 0x91,
/// Quits partial update mode
PartialOut = 0x92,

// The following commands are part of an undocumented hack by the manufacturer to select an arbitrary LUT.
// Normally the temperature and NEW/OLD buffer settings are used to select the correct LUT (flicker table)
// for healthy operation in a variety of temperatures. By abusing this command and setting a normally
// out-of-range value in addition to putting custom LUT's in OTP memory, one can select LUT's for other use-cases, such as:
//
// - fast refresh (LUT with fewer flickers): 0x5A
// - partial refresh (LUT without flicker): 0x6E
// - 4-grayscale (complicated hack that uses OLD/NEW buffers to encode grayscale values): 0x5F
//
// USE AT OWN RISK
//
/// This command sets cascade settings (such as allowing override temperature) for controlling primary and secondary displays
CascadeSetting = 0xE0,
/// This command is used when cascading temperature between primary and secondary displays
ForceTemperature = 0xE5,
}

impl traits::Command for Command {
Expand Down
Loading
Loading