diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..71d1ca5 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,37 @@ + +name: Rust + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - stable + - beta + steps: + - uses: actions/checkout@v1 + - name: Install ARM toolchain + run: rustup target add thumbv7em-none-eabihf + - name: Check Fmt + run: cargo fmt --all -- --check + - name: Build lib + run: cargo check --all-targets --verbose + - name: Clippy + run: cargo clippy --all-targets --all-features -- -D warnings -A clippy::new_ret_no_self + - name: Build examples + run: cargo build --examples --all-targets --verbose + - name: Run tests + run: cargo test --verbose + - name: Build docs + run: cargo doc --all-features + diff --git a/.travis.yml b/.travis.yml index 31e2682..3ddb454 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,98 +1,22 @@ -# Based on the "trust" template v0.1.2 -# https://github.com/japaric/trust/tree/v0.1.2 language: rust rust: - stable - - beta - - nightly sudo: required env: TARGET=x86_64-unknown-linux-gnu -# TODO Rust builds on stable by default, this can be -# overridden on a case by case basis down below. -matrix: - allow_failures: - - rust: nightly - - name: clippy - fast_finish: true - # TODO These are all the build jobs. Adjust as necessary. Comment out what you - # don't need - include: - # Linux - #- env: TARGET=aarch64-unknown-linux-gnu - # Raspberry Pi - - env: TARGET=arm-unknown-linux-gnueabi - # Raspberry Pi 3... - - env: TARGET=armv7-unknown-linux-gnueabihf - - # is already included as global env - #- env: TARGET=x86_64-unknown-linux-gnu - - env: TARGET=x86_64-unknown-linux-musl - - # Bare metal - # These targets don't support std and as such are likely not suitable for - # most crates. - - env: TARGET=thumbv6m-none-eabi - before_script: rustup target add $TARGET - script: cargo check --verbose --target $TARGET - - env: TARGET=thumbv7em-none-eabi - before_script: rustup target add $TARGET - script: cargo check --verbose --target $TARGET - - env: TARGET=thumbv7em-none-eabihf - before_script: rustup target add $TARGET - script: cargo check --verbose --target $TARGET - - env: TARGET=thumbv7m-none-eabi - before_script: rustup target add $TARGET - script: cargo check --verbose --target $TARGET - - - name: "fmt" - rust: stable - env: RUN=FMT - before_script: - - rustup component add rustfmt - script: - - cargo fmt --all -- --check - - cargo doc --all-features --release - - cd examples/epd4in2_full && cargo fmt --all -- --check && cd ../../ - - cd examples/epd2in9_full && cargo fmt --all -- --check && cd ../../ - - cd examples/epd1in54_full && cargo fmt --all -- --check && cd ../../ - - cd examples/epd1in54_no_graphics && cargo fmt --all -- --check && cd ../../ - - cd examples/epd4in2_var_display_buffer && cargo fmt --all -- --check && cd ../../ - - cd examples/epd4in2_full_blue_pill && cargo fmt --all -- --check && cd ../../ - - name: "clippy" - rust: stable - env: RUN=FMT - before_script: - - rustup component add clippy - script: - - cargo clippy --all-targets --all-features -- -D warnings -A clippy::new_ret_no_self - - - name: "check examples" - rust: stable - before_script: - - rustup target add thumbv7m-none-eabi - script: - - cd examples/epd4in2_full_blue_pill && cargo check && cd ../../ - - cd examples/epd4in2_full && cargo check && cd ../../ - - cd examples/epd2in9_full && cargo check && cd ../../ - - cd examples/epd1in54_full && cargo check && cd ../../ - - cd examples/epd1in54_no_graphics && cargo check && cd ../../ - - cd examples/epd4in2_var_display_buffer && cargo check && cd ../../ - - name - before_install: - set -e - rustup self update -install: - - cargo install cargo-update || echo "cargo-update already installed" - - cargo install-update -a # update outdated cached binaries - - cargo install cross || echo "cross already installed" +# install: +# - cargo install cargo-update || echo "cargo-update already installed" +# - cargo install-update -a # update outdated cached binaries +# - cargo install cross || echo "cross already installed" #TODO: remove -A clippy::new_ret_no_self when new version of clippy gets released! script: - - cross check --verbose --target $TARGET - - cross test --all-features --release --verbose --target $TARGET + - cargo check --all-features + - cargo test --all-features cache: cargo before_cache: @@ -100,7 +24,7 @@ before_cache: - chmod -R a+r $HOME/.cargo - | if [[ "$TRAVIS_RUST_VERSION" == stable ]]; then - cargo install cargo-tarpaulin -f + cargo install cargo-tarpaulin fi after_success: | @@ -109,7 +33,7 @@ after_success: | # cargo tarpaulin --ciserver travis-ci --coveralls $TRAVIS_JOB_ID # Uncomment the following two lines create and upload a report for codecov.io - cargo tarpaulin --out Xml + cargo tarpaulin --all-features --out Xml bash <(curl -s https://codecov.io/bash) -t "$CODECOV_TOKEN" fi diff --git a/CHANGELOG.md b/CHANGELOG.md index 459d601..51cc2a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added + - New supported epds: epd7in5 (thanks to @str4d), epd7in5 v2 (thanks to @asaaki), epd1in54b (thanks to @jkristell) + - Added update_and_display_frame to WaveshareDisplay trait (fixes #38) + - also improve position of busy_wait (#30) once more + - More Documenation + +### Changed + - Update embedded-graphics to 0.6 (changes Display Trait) (and to 0.5 before thanks to @dbr) + - Remove useless Featuregates (Doesn't change size) + - Update and integrate a few important examples and remove the others + - Use Embedded_hal:digital::v2 + + +### Fixed + - Doc Tests + ## [v0.3.2] - 2019-06-17 diff --git a/Cargo.toml b/Cargo.toml index 3828456..abd2aef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,28 +13,20 @@ version = "0.3.2" edition = "2018" [badges] -# Travis CI: `repository` in format "/" is required. -# `branch` is optional; default is `master` -travis-ci = { repository = "caemor/epd-waveshare" } +# travis-ci = { repository = "caemor/epd-waveshare" } + +[dependencies] +embedded-graphics = { version = "0.6.0", optional = true} +embedded-hal = {version = "0.2.3", features = ["unproven"]} + +[dev-dependencies] +linux-embedded-hal = "0.3" +embedded-hal-mock = "0.7" [features] -default = ["epd1in54", "epd1in54b", "epd2in9", "epd2in9b", "epd4in2", "epd7in5", "epd7in5_v2", "graphics"] +default = ["graphics"] graphics = ["embedded-graphics"] -epd1in54 = [] -epd1in54b = [] -epd2in9 = [] -epd2in9b = [] -epd4in2 = [] -epd7in5 = [] -epd7in5_v2 = [] -# offers an alternative fast full lut for type_a displays, but the refresh isnt as clean looking -type_a_alternative_faster_lut = [] -[dependencies.embedded-graphics] -optional = true -version = "0.5.2" - -[dependencies.embedded-hal] -features = ["unproven"] -version = "0.2.3" +# Offers an alternative fast full lut for type_a displays, but the refreshed screen isnt as clean looking +type_a_alternative_faster_lut = [] diff --git a/README.md b/README.md index 76a3cc1..d7a0489 100644 --- a/README.md +++ b/README.md @@ -10,30 +10,28 @@ 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). 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). +There are multiple examples in the examples folder. Use `cargo run --example example_name` to try them. ```Rust // Setup the epd let mut epd = EPD4in2::new(&mut spi, cs, busy, dc, rst, &mut delay)?; // Setup the graphics -let mut buffer = Buffer4in2::default(); -let mut display = Display::new(epd.width(), epd.height(), &mut buffer.buffer); +let mut display = Display4in2::default(); // Draw some text display.draw( - Font12x16::render_str("Hello Rust!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), + let _ = Text::new("Hello Rust!", Point::new(x, y)) + .into_styled(text_style!( + font = Font12x16, + text_color = Black, + background_color = White + )) + .draw(display); ); -// Transfer the frame data to the epd -epd.update_frame(&mut spi, &display.buffer())?; - -// Display the frame on the epd -epd.display_frame(&mut spi)?; +// Transfer the frame data to the epd and display it +epd.update_and_display_frame(&mut spi, &display.buffer())?; ``` ## (Supported) Devices @@ -45,7 +43,7 @@ epd.display_frame(&mut spi)?; | [4.2 Inch B/W (A)](https://www.waveshare.com/product/4.2inch-e-paper-module.htm) | Black, White | ✕ | Not officially [[2](#2-42-inch-e-ink-blackwhite---partial-refresh)] | ✔ | ✔ | | [1.54 Inch B/W (A)](https://www.waveshare.com/1.54inch-e-Paper-Module.htm) | Black, White | ✕ | ✔ | ✔ | ✔ | | [2.13 Inch B/W (A)](https://www.waveshare.com/product/2.13inch-e-paper-hat.htm) | Black, White | ✕ | ✔ | | | -| [2.9 Inch B/W (A)](https://www.waveshare.com/product/2.9inch-e-paper-module.htm) | Black, White | ✕ | ✔ | ✔ | ✔ [[3](#3-29-inch-e-ink-blackwhite---tests)] | +| [2.9 Inch B/W (A)](https://www.waveshare.com/product/2.9inch-e-paper-module.htm) | Black, White | ✕ | ✔ | ✔ | ✔ | | [1.54 Inch B/W/R (B)](https://www.waveshare.com/product/modules/oleds-lcds/e-paper/1.54inch-e-paper-module-b.htm) | Black, White, Red | ✕ | ✕ | ✔ | ✔ | ### [1]: 7.5 Inch B/W V2 (A) @@ -62,10 +60,6 @@ Out of the Box the original driver from Waveshare only supports full updates. That means: Be careful with the quick refresh updates:
It's possible with this driver but might lead to ghosting / burn-in effects therefore it's hidden behind a feature. -### [3]: 2.9 Inch E-Ink Black/White - Tests - -Since my 2.9 Inch Display has some blurring issues I am not absolutly sure if everything was working correctly as it should :-) - ### Interface | Interface | Description | diff --git a/examples/Readme.md b/examples/Readme.md deleted file mode 100644 index a1c8bc9..0000000 --- a/examples/Readme.md +++ /dev/null @@ -1,47 +0,0 @@ -# Examples: - -All of these examples are projects of their own. - -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. - -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. - -### epd4in2_full_blue_pill - -Connect epd4in2 display to blue pill board: -- BUSY -> A10 -- RST -> A9 -- DC -> A8 -- CS -> B12 -- CLK -> B13 -- DIN -> B15 -- GND -> G -- VCC -> 3.3 - -For compiling and flashing, please refer to [TeXitois blue pill quickstart](https://github.com/TeXitoi/blue-pill-quickstart/blob/master/README.md). - -Basically: - -```shell -curl https://sh.rustup.rs -sSf | sh -rustup target add thumbv7m-none-eabi -sudo apt-get install gdb-arm-none-eabi openocd -cd epd4in2_full_blue_pill -# connect ST-Link v2 to the blue pill and the computer -# openocd in another terminal -cargo run --release -``` - -Ff you can't connect to openocd you might need to adapt your udev rules or use sudo ([openOCD Problems](https://rust-embedded.github.io/discovery/03-setup/linux.html#udev-rules)) - - diff --git a/examples/epd1in54_full/Cargo.toml b/examples/epd1in54_full/Cargo.toml deleted file mode 100644 index d900b46..0000000 --- a/examples/epd1in54_full/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -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.2" -embedded-graphics = "0.5.2" -embedded-hal = { version = "0.2.2", features = ["unproven"] } diff --git a/examples/epd1in54_full/src/main.rs b/examples/epd1in54_full/src/main.rs deleted file mode 100644 index a308905..0000000 --- a/examples/epd1in54_full/src/main.rs +++ /dev/null @@ -1,150 +0,0 @@ -#![deny(warnings)] - -use embedded_graphics::{coord::Coord, fonts::Font6x8, prelude::*, Drawing}; -use embedded_hal::prelude::*; -use epd_waveshare::{ - epd1in54::{Display1in54, EPD1in54}, - graphics::{Display, DisplayRotation}, - 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").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 = 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).expect("clear frame 1"); - epd.display_frame(&mut spi).expect("disp 1"); - - println!("Test all the rotations"); - let mut display = Display1in54::default(); - display.set_rotation(DisplayRotation::Rotate0); - display.draw( - Font6x8::render_str("Rotate 0!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - display.set_rotation(DisplayRotation::Rotate90); - display.draw( - Font6x8::render_str("Rotate 90!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - display.set_rotation(DisplayRotation::Rotate180); - display.draw( - Font6x8::render_str("Rotate 180!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - display.set_rotation(DisplayRotation::Rotate270); - display.draw( - Font6x8::render_str("Rotate 270!") - .stroke(Some(Color::Black)) - .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"); - delay.delay_ms(5000u16); - - // a quickly moving `Hello World!` - display.set_rotation(DisplayRotation::Rotate0); - epd.set_lut(&mut spi, Some(RefreshLUT::QUICK)) - .expect("SET LUT QUICK error"); - let limit = 20; - for i in 0..limit { - println!("Moving Hello World. Loop {} from {}", (i + 1), limit); - - display.draw( - Font6x8::render_str(" Hello World! ") - .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 * 6, 50)) - .into_iter(), - ); - - 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"); - - Ok(()) -} diff --git a/examples/epd1in54_no_graphics/src/main.rs b/examples/epd1in54_no_graphics.rs similarity index 94% rename from examples/epd1in54_no_graphics/src/main.rs rename to examples/epd1in54_no_graphics.rs index bdff7a9..7fd30ae 100644 --- a/examples/epd1in54_no_graphics/src/main.rs +++ b/examples/epd1in54_no_graphics.rs @@ -12,20 +12,14 @@ use linux_embedded_hal::{ // 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> { +fn main() -> 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) + .mode(spidev::SpiModeFlags::SPI_MODE_0) .build(); spi.configure(&options).expect("spi configuration"); diff --git a/examples/epd1in54_no_graphics/Cargo.toml b/examples/epd1in54_no_graphics/Cargo.toml deleted file mode 100644 index 3868a67..0000000 --- a/examples/epd1in54_no_graphics/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[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/epd2in9_full/Cargo.toml b/examples/epd2in9_full/Cargo.toml deleted file mode 100644 index da71ecb..0000000 --- a/examples/epd2in9_full/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -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.2" -embedded-graphics = "0.5.2" -embedded-hal = { version = "0.2.2", features = ["unproven"] } diff --git a/examples/epd2in9_full/src/main.rs b/examples/epd2in9_full/src/main.rs deleted file mode 100644 index e4e25a0..0000000 --- a/examples/epd2in9_full/src/main.rs +++ /dev/null @@ -1,154 +0,0 @@ -#![deny(warnings)] - -use embedded_graphics::{coord::Coord, fonts::Font6x8, prelude::*, Drawing}; -use embedded_hal::prelude::*; -use epd_waveshare::{ - epd2in9::{Display2in9, EPD2in9}, - graphics::{Display, DisplayRotation}, - 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 - -//TODO: Test this implemenation with a new display -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").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 = 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 = EPD2in9::new(&mut spi, cs_pin, busy, dc, rst, &mut delay)?; - - // Clear the full screen - epd.clear_frame(&mut spi).expect("clear frame 1"); - epd.display_frame(&mut spi).expect("disp 1"); - - println!("Test all the rotations"); - let mut display = Display2in9::default(); - epd.update_frame(&mut spi, display.buffer()).unwrap(); - epd.display_frame(&mut spi).expect("display frame x03"); - - display.set_rotation(DisplayRotation::Rotate0); - display.draw( - Font6x8::render_str("Rotate 0!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - display.set_rotation(DisplayRotation::Rotate90); - display.draw( - Font6x8::render_str("Rotate 90!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - display.set_rotation(DisplayRotation::Rotate180); - display.draw( - Font6x8::render_str("Rotate 180!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - display.set_rotation(DisplayRotation::Rotate270); - display.draw( - Font6x8::render_str("Rotate 270!") - .stroke(Some(Color::Black)) - .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"); - delay.delay_ms(5000u16); - - // a quickly moving `Hello World!` - display.set_rotation(DisplayRotation::Rotate0); - epd.set_lut(&mut spi, Some(RefreshLUT::QUICK)) - .expect("SET LUT QUICK error"); - let limit = 20; - for i in 0..limit { - println!("Moving Hello World. Loop {} from {}", (i + 1), limit); - - display.draw( - Font6x8::render_str(" Hello World! ") - .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 * 6, 50)) - .into_iter(), - ); - - 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"); - - Ok(()) -} diff --git a/examples/epd4in2.rs b/examples/epd4in2.rs new file mode 100644 index 0000000..04730ed --- /dev/null +++ b/examples/epd4in2.rs @@ -0,0 +1,150 @@ +#![deny(warnings)] + +use embedded_graphics::{ + fonts::{Font12x16, Font6x8, Text}, + prelude::*, + primitives::{Circle, Line}, + style::PrimitiveStyle, + text_style, +}; +use embedded_hal::prelude::*; +use epd_waveshare::{ + color::*, + epd4in2::{Display4in2, EPD4in2}, + graphics::{Display, DisplayRotation}, + 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() -> 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::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 = 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 mut display = Display4in2::default(); + + display.set_rotation(DisplayRotation::Rotate0); + draw_text(&mut display, "Rotate 0!", 5, 50); + + display.set_rotation(DisplayRotation::Rotate90); + draw_text(&mut display, "Rotate 90!", 5, 50); + + display.set_rotation(DisplayRotation::Rotate180); + draw_text(&mut display, "Rotate 180!", 5, 50); + + display.set_rotation(DisplayRotation::Rotate270); + draw_text(&mut display, "Rotate 270!", 5, 50); + + epd4in2.update_frame(&mut spi, &display.buffer())?; + 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 + let _ = Circle::new(Point::new(64, 64), 64) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); + let _ = Line::new(Point::new(64, 64), Point::new(0, 64)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); + let _ = Line::new(Point::new(64, 64), Point::new(80, 80)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); + + // draw white on black background + let _ = Text::new("It's working-WoB!", Point::new(175, 250)) + .into_styled(text_style!( + font = Font6x8, + text_color = White, + background_color = Black + )) + .draw(&mut display); + + // use bigger/different font + let _ = Text::new("It's working-WoB!", Point::new(50, 200)) + .into_styled(text_style!( + font = Font12x16, + text_color = White, + background_color = Black + )) + .draw(&mut display); + + // a moving `Hello World!` + let limit = 10; + epd4in2.set_lut(&mut spi, Some(RefreshLUT::QUICK)).unwrap(); + epd4in2.clear_frame(&mut spi).unwrap(); + for i in 0..limit { + //println!("Moving Hello World. Loop {} from {}", (i + 1), limit); + + draw_text(&mut display, " Hello World! ", 5 + i * 12, 50); + + 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) +} + +fn draw_text(display: &mut Display4in2, text: &str, x: i32, y: i32) { + let _ = Text::new(text, Point::new(x, y)) + .into_styled(text_style!( + font = Font6x8, + text_color = Black, + background_color = White + )) + .draw(display); +} diff --git a/examples/epd4in2_full/Cargo.toml b/examples/epd4in2_full/Cargo.toml deleted file mode 100644 index c8b1068..0000000 --- a/examples/epd4in2_full/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[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.5.2" -embedded-hal = { version = "0.2.2", features = ["unproven"] } diff --git a/examples/epd4in2_full/src/main.rs b/examples/epd4in2_full/src/main.rs deleted file mode 100644 index b333b23..0000000 --- a/examples/epd4in2_full/src/main.rs +++ /dev/null @@ -1,189 +0,0 @@ -#![deny(warnings)] - -use embedded_graphics::{ - coord::Coord, - fonts::{Font12x16, Font6x8}, - prelude::*, - primitives::{Circle, Line}, - Drawing, -}; -use embedded_hal::prelude::*; -use epd_waveshare::{ - epd4in2::{Display4in2, EPD4in2}, - graphics::{Display, DisplayRotation}, - 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 mut display = Display4in2::default(); - display.set_rotation(DisplayRotation::Rotate0); - display.draw( - Font6x8::render_str("Rotate 0!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - display.set_rotation(DisplayRotation::Rotate90); - display.draw( - Font6x8::render_str("Rotate 90!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - display.set_rotation(DisplayRotation::Rotate180); - display.draw( - Font6x8::render_str("Rotate 180!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - display.set_rotation(DisplayRotation::Rotate270); - display.draw( - Font6x8::render_str("Rotate 270!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - epd4in2.update_frame(&mut spi, &display.buffer()).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) - .stroke(Some(Color::Black)) - .into_iter(), - ); - display.draw( - Line::new(Coord::new(64, 64), Coord::new(0, 64)) - .stroke(Some(Color::Black)) - .into_iter(), - ); - display.draw( - Line::new(Coord::new(64, 64), Coord::new(80, 80)) - .stroke(Some(Color::Black)) - .into_iter(), - ); - - // draw white on black background - display.draw( - Font6x8::render_str("It's working-WoB!") - // Using Style here - .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 - .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! ") - .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/examples/epd4in2_full_blue_pill/.cargo/config b/examples/epd4in2_full_blue_pill/.cargo/config deleted file mode 100644 index 313e8f0..0000000 --- a/examples/epd4in2_full_blue_pill/.cargo/config +++ /dev/null @@ -1,12 +0,0 @@ -[target.thumbv7m-none-eabi] - -# uncomment ONE of these three option to make `cargo run` start a GDB session -# which option to pick depends on your system -runner = "arm-none-eabi-gdb -q -x openocd.gdb" -# runner = "gdb-multiarch -q -x openocd.gdb" -# runner = "gdb -q -x openocd.gdb" - -rustflags = ["-C", "link-arg=-Tlink.x"] - -[build] -target = "thumbv7m-none-eabi" \ No newline at end of file diff --git a/examples/epd4in2_full_blue_pill/Cargo.toml b/examples/epd4in2_full_blue_pill/Cargo.toml deleted file mode 100644 index b4abfbb..0000000 --- a/examples/epd4in2_full_blue_pill/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[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"]} - -embedded-graphics = "0.5.2" -embedded-hal = { version = "0.2.2", features = ["unproven"] } - -stm32f1xx-hal = { version = "0.2", features = ["rt", "stm32f103" ] } -cortex-m = "0.5.0" -cortex-m-rt = { version = "0.6.6", features = ["device"] } -panic-semihosting = "0.5" diff --git a/examples/epd4in2_full_blue_pill/memory.x b/examples/epd4in2_full_blue_pill/memory.x deleted file mode 100644 index fc092da..0000000 --- a/examples/epd4in2_full_blue_pill/memory.x +++ /dev/null @@ -1,6 +0,0 @@ -/* Linker script for the STM32F103C8T6 */ -MEMORY -{ - FLASH : ORIGIN = 0x08000000, LENGTH = 64K - RAM : ORIGIN = 0x20000000, LENGTH = 20K -} \ No newline at end of file diff --git a/examples/epd4in2_full_blue_pill/openocd.cfg b/examples/epd4in2_full_blue_pill/openocd.cfg deleted file mode 100644 index 5ac6a60..0000000 --- a/examples/epd4in2_full_blue_pill/openocd.cfg +++ /dev/null @@ -1,2 +0,0 @@ -source [find interface/stlink-v2.cfg] -source [find target/stm32f1x.cfg] \ No newline at end of file diff --git a/examples/epd4in2_full_blue_pill/openocd.gdb b/examples/epd4in2_full_blue_pill/openocd.gdb deleted file mode 100644 index 5f5b068..0000000 --- a/examples/epd4in2_full_blue_pill/openocd.gdb +++ /dev/null @@ -1,10 +0,0 @@ -target remote :3333 -set print asm-demangle on -monitor arm semihosting enable - -# detect unhandled exceptions, hard faults and panics -break DefaultHandler -break HardFault -break rust_begin_unwind - -load \ No newline at end of file diff --git a/examples/epd4in2_full_blue_pill/src/main.rs b/examples/epd4in2_full_blue_pill/src/main.rs deleted file mode 100644 index a20143b..0000000 --- a/examples/epd4in2_full_blue_pill/src/main.rs +++ /dev/null @@ -1,192 +0,0 @@ -#![no_main] -#![no_std] - -// set the panic handler -#[allow(unused_imports)] -use panic_semihosting; - -use cortex_m_rt::entry; -use stm32f1xx_hal::prelude::*; -use stm32f1xx_hal::{delay, spi}; - -use embedded_graphics::{ - coord::Coord, - fonts::{Font12x16, Font6x8}, - prelude::*, - primitives::{Circle, Line}, - Drawing, -}; -use epd_waveshare::{ - epd4in2::Display4in2, - graphics::{Display, DisplayRotation}, - prelude::*, -}; - -#[entry] -fn main() -> ! { - let core = cortex_m::Peripherals::take().unwrap(); - let device = stm32f1xx_hal::stm32::Peripherals::take().unwrap(); - let mut rcc = device.RCC.constrain(); - let mut flash = device.FLASH.constrain(); - - let clocks = rcc - .cfgr - .use_hse(8.mhz()) - .sysclk(72.mhz()) - .pclk1(36.mhz()) - .freeze(&mut flash.acr); - - let mut gpioa = device.GPIOA.split(&mut rcc.apb2); - let mut gpiob = device.GPIOB.split(&mut rcc.apb2); - - let mut delay = delay::Delay::new(core.SYST, clocks); - - // spi setup - let sck = gpiob.pb13.into_alternate_push_pull(&mut gpiob.crh); - let miso = gpiob.pb14; - let mosi = gpiob.pb15.into_alternate_push_pull(&mut gpiob.crh); - let mut spi = spi::Spi::spi2( - device.SPI2, - (sck, miso, mosi), - epd_waveshare::SPI_MODE, - 4.mhz(), - clocks, - &mut rcc.apb1, - ); - // epd setup - let mut epd4in2 = epd_waveshare::epd4in2::EPD4in2::new( - &mut spi, - gpiob.pb12.into_push_pull_output(&mut gpiob.crh), - gpioa.pa10.into_floating_input(&mut gpioa.crh), - gpioa.pa8.into_push_pull_output(&mut gpioa.crh), - gpioa.pa9.into_push_pull_output(&mut gpioa.crh), - &mut delay, - ) - .unwrap(); - epd4in2.set_lut(&mut spi, Some(RefreshLUT::QUICK)).unwrap(); - epd4in2.clear_frame(&mut spi).unwrap(); - - //println!("Test all the rotations"); - let mut display = Display4in2::default(); - display.set_rotation(DisplayRotation::Rotate0); - display.draw( - Font6x8::render_str("Rotate 0!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - display.set_rotation(DisplayRotation::Rotate90); - display.draw( - Font6x8::render_str("Rotate 90!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - display.set_rotation(DisplayRotation::Rotate180); - display.draw( - Font6x8::render_str("Rotate 180!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - display.set_rotation(DisplayRotation::Rotate270); - display.draw( - Font6x8::render_str("Rotate 270!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - epd4in2.update_frame(&mut spi, &display.buffer()).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) - .stroke(Some(Color::Black)) - .into_iter(), - ); - display.draw( - Line::new(Coord::new(64, 64), Coord::new(0, 64)) - .stroke(Some(Color::Black)) - .into_iter(), - ); - display.draw( - Line::new(Coord::new(64, 64), Coord::new(80, 80)) - .stroke(Some(Color::Black)) - .into_iter(), - ); - - // draw white on black background - display.draw( - Font6x8::render_str("It's working-WoB!") - // Using Style here - .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 - .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; - epd4in2.set_lut(&mut spi, Some(RefreshLUT::QUICK)).unwrap(); - epd4in2.clear_frame(&mut spi).unwrap(); - for i in 0..limit { - //println!("Moving Hello World. Loop {} from {}", (i + 1), limit); - - display.draw( - Font6x8::render_str(" Hello World! ") - .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).expect("epd goes to sleep"); - - loop { - // sleep - cortex_m::asm::wfi(); - } -} diff --git a/examples/epd4in2_var_display_buffer/Cargo.toml b/examples/epd4in2_var_display_buffer/Cargo.toml deleted file mode 100644 index c8b1068..0000000 --- a/examples/epd4in2_var_display_buffer/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[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.5.2" -embedded-hal = { version = "0.2.2", features = ["unproven"] } diff --git a/examples/epd4in2_var_display_buffer/src/main.rs b/examples/epd4in2_variable_size.rs similarity index 54% rename from examples/epd4in2_var_display_buffer/src/main.rs rename to examples/epd4in2_variable_size.rs index f15ab4b..6c03851 100644 --- a/examples/epd4in2_var_display_buffer/src/main.rs +++ b/examples/epd4in2_variable_size.rs @@ -1,14 +1,16 @@ #![deny(warnings)] +#![deny(warnings)] use embedded_graphics::{ - coord::Coord, - fonts::{Font12x16, Font6x8}, + fonts::{Font12x16, Font6x8, Text}, prelude::*, primitives::{Circle, Line}, - Drawing, + style::PrimitiveStyle, + text_style, }; use embedded_hal::prelude::*; use epd_waveshare::{ + color::*, epd4in2::{self, EPD4in2}, graphics::{Display, DisplayRotation, VarDisplay}, prelude::*, @@ -23,20 +25,14 @@ use linux_embedded_hal::{ // 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> { +fn main() -> 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) + .mode(spidev::SpiModeFlags::SPI_MODE_0) .build(); spi.configure(&options).expect("spi configuration"); @@ -77,40 +73,16 @@ fn run() -> Result<(), std::io::Error> { 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!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); + draw_text(&mut display, "Rotate 0!", 5, 50); display.set_rotation(DisplayRotation::Rotate90); - display.draw( - Font6x8::render_str("Rotate 90!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); + draw_text(&mut display, "Rotate 90!", 5, 50); display.set_rotation(DisplayRotation::Rotate180); - display.draw( - Font6x8::render_str("Rotate 180!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); + draw_text(&mut display, "Rotate 180!", 5, 50); display.set_rotation(DisplayRotation::Rotate270); - display.draw( - Font6x8::render_str("Rotate 270!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); + draw_text(&mut display, "Rotate 270!", 5, 50); epd4in2 .update_partial_frame(&mut spi, &display.buffer(), x, y, width, height) @@ -121,68 +93,49 @@ fn run() -> Result<(), std::io::Error> { delay.delay_ms(5000u16); println!("Now test new graphics with default rotation and some special stuff:"); + display.set_rotation(DisplayRotation::Rotate0); display.clear_buffer(Color::White); // draw a analog clock - display.draw( - Circle::new(Coord::new(64, 64), 64) - .stroke(Some(Color::Black)) - .into_iter(), - ); - display.draw( - Line::new(Coord::new(64, 64), Coord::new(0, 64)) - .stroke(Some(Color::Black)) - .into_iter(), - ); - display.draw( - Line::new(Coord::new(64, 64), Coord::new(80, 80)) - .stroke(Some(Color::Black)) - .into_iter(), - ); + // draw a analog clock + let _ = Circle::new(Point::new(64, 64), 64) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); + let _ = Line::new(Point::new(64, 64), Point::new(0, 64)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); + let _ = Line::new(Point::new(64, 64), Point::new(80, 80)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); // draw white on black background - display.draw( - Font6x8::render_str("It's working-WoB!") - // Using Style here - .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(), - ); + let _ = Text::new("It's working-WoB!", Point::new(175, 250)) + .into_styled(text_style!( + font = Font6x8, + text_color = White, + background_color = Black + )) + .draw(&mut display); // use bigger/different font - display.draw( - Font12x16::render_str("It's working-BoW!") - // Using Style here - .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(), - ); + let _ = Text::new("It's working-WoB!", Point::new(50, 200)) + .into_styled(text_style!( + font = Font12x16, + text_color = White, + background_color = Black + )) + .draw(&mut display); // 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! ") - .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(); + draw_text(&mut display, " Hello World! ", 5 + i * 12, 50); + + epd4in2 + .update_partial_frame(&mut spi, &display.buffer(), x, y, width, height) + .unwrap(); epd4in2 .display_frame(&mut spi) .expect("display frame new graphics"); @@ -193,3 +146,13 @@ fn run() -> Result<(), std::io::Error> { println!("Finished tests - going to sleep"); epd4in2.sleep(&mut spi) } + +fn draw_text(display: &mut VarDisplay, text: &str, x: i32, y: i32) { + let _ = Text::new(text, Point::new(x, y)) + .into_styled(text_style!( + font = Font6x8, + text_color = Black, + background_color = White + )) + .draw(display); +} diff --git a/examples/epd7in5_full/Cargo.toml b/examples/epd7in5_full/Cargo.toml deleted file mode 100644 index e9e1b8e..0000000 --- a/examples/epd7in5_full/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "embedded_linux_eink_example" -version = "0.1.0" -authors = [ - "Christoph Groß ", - "Jack Grigg ", -] -edition = "2018" - -[dependencies] -embedded-graphics = "0.5.2" -embedded-hal = { version = "0.2.2", features = ["unproven"] } -epd-waveshare = { path = "../../", default-features = false, features = ["epd7in5", "graphics"]} -linux-embedded-hal = "0.2.2" diff --git a/examples/epd7in5_full/src/main.rs b/examples/epd7in5_full/src/main.rs deleted file mode 100644 index 1a3ed2f..0000000 --- a/examples/epd7in5_full/src/main.rs +++ /dev/null @@ -1,188 +0,0 @@ -#![deny(warnings)] - -use embedded_graphics::{ - coord::Coord, - fonts::{Font12x16, Font6x8}, - prelude::*, - primitives::{Circle, Line}, - Drawing, -}; -use embedded_hal::prelude::*; -use epd_waveshare::{ - epd7in5::{Display7in5, EPD7in5}, - graphics::{Display, DisplayRotation}, - 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(8); - 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(24); - busy.export().expect("busy export"); - while !busy.is_exported() {} - busy.set_direction(Direction::In).expect("busy Direction"); - - let dc = Pin::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 = Pin::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"); - - let mut delay = Delay {}; - - let mut epd7in5 = - EPD7in5::new(&mut spi, cs, busy, dc, rst, &mut delay).expect("eink initalize error"); - - println!("Test all the rotations"); - let mut display = Display7in5::default(); - display.set_rotation(DisplayRotation::Rotate0); - display.draw( - Font6x8::render_str("Rotate 0!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - display.set_rotation(DisplayRotation::Rotate90); - display.draw( - Font6x8::render_str("Rotate 90!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - display.set_rotation(DisplayRotation::Rotate180); - display.draw( - Font6x8::render_str("Rotate 180!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - display.set_rotation(DisplayRotation::Rotate270); - display.draw( - Font6x8::render_str("Rotate 270!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - epd7in5.update_frame(&mut spi, &display.buffer()).unwrap(); - epd7in5 - .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) - .stroke(Some(Color::Black)) - .into_iter(), - ); - display.draw( - Line::new(Coord::new(64, 64), Coord::new(0, 64)) - .stroke(Some(Color::Black)) - .into_iter(), - ); - display.draw( - Line::new(Coord::new(64, 64), Coord::new(80, 80)) - .stroke(Some(Color::Black)) - .into_iter(), - ); - - // draw white on black background - display.draw( - Font6x8::render_str("It's working-WoB!") - // Using Style here - .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 - .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! ") - .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(), - ); - - epd7in5.update_frame(&mut spi, &display.buffer()).unwrap(); - epd7in5 - .display_frame(&mut spi) - .expect("display frame new graphics"); - - delay.delay_ms(1_000u16); - } - - println!("Finished tests - going to sleep"); - epd7in5.sleep(&mut spi) -} diff --git a/examples/epd7in5_v2_full/Cargo.toml b/examples/epd7in5_v2_full/Cargo.toml deleted file mode 100644 index d12cdd1..0000000 --- a/examples/epd7in5_v2_full/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "embedded_linux_eink_example" -version = "0.1.0" -authors = [ - "Christoph Groß ", - "Jack Grigg ", - "Christoph Grabo ", -] -edition = "2018" - -[dependencies] -embedded-graphics = "0.5.2" -embedded-hal = { version = "0.2.3", features = ["unproven"] } -epd-waveshare = { path = "../../", default-features = false, features = ["epd7in5_v2", "graphics"]} -linux-embedded-hal = "0.3.0" diff --git a/examples/epd7in5_v2_full/src/main.rs b/examples/epd7in5_v2_full/src/main.rs deleted file mode 100644 index e7381f1..0000000 --- a/examples/epd7in5_v2_full/src/main.rs +++ /dev/null @@ -1,188 +0,0 @@ -#![deny(warnings)] - -use embedded_graphics::{ - coord::Coord, - fonts::{Font12x16, Font6x8}, - prelude::*, - primitives::{Circle, Line}, - Drawing, -}; -use embedded_hal::prelude::*; -use epd_waveshare::{ - epd7in5_v2::{Display7in5, EPD7in5}, - graphics::{Display, DisplayRotation}, - 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::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 = Pin::new(8); - 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(24); - busy.export().expect("busy export"); - while !busy.is_exported() {} - busy.set_direction(Direction::In).expect("busy Direction"); - - let dc = Pin::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 = Pin::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"); - - let mut delay = Delay {}; - - let mut epd7in5 = - EPD7in5::new(&mut spi, cs, busy, dc, rst, &mut delay).expect("eink initalize error"); - - println!("Test all the rotations"); - let mut display = Display7in5::default(); - display.set_rotation(DisplayRotation::Rotate0); - display.draw( - Font6x8::render_str("Rotate 0!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - display.set_rotation(DisplayRotation::Rotate90); - display.draw( - Font6x8::render_str("Rotate 90!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - display.set_rotation(DisplayRotation::Rotate180); - display.draw( - Font6x8::render_str("Rotate 180!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - display.set_rotation(DisplayRotation::Rotate270); - display.draw( - Font6x8::render_str("Rotate 270!") - .stroke(Some(Color::Black)) - .fill(Some(Color::White)) - .translate(Coord::new(5, 50)) - .into_iter(), - ); - - epd7in5.update_frame(&mut spi, &display.buffer()).unwrap(); - epd7in5 - .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) - .stroke(Some(Color::Black)) - .into_iter(), - ); - display.draw( - Line::new(Coord::new(64, 64), Coord::new(0, 64)) - .stroke(Some(Color::Black)) - .into_iter(), - ); - display.draw( - Line::new(Coord::new(64, 64), Coord::new(80, 80)) - .stroke(Some(Color::Black)) - .into_iter(), - ); - - // draw white on black background - display.draw( - Font6x8::render_str("It's working-WoB!") - // Using Style here - .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 - .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! ") - .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(), - ); - - epd7in5.update_frame(&mut spi, &display.buffer()).unwrap(); - epd7in5 - .display_frame(&mut spi) - .expect("display frame new graphics"); - - delay.delay_ms(1_000u16); - } - - println!("Finished tests - going to sleep"); - epd7in5.sleep(&mut spi) -} diff --git a/src/color.rs b/src/color.rs index a7d882c..3f70397 100644 --- a/src/color.rs +++ b/src/color.rs @@ -1,5 +1,13 @@ //! B/W Color for EPDs +#[cfg(feature = "graphics")] +use embedded_graphics::pixelcolor::BinaryColor; + +#[cfg(feature = "graphics")] +pub use BinaryColor::Off as White; +#[cfg(feature = "graphics")] +pub use BinaryColor::On as Black; + /// Only for the Black/White-Displays #[derive(Clone, Copy, PartialEq, Debug)] pub enum Color { @@ -62,11 +70,6 @@ impl Color { } } -#[cfg(feature = "graphics")] -use embedded_graphics::prelude::*; -#[cfg(feature = "graphics")] -impl PixelColor for Color {} - impl From for Color { fn from(value: u8) -> Self { Color::from_u8(value) diff --git a/src/epd1in54/graphics.rs b/src/epd1in54/graphics.rs index b63c460..2cdc1d2 100644 --- a/src/epd1in54/graphics.rs +++ b/src/epd1in54/graphics.rs @@ -1,6 +1,6 @@ use crate::epd1in54::{DEFAULT_BACKGROUND_COLOR, HEIGHT, WIDTH}; use crate::graphics::{Display, DisplayRotation}; -use crate::prelude::*; +use embedded_graphics::pixelcolor::BinaryColor; use embedded_graphics::prelude::*; /// Full size buffer for use with the 1in54 EPD @@ -22,12 +22,15 @@ impl Default for Display1in54 { } } -impl Drawing for Display1in54 { - fn draw(&mut self, item_pixels: T) - where - T: IntoIterator>, - { - self.draw_helper(WIDTH, HEIGHT, item_pixels); +impl DrawTarget for Display1in54 { + type Error = core::convert::Infallible; + + fn draw_pixel(&mut self, pixel: Pixel) -> Result<(), Self::Error> { + self.draw_helper(WIDTH, HEIGHT, pixel) + } + + fn size(&self) -> Size { + Size::new(WIDTH, HEIGHT) } } @@ -52,10 +55,9 @@ impl Display for Display1in54 { #[cfg(test)] mod tests { use super::*; - use crate::color::Color; + use crate::color::{Black, Color}; use crate::graphics::{Display, DisplayRotation}; - use embedded_graphics::coord::Coord; - use embedded_graphics::primitives::Line; + use embedded_graphics::{primitives::Line, style::PrimitiveStyle}; // test buffer length #[test] @@ -76,11 +78,9 @@ mod tests { #[test] fn graphics_rotation_0() { let mut display = Display1in54::default(); - display.draw( - Line::new(Coord::new(0, 0), Coord::new(7, 0)) - .stroke(Some(Color::Black)) - .into_iter(), - ); + let _ = Line::new(Point::new(0, 0), Point::new(7, 0)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); let buffer = display.buffer(); @@ -95,11 +95,9 @@ mod tests { fn graphics_rotation_90() { let mut display = Display1in54::default(); display.set_rotation(DisplayRotation::Rotate90); - display.draw( - Line::new(Coord::new(0, 192), Coord::new(0, 199)) - .stroke(Some(Color::Black)) - .into_iter(), - ); + let _ = Line::new(Point::new(0, 192), Point::new(0, 199)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); let buffer = display.buffer(); @@ -114,11 +112,9 @@ mod tests { fn graphics_rotation_180() { let mut display = Display1in54::default(); display.set_rotation(DisplayRotation::Rotate180); - display.draw( - Line::new(Coord::new(192, 199), Coord::new(199, 199)) - .stroke(Some(Color::Black)) - .into_iter(), - ); + let _ = Line::new(Point::new(192, 199), Point::new(199, 199)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); let buffer = display.buffer(); @@ -136,11 +132,9 @@ mod tests { fn graphics_rotation_270() { let mut display = Display1in54::default(); display.set_rotation(DisplayRotation::Rotate270); - display.draw( - Line::new(Coord::new(199, 0), Coord::new(199, 7)) - .stroke(Some(Color::Black)) - .into_iter(), - ); + let _ = Line::new(Point::new(199, 0), Point::new(199, 7)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); let buffer = display.buffer(); diff --git a/src/epd1in54/mod.rs b/src/epd1in54/mod.rs index e66f032..bc5cd5b 100644 --- a/src/epd1in54/mod.rs +++ b/src/epd1in54/mod.rs @@ -2,41 +2,51 @@ //! //! # Example for the 1.54 in E-Ink Display //! -//! ```rust,ignore -//! use epd_waveshare::{ -//! epd1in54::{EPD1in54, Display1in54}, -//! graphics::{Display, DisplayRotation}, -//! prelude::*, -//! }; -//! use embedded_graphics::Drawing; +//!```rust, no_run +//!# use embedded_hal_mock::*; +//!# fn main() -> Result<(), MockError> { +//!use embedded_graphics::{ +//! pixelcolor::BinaryColor::On as Black, prelude::*, primitives::Line, style::PrimitiveStyle, +//!}; +//!use epd_waveshare::{epd1in54::*, prelude::*}; +//!# +//!# let expectations = []; +//!# let mut spi = spi::Mock::new(&expectations); +//!# let expectations = []; +//!# let cs_pin = pin::Mock::new(&expectations); +//!# let busy_in = pin::Mock::new(&expectations); +//!# let dc = pin::Mock::new(&expectations); +//!# let rst = pin::Mock::new(&expectations); +//!# let mut delay = delay::MockNoop::new(); //! -//! // Setup EPD -//! let mut epd = EPD1in54::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay).unwrap(); +//!// Setup EPD +//!let mut epd = EPD1in54::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?; //! -//! // Use display graphics -//! let mut display = Display1in54::default(); +//!// Use display graphics from embedded-graphics +//!let mut display = Display1in54::default(); //! -//! // Write some hello world in the screenbuffer -//! display.draw( -//! Font6x8::render_str("Hello World!") -//! .stroke(Some(Color::Black)) -//! .fill(Some(Color::White)) -//! .translate(Coord::new(5, 50)) -//! .into_iter(), -//! ); +//!// Use embedded graphics for drawing a line +//!let _ = Line::new(Point::new(0, 120), Point::new(0, 295)) +//! .into_styled(PrimitiveStyle::with_stroke(Black, 1)) +//! .draw(&mut display); //! -//! // Display updated frame -//! epd.update_frame(&mut spi, &display.buffer()).unwrap(); -//! epd.display_frame(&mut spi).expect("display frame new graphics"); +//! // Display updated frame +//!epd.update_frame(&mut spi, &display.buffer())?; +//!epd.display_frame(&mut spi)?; //! -//! // Set the EPD to sleep -//! epd.sleep(&mut spi).expect("sleep"); -//! ``` +//!// Set the EPD to sleep +//!epd.sleep(&mut spi)?; +//!# Ok(()) +//!# } +//!``` +/// Width of the display pub const WIDTH: u32 = 200; +/// Height of the display pub const HEIGHT: u32 = 200; -//const DPI: u16 = 184; +/// Default Background Color pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White; +//const DPI: u16 = 184; const IS_BUSY_LOW: bool = false; use embedded_hal::{ @@ -179,21 +189,19 @@ where } fn sleep(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + self.wait_until_idle(); // 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::DEEP_SLEEP_MODE, &[0x00])?; - - self.wait_until_idle(); Ok(()) } fn update_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.use_full_frame(spi)?; self.interface .cmd_with_data(spi, Command::WRITE_RAM, buffer)?; - - self.wait_until_idle(); Ok(()) } @@ -207,17 +215,17 @@ where width: u32, height: u32, ) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.set_ram_area(spi, x, y, x + width, y + height)?; self.set_ram_counter(spi, x, y)?; self.interface .cmd_with_data(spi, Command::WRITE_RAM, buffer)?; - - self.wait_until_idle(); Ok(()) } fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + self.wait_until_idle(); // 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 @@ -227,12 +235,17 @@ where // MASTER Activation should not be interupted to avoid currption of panel images // therefore a terminate command is send self.interface.cmd(spi, Command::NOP)?; + Ok(()) + } - self.wait_until_idle(); + fn update_and_display_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> { + self.update_frame(spi, buffer)?; + self.display_frame(spi)?; Ok(()) } fn clear_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.use_full_frame(spi)?; // clear the ram with the background color @@ -241,8 +254,6 @@ where self.interface.cmd(spi, Command::WRITE_RAM)?; self.interface .data_x_times(spi, color, WIDTH / 8 * HEIGHT)?; - - self.wait_until_idle(); Ok(()) } @@ -301,6 +312,7 @@ where end_x: u32, end_y: u32, ) -> Result<(), SPI::Error> { + self.wait_until_idle(); assert!(start_x < end_x); assert!(start_y < end_y); @@ -323,8 +335,6 @@ where (end_y >> 8) as u8, ], )?; - - self.wait_until_idle(); Ok(()) } @@ -334,6 +344,7 @@ where x: u32, y: u32, ) -> Result<(), SPI::Error> { + self.wait_until_idle(); // 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 @@ -345,18 +356,15 @@ where Command::SET_RAM_Y_ADDRESS_COUNTER, &[y as u8, (y >> 8) as u8], )?; - - self.wait_until_idle(); Ok(()) } fn set_lut_helper(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> { + self.wait_until_idle(); assert!(buffer.len() == 30); self.interface .cmd_with_data(spi, Command::WRITE_LUT_REGISTER, buffer)?; - - self.wait_until_idle(); Ok(()) } } diff --git a/src/epd1in54b/graphics.rs b/src/epd1in54b/graphics.rs index 536ba6a..617d697 100644 --- a/src/epd1in54b/graphics.rs +++ b/src/epd1in54b/graphics.rs @@ -1,8 +1,11 @@ use crate::epd1in54b::{DEFAULT_BACKGROUND_COLOR, HEIGHT, WIDTH}; use crate::graphics::{Display, DisplayRotation}; -use crate::prelude::*; +use embedded_graphics::pixelcolor::BinaryColor; use embedded_graphics::prelude::*; +/// Full size buffer for use with the 1in54 EPD +/// +/// Can also be manually constructed and be used together with VarDisplay pub struct Display1in54b { buffer: [u8; WIDTH as usize * HEIGHT as usize / 8], rotation: DisplayRotation, @@ -18,12 +21,15 @@ impl Default for Display1in54b { } } -impl Drawing for Display1in54b { - fn draw(&mut self, item_pixels: T) - where - T: IntoIterator>, - { - self.draw_helper(WIDTH, HEIGHT, item_pixels); +impl DrawTarget for Display1in54b { + type Error = core::convert::Infallible; + + fn draw_pixel(&mut self, pixel: Pixel) -> Result<(), Self::Error> { + self.draw_helper(WIDTH, HEIGHT, pixel) + } + + fn size(&self) -> Size { + Size::new(WIDTH, HEIGHT) } } diff --git a/src/epd1in54b/mod.rs b/src/epd1in54b/mod.rs index 75bd987..9244093 100644 --- a/src/epd1in54b/mod.rs +++ b/src/epd1in54b/mod.rs @@ -14,8 +14,11 @@ use crate::traits::{ mod constants; use crate::epd1in54b::constants::*; +/// Width of epd1in54 in pixels pub const WIDTH: u32 = 200; +/// Height of epd1in54 in pixels pub const HEIGHT: u32 = 200; +/// Default Background Color (white) pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White; const IS_BUSY_LOW: bool = true; @@ -100,6 +103,7 @@ where black: &[u8], red: &[u8], ) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.send_resolution(spi)?; self.interface @@ -113,8 +117,6 @@ where self.interface .cmd(spi, Command::DATA_START_TRANSMISSION_2)?; self.interface.data(spi, red)?; - - self.wait_until_idle(); Ok(()) } } @@ -147,6 +149,7 @@ where } fn sleep(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.interface .cmd_with_data(spi, Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x17])?; //border floating @@ -190,6 +193,7 @@ where } fn update_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.send_resolution(spi)?; self.interface @@ -212,8 +216,6 @@ where self.interface.data_x_times(spi, color, nbits)?; //NOTE: Example code has a delay here - - self.wait_until_idle(); Ok(()) } @@ -227,17 +229,23 @@ where width: u32, height: u32, ) -> Result<(), SPI::Error> { - Ok(()) + unimplemented!() } fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.command(spi, Command::DISPLAY_REFRESH)?; + Ok(()) + } - self.wait_until_idle(); + fn update_and_display_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> { + self.update_frame(spi, buffer)?; + self.display_frame(spi)?; Ok(()) } fn clear_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.send_resolution(spi)?; let color = DEFAULT_BACKGROUND_COLOR.get_byte_value(); @@ -255,8 +263,6 @@ where .cmd(spi, Command::DATA_START_TRANSMISSION_2)?; self.interface .data_x_times(spi, color, WIDTH * HEIGHT / 8)?; - - self.wait_until_idle(); Ok(()) } diff --git a/src/epd2in9/graphics.rs b/src/epd2in9/graphics.rs index d9b723c..fb1154e 100644 --- a/src/epd2in9/graphics.rs +++ b/src/epd2in9/graphics.rs @@ -1,6 +1,6 @@ use crate::epd2in9::{DEFAULT_BACKGROUND_COLOR, HEIGHT, WIDTH}; use crate::graphics::{Display, DisplayRotation}; -use crate::prelude::*; +use embedded_graphics::pixelcolor::BinaryColor; use embedded_graphics::prelude::*; /// Display with Fullsize buffer for use with the 2in9 EPD @@ -22,12 +22,15 @@ impl Default for Display2in9 { } } -impl Drawing for Display2in9 { - fn draw(&mut self, item_pixels: T) - where - T: IntoIterator>, - { - self.draw_helper(WIDTH, HEIGHT, item_pixels); +impl DrawTarget for Display2in9 { + type Error = core::convert::Infallible; + + fn draw_pixel(&mut self, pixel: Pixel) -> Result<(), Self::Error> { + self.draw_helper(WIDTH, HEIGHT, pixel) + } + + fn size(&self) -> Size { + Size::new(WIDTH, HEIGHT) } } diff --git a/src/epd2in9/mod.rs b/src/epd2in9/mod.rs index 2e2665f..6ccd551 100644 --- a/src/epd2in9/mod.rs +++ b/src/epd2in9/mod.rs @@ -1,42 +1,51 @@ //! A simple Driver for the Waveshare 2.9" E-Ink Display via SPI //! -//! Untested! //! //! # Example for the 2.9 in E-Ink Display //! -//! ```rust,ignore -//! use epd_waveshare::{ -//! epd2in9::{EPD2in9, Display2in9}, -//! graphics::{Display, DisplayRotation}, -//! prelude::*, -//! }; -//! use embedded_graphics::Drawing; +//!```rust, no_run +//!# use embedded_hal_mock::*; +//!# fn main() -> Result<(), MockError> { +//!use embedded_graphics::{ +//! pixelcolor::BinaryColor::On as Black, prelude::*, primitives::Line, style::PrimitiveStyle, +//!}; +//!use epd_waveshare::{epd2in9::*, prelude::*}; +//!# +//!# let expectations = []; +//!# let mut spi = spi::Mock::new(&expectations); +//!# let expectations = []; +//!# let cs_pin = pin::Mock::new(&expectations); +//!# let busy_in = pin::Mock::new(&expectations); +//!# let dc = pin::Mock::new(&expectations); +//!# let rst = pin::Mock::new(&expectations); +//!# let mut delay = delay::MockNoop::new(); //! -//! // Setup EPD -//! let mut epd = EPD2in9::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay).unwrap(); +//!// Setup EPD +//!let mut epd = EPD2in9::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?; //! -//! // Use display graphics -//! let mut display = Display2in9::default(); +//!// Use display graphics from embedded-graphics +//!let mut display = Display2in9::default(); //! -//! // Write some hello world in the screenbuffer -//! display.draw( -//! Font6x8::render_str("Hello World!") -//! .stroke(Some(Color::Black)) -//! .fill(Some(Color::White)) -//! .translate(Coord::new(5, 50)) -//! .into_iter(), -//! ); +//!// Use embedded graphics for drawing a line +//!let _ = Line::new(Point::new(0, 120), Point::new(0, 295)) +//! .into_styled(PrimitiveStyle::with_stroke(Black, 1)) +//! .draw(&mut display); //! -//! // Display updated frame -//! epd.update_frame(&mut spi, &display.buffer()).unwrap(); -//! epd.display_frame(&mut spi).expect("display frame new graphics"); +//! // Display updated frame +//!epd.update_frame(&mut spi, &display.buffer())?; +//!epd.display_frame(&mut spi)?; //! -//! // Set the EPD to sleep -//! epd.sleep(&mut spi).expect("sleep"); -//! ``` +//!// Set the EPD to sleep +//!epd.sleep(&mut spi)?; +//!# Ok(()) +//!# } +//!``` +/// Width of epd2in9 in pixels pub const WIDTH: u32 = 128; +/// Height of epd2in9 in pixels pub const HEIGHT: u32 = 296; +/// Default Background Color (white) pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White; const IS_BUSY_LOW: bool = false; @@ -87,6 +96,8 @@ where ) -> Result<(), SPI::Error> { self.interface.reset(delay); + self.wait_until_idle(); + // 3 Databytes: // A[7:0] // 0.. A[8] @@ -166,12 +177,11 @@ where } fn sleep(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + self.wait_until_idle(); // 0x00 for Normal mode (Power on Reset), 0x01 for Deep Sleep Mode //TODO: is 0x00 needed here? (see also epd1in54) self.interface .cmd_with_data(spi, Command::DEEP_SLEEP_MODE, &[0x00])?; - - self.wait_until_idle(); Ok(()) } @@ -180,19 +190,17 @@ where spi: &mut SPI, delay: &mut DELAY, ) -> Result<(), SPI::Error> { - self.init(spi, delay)?; - self.wait_until_idle(); + self.init(spi, delay)?; Ok(()) } fn update_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.use_full_frame(spi)?; self.interface .cmd_with_data(spi, Command::WRITE_RAM, buffer)?; - - self.wait_until_idle(); Ok(()) } @@ -206,17 +214,17 @@ where width: u32, height: u32, ) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.set_ram_area(spi, x, y, x + width, y + height)?; self.set_ram_counter(spi, x, y)?; self.interface .cmd_with_data(spi, Command::WRITE_RAM, buffer)?; - - self.wait_until_idle(); Ok(()) } fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + self.wait_until_idle(); // 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 @@ -226,12 +234,17 @@ where // MASTER Activation should not be interupted to avoid currption of panel images // therefore a terminate command is send self.interface.cmd(spi, Command::NOP)?; + Ok(()) + } - self.wait_until_idle(); + fn update_and_display_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> { + self.update_frame(spi, buffer)?; + self.display_frame(spi)?; Ok(()) } fn clear_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.use_full_frame(spi)?; // clear the ram with the background color @@ -240,8 +253,6 @@ where self.interface.cmd(spi, Command::WRITE_RAM)?; self.interface .data_x_times(spi, color, WIDTH / 8 * HEIGHT)?; - - self.wait_until_idle(); Ok(()) } @@ -325,6 +336,7 @@ where } fn set_ram_counter(&mut self, spi: &mut SPI, x: u32, y: u32) -> Result<(), SPI::Error> { + self.wait_until_idle(); // 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 @@ -336,17 +348,15 @@ where Command::SET_RAM_Y_ADDRESS_COUNTER, &[y as u8, (y >> 8) as u8], )?; - - self.wait_until_idle(); Ok(()) } /// Set your own LUT, this function is also used internally for set_lut fn set_lut_helper(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> { + self.wait_until_idle(); assert!(buffer.len() == 30); self.interface .cmd_with_data(spi, Command::WRITE_LUT_REGISTER, buffer)?; - self.wait_until_idle(); Ok(()) } } diff --git a/src/epd4in2/graphics.rs b/src/epd4in2/graphics.rs index 41332ab..101501e 100644 --- a/src/epd4in2/graphics.rs +++ b/src/epd4in2/graphics.rs @@ -1,6 +1,6 @@ use crate::epd4in2::{DEFAULT_BACKGROUND_COLOR, HEIGHT, WIDTH}; use crate::graphics::{Display, DisplayRotation}; -use crate::prelude::*; +use embedded_graphics::pixelcolor::BinaryColor; use embedded_graphics::prelude::*; /// Full size buffer for use with the 4in2 EPD @@ -22,12 +22,15 @@ impl Default for Display4in2 { } } -impl Drawing for Display4in2 { - fn draw(&mut self, item_pixels: T) - where - T: IntoIterator>, - { - self.draw_helper(WIDTH, HEIGHT, item_pixels); +impl DrawTarget for Display4in2 { + type Error = core::convert::Infallible; + + fn draw_pixel(&mut self, pixel: Pixel) -> Result<(), Self::Error> { + self.draw_helper(WIDTH, HEIGHT, pixel) + } + + fn size(&self) -> Size { + Size::new(WIDTH, HEIGHT) } } @@ -52,11 +55,11 @@ impl Display for Display4in2 { #[cfg(test)] mod tests { use super::*; + use crate::color::Black; use crate::color::Color; use crate::epd4in2; use crate::graphics::{Display, DisplayRotation}; - use embedded_graphics::coord::Coord; - use embedded_graphics::primitives::Line; + use embedded_graphics::{primitives::Line, style::PrimitiveStyle}; // test buffer length #[test] @@ -77,11 +80,9 @@ mod tests { #[test] fn graphics_rotation_0() { let mut display = Display4in2::default(); - display.draw( - Line::new(Coord::new(0, 0), Coord::new(7, 0)) - .stroke(Some(Color::Black)) - .into_iter(), - ); + let _ = Line::new(Point::new(0, 0), Point::new(7, 0)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); let buffer = display.buffer(); @@ -96,11 +97,9 @@ mod tests { fn graphics_rotation_90() { let mut display = Display4in2::default(); display.set_rotation(DisplayRotation::Rotate90); - display.draw( - Line::new(Coord::new(0, 392), Coord::new(0, 399)) - .stroke(Some(Color::Black)) - .into_iter(), - ); + let _ = Line::new(Point::new(0, 392), Point::new(0, 399)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); let buffer = display.buffer(); @@ -115,11 +114,10 @@ mod tests { fn graphics_rotation_180() { let mut display = Display4in2::default(); display.set_rotation(DisplayRotation::Rotate180); - display.draw( - Line::new(Coord::new(392, 299), Coord::new(399, 299)) - .stroke(Some(Color::Black)) - .into_iter(), - ); + + let _ = Line::new(Point::new(392, 299), Point::new(399, 299)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); let buffer = display.buffer(); @@ -137,11 +135,9 @@ mod tests { fn graphics_rotation_270() { let mut display = Display4in2::default(); display.set_rotation(DisplayRotation::Rotate270); - display.draw( - Line::new(Coord::new(299, 0), Coord::new(299, 7)) - .stroke(Some(Color::Black)) - .into_iter(), - ); + let _ = Line::new(Point::new(299, 0), Point::new(299, 7)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); let buffer = display.buffer(); diff --git a/src/epd4in2/mod.rs b/src/epd4in2/mod.rs index f983083..582b409 100644 --- a/src/epd4in2/mod.rs +++ b/src/epd4in2/mod.rs @@ -1,46 +1,49 @@ //! A simple Driver for the Waveshare 4.2" E-Ink Display via SPI //! -//! The other Waveshare E-Ink Displays should be added later on //! //! Build with the help of documentation/code from [Waveshare](https://www.waveshare.com/wiki/4.2inch_e-Paper_Module), //! [Ben Krasnows partial Refresh tips](https://benkrasnow.blogspot.de/2017/10/fast-partial-refresh-on-42-e-paper.html) and //! the driver documents in the `pdfs`-folder as orientation. //! -//! This driver was built using [`embedded-hal`] traits. -//! -//! [`embedded-hal`]: https://docs.rs/embedded-hal/~0.1 -//! -//! # Requirements -//! -//! ### SPI -//! -//! - MISO is not connected/available -//! - SPI_MODE_0 is used (CPHL = 0, CPOL = 0) -//! - 8 bits per word, MSB first -//! - Max. Speed tested was 8Mhz but more should be possible -//! -//! ### Other.... -//! -//! - Buffersize: Wherever a buffer is used it always needs to be of the size: `width / 8 * length`, -//! where width and length being either the full e-ink size or the partial update window size -//! //! # Examples //! -//! ```ignore -//! let mut epd4in2 = EPD4in2::new(spi, cs, busy, dc, rst, delay).unwrap(); +//!```rust, no_run +//!# use embedded_hal_mock::*; +//!# fn main() -> Result<(), MockError> { +//!use embedded_graphics::{ +//! pixelcolor::BinaryColor::On as Black, prelude::*, primitives::Line, style::PrimitiveStyle, +//!}; +//!use epd_waveshare::{epd4in2::*, prelude::*}; +//!# +//!# let expectations = []; +//!# let mut spi = spi::Mock::new(&expectations); +//!# let expectations = []; +//!# let cs_pin = pin::Mock::new(&expectations); +//!# let busy_in = pin::Mock::new(&expectations); +//!# let dc = pin::Mock::new(&expectations); +//!# let rst = pin::Mock::new(&expectations); +//!# let mut delay = delay::MockNoop::new(); //! -//! let mut buffer = [0u8, epd4in2.get_width() / 8 * epd4in2.get_height()]; +//!// Setup EPD +//!let mut epd = EPD4in2::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?; //! -//! // draw something into the buffer +//!// Use display graphics from embedded-graphics +//!let mut display = Display4in2::default(); //! -//! epd4in2.display_and_transfer_buffer(buffer, None); +//!// Use embedded graphics for drawing a line +//!let _ = Line::new(Point::new(0, 120), Point::new(0, 295)) +//! .into_styled(PrimitiveStyle::with_stroke(Black, 1)) +//! .draw(&mut display); //! -//! // wait and look at the image +//! // Display updated frame +//!epd.update_frame(&mut spi, &display.buffer())?; +//!epd.display_frame(&mut spi)?; //! -//! epd4in2.clear_frame(None); -//! -//! epd4in2.sleep(); -//! ``` +//!// Set the EPD to sleep +//!epd.sleep(&mut spi)?; +//!# Ok(()) +//!# } +//!``` //! //! //! @@ -58,8 +61,11 @@ use crate::traits::{InternalWiAdditions, RefreshLUT, WaveshareDisplay}; mod constants; use crate::epd4in2::constants::*; +/// Width of the display pub const WIDTH: u32 = 400; +/// Height of the display pub const HEIGHT: u32 = 300; +/// Default Background Color pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White; const IS_BUSY_LOW: bool = true; @@ -142,21 +148,6 @@ where DC: OutputPin, RST: OutputPin, { - /// Creates a new driver from a SPI peripheral, CS Pin, Busy InputPin, DC - /// - /// This already initialises the device. That means [init()](init()) isn't needed directly afterwards - /// - /// # Example - /// - /// ```ignore - /// //buffer = some image data; - /// - /// let mut epd4in2 = EPD4in2::new(spi, cs, busy, dc, rst, delay); - /// - /// epd4in2.display_and_transfer_frame(buffer, None); - /// - /// epd4in2.sleep(); - /// ``` fn new>( spi: &mut SPI, cs: CS, @@ -188,6 +179,7 @@ where } fn sleep(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.interface .cmd_with_data(spi, Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x17])?; //border floating self.command(spi, Command::VCM_DC_SETTING)?; // VCOM to 0V @@ -202,12 +194,11 @@ where self.wait_until_idle(); self.interface .cmd_with_data(spi, Command::DEEP_SLEEP, &[0xA5])?; - - self.wait_until_idle(); Ok(()) } fn update_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> { + self.wait_until_idle(); let color_value = self.color.get_byte_value(); self.send_resolution(spi)?; @@ -226,8 +217,6 @@ where self.interface .cmd_with_data(spi, Command::DATA_START_TRANSMISSION_2, buffer)?; - - self.wait_until_idle(); Ok(()) } @@ -240,6 +229,7 @@ where width: u32, height: u32, ) -> Result<(), SPI::Error> { + self.wait_until_idle(); if buffer.len() as u32 != width / 8 * height { //TODO: panic!! or sth like that //return Err("Wrong buffersize"); @@ -273,19 +263,23 @@ where self.send_data(spi, buffer)?; self.command(spi, Command::PARTIAL_OUT)?; - - self.wait_until_idle(); Ok(()) } fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.command(spi, Command::DISPLAY_REFRESH)?; + Ok(()) + } - self.wait_until_idle(); + fn update_and_display_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> { + self.update_frame(spi, buffer)?; + self.command(spi, Command::DISPLAY_REFRESH)?; Ok(()) } fn clear_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.send_resolution(spi)?; let color_value = self.color.get_byte_value(); @@ -299,8 +293,6 @@ where .cmd(spi, Command::DATA_START_TRANSMISSION_2)?; self.interface .data_x_times(spi, color_value, WIDTH / 8 * HEIGHT)?; - - self.wait_until_idle(); Ok(()) } @@ -397,6 +389,7 @@ where lut_wb: &[u8], lut_bb: &[u8], ) -> Result<(), SPI::Error> { + self.wait_until_idle(); // LUT VCOM self.cmd_with_data(spi, Command::LUT_FOR_VCOM, lut_vcom)?; @@ -411,8 +404,6 @@ where // LUT BLACK to BLACK self.cmd_with_data(spi, Command::LUT_BLACK_TO_BLACK, lut_bb)?; - - self.wait_until_idle(); Ok(()) } } diff --git a/src/epd7in5/graphics.rs b/src/epd7in5/graphics.rs index c7c20dd..62d1563 100644 --- a/src/epd7in5/graphics.rs +++ b/src/epd7in5/graphics.rs @@ -1,6 +1,6 @@ use crate::epd7in5::{DEFAULT_BACKGROUND_COLOR, HEIGHT, WIDTH}; use crate::graphics::{Display, DisplayRotation}; -use crate::prelude::*; +use embedded_graphics::pixelcolor::BinaryColor; use embedded_graphics::prelude::*; /// Full size buffer for use with the 7in5 EPD @@ -22,12 +22,15 @@ impl Default for Display7in5 { } } -impl Drawing for Display7in5 { - fn draw(&mut self, item_pixels: T) - where - T: IntoIterator>, - { - self.draw_helper(WIDTH, HEIGHT, item_pixels); +impl DrawTarget for Display7in5 { + type Error = core::convert::Infallible; + + fn draw_pixel(&mut self, pixel: Pixel) -> Result<(), Self::Error> { + self.draw_helper(WIDTH, HEIGHT, pixel) + } + + fn size(&self) -> Size { + Size::new(WIDTH, HEIGHT) } } @@ -52,11 +55,11 @@ impl Display for Display7in5 { #[cfg(test)] mod tests { use super::*; + use crate::color::Black; use crate::color::Color; use crate::epd7in5; use crate::graphics::{Display, DisplayRotation}; - use embedded_graphics::coord::Coord; - use embedded_graphics::primitives::Line; + use embedded_graphics::{primitives::Line, style::PrimitiveStyle}; // test buffer length #[test] @@ -77,11 +80,9 @@ mod tests { #[test] fn graphics_rotation_0() { let mut display = Display7in5::default(); - display.draw( - Line::new(Coord::new(0, 0), Coord::new(7, 0)) - .stroke(Some(Color::Black)) - .into_iter(), - ); + let _ = Line::new(Point::new(0, 0), Point::new(7, 0)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); let buffer = display.buffer(); @@ -96,11 +97,9 @@ mod tests { fn graphics_rotation_90() { let mut display = Display7in5::default(); display.set_rotation(DisplayRotation::Rotate90); - display.draw( - Line::new(Coord::new(0, 632), Coord::new(0, 639)) - .stroke(Some(Color::Black)) - .into_iter(), - ); + let _ = Line::new(Point::new(0, 632), Point::new(0, 639)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); let buffer = display.buffer(); @@ -115,11 +114,9 @@ mod tests { fn graphics_rotation_180() { let mut display = Display7in5::default(); display.set_rotation(DisplayRotation::Rotate180); - display.draw( - Line::new(Coord::new(632, 383), Coord::new(639, 383)) - .stroke(Some(Color::Black)) - .into_iter(), - ); + let _ = Line::new(Point::new(632, 383), Point::new(639, 383)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); let buffer = display.buffer(); @@ -134,11 +131,9 @@ mod tests { fn graphics_rotation_270() { let mut display = Display7in5::default(); display.set_rotation(DisplayRotation::Rotate270); - display.draw( - Line::new(Coord::new(383, 0), Coord::new(383, 7)) - .stroke(Some(Color::Black)) - .into_iter(), - ); + let _ = Line::new(Point::new(383, 0), Point::new(383, 7)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); let buffer = display.buffer(); diff --git a/src/epd7in5/mod.rs b/src/epd7in5/mod.rs index 42711d8..2c6e76b 100644 --- a/src/epd7in5/mod.rs +++ b/src/epd7in5/mod.rs @@ -23,8 +23,11 @@ mod graphics; #[cfg(feature = "graphics")] pub use self::graphics::Display7in5; +/// Width of the display pub const WIDTH: u32 = 640; +/// Height of the display pub const HEIGHT: u32 = 384; +/// Default Background Color pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White; const IS_BUSY_LOW: bool = true; @@ -105,24 +108,6 @@ where DC: OutputPin, RST: OutputPin, { - /// Creates a new driver from a SPI peripheral, CS Pin, Busy InputPin, DC - /// - /// This already initialises the device. That means [init()] isn't needed - /// directly afterwards. - /// - /// [init()]: InternalWiAdditions::init - /// - /// # Example - /// - /// ```ignore - /// //buffer = some image data; - /// - /// let mut epd7in5 = EPD7in5::new(spi, cs, busy, dc, rst, delay); - /// - /// epd7in5.display_and_transfer_frame(buffer, None); - /// - /// epd7in5.sleep(); - /// ``` fn new>( spi: &mut SPI, cs: CS, @@ -150,15 +135,15 @@ where } fn sleep(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.command(spi, Command::POWER_OFF)?; self.wait_until_idle(); self.cmd_with_data(spi, Command::DEEP_SLEEP, &[0xA5])?; - - self.wait_until_idle(); Ok(()) } fn update_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.command(spi, Command::DATA_START_TRANSMISSION_1)?; for byte in buffer { let mut temp = *byte; @@ -171,8 +156,6 @@ where self.send_data(spi, &[data])?; } } - - self.wait_until_idle(); Ok(()) } @@ -189,21 +172,25 @@ where } fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.command(spi, Command::DISPLAY_REFRESH)?; + Ok(()) + } - self.wait_until_idle(); + fn update_and_display_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> { + self.update_frame(spi, buffer)?; + self.command(spi, Command::DISPLAY_REFRESH)?; Ok(()) } fn clear_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.send_resolution(spi)?; // The Waveshare controllers all implement clear using 0x33 self.command(spi, Command::DATA_START_TRANSMISSION_1)?; self.interface .data_x_times(spi, 0x33, WIDTH / 8 * HEIGHT * 4)?; - - self.wait_until_idle(); Ok(()) } diff --git a/src/epd7in5_v2/graphics.rs b/src/epd7in5_v2/graphics.rs index aa6f464..8c3211f 100644 --- a/src/epd7in5_v2/graphics.rs +++ b/src/epd7in5_v2/graphics.rs @@ -1,6 +1,6 @@ use crate::epd7in5_v2::{DEFAULT_BACKGROUND_COLOR, HEIGHT, WIDTH}; use crate::graphics::{Display, DisplayRotation}; -use crate::prelude::*; +use embedded_graphics::pixelcolor::BinaryColor; use embedded_graphics::prelude::*; /// Full size buffer for use with the 7in5 EPD @@ -22,12 +22,15 @@ impl Default for Display7in5 { } } -impl Drawing for Display7in5 { - fn draw(&mut self, item_pixels: T) - where - T: IntoIterator>, - { - self.draw_helper(WIDTH, HEIGHT, item_pixels); +impl DrawTarget for Display7in5 { + type Error = core::convert::Infallible; + + fn draw_pixel(&mut self, pixel: Pixel) -> Result<(), Self::Error> { + self.draw_helper(WIDTH, HEIGHT, pixel) + } + + fn size(&self) -> Size { + Size::new(WIDTH, HEIGHT) } } @@ -52,11 +55,10 @@ impl Display for Display7in5 { #[cfg(test)] mod tests { use super::*; - use crate::color::Color; + use crate::color::{Black, Color}; use crate::epd7in5_v2; use crate::graphics::{Display, DisplayRotation}; - use embedded_graphics::coord::Coord; - use embedded_graphics::primitives::Line; + use embedded_graphics::{primitives::Line, style::PrimitiveStyle}; // test buffer length #[test] @@ -77,11 +79,10 @@ mod tests { #[test] fn graphics_rotation_0() { let mut display = Display7in5::default(); - display.draw( - Line::new(Coord::new(0, 0), Coord::new(7, 0)) - .stroke(Some(Color::Black)) - .into_iter(), - ); + + let _ = Line::new(Point::new(0, 0), Point::new(7, 0)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); let buffer = display.buffer(); @@ -96,11 +97,10 @@ mod tests { fn graphics_rotation_90() { let mut display = Display7in5::default(); display.set_rotation(DisplayRotation::Rotate90); - display.draw( - Line::new(Coord::new(0, 792), Coord::new(0, 799)) - .stroke(Some(Color::Black)) - .into_iter(), - ); + + let _ = Line::new(Point::new(0, 792), Point::new(0, 799)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); let buffer = display.buffer(); @@ -115,11 +115,10 @@ mod tests { fn graphics_rotation_180() { let mut display = Display7in5::default(); display.set_rotation(DisplayRotation::Rotate180); - display.draw( - Line::new(Coord::new(792, 479), Coord::new(799, 479)) - .stroke(Some(Color::Black)) - .into_iter(), - ); + + let _ = Line::new(Point::new(792, 479), Point::new(799, 479)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); let buffer = display.buffer(); @@ -134,11 +133,10 @@ mod tests { fn graphics_rotation_270() { let mut display = Display7in5::default(); display.set_rotation(DisplayRotation::Rotate270); - display.draw( - Line::new(Coord::new(479, 0), Coord::new(479, 7)) - .stroke(Some(Color::Black)) - .into_iter(), - ); + + let _ = Line::new(Point::new(479, 0), Point::new(479, 7)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); let buffer = display.buffer(); diff --git a/src/epd7in5_v2/mod.rs b/src/epd7in5_v2/mod.rs index e1d95ca..89d5614 100644 --- a/src/epd7in5_v2/mod.rs +++ b/src/epd7in5_v2/mod.rs @@ -17,7 +17,7 @@ use embedded_hal::{ use crate::color::Color; use crate::interface::DisplayInterface; -use crate::traits::{InternalWiAdditions, RefreshLUT, WaveshareDisplay, WaveshareDisplayExt}; +use crate::traits::{InternalWiAdditions, RefreshLUT, WaveshareDisplay}; pub(crate) mod command; use self::command::Command; @@ -27,8 +27,11 @@ mod graphics; #[cfg(feature = "graphics")] pub use self::graphics::Display7in5; +/// Width of the display pub const WIDTH: u32 = 800; +/// Height of the display pub const HEIGHT: u32 = 480; +/// Default Background Color pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White; const IS_BUSY_LOW: bool = true; @@ -87,24 +90,6 @@ where DC: OutputPin, RST: OutputPin, { - /// Creates a new driver from a SPI peripheral, CS Pin, Busy InputPin, DC - /// - /// This already initialises the device. That means [init()] isn't needed - /// directly afterwards. - /// - /// [init()]: InternalWiAdditions::init - /// - /// # Example - /// - /// ```rust,ignore - /// //buffer = some image data; - /// - /// let mut epd7in5 = EPD7in5::new(spi, cs, busy, dc, rst, delay); - /// - /// epd7in5.update_and_display_frame(&mut spi, &buffer); - /// - /// epd7in5.sleep(); - /// ``` fn new>( spi: &mut SPI, cs: CS, @@ -132,6 +117,7 @@ where } fn sleep(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.command(spi, Command::POWER_OFF)?; self.wait_until_idle(); self.cmd_with_data(spi, Command::DEEP_SLEEP, &[0xA5])?; @@ -139,9 +125,8 @@ where } fn update_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> { - self.command(spi, Command::DATA_START_TRANSMISSION_2)?; - self.send_data(spi, buffer)?; self.wait_until_idle(); + self.cmd_with_data(spi, Command::DATA_START_TRANSMISSION_2, buffer)?; Ok(()) } @@ -158,12 +143,19 @@ where } fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { - self.command(spi, Command::DISPLAY_REFRESH)?; self.wait_until_idle(); + self.command(spi, Command::DISPLAY_REFRESH)?; + Ok(()) + } + + fn update_and_display_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> { + self.update_frame(spi, buffer)?; + self.command(spi, Command::DISPLAY_REFRESH)?; Ok(()) } fn clear_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + self.wait_until_idle(); self.send_resolution(spi)?; self.command(spi, Command::DATA_START_TRANSMISSION_1)?; @@ -173,7 +165,6 @@ where self.interface.data_x_times(spi, 0x00, WIDTH * HEIGHT / 8)?; self.command(spi, Command::DISPLAY_REFRESH)?; - self.wait_until_idle(); Ok(()) } @@ -206,26 +197,6 @@ where } } -impl WaveshareDisplayExt - for EPD7in5 -where - SPI: Write, - CS: OutputPin, - BUSY: InputPin, - DC: OutputPin, - RST: OutputPin, -{ - fn update_and_display_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> { - self.command(spi, Command::DATA_START_TRANSMISSION_2)?; - for b in buffer { - self.send_data(spi, &[255 - b])?; - } - self.command(spi, Command::DISPLAY_REFRESH)?; - self.wait_until_idle(); - Ok(()) - } -} - impl EPD7in5 where SPI: Write, diff --git a/src/graphics.rs b/src/graphics.rs index d01ecb7..e8ff524 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -1,7 +1,7 @@ //! Graphics Support for EPDs use crate::color::Color; -use embedded_graphics::prelude::*; +use embedded_graphics::{pixelcolor::BinaryColor, prelude::*}; /// Displayrotation #[derive(Clone, Copy)] @@ -22,7 +22,13 @@ impl Default for DisplayRotation { } } -pub trait Display: Drawing { +/// Necessary traits for all displays to implement for drawing +/// +/// Adds support for: +/// - Drawing (With the help of DrawTarget/Embedded Graphics) +/// - Rotations +/// - Clearing +pub trait Display: DrawTarget { /// 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() { @@ -45,31 +51,36 @@ pub trait Display: Drawing { /// 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: IntoIterator>, - { + fn draw_helper( + &mut self, + width: u32, + height: u32, + pixel: Pixel, + ) -> Result<(), Self::Error> { let rotation = self.rotation(); let buffer = self.get_mut_buffer(); - for Pixel(UnsignedCoord(x, y), color) in item_pixels { - 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) = find_position(x, y, width, height, rotation); - let index = index as usize; + let Pixel(point, color) = pixel; + if outside_display(point, width, height, rotation) { + return Ok(()); + } + + // Give us index inside the buffer and the bit-position in that u8 which needs to be changed + let (index, bit) = find_position(point.x as u32, point.y as u32, width, height, rotation); + let index = index as usize; - // "Draw" the Pixel on that bit - match color { - Color::Black => { - buffer[index] &= !bit; - } - Color::White => { - buffer[index] |= bit; - } + // "Draw" the Pixel on that bit + match color { + // Black + BinaryColor::On => { + buffer[index] &= !bit; + } + // White + BinaryColor::Off => { + buffer[index] |= bit; } } + Ok(()) } } @@ -83,8 +94,10 @@ pub trait Display: Drawing { /// # use epd_waveshare::epd2in9::DEFAULT_BACKGROUND_COLOR; /// # use epd_waveshare::prelude::*; /// # use epd_waveshare::graphics::VarDisplay; +/// # use epd_waveshare::color::Black; /// # use embedded_graphics::prelude::*; /// # use embedded_graphics::primitives::{Circle, Line}; +/// # use embedded_graphics::style::PrimitiveStyle; /// let width = 128; /// let height = 296; /// @@ -93,11 +106,9 @@ pub trait Display: Drawing { /// /// display.set_rotation(DisplayRotation::Rotate90); /// -/// display.draw( -/// Line::new(Coord::new(0, 120), Coord::new(0, 295)) -/// .stroke(Some(Color::Black)) -/// .into_iter(), -/// ); +/// let _ = Line::new(Point::new(0, 120), Point::new(0, 295)) +/// .into_styled(PrimitiveStyle::with_stroke(Black, 1)) +/// .draw(&mut display); /// ``` pub struct VarDisplay<'a> { width: u32, @@ -107,6 +118,9 @@ pub struct VarDisplay<'a> { } impl<'a> VarDisplay<'a> { + /// Create a new variable sized display. + /// + /// Buffersize must be at least width / 8 * height bytes. pub fn new(width: u32, height: u32, buffer: &'a mut [u8]) -> VarDisplay<'a> { let len = buffer.len() as u32; assert!(width / 8 * height >= len); @@ -119,12 +133,15 @@ impl<'a> VarDisplay<'a> { } } -impl<'a> Drawing for VarDisplay<'a> { - fn draw(&mut self, item_pixels: T) - where - T: IntoIterator>, - { - self.draw_helper(self.width, self.height, item_pixels); +impl<'a> DrawTarget for VarDisplay<'a> { + type Error = core::convert::Infallible; + + fn draw_pixel(&mut self, pixel: Pixel) -> Result<(), Self::Error> { + self.draw_helper(self.width, self.height, pixel) + } + + fn size(&self) -> Size { + Size::new(self.width, self.height) } } @@ -147,7 +164,11 @@ impl<'a> Display for VarDisplay<'a> { } // Checks if a pos is outside the defined display -fn outside_display(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRotation) -> bool { +fn outside_display(p: Point, width: u32, height: u32, rotation: DisplayRotation) -> bool { + if p.x < 0 || p.y < 0 { + return true; + } + let (x, y) = (p.x as u32, p.y as u32); match rotation { DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => { if x >= width || y >= height { @@ -189,10 +210,9 @@ fn find_position(x: u32, y: u32, width: u32, height: u32, rotation: DisplayRotat #[cfg(test)] mod tests { use super::{find_position, outside_display, Display, DisplayRotation, VarDisplay}; + use crate::color::Black; use crate::color::Color; - use embedded_graphics::coord::Coord; - use embedded_graphics::prelude::*; - use embedded_graphics::primitives::Line; + use embedded_graphics::{prelude::*, primitives::Line, style::PrimitiveStyle}; #[test] fn buffer_clear() { @@ -228,7 +248,7 @@ mod tests { for x in 0..(width + height) { //limit x because it runs too long for y in 0..(u32::max_value()) { - if outside_display(x, y, width, height, rotation2) { + if outside_display(Point::new(x as i32, y as i32), width, height, rotation2) { break; } else { let (idx, _) = find_position(x, y, width, height, rotation2); @@ -247,11 +267,9 @@ mod tests { let mut buffer = [DEFAULT_BACKGROUND_COLOR.get_byte_value(); 128 / 8 * 296]; let mut display = VarDisplay::new(width, height, &mut buffer); - display.draw( - Line::new(Coord::new(0, 0), Coord::new(7, 0)) - .stroke(Some(Color::Black)) - .into_iter(), - ); + let _ = Line::new(Point::new(0, 0), Point::new(7, 0)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); let buffer = display.buffer(); @@ -273,11 +291,9 @@ mod tests { display.set_rotation(DisplayRotation::Rotate90); - display.draw( - Line::new(Coord::new(0, 120), Coord::new(0, 295)) - .stroke(Some(Color::Black)) - .into_iter(), - ); + let _ = Line::new(Point::new(0, 120), Point::new(0, 295)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); let buffer = display.buffer(); diff --git a/src/lib.rs b/src/lib.rs index ea23b5d..52edc10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,58 +1,66 @@ -//! A simple Driver for the Waveshare E-Ink Displays via SPI +//! A simple Driver for the [Waveshare](https://github.com/waveshare/e-Paper) E-Ink Displays via SPI //! -//! This driver was built using [`embedded-hal`] traits. +//! - Built using [`embedded-hal`] traits. +//! - Graphics support is added through [`embedded-graphics`] //! -//! [`embedded-hal`]: https://docs.rs/embedded-hal/~0.1 +//! [`embedded-graphics`]: https://docs.rs/embedded-graphics/ +//! [`embedded-hal`]: https://docs.rs/embedded-hal //! -//! # Requirements + //! -//! ### SPI +//! # Example //! -//! - MISO is not connected/available -//! - SPI_MODE_0 is used (CPHL = 0, CPOL = 0) -//! - 8 bits per word, MSB first -//! - Max. Speed tested by myself was 8Mhz but more should be possible (Ben Krasnow used 18Mhz with his implemenation) +//!```rust, no_run +//!# use embedded_hal_mock::*; +//!# fn main() -> Result<(), MockError> { +//!use embedded_graphics::{ +//! pixelcolor::BinaryColor::On as Black, prelude::*, primitives::Line, style::PrimitiveStyle, +//!}; +//!use epd_waveshare::{epd1in54::*, prelude::*}; +//!# +//!# let expectations = []; +//!# let mut spi = spi::Mock::new(&expectations); +//!# let expectations = []; +//!# let cs_pin = pin::Mock::new(&expectations); +//!# let busy_in = pin::Mock::new(&expectations); +//!# let dc = pin::Mock::new(&expectations); +//!# let rst = pin::Mock::new(&expectations); +//!# let mut delay = delay::MockNoop::new(); //! -//! ### Other.... +//!// Setup EPD +//!let mut epd = EPD1in54::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?; //! -//! - Buffersize: Wherever a buffer is used it always needs to be of the size: `width / 8 * length`, -//! where width and length being either the full e-ink size or the partial update window size +//!// Use display graphics from embedded-graphics +//!let mut display = Display1in54::default(); //! -//! # Examples +//!// Use embedded graphics for drawing a line +//!let _ = Line::new(Point::new(0, 120), Point::new(0, 295)) +//! .into_styled(PrimitiveStyle::with_stroke(Black, 1)) +//! .draw(&mut display); //! -//! ```rust,ignore -//! use epd_waveshare::{ -//! epd2in9::{EPD2in9, Display2in9}, -//! graphics::{Display, DisplayRotation}, -//! prelude::*, -//! }; -//! use embedded_graphics::Drawing; +//! // Display updated frame +//!epd.update_frame(&mut spi, &display.buffer())?; +//!epd.display_frame(&mut spi)?; //! -//! // Setup EPD -//! let mut epd = EPD2in9::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay).unwrap(); +//!// Set the EPD to sleep +//!epd.sleep(&mut spi)?; +//!# Ok(()) +//!# } +//!``` //! -//! // Use display graphics -//! let mut display = Display2in9::default(); +//! # Other information and requirements //! -//! // Write some hello world in the screenbuffer -//! display.draw( -//! Font6x8::render_str("Hello World!") -//! .stroke(Some(Color::Black)) -//! .fill(Some(Color::White)) -//! .translate(Coord::new(5, 50)) -//! .into_iter(), -//! ); +//! - Buffersize: Wherever a buffer is used it always needs to be of the size: `width / 8 * length`, +//! where width and length being either the full e-ink size or the partial update window size //! -//! // Display updated frame -//! epd.update_frame(&mut spi, &display.buffer()).unwrap(); -//! epd.display_frame(&mut spi).expect("display frame new graphics"); +//! ### SPI //! -//! // Set the EPD to sleep -//! epd.sleep(&mut spi).expect("sleep"); -//! ``` +//! MISO is not connected/available. SPI_MODE_0 is used (CPHL = 0, CPOL = 0) with 8 bits per word, MSB first. //! +//! Maximum speed tested by myself was 8Mhz but more should be possible (Ben Krasnow used 18Mhz with his implemenation) //! #![no_std] +#![deny(missing_docs)] #[cfg(feature = "graphics")] pub mod graphics; @@ -64,36 +72,20 @@ pub mod color; /// Interface for the physical connection between display and the controlling device mod interface; -#[cfg(feature = "epd7in5")] -pub mod epd7in5; -#[cfg(feature = "epd7in5_v2")] -pub mod epd7in5_v2; - -#[cfg(feature = "epd4in2")] -pub mod epd4in2; - -#[cfg(feature = "epd1in54")] pub mod epd1in54; - -#[cfg(feature = "epd1in54b")] pub mod epd1in54b; - -#[cfg(feature = "epd2in9")] pub mod epd2in9; - -#[cfg(feature = "epd2in9b")] pub mod epd2in9b; - -#[cfg(any(feature = "epd1in54", feature = "epd2in9"))] +pub mod epd4in2; +pub mod epd7in5; +pub mod epd7in5_v2; pub(crate) mod type_a; +/// Includes everything important besides the chosen Display pub mod prelude { pub use crate::color::{Color, TriColor}; pub use crate::traits::{RefreshLUT, WaveshareDisplay, WaveshareThreeColorDisplay}; - #[cfg(feature = "epd7in5_v2")] - pub use crate::traits::WaveshareDisplayExt; - pub use crate::SPI_MODE; #[cfg(feature = "graphics")] diff --git a/src/traits.rs b/src/traits.rs index 9f83da5..eed2672 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -38,11 +38,11 @@ where /// This initialises the EPD and powers it up /// /// This function is already called from - /// - [new()](WaveshareInterface::new()) + /// - [new()](WaveshareDisplay::new()) /// - [`wake_up`] /// /// - /// This function calls [reset()](WaveshareInterface::reset()), + /// This function calls [reset](WaveshareDisplay::reset), /// so you don't need to call reset your self when trying to wake your device up /// after setting it to sleep. fn init>( @@ -75,7 +75,47 @@ where /// All the functions to interact with the EPDs /// -/// This trait includes all public functions to use the EPDS +/// This trait includes all public functions to use the EPDs +/// +/// # Example +/// +///```rust, no_run +///# use embedded_hal_mock::*; +///# fn main() -> Result<(), MockError> { +///use embedded_graphics::{ +/// pixelcolor::BinaryColor::On as Black, prelude::*, primitives::Line, style::PrimitiveStyle, +///}; +///use epd_waveshare::{epd4in2::*, prelude::*}; +///# +///# let expectations = []; +///# let mut spi = spi::Mock::new(&expectations); +///# let expectations = []; +///# let cs_pin = pin::Mock::new(&expectations); +///# let busy_in = pin::Mock::new(&expectations); +///# let dc = pin::Mock::new(&expectations); +///# let rst = pin::Mock::new(&expectations); +///# let mut delay = delay::MockNoop::new(); +/// +///// Setup EPD +///let mut epd = EPD4in2::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay)?; +/// +///// Use display graphics from embedded-graphics +///let mut display = Display4in2::default(); +/// +///// Use embedded graphics for drawing a line +///let _ = Line::new(Point::new(0, 120), Point::new(0, 295)) +/// .into_styled(PrimitiveStyle::with_stroke(Black, 1)) +/// .draw(&mut display); +/// +/// // Display updated frame +///epd.update_frame(&mut spi, &display.buffer())?; +///epd.display_frame(&mut spi)?; +/// +///// Set the EPD to sleep +///epd.sleep(&mut spi)?; +///# Ok(()) +///# } +///``` pub trait WaveshareDisplay where SPI: Write, @@ -86,7 +126,7 @@ where { /// Creates a new driver from a SPI peripheral, CS Pin, Busy InputPin, DC /// - /// This already initialises the device. That means [init()](WaveshareInterface::init()) isn't needed directly afterwards + /// This already initialises the device. fn new>( spi: &mut SPI, cs: CS, @@ -101,19 +141,18 @@ where /// Let the device enter deep-sleep mode to save power. /// /// The deep sleep mode returns to standby with a hardware reset. - /// But you can also use [wake_up()](WaveshareInterface::wake_up()) to awaken. - /// But as you need to power it up once more anyway you can also just directly use [new()](WaveshareInterface::new()) for resetting - /// and initialising which already contains the reset fn sleep(&mut self, spi: &mut SPI) -> Result<(), SPI::Error>; /// Wakes the device up from sleep + /// + /// Also reintialises the device if necessary. fn wake_up>( &mut self, spi: &mut SPI, delay: &mut DELAY, ) -> Result<(), SPI::Error>; - /// Sets the backgroundcolor for various commands like [clear_frame()](WaveshareInterface::clear_frame()) + /// Sets the backgroundcolor for various commands like [clear_frame](WaveshareDisplay::clear_frame) fn set_background_color(&mut self, color: Color); /// Get current background color @@ -148,9 +187,12 @@ where /// This function waits until the device isn`t busy anymore fn display_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error>; + /// Provide a combined update&display and save some time (skipping a busy check in between) + fn update_and_display_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> 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`] + /// The background color can be changed with [`WaveshareDisplay::set_background_color`] fn clear_frame(&mut self, spi: &mut SPI) -> Result<(), SPI::Error>; /// Trait for using various Waveforms from different LUTs @@ -174,15 +216,3 @@ where /// if the device is still busy fn is_busy(&self) -> bool; } -/// Tiny optional extension trait -pub trait WaveshareDisplayExt -where - SPI: Write, - CS: OutputPin, - BUSY: InputPin, - DC: OutputPin, - RST: OutputPin, -{ - // provide a combined update&display and save some time (skipping a busy check in between) - fn update_and_display_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error>; -} diff --git a/src/type_a/command.rs b/src/type_a/command.rs index 30a2df6..e966aa9 100644 --- a/src/type_a/command.rs +++ b/src/type_a/command.rs @@ -12,28 +12,28 @@ use crate::traits; #[derive(Copy, Clone)] pub(crate) enum Command { /// Driver Output control - /// 3 Databytes: - /// A[7:0] - /// 0.. A[8] - /// 0.. B[2:0] - /// Default: Set A[8:0] = 0x127 and B[2:0] = 0x0 + /// 3 Databytes: + /// A[7:0] + /// 0.. A[8] + /// 0.. B[2:0] + /// Default: Set A[8:0] = 0x127 and B[2:0] = 0x0 DRIVER_OUTPUT_CONTROL = 0x01, /// Booster Soft start control - /// 3 Databytes: - /// 1.. A[6:0] - /// 1.. B[6:0] - /// 1.. C[6:0] - /// Default: A[7:0] = 0xCF, B[7:0] = 0xCE, C[7:0] = 0x8D + /// 3 Databytes: + /// 1.. A[6:0] + /// 1.. B[6:0] + /// 1.. C[6:0] + /// Default: A[7:0] = 0xCF, B[7:0] = 0xCE, C[7:0] = 0x8D BOOSTER_SOFT_START_CONTROL = 0x0C, GATE_SCAN_START_POSITION = 0x0F, //TODO: useful? // GATE_SCAN_START_POSITION = 0x0F, /// Deep Sleep Mode Control - /// 1 Databyte: - /// 0.. A[0] - /// Values: - /// A[0] = 0: Normal Mode (POR) - /// A[0] = 1: Enter Deep Sleep Mode + /// 1 Databyte: + /// 0.. A[0] + /// Values: + /// A[0] = 0: Normal Mode (POR) + /// A[0] = 1: Enter Deep Sleep Mode DEEP_SLEEP_MODE = 0x10, // /// Data Entry mode setting DATA_ENTRY_MODE_SETTING = 0x11,