diff --git a/.travis.yml b/.travis.yml index 055f401..00d2863 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,5 @@ # Based on the "trust" template v0.1.2 # https://github.com/japaric/trust/tree/v0.1.2 - -dist: trusty language: rust rust: - stable @@ -11,66 +9,22 @@ sudo: required # TODO Rust builds on stable by default, this can be # overridden on a case by case basis down below. - - matrix: allow_failures: - # - nightly + - nightly # TODO These are all the build jobs. Adjust as necessary. Comment out what you # don't need include: - # Android - #- env: TARGET=aarch64-linux-android DISABLE_TESTS=1 - #- env: TARGET=arm-linux-androideabi DISABLE_TESTS=1 - #- env: TARGET=armv7-linux-androideabi DISABLE_TESTS=1 - #- env: TARGET=i686-linux-android DISABLE_TESTS=1 - #- env: TARGET=x86_64-linux-android DISABLE_TESTS=1 - - # iOS - #- env: TARGET=aarch64-apple-ios DISABLE_TESTS=1 - # os: osx - #- env: TARGET=armv7-apple-ios DISABLE_TESTS=1 - # os: osx - #- env: TARGET=armv7s-apple-ios DISABLE_TESTS=1 - # os: osx - #- env: TARGET=i386-apple-ios DISABLE_TESTS=1 - # os: osx - #- env: TARGET=x86_64-apple-ios DISABLE_TESTS=1 - # os: osx - # Linux #- env: TARGET=aarch64-unknown-linux-gnu # Raspberry Pi - - env: TARGET=arm-unknown-linux-gnueabi DISABLE_EXAMPLES=1 + - env: TARGET=arm-unknown-linux-gnueabi # Raspberry Pi 3... - - env: TARGET=armv7-unknown-linux-gnueabihf DISABLE_EXAMPLES=1 - #- env: TARGET=i686-unknown-linux-gnu - #- env: TARGET=i686-unknown-linux-musl - #- env: TARGET=mips-unknown-linux-gnu - #- env: TARGET=mips64-unknown-linux-gnuabi64 - #- env: TARGET=mips64el-unknown-linux-gnuabi64 - #- env: TARGET=mipsel-unknown-linux-gnu - #- env: TARGET=powerpc-unknown-linux-gnu - #- env: TARGET=powerpc64-unknown-linux-gnu - #- env: TARGET=powerpc64le-unknown-linux-gnu - #- env: TARGET=s390x-unknown-linux-gnu DISABLE_TESTS=1 + - env: TARGET=armv7-unknown-linux-gnueabihf + - env: TARGET=x86_64-unknown-linux-gnu - env: TARGET=x86_64-unknown-linux-musl - # OSX - #- env: TARGET=i686-apple-darwin - # os: osx - #- env: TARGET=x86_64-apple-darwin - # os: osx - - # *BSD - #- env: TARGET=i686-unknown-freebsd DISABLE_TESTS=1 - #- env: TARGET=x86_64-unknown-freebsd DISABLE_TESTS=1 - #- env: TARGET=x86_64-unknown-netbsd DISABLE_TESTS=1 - - # Windows - #- env: TARGET=x86_64-pc-windows-gnu - # Bare metal # These targets don't support std and as such are likely not suitable for # most crates. @@ -79,27 +33,6 @@ matrix: - env: TARGET=thumbv7em-none-eabihf - env: TARGET=thumbv7m-none-eabi - # Testing other channels - #- env: TARGET=x86_64-unknown-linux-gnu - # rust: nightly - #- env: TARGET=x86_64-apple-darwin - # os: osx - # rust: nightly - - #- env: TEST_DIR=examples/embedded_linux - #- env: TEST_DIR=examples/f3_stm32f30x - -addons: - apt: - packages: - - libcurl4-openssl-dev - - libelf-dev - - libdw-dev - - cmake - - gcc - - binutils-dev - - libiberty-dev - before_install: - set -e @@ -108,9 +41,7 @@ before_install: install: - cargo install cargo-update || echo "cargo-update already installed" - - cargo install cargo-travis || echo "cargo-travis already installed" - cargo install-update -a # update outdated cached binaries - - rustup override set nightly - rustup target add thumbv7m-none-eabi - rustup component add clippy-preview - rustup component add rustfmt-preview @@ -122,17 +53,11 @@ script: - cargo check - cargo test --all-features --release - cargo doc --all-features --release - - cd examples/embedded_linux_epd4in2 && cargo fmt --all -- --check && cargo check && cd ../../ - - cd examples/embedded_linux_epd1in54 && cargo fmt --all -- --check && cargo check && cd ../../ - - cd examples/embedded_linux_epd2in9 && cargo fmt --all -- --check && cargo check && cd ../../ - # - cd examples/stm32f3discovery && cargo check --target thumbv7m-none-eabi && cd ../../ - #- cd ../f3_stm32f30x && cargo build - -after_success: -# measure code coverage and upload to coveralls.io - - cargo coveralls -#- cargo doc-upload - + - cd examples/epd4in2_full && cargo fmt --all -- --check && cargo check && cd ../../ + - cd examples/epd2in9_full && cargo fmt --all -- --check && cargo check && cd ../../ + - cd examples/epd1in54_full && cargo fmt --all -- --check && cargo check && cd ../../ + - cd examples/epd1in54_no_graphics && cargo fmt --all -- --check && cargo check && cd ../../ + - cd examples/epd4in2_var_display_buffer && cargo fmt --all -- --check && cargo check && cd ../../ cache: cargo diff --git a/README.md b/README.md index 6c27d07..9b0c441 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Other similiar libraries with support for much more displays are [u8g2](https:// ## Examples -There are multiple examples in the examples folder. For more infos about the examples see the seperate Readme [there](/examples/Readme.md). +There are multiple examples in the examples folder. For more infos about the examples see the seperate Readme [there](/examples/Readme.md). These examples are all rust projects of their own, so you need to go inside the project to execute it (cargo run --example doesn't work). ```Rust // Setup the epd @@ -92,9 +92,6 @@ They are also called A and B, but you shouldn't get confused and mix it with the | | 7.5in (B) | | | 7.5in (C) | -## TODO's - -- [ ] improve the partial drawing/check the timings/timing improvements/.... diff --git a/examples/Readme.md b/examples/Readme.md index 37688de..dac96c2 100644 --- a/examples/Readme.md +++ b/examples/Readme.md @@ -1,12 +1,20 @@ # Examples: -### embedded_linux_epd4in2 +All of these examples are projects of their own. -Basic example of using a Raspberry Pi with the 4in2 Waveshare E-Ink Display. +A few notes: + - If not stated otherwise the example is for a Raspberry Pi running Linux. + - epdXinYY_full showcase most of what can be done with this crate. This means that they are using graphics feature and use the DisplayXinYY with its buffer. -It showcases most of what can be done with the crate. +Special Examples: + +### epd4in2_var_display_buffer + +This examples used the graphics feature with VarDisplay and therefore a variable buffer(size). + +### epd1in54_no_graphics (Fastest Example) + +This example doesn't use the graphics feature and handles all the "drawing" by itself. It also has a speeddemonstration included. -### embedded_linux_epd1in54 -This example doesn't use the graphics feature of the crate and shows the fast update rate of the 1in54 EPD. diff --git a/examples/embedded_linux_epd1in54/Cargo.toml b/examples/epd1in54_full/Cargo.toml similarity index 63% rename from examples/embedded_linux_epd1in54/Cargo.toml rename to examples/epd1in54_full/Cargo.toml index 351ebe6..c6d89d4 100644 --- a/examples/embedded_linux_epd1in54/Cargo.toml +++ b/examples/epd1in54_full/Cargo.toml @@ -2,13 +2,12 @@ name = "embedded_linux_eink_example" version = "0.1.0" authors = ["Christoph Groß "] +edition = "2018" [dependencies] epd-waveshare = { path = "../../", default-features = false, features = ["epd1in54", "graphics"]} -linux-embedded-hal = "0.2.1" - -embedded-graphics = "0.4.3" - -embedded-hal = { version = "0.2.1", features = ["unproven"] } +linux-embedded-hal = "0.2.2" +embedded-graphics = "0.4.5" +embedded-hal = { version = "0.2.2", features = ["unproven"] } diff --git a/examples/embedded_linux_epd1in54/src/main.rs b/examples/epd1in54_full/src/main.rs similarity index 86% rename from examples/embedded_linux_epd1in54/src/main.rs rename to examples/epd1in54_full/src/main.rs index 94031d7..81e7be2 100644 --- a/examples/embedded_linux_epd1in54/src/main.rs +++ b/examples/epd1in54_full/src/main.rs @@ -1,36 +1,26 @@ -// the library for the embedded linux device -extern crate linux_embedded_hal as lin_hal; -use lin_hal::spidev::{self, SpidevOptions}; -use lin_hal::sysfs_gpio::Direction; -use lin_hal::Delay; -use lin_hal::{Pin, Spidev}; - -// the eink library -extern crate epd_waveshare; +#![deny(warnings)] + +use embedded_graphics::{coord::Coord, fonts::Font6x8, prelude::*, Drawing}; +use embedded_hal::prelude::*; use epd_waveshare::{ - epd1in54::{Buffer1in54, EPD1in54}, + epd1in54::{Display1in54, EPD1in54}, graphics::{Display, DisplayRotation}, prelude::*, }; - -// Graphics -extern crate embedded_graphics; -use embedded_graphics::coord::Coord; -use embedded_graphics::fonts::Font6x8; -use embedded_graphics::prelude::*; -//use embedded_graphics::primitives::{Circle, Line}; -use embedded_graphics::Drawing; - -// HAL (Traits) -extern crate embedded_hal; -use embedded_hal::prelude::*; +use linux_embedded_hal::{ + spidev::{self, SpidevOptions}, + sysfs_gpio::Direction, + Delay, Pin, Spidev, +}; // 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 fn main() { - run().unwrap(); + if let Err(e) = run() { + eprintln!("Program exited early with error: {}", e); + } } fn run() -> Result<(), std::io::Error> { @@ -86,8 +76,7 @@ fn run() -> Result<(), std::io::Error> { epd.display_frame(&mut spi).expect("disp 1"); println!("Test all the rotations"); - let mut buffer = Buffer1in54::default(); - let mut display = Display::new(epd.width(), epd.height(), &mut buffer.buffer); + let mut display = Display1in54::default(); display.set_rotation(DisplayRotation::Rotate0); display.draw( Font6x8::render_str("Rotate 0!") diff --git a/examples/epd1in54_no_graphics/Cargo.toml b/examples/epd1in54_no_graphics/Cargo.toml new file mode 100644 index 0000000..3868a67 --- /dev/null +++ b/examples/epd1in54_no_graphics/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "embedded_linux_eink_example" +version = "0.1.0" +authors = ["Christoph Groß "] +edition = "2018" + +[dependencies] + +epd-waveshare = { path = "../../", default-features = false, features = ["epd1in54"]} + +linux-embedded-hal = "0.2.2" +embedded-hal = { version = "0.2.2", features = ["unproven"] } diff --git a/examples/epd1in54_no_graphics/src/main.rs b/examples/epd1in54_no_graphics/src/main.rs new file mode 100644 index 0000000..bdff7a9 --- /dev/null +++ b/examples/epd1in54_no_graphics/src/main.rs @@ -0,0 +1,105 @@ +#![deny(warnings)] + +use embedded_hal::prelude::*; +use epd_waveshare::{epd1in54::EPD1in54, prelude::*}; +use linux_embedded_hal::{ + spidev::{self, SpidevOptions}, + sysfs_gpio::Direction, + Delay, Pin, Spidev, +}; + +// 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 + +fn main() { + if let Err(e) = run() { + eprintln!("Program exited early with error: {}", e); + } +} + +fn run() -> Result<(), std::io::Error> { + // Configure SPI + // SPI settings are from eink-waveshare-rs documenation + let mut spi = Spidev::open("/dev/spidev0.0")?; + 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"); + + // Configure Digital I/O Pin to be used as Chip Select for SPI + let cs_pin = Pin::new(26); //BCM7 CE0 + cs_pin.export().expect("cs_pin export"); + while !cs_pin.is_exported() {} + cs_pin + .set_direction(Direction::Out) + .expect("cs_pin Direction"); + cs_pin.set_value(1).expect("cs_pin Value set to 1"); + + // Configure Busy Input Pin + let busy = Pin::new(5); //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"); + + // Configure Data/Command OutputPin + let dc = Pin::new(6); //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"); + + // Configure Reset OutputPin + let rst = Pin::new(16); //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"); + + // Configure Delay + let mut delay = Delay {}; + + // Setup of the needed pins is finished here + // Now the "real" usage of the eink-waveshare-rs crate begins + let mut epd = EPD1in54::new(&mut spi, cs_pin, busy, dc, rst, &mut delay)?; + + // Clear the full screen + epd.clear_frame(&mut spi)?; + epd.display_frame(&mut spi)?; + + // Speeddemo + epd.set_lut(&mut spi, Some(RefreshLUT::QUICK))?; + let small_buffer = [Color::Black.get_byte_value(); 32]; //16x16 + let number_of_runs = 1; + for i in 0..number_of_runs { + let offset = i * 8 % 150; + epd.update_partial_frame(&mut spi, &small_buffer, 25 + offset, 25 + offset, 16, 16)?; + epd.display_frame(&mut spi)?; + } + + // Clear the full screen + epd.clear_frame(&mut spi)?; + epd.display_frame(&mut spi)?; + + // Draw some squares + let small_buffer = [Color::Black.get_byte_value(); 3200]; //160x160 + epd.update_partial_frame(&mut spi, &small_buffer, 20, 20, 160, 160)?; + + let small_buffer = [Color::White.get_byte_value(); 800]; //80x80 + epd.update_partial_frame(&mut spi, &small_buffer, 60, 60, 80, 80)?; + + let small_buffer = [Color::Black.get_byte_value(); 8]; //8x8 + epd.update_partial_frame(&mut spi, &small_buffer, 96, 96, 8, 8)?; + + // Display updated frame + epd.display_frame(&mut spi)?; + delay.delay_ms(5000u16); + + // Set the EPD to sleep + epd.sleep(&mut spi)?; + + Ok(()) +} diff --git a/examples/embedded_linux_epd2in9/Cargo.toml b/examples/epd2in9_full/Cargo.toml similarity index 63% rename from examples/embedded_linux_epd2in9/Cargo.toml rename to examples/epd2in9_full/Cargo.toml index 1dfb5e4..b7da641 100644 --- a/examples/embedded_linux_epd2in9/Cargo.toml +++ b/examples/epd2in9_full/Cargo.toml @@ -2,13 +2,12 @@ name = "embedded_linux_eink_example" version = "0.1.0" authors = ["Christoph Groß "] +edition = "2018" [dependencies] epd-waveshare = { path = "../../", default-features = false, features = ["epd2in9", "graphics"]} -linux-embedded-hal = "0.2.1" - -embedded-graphics = "0.4.3" - -embedded-hal = { version = "0.2.1", features = ["unproven"] } +linux-embedded-hal = "0.2.2" +embedded-graphics = "0.4.5" +embedded-hal = { version = "0.2.2", features = ["unproven"] } diff --git a/examples/embedded_linux_epd2in9/src/main.rs b/examples/epd2in9_full/src/main.rs similarity index 86% rename from examples/embedded_linux_epd2in9/src/main.rs rename to examples/epd2in9_full/src/main.rs index ffeeaae..b3dfd3a 100644 --- a/examples/embedded_linux_epd2in9/src/main.rs +++ b/examples/epd2in9_full/src/main.rs @@ -1,29 +1,17 @@ -// the library for the embedded linux device -extern crate linux_embedded_hal as lin_hal; -use lin_hal::spidev::{self, SpidevOptions}; -use lin_hal::sysfs_gpio::Direction; -use lin_hal::Delay; -use lin_hal::{Pin, Spidev}; - -// the eink library -extern crate epd_waveshare; +#![deny(warnings)] + +use embedded_graphics::{coord::Coord, fonts::Font6x8, prelude::*, Drawing}; +use embedded_hal::prelude::*; use epd_waveshare::{ - epd2in9::{Buffer2in9, EPD2in9}, + epd2in9::{Display2in9, EPD2in9}, graphics::{Display, DisplayRotation}, prelude::*, }; - -// Graphics -extern crate embedded_graphics; -use embedded_graphics::coord::Coord; -use embedded_graphics::fonts::Font6x8; -use embedded_graphics::prelude::*; -//use embedded_graphics::primitives::{Circle, Line}; -use embedded_graphics::Drawing; - -// HAL (Traits) -extern crate embedded_hal; -use embedded_hal::prelude::*; +use linux_embedded_hal::{ + spidev::{self, SpidevOptions}, + sysfs_gpio::Direction, + Delay, Pin, Spidev, +}; // activate spi, gpio in raspi-config // needs to be run with sudo because of some sysfs_gpio permission problems and follow-up timing problems @@ -31,7 +19,9 @@ use embedded_hal::prelude::*; //TODO: Test this implemenation with a new display fn main() { - run().unwrap(); + if let Err(e) = run() { + eprintln!("Program exited early with error: {}", e); + } } fn run() -> Result<(), std::io::Error> { @@ -87,8 +77,7 @@ fn run() -> Result<(), std::io::Error> { epd.display_frame(&mut spi).expect("disp 1"); println!("Test all the rotations"); - let mut buffer = Buffer2in9::default(); - let mut display = Display::new(epd.width(), epd.height(), &mut buffer.buffer); + let mut display = Display2in9::default(); epd.update_frame(&mut spi, display.buffer()).unwrap(); epd.display_frame(&mut spi).expect("display frame x03"); diff --git a/examples/embedded_linux_epd4in2/Cargo.toml b/examples/epd4in2_full/Cargo.toml similarity index 75% rename from examples/embedded_linux_epd4in2/Cargo.toml rename to examples/epd4in2_full/Cargo.toml index 02bb866..59f3a0d 100644 --- a/examples/embedded_linux_epd4in2/Cargo.toml +++ b/examples/epd4in2_full/Cargo.toml @@ -2,6 +2,7 @@ name = "embedded_linux_eink_example" version = "0.1.0" authors = ["Christoph Groß "] +edition = "2018" [dependencies] @@ -9,8 +10,6 @@ authors = ["Christoph Groß "] #epd_waveshare = { path = "../../"} epd-waveshare = { path = "../../", default-features = false, features = ["epd4in2", "graphics"]} -linux-embedded-hal = "0.2.1" - -embedded-graphics = "0.4.3" - -embedded-hal = { version = "0.2.1", features = ["unproven"] } +linux-embedded-hal = "0.2.2" +embedded-graphics = "0.4.5" +embedded-hal = { version = "0.2.2", features = ["unproven"] } diff --git a/examples/embedded_linux_epd4in2/src/main.rs b/examples/epd4in2_full/src/main.rs similarity index 87% rename from examples/embedded_linux_epd4in2/src/main.rs rename to examples/epd4in2_full/src/main.rs index f6fe437..2e903dd 100644 --- a/examples/embedded_linux_epd4in2/src/main.rs +++ b/examples/epd4in2_full/src/main.rs @@ -1,36 +1,32 @@ -// the library for the embedded linux device -extern crate linux_embedded_hal as lin_hal; -use lin_hal::spidev::{self, SpidevOptions}; -use lin_hal::sysfs_gpio::Direction; -use lin_hal::Delay; -use lin_hal::{Pin, Spidev}; - -// the eink library -extern crate epd_waveshare; +#![deny(warnings)] + +use embedded_graphics::{ + coord::Coord, + fonts::{Font12x16, Font6x8}, + prelude::*, + primitives::{Circle, Line}, + Drawing, +}; +use embedded_hal::prelude::*; use epd_waveshare::{ - epd4in2::{Buffer4in2, EPD4in2}, + epd4in2::{Display4in2, EPD4in2}, graphics::{Display, DisplayRotation}, prelude::*, }; - -// Graphics -extern crate embedded_graphics; -use embedded_graphics::coord::Coord; -use embedded_graphics::fonts::{Font12x16, Font6x8}; -use embedded_graphics::prelude::*; -use embedded_graphics::primitives::{Circle, Line}; -use embedded_graphics::Drawing; - -// HAL (Traits) -extern crate embedded_hal; -use embedded_hal::prelude::*; +use linux_embedded_hal::{ + spidev::{self, SpidevOptions}, + sysfs_gpio::Direction, + Delay, Pin, Spidev, +}; // 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 fn main() { - run().map_err(|e| println!("{}", e.to_string())).unwrap(); + if let Err(e) = run() { + eprintln!("Program exited early with error: {}", e); + } } fn run() -> Result<(), std::io::Error> { @@ -75,8 +71,7 @@ fn run() -> Result<(), std::io::Error> { EPD4in2::new(&mut spi, cs, busy, dc, rst, &mut delay).expect("eink initalize error"); println!("Test all the rotations"); - let mut buffer = Buffer4in2::default(); - let mut display = Display::new(epd4in2.width(), epd4in2.height(), &mut buffer.buffer); + let mut display = Display4in2::default(); display.set_rotation(DisplayRotation::Rotate0); display.draw( Font6x8::render_str("Rotate 0!") diff --git a/examples/epd4in2_var_display_buffer/Cargo.toml b/examples/epd4in2_var_display_buffer/Cargo.toml new file mode 100644 index 0000000..59f3a0d --- /dev/null +++ b/examples/epd4in2_var_display_buffer/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "embedded_linux_eink_example" +version = "0.1.0" +authors = ["Christoph Groß "] +edition = "2018" + +[dependencies] + +## The Only difference between this one and the one without default features sizewise seems to be a different .d-file Size (dependencies-file) +#epd_waveshare = { path = "../../"} +epd-waveshare = { path = "../../", default-features = false, features = ["epd4in2", "graphics"]} + +linux-embedded-hal = "0.2.2" +embedded-graphics = "0.4.5" +embedded-hal = { version = "0.2.2", features = ["unproven"] } diff --git a/examples/epd4in2_var_display_buffer/src/main.rs b/examples/epd4in2_var_display_buffer/src/main.rs new file mode 100644 index 0000000..fa421a1 --- /dev/null +++ b/examples/epd4in2_var_display_buffer/src/main.rs @@ -0,0 +1,195 @@ +#![deny(warnings)] + +use embedded_graphics::{ + coord::Coord, + fonts::{Font12x16, Font6x8}, + prelude::*, + primitives::{Circle, Line}, + Drawing, +}; +use embedded_hal::prelude::*; +use epd_waveshare::{ + epd4in2::{self, EPD4in2}, + graphics::{Display, DisplayRotation, VarDisplay}, + prelude::*, +}; +use linux_embedded_hal::{ + spidev::{self, SpidevOptions}, + sysfs_gpio::Direction, + Delay, Pin, Spidev, +}; + +// 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 + +fn main() { + if let Err(e) = run() { + eprintln!("Program exited early with error: {}", e); + } +} + +fn run() -> Result<(), std::io::Error> { + // Configure SPI + // Settings are taken from + let mut spi = Spidev::open("/dev/spidev0.0").expect("spidev directory"); + 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"); + + // Configure Digital I/O Pin to be used as Chip Select for SPI + let cs = Pin::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 = Pin::new(5); //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 = Pin::new(6); //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 = Pin::new(16); //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 epd4in2 = + EPD4in2::new(&mut spi, cs, busy, dc, rst, &mut delay).expect("eink initalize error"); + + println!("Test all the rotations"); + + 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); + display.set_rotation(DisplayRotation::Rotate0); + display.draw( + Font6x8::render_str("Rotate 0!") + .with_stroke(Some(Color::Black)) + .with_fill(Some(Color::White)) + .translate(Coord::new(5, 50)) + .into_iter(), + ); + + display.set_rotation(DisplayRotation::Rotate90); + display.draw( + Font6x8::render_str("Rotate 90!") + .with_stroke(Some(Color::Black)) + .with_fill(Some(Color::White)) + .translate(Coord::new(5, 50)) + .into_iter(), + ); + + display.set_rotation(DisplayRotation::Rotate180); + display.draw( + Font6x8::render_str("Rotate 180!") + .with_stroke(Some(Color::Black)) + .with_fill(Some(Color::White)) + .translate(Coord::new(5, 50)) + .into_iter(), + ); + + display.set_rotation(DisplayRotation::Rotate270); + display.draw( + Font6x8::render_str("Rotate 270!") + .with_stroke(Some(Color::Black)) + .with_fill(Some(Color::White)) + .translate(Coord::new(5, 50)) + .into_iter(), + ); + + epd4in2 + .update_partial_frame(&mut spi, &display.buffer(), x, y, width, height) + .unwrap(); + epd4in2 + .display_frame(&mut spi) + .expect("display frame new graphics"); + delay.delay_ms(5000u16); + + println!("Now test new graphics with default rotation and some special stuff:"); + display.clear_buffer(Color::White); + + // draw a analog clock + display.draw( + Circle::new(Coord::new(64, 64), 64) + .with_stroke(Some(Color::Black)) + .into_iter(), + ); + display.draw( + Line::new(Coord::new(64, 64), Coord::new(0, 64)) + .with_stroke(Some(Color::Black)) + .into_iter(), + ); + display.draw( + Line::new(Coord::new(64, 64), Coord::new(80, 80)) + .with_stroke(Some(Color::Black)) + .into_iter(), + ); + + // draw white on black background + display.draw( + Font6x8::render_str("It's working-WoB!") + // Using Style here + .with_style(Style { + fill_color: Some(Color::Black), + stroke_color: Some(Color::White), + stroke_width: 0u8, // Has no effect on fonts + }) + .translate(Coord::new(175, 250)) + .into_iter(), + ); + + // use bigger/different font + display.draw( + Font12x16::render_str("It's working-BoW!") + // Using Style here + .with_style(Style { + fill_color: Some(Color::White), + stroke_color: Some(Color::Black), + stroke_width: 0u8, // Has no effect on fonts + }) + .translate(Coord::new(50, 200)) + .into_iter(), + ); + + // a moving `Hello World!` + let limit = 10; + for i in 0..limit { + println!("Moving Hello World. Loop {} from {}", (i + 1), limit); + + display.draw( + Font6x8::render_str(" Hello World! ") + .with_style(Style { + fill_color: Some(Color::White), + stroke_color: Some(Color::Black), + stroke_width: 0u8, // Has no effect on fonts + }) + .translate(Coord::new(5 + i * 12, 50)) + .into_iter(), + ); + + epd4in2.update_frame(&mut spi, &display.buffer()).unwrap(); + epd4in2 + .display_frame(&mut spi) + .expect("display frame new graphics"); + + delay.delay_ms(1_000u16); + } + + println!("Finished tests - going to sleep"); + epd4in2.sleep(&mut spi) +} diff --git a/src/epd1in54/graphics.rs b/src/epd1in54/graphics.rs index 5fe2417..7206b10 100644 --- a/src/epd1in54/graphics.rs +++ b/src/epd1in54/graphics.rs @@ -1,44 +1,73 @@ use crate::epd1in54::{DEFAULT_BACKGROUND_COLOR, HEIGHT, WIDTH}; +use crate::graphics::{Display, DisplayRotation}; +use crate::prelude::*; +use embedded_graphics::prelude::*; /// Full size buffer for use with the 1in54 EPD /// /// Can also be manuall constructed: /// `buffer: [DEFAULT_BACKGROUND_COLOR.get_byte_value(); WIDTH / 8 * HEIGHT]` -pub struct Buffer1in54BlackWhite { - pub buffer: [u8; WIDTH as usize * HEIGHT as usize / 8], +pub struct Display1in54 { + buffer: [u8; WIDTH as usize * HEIGHT as usize / 8], + rotation: DisplayRotation, } -impl Default for Buffer1in54BlackWhite { +impl Default for Display1in54 { fn default() -> Self { - Buffer1in54BlackWhite { + Display1in54 { buffer: [DEFAULT_BACKGROUND_COLOR.get_byte_value(); WIDTH as usize * HEIGHT as usize / 8], + rotation: DisplayRotation::default(), } } } +impl Drawing for Display1in54 { + fn draw(&mut self, item_pixels: T) + where + T: Iterator>, + { + self.draw_helper(WIDTH, HEIGHT, item_pixels); + } +} + +impl Display for Display1in54 { + fn buffer(&self) -> &[u8] { + &self.buffer + } + + fn get_mut_buffer(&mut self) -> &mut [u8] { + &mut self.buffer + } + + fn set_rotation(&mut self, rotation: DisplayRotation) { + self.rotation = rotation; + } + + fn rotation(&self) -> DisplayRotation { + self.rotation + } +} + #[cfg(test)] mod tests { use super::*; use crate::color::Color; use crate::graphics::{Display, DisplayRotation}; use embedded_graphics::coord::Coord; - use embedded_graphics::prelude::*; use embedded_graphics::primitives::Line; // test buffer length #[test] fn graphics_size() { - let mut display1in54 = Buffer1in54BlackWhite::default(); - let display = Display::new(WIDTH, HEIGHT, &mut display1in54.buffer); + let display = Display1in54::default(); assert_eq!(display.buffer().len(), 5000); } // test default background color on all bytes #[test] fn graphics_default() { - let mut display1in54 = Buffer1in54BlackWhite::default(); - let display = Display::new(WIDTH, HEIGHT, &mut display1in54.buffer); + let display = Display1in54::default(); for &byte in display.buffer() { assert_eq!(byte, DEFAULT_BACKGROUND_COLOR.get_byte_value()); } @@ -46,8 +75,7 @@ mod tests { #[test] fn graphics_rotation_0() { - let mut display1in54 = Buffer1in54BlackWhite::default(); - let mut display = Display::new(WIDTH, HEIGHT, &mut display1in54.buffer); + let mut display = Display1in54::default(); display.draw( Line::new(Coord::new(0, 0), Coord::new(7, 0)) .with_stroke(Some(Color::Black)) @@ -65,8 +93,7 @@ mod tests { #[test] fn graphics_rotation_90() { - let mut display1in54 = Buffer1in54BlackWhite::default(); - let mut display = Display::new(WIDTH, HEIGHT, &mut display1in54.buffer); + let mut display = Display1in54::default(); display.set_rotation(DisplayRotation::Rotate90); display.draw( Line::new(Coord::new(0, 192), Coord::new(0, 199)) @@ -85,8 +112,7 @@ mod tests { #[test] fn graphics_rotation_180() { - let mut display1in54 = Buffer1in54BlackWhite::default(); - let mut display = Display::new(WIDTH, HEIGHT, &mut display1in54.buffer); + let mut display = Display1in54::default(); display.set_rotation(DisplayRotation::Rotate180); display.draw( Line::new(Coord::new(192, 199), Coord::new(199, 199)) @@ -108,8 +134,7 @@ mod tests { #[test] fn graphics_rotation_270() { - let mut display1in54 = Buffer1in54BlackWhite::default(); - let mut display = Display::new(WIDTH, HEIGHT, &mut display1in54.buffer); + let mut display = Display1in54::default(); display.set_rotation(DisplayRotation::Rotate270); display.draw( Line::new(Coord::new(199, 0), Coord::new(199, 7)) diff --git a/src/epd1in54/mod.rs b/src/epd1in54/mod.rs index 20c6f7d..158e0ae 100644 --- a/src/epd1in54/mod.rs +++ b/src/epd1in54/mod.rs @@ -2,19 +2,19 @@ //! //! # Example for the 1.54 in E-Ink Display //! -//! ```ignore +//! ```rust,ignore //! use epd_waveshare::{ -//! epd1in54::{EPD1in54, Buffer1in54}, +//! epd1in54::{EPD1in54, Display1in54}, //! graphics::{Display, DisplayRotation}, //! prelude::*, //! }; +//! use embedded_graphics::Drawing; //! //! // Setup EPD -//! let mut epd = EPD1in54::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?; +//! let mut epd = EPD1in54::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay).unwrap(); //! //! // Use display graphics -//! let mut buffer = Buffer1in54::default(); -//! let mut display = Display::new(epd.width(), epd.height(), &mut buffer.buffer); +//! let mut display = Display1in54::default(); //! //! // Write some hello world in the screenbuffer //! display.draw( @@ -37,6 +37,7 @@ pub const WIDTH: u32 = 200; pub const HEIGHT: u32 = 200; //const DPI: u16 = 184; pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White; +const IS_BUSY_LOW: bool = false; use embedded_hal::{ blocking::{delay::*, spi::Write}, @@ -54,8 +55,10 @@ use crate::traits::{RefreshLUT, WaveshareDisplay}; use crate::interface::DisplayInterface; +#[cfg(feature = "graphics")] mod graphics; -pub use crate::epd1in54::graphics::Buffer1in54BlackWhite as Buffer1in54; +#[cfg(feature = "graphics")] +pub use crate::epd1in54::graphics::Display1in54; /// EPD1in54 driver /// @@ -248,6 +251,10 @@ where RefreshLUT::QUICK => self.set_lut_helper(spi, &LUT_PARTIAL_UPDATE), } } + + fn is_busy(&self) -> bool { + self.interface.is_busy(IS_BUSY_LOW) + } } impl EPD1in54 @@ -259,7 +266,7 @@ where RST: OutputPin, { fn wait_until_idle(&mut self) { - self.interface.wait_until_idle(false); + self.interface.wait_until_idle(IS_BUSY_LOW); } pub(crate) fn use_full_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { diff --git a/src/epd2in9/graphics.rs b/src/epd2in9/graphics.rs index a2ba8cb..816087c 100644 --- a/src/epd2in9/graphics.rs +++ b/src/epd2in9/graphics.rs @@ -1,40 +1,69 @@ use crate::epd2in9::{DEFAULT_BACKGROUND_COLOR, HEIGHT, WIDTH}; +use crate::graphics::{Display, DisplayRotation}; +use crate::prelude::*; +use embedded_graphics::prelude::*; -/// Full size buffer for use with the 2in9 EPD +/// Display with Fullsize buffer for use with the 2in9 EPD /// /// Can also be manuall constructed: /// `buffer: [DEFAULT_BACKGROUND_COLOR.get_byte_value(); WIDTH / 8 * HEIGHT]` -pub struct Buffer2in9 { - pub buffer: [u8; WIDTH as usize * HEIGHT as usize / 8], +pub struct Display2in9 { + buffer: [u8; WIDTH as usize * HEIGHT as usize / 8], + rotation: DisplayRotation, } -impl Default for Buffer2in9 { +impl Default for Display2in9 { fn default() -> Self { - Buffer2in9 { + Display2in9 { buffer: [DEFAULT_BACKGROUND_COLOR.get_byte_value(); WIDTH as usize * HEIGHT as usize / 8], + rotation: DisplayRotation::default(), } } } +impl Drawing for Display2in9 { + fn draw(&mut self, item_pixels: T) + where + T: Iterator>, + { + self.draw_helper(WIDTH, HEIGHT, item_pixels); + } +} + +impl Display for Display2in9 { + fn buffer(&self) -> &[u8] { + &self.buffer + } + + fn get_mut_buffer(&mut self) -> &mut [u8] { + &mut self.buffer + } + + fn set_rotation(&mut self, rotation: DisplayRotation) { + self.rotation = rotation; + } + + fn rotation(&self) -> DisplayRotation { + self.rotation + } +} + #[cfg(test)] mod tests { use super::*; - use crate::graphics::Display; // test buffer length #[test] fn graphics_size() { - let mut buffer = Buffer2in9::default(); - let display = Display::new(WIDTH, HEIGHT, &mut buffer.buffer); + let display = Display2in9::default(); assert_eq!(display.buffer().len(), 4736); } // test default background color on all bytes #[test] fn graphics_default() { - let mut buffer = Buffer2in9::default(); - let display = Display::new(WIDTH, HEIGHT, &mut buffer.buffer); + let display = Display2in9::default(); for &byte in display.buffer() { assert_eq!(byte, DEFAULT_BACKGROUND_COLOR.get_byte_value()); } diff --git a/src/epd2in9/mod.rs b/src/epd2in9/mod.rs index e7de76f..f050c28 100644 --- a/src/epd2in9/mod.rs +++ b/src/epd2in9/mod.rs @@ -4,19 +4,19 @@ //! //! # Example for the 2.9 in E-Ink Display //! -//! ```ignore +//! ```rust,ignore //! use epd_waveshare::{ -//! epd2in9::{EPD2in9, Buffer2in9}, +//! epd2in9::{EPD2in9, Display2in9}, //! graphics::{Display, DisplayRotation}, //! prelude::*, //! }; +//! use embedded_graphics::Drawing; //! //! // Setup EPD -//! let mut epd = EPD2in9::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?; +//! let mut epd = EPD2in9::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay).unwrap(); //! //! // Use display graphics -//! let mut buffer = Buffer2in9::default(); -//! let mut display = Display::new(epd.width(), epd.height(), &mut buffer.buffer); +//! let mut display = Display2in9::default(); //! //! // Write some hello world in the screenbuffer //! display.draw( @@ -38,6 +38,7 @@ pub const WIDTH: u32 = 128; pub const HEIGHT: u32 = 296; pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White; +const IS_BUSY_LOW: bool = false; use embedded_hal::{ blocking::{delay::*, spi::Write}, @@ -55,8 +56,10 @@ use crate::traits::*; use crate::interface::DisplayInterface; +#[cfg(feature = "graphics")] mod graphics; -pub use crate::epd2in9::graphics::Buffer2in9; +#[cfg(feature = "graphics")] +pub use crate::epd2in9::graphics::Display2in9; /// EPD2in9 driver /// @@ -247,6 +250,10 @@ where RefreshLUT::QUICK => self.set_lut_helper(spi, &LUT_PARTIAL_UPDATE), } } + + fn is_busy(&self) -> bool { + self.interface.is_busy(IS_BUSY_LOW) + } } impl EPD2in9 @@ -258,7 +265,7 @@ where RST: OutputPin, { fn wait_until_idle(&mut self) { - self.interface.wait_until_idle(false); + self.interface.wait_until_idle(IS_BUSY_LOW); } fn use_full_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { diff --git a/src/epd4in2/constants.rs b/src/epd4in2/constants.rs index 0578335..8ae34b9 100644 --- a/src/epd4in2/constants.rs +++ b/src/epd4in2/constants.rs @@ -1,9 +1,3 @@ -use crate::color::Color; - -pub const WIDTH: u32 = 400; -pub const HEIGHT: u32 = 300; -pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White; - #[rustfmt::skip] pub(crate) const LUT_VCOM0: [u8; 44] = [ 0x00, 0x17, 0x00, 0x00, 0x00, 0x02, diff --git a/src/epd4in2/graphics.rs b/src/epd4in2/graphics.rs index 5e656e8..38ddd89 100644 --- a/src/epd4in2/graphics.rs +++ b/src/epd4in2/graphics.rs @@ -1,22 +1,54 @@ -use crate::epd4in2::constants::{DEFAULT_BACKGROUND_COLOR, HEIGHT, WIDTH}; +use crate::epd4in2::{DEFAULT_BACKGROUND_COLOR, HEIGHT, WIDTH}; +use crate::graphics::{Display, DisplayRotation}; +use crate::prelude::*; +use embedded_graphics::prelude::*; /// Full size buffer for use with the 4in2 EPD /// /// Can also be manuall constructed: /// `buffer: [DEFAULT_BACKGROUND_COLOR.get_byte_value(); WIDTH / 8 * HEIGHT]` -pub struct Buffer4in2 { - pub buffer: [u8; WIDTH as usize * HEIGHT as usize / 8], +pub struct Display4in2 { + buffer: [u8; WIDTH as usize * HEIGHT as usize / 8], + rotation: DisplayRotation, } -impl Default for Buffer4in2 { +impl Default for Display4in2 { fn default() -> Self { - Buffer4in2 { + Display4in2 { buffer: [DEFAULT_BACKGROUND_COLOR.get_byte_value(); WIDTH as usize * HEIGHT as usize / 8], + rotation: DisplayRotation::default(), } } } +impl Drawing for Display4in2 { + fn draw(&mut self, item_pixels: T) + where + T: Iterator>, + { + self.draw_helper(WIDTH, HEIGHT, item_pixels); + } +} + +impl Display for Display4in2 { + fn buffer(&self) -> &[u8] { + &self.buffer + } + + fn get_mut_buffer(&mut self) -> &mut [u8] { + &mut self.buffer + } + + fn set_rotation(&mut self, rotation: DisplayRotation) { + self.rotation = rotation; + } + + fn rotation(&self) -> DisplayRotation { + self.rotation + } +} + #[cfg(test)] mod tests { use super::*; @@ -24,35 +56,27 @@ mod tests { use crate::epd4in2; use crate::graphics::{Display, DisplayRotation}; use embedded_graphics::coord::Coord; - use embedded_graphics::prelude::*; use embedded_graphics::primitives::Line; // test buffer length #[test] fn graphics_size() { - let mut display4in2 = Buffer4in2::default(); - let display = Display::new(WIDTH, HEIGHT, &mut display4in2.buffer); + let display = Display4in2::default(); assert_eq!(display.buffer().len(), 15000); } // test default background color on all bytes #[test] fn graphics_default() { - let mut display4in2 = Buffer4in2::default(); - let display = Display::new(WIDTH, HEIGHT, &mut display4in2.buffer); - use crate::epd4in2; + let display = Display4in2::default(); for &byte in display.buffer() { - assert_eq!( - byte, - epd4in2::constants::DEFAULT_BACKGROUND_COLOR.get_byte_value() - ); + assert_eq!(byte, epd4in2::DEFAULT_BACKGROUND_COLOR.get_byte_value()); } } #[test] fn graphics_rotation_0() { - let mut display4in2 = Buffer4in2::default(); - let mut display = Display::new(WIDTH, HEIGHT, &mut display4in2.buffer); + let mut display = Display4in2::default(); display.draw( Line::new(Coord::new(0, 0), Coord::new(7, 0)) .with_stroke(Some(Color::Black)) @@ -64,17 +88,13 @@ mod tests { assert_eq!(buffer[0], Color::Black.get_byte_value()); for &byte in buffer.iter().skip(1) { - assert_eq!( - byte, - epd4in2::constants::DEFAULT_BACKGROUND_COLOR.get_byte_value() - ); + assert_eq!(byte, epd4in2::DEFAULT_BACKGROUND_COLOR.get_byte_value()); } } #[test] fn graphics_rotation_90() { - let mut display4in2 = Buffer4in2::default(); - let mut display = Display::new(WIDTH, HEIGHT, &mut display4in2.buffer); + let mut display = Display4in2::default(); display.set_rotation(DisplayRotation::Rotate90); display.draw( Line::new(Coord::new(0, 392), Coord::new(0, 399)) @@ -87,17 +107,13 @@ mod tests { assert_eq!(buffer[0], Color::Black.get_byte_value()); for &byte in buffer.iter().skip(1) { - assert_eq!( - byte, - epd4in2::constants::DEFAULT_BACKGROUND_COLOR.get_byte_value() - ); + assert_eq!(byte, epd4in2::DEFAULT_BACKGROUND_COLOR.get_byte_value()); } } #[test] fn graphics_rotation_180() { - let mut display4in2 = Buffer4in2::default(); - let mut display = Display::new(WIDTH, HEIGHT, &mut display4in2.buffer); + let mut display = Display4in2::default(); display.set_rotation(DisplayRotation::Rotate180); display.draw( Line::new(Coord::new(392, 299), Coord::new(399, 299)) @@ -113,17 +129,13 @@ mod tests { assert_eq!(buffer[0], Color::Black.get_byte_value()); for &byte in buffer.iter().skip(1) { - assert_eq!( - byte, - epd4in2::constants::DEFAULT_BACKGROUND_COLOR.get_byte_value() - ); + assert_eq!(byte, epd4in2::DEFAULT_BACKGROUND_COLOR.get_byte_value()); } } #[test] fn graphics_rotation_270() { - let mut display4in2 = Buffer4in2::default(); - let mut display = Display::new(WIDTH, HEIGHT, &mut display4in2.buffer); + let mut display = Display4in2::default(); display.set_rotation(DisplayRotation::Rotate270); display.draw( Line::new(Coord::new(299, 0), Coord::new(299, 7)) @@ -139,10 +151,7 @@ mod tests { assert_eq!(buffer[0], Color::Black.get_byte_value()); for &byte in buffer.iter().skip(1) { - assert_eq!( - byte, - epd4in2::constants::DEFAULT_BACKGROUND_COLOR.get_byte_value() - ); + assert_eq!(byte, epd4in2::DEFAULT_BACKGROUND_COLOR.get_byte_value()); } } } diff --git a/src/epd4in2/mod.rs b/src/epd4in2/mod.rs index 234ff2c..21e9ba7 100644 --- a/src/epd4in2/mod.rs +++ b/src/epd4in2/mod.rs @@ -56,15 +56,22 @@ use crate::traits::{InternalWiAdditions, RefreshLUT, WaveshareDisplay}; //The Lookup Tables for the Display mod constants; -pub use self::constants::*; +use crate::epd4in2::constants::*; + +pub const WIDTH: u32 = 400; +pub const HEIGHT: u32 = 300; +pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White; +const IS_BUSY_LOW: bool = true; use crate::color::Color; pub(crate) mod command; use self::command::Command; +#[cfg(feature = "graphics")] mod graphics; -pub use self::graphics::Buffer4in2; +#[cfg(feature = "graphics")] +pub use self::graphics::Display4in2; /// EPD4in2 driver /// @@ -322,6 +329,10 @@ where ), } } + + fn is_busy(&self) -> bool { + self.interface.is_busy(IS_BUSY_LOW) + } } impl EPD4in2 @@ -350,7 +361,7 @@ where } fn wait_until_idle(&mut self) { - self.interface.wait_until_idle(true) + self.interface.wait_until_idle(IS_BUSY_LOW) } fn send_resolution(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { diff --git a/src/graphics.rs b/src/graphics.rs index 52cef7e..3b8076e 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -22,71 +22,131 @@ impl Default for DisplayRotation { } } -pub struct Display<'a> { - width: u32, - height: u32, - rotation: DisplayRotation, - buffer: &'a mut [u8], //buffer: Box//[u8; 15000] -} - -impl<'a> Display<'a> { - pub fn new(width: u32, height: u32, buffer: &'a mut [u8]) -> Display<'a> { - let len = buffer.len() as u32; - assert!(width / 8 * height >= len); - Display { - width, - height, - rotation: DisplayRotation::default(), - buffer, +pub trait Display: Drawing { + /// Clears the buffer of the display with the chosen background color + fn clear_buffer(&mut self, background_color: Color) { + for elem in self.get_mut_buffer().iter_mut() { + *elem = background_color.get_byte_value(); } } - pub fn buffer(&self) -> &[u8] { - &self.buffer - } + /// Returns the buffer + fn buffer(&self) -> &[u8]; - pub fn clear_buffer(&mut self, background_color: Color) { - for elem in &mut self.buffer.iter_mut() { - *elem = background_color.get_byte_value(); - } - } + /// Returns a mutable buffer + fn get_mut_buffer(&mut self) -> &mut [u8]; - pub fn set_rotation(&mut self, rotation: DisplayRotation) { - self.rotation = rotation; - } + /// Sets the rotation of the display + fn set_rotation(&mut self, rotation: DisplayRotation); - pub fn rotation(&self) -> DisplayRotation { - self.rotation - } -} + /// Get the current rotation of the display + fn rotation(&self) -> DisplayRotation; -impl<'a> Drawing for Display<'a> { - fn draw(&mut self, item_pixels: T) + /// Helperfunction for the Embedded Graphics draw trait + /// + /// Becomes uneccesary when const_generics become stablised + fn draw_helper(&mut self, width: u32, height: u32, item_pixels: T) where T: Iterator>, { + let rotation = self.rotation(); + let buffer = self.get_mut_buffer(); for Pixel(UnsignedCoord(x, y), color) in item_pixels { - if outside_display(x, y, self.width, self.height, self.rotation) { + if outside_display(x, y, width, height, rotation) { continue; } // Give us index inside the buffer and the bit-position in that u8 which needs to be changed - let (index, bit) = rotation(x, y, self.width, self.height, self.rotation); + let (index, bit) = find_position(x, y, width, height, rotation); let index = index as usize; // "Draw" the Pixel on that bit match color { Color::Black => { - self.buffer[index] &= !bit; + buffer[index] &= !bit; } Color::White => { - self.buffer[index] |= bit; + buffer[index] |= bit; } } } } } +/// A variable Display without a predefined buffer +/// +/// The buffer can be created as following: +/// buffer: [DEFAULT_BACKGROUND_COLOR.get_byte_value(); WIDTH / 8 * HEIGHT] +/// +/// Example: +/// ```rust,no_run +/// # use epd_waveshare::epd2in9::DEFAULT_BACKGROUND_COLOR; +/// # use epd_waveshare::prelude::*; +/// # use epd_waveshare::graphics::VarDisplay; +/// # use embedded_graphics::prelude::*; +/// # use embedded_graphics::primitives::{Circle, Line}; +/// let width = 128; +/// let height = 296; +/// +/// let mut buffer = [DEFAULT_BACKGROUND_COLOR.get_byte_value(); 128 / 8 * 296]; +/// let mut display = VarDisplay::new(width, height, &mut buffer); +/// +/// display.set_rotation(DisplayRotation::Rotate90); +/// +/// display.draw( +/// Line::new(Coord::new(0, 120), Coord::new(0, 295)) +/// .with_stroke(Some(Color::Black)) +/// .into_iter(), +/// ); +/// ``` +pub struct VarDisplay<'a> { + width: u32, + height: u32, + rotation: DisplayRotation, + buffer: &'a mut [u8], //buffer: Box//[u8; 15000] +} + +impl<'a> VarDisplay<'a> { + pub fn new(width: u32, height: u32, buffer: &'a mut [u8]) -> VarDisplay<'a> { + let len = buffer.len() as u32; + assert!(width / 8 * height >= len); + VarDisplay { + width, + height, + rotation: DisplayRotation::default(), + buffer, + } + } +} + +impl<'a> Drawing for VarDisplay<'a> { + fn draw(&mut self, item_pixels: T) + where + T: Iterator>, + { + self.draw_helper(self.width, self.height, item_pixels); + } +} + +impl<'a> Display for VarDisplay<'a> { + fn buffer(&self) -> &[u8] { + &self.buffer + } + + fn get_mut_buffer(&mut self) -> &mut [u8] { + &mut self.buffer + } + + fn set_rotation(&mut self, rotation: DisplayRotation) { + self.rotation = rotation; + } + + fn rotation(&self) -> DisplayRotation { + self.rotation + } +} + +// Checks if a pos is outside the defined display fn outside_display(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRotation) -> bool { match rotation { DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => { @@ -105,7 +165,7 @@ fn outside_display(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRot #[rustfmt::skip] //returns index position in the u8-slice and the bit-position inside that u8 -fn rotation(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRotation) -> (u32, u8) { +fn find_position(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRotation) -> (u32, u8) { match rotation { DisplayRotation::Rotate0 => ( x / 8 + (width / 8) * y, @@ -128,7 +188,7 @@ fn rotation(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRotation) #[cfg(test)] mod tests { - use super::{outside_display, rotation, Display, DisplayRotation}; + use super::{find_position, outside_display, Display, DisplayRotation, VarDisplay}; use crate::color::Color; use embedded_graphics::coord::Coord; use embedded_graphics::prelude::*; @@ -139,7 +199,7 @@ mod tests { use crate::epd4in2::{HEIGHT, WIDTH}; let mut buffer = [Color::Black.get_byte_value(); WIDTH as usize / 8 * HEIGHT as usize]; - let mut display = Display::new(WIDTH, HEIGHT, &mut buffer); + let mut display = VarDisplay::new(WIDTH, HEIGHT, &mut buffer); for &byte in display.buffer.iter() { assert_eq!(byte, Color::Black.get_byte_value()); @@ -171,7 +231,7 @@ mod tests { if outside_display(x, y, width, height, rotation2) { break; } else { - let (idx, _) = rotation(x, y, width, height, rotation2); + let (idx, _) = find_position(x, y, width, height, rotation2); assert!(idx < max_value); } } @@ -185,7 +245,7 @@ mod tests { let height = 296; let mut buffer = [DEFAULT_BACKGROUND_COLOR.get_byte_value(); 128 / 8 * 296]; - let mut display = Display::new(width, height, &mut buffer); + let mut display = VarDisplay::new(width, height, &mut buffer); display.draw( Line::new(Coord::new(0, 0), Coord::new(7, 0)) @@ -209,7 +269,7 @@ mod tests { let height = 296; let mut buffer = [DEFAULT_BACKGROUND_COLOR.get_byte_value(); 128 / 8 * 296]; - let mut display = Display::new(width, height, &mut buffer); + let mut display = VarDisplay::new(width, height, &mut buffer); display.set_rotation(DisplayRotation::Rotate90); diff --git a/src/interface.rs b/src/interface.rs index c046a47..27dec7b 100644 --- a/src/interface.rs +++ b/src/interface.rs @@ -127,16 +127,33 @@ where /// Most likely there was a mistake with the 2in9 busy connection /// //TODO: use the #cfg feature to make this compile the right way for the certain types pub(crate) fn wait_until_idle(&mut self, is_busy_low: bool) { - // TODO: removal of delay. TEST! + //tested: worked without the delay for all tested devices //self.delay_ms(1); - //low: busy, high: idle - while (is_busy_low && self.busy.is_low()) || (!is_busy_low && self.busy.is_high()) { - //TODO: REMOVAL of DELAY: it's only waiting for the signal anyway and should continue work asap + + while self.is_busy(is_busy_low) { + //tested: REMOVAL of DELAY: it's only waiting for the signal anyway and should continue work asap //old: shorten the time? it was 100 in the beginning //self.delay_ms(5); } } + /// Checks if device is still busy + /// + /// This is normally handled by the more complicated commands themselves, + /// but in the case you send data and commands directly you might need to check + /// if the device is still busy + /// + /// is_busy_low + /// + /// - TRUE for epd4in2, epd2in13, epd2in7, epd5in83, epd7in5 + /// - FALSE for epd2in9, epd1in54 (for all Display Type A ones?) + /// + /// Most likely there was a mistake with the 2in9 busy connection + /// //TODO: use the #cfg feature to make this compile the right way for the certain types + pub(crate) fn is_busy(&self, is_busy_low: bool) -> bool { + (is_busy_low && self.busy.is_low()) || (!is_busy_low && self.busy.is_high()) + } + /// Resets the device. /// /// Often used to awake the module from deep sleep. See [EPD4in2::sleep()](EPD4in2::sleep()) diff --git a/src/lib.rs b/src/lib.rs index d210ed9..f2dd16f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,22 +20,35 @@ //! //! # Examples //! -//! ```ignore -//! use eink-waveshare-rs::epd4in2::EPD4in2; -//! -//! let mut epd4in2 = EPD4in2::new(spi, cs, busy, dc, rst, delay).unwrap(); -//! -//! let mut buffer = [0u8, epd4in2.get_width() / 8 * epd4in2.get_height()]; -//! -//! // draw something into the buffer -//! -//! epd4in2.display_and_transfer_buffer(buffer, None); -//! -//! // wait and look at the image -//! -//! epd4in2.clear_frame(None); -//! -//! epd4in2.sleep(); +//! ```rust,ignore +//! use epd_waveshare::{ +//! epd2in9::{EPD2in9, Display2in9}, +//! graphics::{Display, DisplayRotation}, +//! prelude::*, +//! }; +//! use embedded_graphics::Drawing; +//! +//! // Setup EPD +//! let mut epd = EPD2in9::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay).unwrap(); +//! +//! // Use display graphics +//! let mut display = Display2in9::default(); +//! +//! // Write some hello world in the screenbuffer +//! display.draw( +//! Font6x8::render_str("Hello World!") +//! .with_stroke(Some(Color::Black)) +//! .with_fill(Some(Color::White)) +//! .translate(Coord::new(5, 50)) +//! .into_iter(), +//! ); +//! +//! // Display updated frame +//! epd.update_frame(&mut spi, &display.buffer()).unwrap(); +//! epd.display_frame(&mut spi).expect("display frame new graphics"); +//! +//! // Set the EPD to sleep +//! epd.sleep(&mut spi).expect("sleep"); //! ``` //! //! @@ -67,6 +80,9 @@ pub mod prelude { pub use crate::color::Color; pub use crate::traits::{RefreshLUT, WaveshareDisplay}; pub use crate::SPI_MODE; + + #[cfg(feature = "graphics")] + pub use crate::graphics::{Display, DisplayRotation}; } use embedded_hal::spi::{Mode, Phase, Polarity}; diff --git a/src/traits.rs b/src/traits.rs index 2b5dc33..68f1c4d 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -125,6 +125,7 @@ where fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error>; /// Clears the frame buffer on the EPD with the declared background color + /// /// The background color can be changed with [`set_background_color`] fn clear_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error>; @@ -141,4 +142,11 @@ where spi: &mut SPI, refresh_rate: Option, ) -> Result<(), SPI::Error>; + + /// Checks if the display is busy transmitting data + /// + /// This is normally handled by the more complicated commands themselves, + /// but in the case you send data and commands directly you might need to check + /// if the device is still busy + fn is_busy(&self) -> bool; }