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
3 changes: 2 additions & 1 deletion examples/epd4in2_variable_size.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ fn main() -> Result<(), SPIError> {
let (x, y, width, height) = (50, 50, 250, 250);

let mut buffer = [epd4in2::DEFAULT_BACKGROUND_COLOR.get_byte_value(); 62500]; //250*250
let mut display = VarDisplay::new(width, height, &mut buffer, false).unwrap();
let mut display =
VarDisplay::new(width, height, &mut buffer, DisplayMode::BwrBitOff as u8).unwrap();
display.set_rotation(DisplayRotation::Rotate0);
draw_text(&mut display, "Rotate 0!", 5, 50);

Expand Down
174 changes: 174 additions & 0 deletions examples/epd4in2bc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#![deny(warnings)]

use embedded_graphics::{
mono_font::MonoTextStyleBuilder,
prelude::*,
primitives::{Circle, Line, PrimitiveStyle},
text::{Baseline, Text, TextStyleBuilder},
};
use embedded_hal::delay::DelayNs;
use epd_waveshare::{
color::*,
epd4in2bc::{Display4in2bc, Epd4in2bc},
graphics::DisplayRotation,
prelude::*,
};
use linux_embedded_hal::{
spidev::{self, SpidevOptions},
sysfs_gpio::Direction,
Delay, SPIError, SpidevDevice, SysfsPin,
};

// activate spi, gpio in raspi-config
// needs to be run with sudo because of some sysfs_gpio permission problems and follow-up timing problems
// see https://github.com/rust-embedded/rust-sysfs-gpio/issues/5 and follow-up issues
/** BCM Physical
* EPD_CS_PIN = 26; = 37 (On raspberry Pi put it on CE0 pin 8 physical 24)
EPD_BUSY_PIN = 24; = 18
EPD_DC_PIN = 25; = 22
EPD_RST_PIN = 17; = 11
*/
fn main() -> Result<(), SPIError> {
// Configure SPI
// Settings are taken from
let mut spi = SpidevDevice::open("/dev/spidev0.0").expect("spidev directory");
let options = SpidevOptions::new()
.bits_per_word(8)
.max_speed_hz(4_000_000)
.mode(spidev::SpiModeFlags::SPI_MODE_0)
.build();
spi.configure(&options).expect("spi configuration");

// Configure Digital I/O Pin to be used as Chip Select for SPI
let cs = SysfsPin::new(26); //BCM7 CE0
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); //pin 29
busy.export().expect("busy export");
while !busy.is_exported() {}
busy.set_direction(Direction::In).expect("busy Direction");
//busy.set_value(1).expect("busy Value set to 1");

let dc = SysfsPin::new(25); //pin 31 //bcm6
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); //pin 36 //bcm16
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");

let mut delay = Delay {};

let mut epd4in2bc =
Epd4in2bc::new(&mut spi, busy, dc, rst, &mut delay, None).expect("eink initalize error");

println!("Test all the rotations");
let mut display = Display4in2bc::default();
display.clear(TriColor::White).ok();

display.set_rotation(DisplayRotation::Rotate0);
draw_text(&mut display, "Rotation 0!", 5, 50);

display.set_rotation(DisplayRotation::Rotate90);
draw_text(&mut display, "Rotation 90!", 5, 50);

display.set_rotation(DisplayRotation::Rotate180);
draw_text(&mut display, "Rotation 180!", 5, 50);

display.set_rotation(DisplayRotation::Rotate270);
draw_text(&mut display, "Rotation 270!", 5, 50);

epd4in2bc.update_color_frame(
&mut spi,
&mut delay,
display.bw_buffer(),
display.chromatic_buffer(),
)?;
epd4in2bc
.display_frame(&mut spi, &mut delay)
.expect("display frame new graphics");

delay.delay_ms(5000);

println!("Now test new graphics with default rotation and three colors:");
display.clear(TriColor::White).ok();

// draw a analog clock
let _ = Circle::with_center(Point::new(64, 64), 80)
.into_styled(PrimitiveStyle::with_stroke(TriColor::Black, 1))
.draw(&mut display);
let _ = Line::new(Point::new(64, 64), Point::new(30, 40))
.into_styled(PrimitiveStyle::with_stroke(TriColor::Black, 4))
.draw(&mut display);
let _ = Line::new(Point::new(64, 64), Point::new(80, 40))
.into_styled(PrimitiveStyle::with_stroke(TriColor::Black, 1))
.draw(&mut display);

// draw text white on Red background by using the chromatic buffer
let style = MonoTextStyleBuilder::new()
.font(&embedded_graphics::mono_font::ascii::FONT_6X10)
.text_color(TriColor::White)
.background_color(TriColor::Chromatic)
.build();
let text_style = TextStyleBuilder::new().baseline(Baseline::Top).build();

let _ = Text::with_text_style("It's working-WoB!", Point::new(90, 10), style, text_style)
.draw(&mut display);

// use bigger/different font
let style = MonoTextStyleBuilder::new()
.font(&embedded_graphics::mono_font::ascii::FONT_10X20)
.text_color(TriColor::White)
.background_color(TriColor::Chromatic)
.build();

let _ = Text::with_text_style("It's working\nWoB!", Point::new(90, 40), style, text_style)
.draw(&mut display);

// we used three colors, so we need to update both bw-buffer and chromatic-buffer

epd4in2bc.update_color_frame(
&mut spi,
&mut delay,
display.bw_buffer(),
display.chromatic_buffer(),
)?;
epd4in2bc
.display_frame(&mut spi, &mut delay)
.expect("display frame new graphics");

println!("Second frame done. Waiting 5s");
delay.delay_ms(5000);

// clear both bw buffer and chromatic buffer
display.clear(TriColor::White).ok();
epd4in2bc.update_color_frame(
&mut spi,
&mut delay,
display.bw_buffer(),
display.chromatic_buffer(),
)?;
epd4in2bc.display_frame(&mut spi, &mut delay)?;

println!("Finished tests - going to sleep");
epd4in2bc.sleep(&mut spi, &mut delay)
}

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

let text_style = TextStyleBuilder::new().baseline(Baseline::Top).build();

let _ = Text::with_text_style(text, Point::new(x, y), style, text_style).draw(display);
}
84 changes: 45 additions & 39 deletions src/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//! EPD representation of multicolor with separate buffers
//! for each bit makes it hard to properly represent colors here

use crate::prelude::DisplayMode;
#[cfg(feature = "graphics")]
use embedded_graphics_core::pixelcolor::BinaryColor;
#[cfg(feature = "graphics")]
Expand Down Expand Up @@ -80,21 +81,21 @@ pub trait ColorType {

/// Return the data used to set a pixel color
///
/// * bwrbit is used to tell the value of the unused bit when a chromatic
/// * MODE is used to tell the value of the unused bit when a chromatic
/// color is set (TriColor only as for now)
/// * pos is the pixel position in the line, used to know which pixels must be set
///
/// Return values are :
/// * .0 is the mask used to exclude this pixel from the byte (eg: 0x7F in BiColor)
/// * .1 are the bits used to set the color in the byte (eg: 0x80 in BiColor)
/// this is u16 because we set 2 bytes in case of split buffer
fn bitmask(&self, bwrbit: bool, pos: u32) -> (u8, u16);
fn bitmask(&self, mode: DisplayMode, pos: u32) -> (u8, u16);
}

impl ColorType for Color {
const BITS_PER_PIXEL_PER_BUFFER: usize = 1;
const BUFFER_COUNT: usize = 1;
fn bitmask(&self, _bwrbit: bool, pos: u32) -> (u8, u16) {
fn bitmask(&self, _mode: DisplayMode, pos: u32) -> (u8, u16) {
let bit = 0x80 >> (pos % 8);
match self {
Color::Black => (!bit, 0u16),
Expand All @@ -106,27 +107,34 @@ impl ColorType for Color {
impl ColorType for TriColor {
const BITS_PER_PIXEL_PER_BUFFER: usize = 1;
const BUFFER_COUNT: usize = 2;
fn bitmask(&self, bwrbit: bool, pos: u32) -> (u8, u16) {

fn bitmask(&self, mode: DisplayMode, pos: u32) -> (u8, u16) {
let bit = 0x80 >> (pos % 8);
match self {
TriColor::Black => (!bit, 0u16),
TriColor::White => (!bit, bit as u16),
TriColor::Chromatic => (
!bit,
if bwrbit {
(bit as u16) << 8
} else {
(bit as u16) << 8 | bit as u16
},
),
}
let mask = match mode {
DisplayMode::BwrBitOnColorInverted => match self {
TriColor::Black => (bit as u16) << 8,
TriColor::White => (bit as u16) << 8 | bit as u16,
TriColor::Chromatic => 0u16,
},
DisplayMode::BwrBitOn => match self {
TriColor::Black => 0u16,
TriColor::White => bit as u16,
TriColor::Chromatic => (bit as u16) << 8,
},
DisplayMode::BwrBitOff => match self {
TriColor::Black => 0u16,
TriColor::White => bit as u16,
TriColor::Chromatic => (bit as u16) << 8 | bit as u16,
},
};
(!bit, mask)
}
}

impl ColorType for OctColor {
const BITS_PER_PIXEL_PER_BUFFER: usize = 4;
const BUFFER_COUNT: usize = 1;
fn bitmask(&self, _bwrbit: bool, pos: u32) -> (u8, u16) {
fn bitmask(&self, _mode: DisplayMode, pos: u32) -> (u8, u16) {
let mask = !(0xF0 >> ((pos % 2) * 4));
let bits = self.get_nibble() as u16;
(mask, if pos % 2 == 1 { bits } else { bits << 4 })
Expand Down Expand Up @@ -329,16 +337,16 @@ impl From<BinaryColor> for Color {
impl From<embedded_graphics_core::pixelcolor::Rgb888> for Color {
fn from(rgb: embedded_graphics_core::pixelcolor::Rgb888) -> Self {
use embedded_graphics_core::pixelcolor::RgbColor;
if rgb == RgbColor::BLACK {
Color::Black
} else if rgb == RgbColor::WHITE {
Color::White
} else {
// choose closest color
if (rgb.r() as u16 + rgb.g() as u16 + rgb.b() as u16) > 255 * 3 / 2 {
Color::White
} else {
Color::Black
match rgb {
RgbColor::BLACK => Color::Black,
RgbColor::WHITE => Color::White,
_ => {
// choose closest color
if (rgb.r() as u16 + rgb.g() as u16 + rgb.b() as u16) > 255 * 3 / 2 {
Color::White
} else {
Color::Black
}
}
}
}
Expand Down Expand Up @@ -465,13 +473,11 @@ impl From<BinaryColor> for TriColor {
impl From<embedded_graphics_core::pixelcolor::Rgb888> for TriColor {
fn from(rgb: embedded_graphics_core::pixelcolor::Rgb888) -> Self {
use embedded_graphics_core::pixelcolor::RgbColor;
if rgb == RgbColor::BLACK {
TriColor::Black
} else if rgb == RgbColor::WHITE {
TriColor::White
} else {
match rgb {
RgbColor::BLACK => TriColor::Black,
RgbColor::WHITE => TriColor::White,
// there is no good approximation here since we don't know which color is 'chromatic'
TriColor::Chromatic
_ => TriColor::Chromatic,
}
}
}
Expand Down Expand Up @@ -533,28 +539,28 @@ mod tests {
#[test]
fn test_tricolor_bitmask() {
assert_eq!(
TriColor::Black.bitmask(false, 0),
TriColor::Black.bitmask(DisplayMode::BwrBitOff, 0),
(0b01111111, u16::from_le_bytes([0b00000000, 0b00000000]))
);
assert_eq!(
TriColor::White.bitmask(false, 0),
TriColor::White.bitmask(DisplayMode::BwrBitOff, 0),
(0b01111111, u16::from_le_bytes([0b10000000, 0b00000000]))
);
assert_eq!(
TriColor::Chromatic.bitmask(false, 0),
TriColor::Chromatic.bitmask(DisplayMode::BwrBitOff, 0),
(0b01111111, u16::from_le_bytes([0b10000000, 0b10000000]))
);

assert_eq!(
TriColor::Black.bitmask(true, 0),
TriColor::Black.bitmask(DisplayMode::BwrBitOn, 0),
(0b01111111, u16::from_le_bytes([0b00000000, 0b00000000]))
);
assert_eq!(
TriColor::White.bitmask(true, 0),
TriColor::White.bitmask(DisplayMode::BwrBitOn, 0),
(0b01111111, u16::from_le_bytes([0b10000000, 0b00000000]))
);
assert_eq!(
TriColor::Chromatic.bitmask(true, 0),
TriColor::Chromatic.bitmask(DisplayMode::BwrBitOn, 0),
(0b01111111, u16::from_le_bytes([0b00000000, 0b10000000]))
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/epd1in02/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use self::constants::{
pub type Display1in02 = crate::graphics::Display<
WIDTH,
HEIGHT,
false,
{ crate::graphics::DisplayMode::BwrBitOff as u8 },
{ buffer_len(WIDTH as usize, HEIGHT as usize) },
Color,
>;
Expand Down
2 changes: 1 addition & 1 deletion src/epd1in54/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ use crate::interface::DisplayInterface;
pub type Display1in54 = crate::graphics::Display<
WIDTH,
HEIGHT,
false,
{ crate::graphics::DisplayMode::BwrBitOff as u8 },
{ buffer_len(WIDTH as usize, HEIGHT as usize) },
Color,
>;
Expand Down
2 changes: 1 addition & 1 deletion src/epd1in54b/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use crate::buffer_len;
pub type Display1in54b = crate::graphics::Display<
WIDTH,
HEIGHT,
false,
{ crate::graphics::DisplayMode::BwrBitOff as u8 },
{ buffer_len(WIDTH as usize, HEIGHT as usize) },
Color,
>;
Expand Down
2 changes: 1 addition & 1 deletion src/epd1in54c/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use crate::buffer_len;
pub type Display1in54c = crate::graphics::Display<
WIDTH,
HEIGHT,
false,
{ crate::graphics::DisplayMode::BwrBitOff as u8 },
{ buffer_len(WIDTH as usize, HEIGHT as usize) },
Color,
>;
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 @@ -50,7 +50,7 @@ compile_error!(
pub type Display2in13 = crate::graphics::Display<
WIDTH,
HEIGHT,
false,
{ crate::graphics::DisplayMode::BwrBitOff as u8 },
{ buffer_len(WIDTH as usize, HEIGHT as usize) },
Color,
>;
Expand Down
Loading
Loading