Skip to content

Asyncify flush #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

/target/
**/target/
**/*.rs.bk
Cargo.lock
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ version = "0.3.0"
edition = "2018"

[package.metadata.docs.rs]
targets = [ "thumbv7m-none-eabi" ]
targets = ["thumbv7m-none-eabi"]
all-features = true

[badges]
Expand All @@ -22,18 +22,20 @@ circle-ci = { repository = "jamwaffles/ssd1331", branch = "master" }
[dependencies]
embedded-hal = "0.2.3"
embedded-graphics-core = { version = "0.3.2", optional = true }
embedded-hal-async = { git = "https://github.com/rust-embedded/embedded-hal" }

[dev-dependencies]
cortex-m = "0.7.3"
cortex-m-rt = "0.6.11"
panic-semihosting = "0.5.3"
embedded-graphics = "0.7.1"
tinybmp = "0.3.1"
stm32f1xx-hal = { version = "0.7.0", features = [ "rt", "stm32f103" ] }
stm32f1xx-hal = { version = "0.7.0", features = ["rt", "stm32f103"] }

[features]
default = ["graphics"]
graphics = ["embedded-graphics-core"]
embedded-async = []

[profile.dev]
codegen-units = 1
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@ You can also export images directly from The GIMP by saving as `.bmp` and choosi

## [Examples](examples)

Examples are stored in per target directories in ssd1331-examples. cd to your preferred example

`cd ssd1331-examples/stm32f1-examples/`

This crate uses [`probe-run`](https://crates.io/crates/probe-run) to run the examples. Once set up, it should be as simple as `cargo run --example <example name> --release`. `--release` will be required for some examples to reduce FLASH usage.

Load a BMP image of the Rust logo and display it in the center of the display. From
[`examples/bmp.rs`](examples/bmp.rs):
[`ssd1331-examples/stm32f1-examples/bmp.rs`](examples/bmp.rs):

```rust
#![no_std]
Expand Down Expand Up @@ -93,7 +97,7 @@ fn main() -> ! {
let (w, h) = disp.dimensions();

let bmp =
Bmp::from_slice(include_bytes!("./rust-pride.bmp")).expect("Failed to load BMP image");
Bmp::from_slice(include_bytes!("../../../assets/rust-pride.bmp")).expect("Failed to load BMP image");

let im: Image<Bmp<Rgb565>> = Image::new(&bmp, Point::zero());

Expand Down
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes.
10 changes: 7 additions & 3 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,23 @@ fi

cargo fmt --all -- --check

cargo build --target $TARGET --all-features --release
# todo not building all features
cargo build --target $TARGET --release

cargo test --lib --target x86_64-unknown-linux-gnu
cargo test --doc --target x86_64-unknown-linux-gnu

if [ -z $DISABLE_EXAMPLES ]; then
cargo build --target $TARGET --all-features --examples
# todo list of example directories in metadata so other
(cd ssd1331-examples/stm32f1-examples && cargo build --examples)
(cd ssd1331-examples/embassy-nrf && cargo build --examples)
fi

# Remove stale docs - the linkchecker might miss links to old files if they're not removed
cargo clean --doc
cargo clean --doc --target $TARGET

cargo doc --all-features --target $TARGET
# todo not building all features
cargo doc --target $TARGET

linkchecker target/$TARGET/doc/ssd1331/index.html
69 changes: 69 additions & 0 deletions src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,75 @@ pub enum Command {
}

impl Command {
/// Send command to SSD1331
#[cfg(feature = "embedded-async")]
pub async fn send_async<SPI, DC, CommE, PinE>(
self,
spi: &mut SPI,
dc: &mut DC,
) -> Result<(), Error<CommE, PinE>>
where
SPI: embedded_hal_async::spi::SpiBusWrite<u8, Error = CommE>,
DC: OutputPin<Error = PinE>,
{
// Transform command into a fixed size array of 7 u8 and the real length for sending
let (data, len) = match self {
Command::Contrast(a, b, c) => ([0x81, a, 0x82, b, 0x83, c, 0], 6),
// TODO: Collapse AllOn and Invert commands into new DisplayMode cmd with enum
Command::AllOn(on) => ([if on { 0xA5 } else { 0xA6 }, 0, 0, 0, 0, 0, 0], 1),
Command::Invert(inv) => ([if inv { 0xA7 } else { 0xA4 }, 0, 0, 0, 0, 0, 0], 1),
Command::DisplayOn(on) => ([0xAE | (on as u8), 0, 0, 0, 0, 0, 0], 1),
Command::ColumnAddress(start, end) => ([0x15, start, end, 0, 0, 0, 0], 3),
Command::RowAddress(start, end) => ([0x75, start, end, 0, 0, 0, 0], 3),
Command::StartLine(line) => ([0xA1, (0x3F & line), 0, 0, 0, 0, 0], 2),
Command::RemapAndColorDepth(hremap, vremap, cmode, addr_inc_mode) => (
[
0xA0,
0x20 | ((vremap as u8) << 4
| (hremap as u8) << 1
| (cmode as u8) << 6
| (addr_inc_mode as u8)),
0,
0,
0,
0,
0,
],
2,
),
Command::Multiplex(ratio) => ([0xA8, ratio, 0, 0, 0, 0, 0], 2),
Command::ReverseComDir(rev) => ([0xC0 | ((rev as u8) << 3), 0, 0, 0, 0, 0, 0], 1),
Command::DisplayOffset(offset) => ([0xA2, offset, 0, 0, 0, 0, 0], 2),
Command::ComPinConfig(alt, lr) => (
[
0xDA,
0x2 | ((alt as u8) << 4) | ((lr as u8) << 5),
0,
0,
0,
0,
0,
],
2,
),
Command::DisplayClockDiv(fosc, div) => {
([0xB3, ((0xF & fosc) << 4) | (0xF & div), 0, 0, 0, 0, 0], 2)
}
Command::PreChargePeriod(phase1, phase2) => (
[0x3e, ((0xF & phase2) << 4) | (0xF & phase1), 0, 0, 0, 0, 0],
2,
),
Command::VcomhDeselect(level) => ([0xBE, (level as u8) << 1, 0, 0, 0, 0, 0], 2),
Command::Noop => ([0xE3, 0, 0, 0, 0, 0, 0], 1),
};

// Command mode. 1 = data, 0 = command
dc.set_low().map_err(Error::Pin)?;

// Send command over the interface
spi.write(&data[0..len]).await.map_err(Error::Comm)
}

/// Send command to SSD1331
pub fn send<SPI, DC, CommE, PinE>(
self,
Expand Down
32 changes: 31 additions & 1 deletion src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const BUF_SIZE: usize = 96 * 64 * 2;
/// let dc = Pin;
///
/// let mut display = Ssd1331::new(spi, dc, Rotate0);
/// let raw = ImageRawLE::new(include_bytes!("../examples/ferris.raw"), 86);
/// let raw = ImageRawLE::new(include_bytes!("../assets/ferris.raw"), 86);
///
/// let image: Image<ImageRawLE<Rgb565>> = Image::new(&raw, Point::zero());
///
Expand Down Expand Up @@ -99,6 +99,36 @@ pub struct Ssd1331<SPI, DC> {
dc: DC,
}

#[cfg(feature = "embedded-async")]
impl<SPI, DC, CommE, PinE> Ssd1331<SPI, DC>
where
SPI: embedded_hal_async::spi::SpiBusWrite<u8, Error = CommE>,
DC: OutputPin<Error = PinE>,
{
/// Send the full framebuffer to the display
///
/// This resets the draw area the full size of the display
pub async fn flush_async(&mut self) -> Result<(), Error<CommE, PinE>> {
// Ensure the display buffer is at the origin of the display before we send the full frame
// to prevent accidental offsets
// self.set_draw_area((0, 0), (DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1))?;

Command::ColumnAddress(0, DISPLAY_WIDTH - 1)
.send_async(&mut self.spi, &mut self.dc)
.await?;
Command::RowAddress(0.into(), (DISPLAY_HEIGHT - 1).into())
.send_async(&mut self.spi, &mut self.dc)
.await?;

// 1 = data, 0 = command
self.dc.set_high().map_err(Error::Pin)?;

embedded_hal_async::spi::SpiBusWrite::write(&mut self.spi, &self.buffer)
.await
.map_err(Error::Comm)
}
}

impl<SPI, DC, CommE, PinE> Ssd1331<SPI, DC>
where
SPI: hal::blocking::spi::Write<u8, Error = CommE>,
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
//!
//! let (w, h) = display.dimensions();
//!
//! let bmp = Bmp::from_slice(include_bytes!("../examples/rust-pride.bmp"))
//! let bmp = Bmp::from_slice(include_bytes!("../assets/rust-pride.bmp"))
//! .expect("Failed to load BMP image");
//!
//! let im: Image<Bmp<Rgb565>> = Image::new(&bmp, Point::zero());
Expand Down
8 changes: 8 additions & 0 deletions ssd1331-examples/embassy-nrf/.cargo/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
runner = "probe-run --chip nRF52840_xxAA"
rustflags = [
"-C", "link-arg=-Tlink.x",
]

[build]
target = "thumbv7em-none-eabihf"
39 changes: 39 additions & 0 deletions ssd1331-examples/embassy-nrf/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[package]
name = "embassy-nrf"
version = "0.1.0"
edition = "2021"

[dependencies]
embedded-hal = "0.2.7"
embedded-hal-async = { version = "0.1.0-alpha.0" }
embedded-graphics-core = { version = "0.3.2", optional = true }
cortex-m = "0.7.3"
cortex-m-rt = "0.6.11"
embedded-graphics = "0.7.1"
tinybmp = "0.3.1"
futures = { version = "0.3.17", default-features = false }
embassy = { version = "0.1.0", git = "https://github.com/embassy-rs/embassy" }
embassy-nrf = { version = "0.1.0", git = "https://github.com/embassy-rs/embassy", features = [
"nightly",
"unstable-traits",
"nrf52840",
"gpiote",
"time-driver-rtc1"
] }
ssd1331 = { path = "../../", features = ["embedded-async"] }

[patch.crates-io]
embassy = { git = "https://github.com/embassy-rs/embassy" }
embassy-nrf = { git = "https://github.com/embassy-rs/embassy" }
embassy-macros = { git = "https://github.com/embassy-rs/embassy" }
embedded-hal-async = { git = "https://github.com/rust-embedded/embedded-hal" }
embedded-hal = { git = "https://github.com/rust-embedded/embedded-hal" }

[profile.dev]
codegen-units = 1
incremental = false

[profile.release]
codegen-units = 1
debug = true
lto = true
File renamed without changes.
113 changes: 113 additions & 0 deletions ssd1331-examples/embassy-nrf/examples/bmp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//! The rust-toolchain will pull in the correct nightly and target so all you
//! need to run is
//!
//! cargo run --release
//!
#![no_main]
#![no_std]
#![feature(type_alias_impl_trait)]

use core::future::pending;
use embassy::interrupt::InterruptExt;
use embassy::time::{Duration, Timer};
use embassy::util::Forever;
use embassy_nrf::gpio::{self, AnyPin, Level, Output, OutputDrive, Pin};
use embassy_nrf::{interrupt, spim};
use embedded_graphics::prelude::*;
use embedded_graphics::{image::Image, pixelcolor::Rgb565};
use ssd1331::{DisplayRotation, Ssd1331};
use tinybmp::Bmp;

// we make a lazily created static
static EXECUTOR: Forever<embassy::executor::Executor> = Forever::new();

#[cortex_m_rt::entry]
fn main() -> ! {
// once we hit runtime we create and fill that executor finally
let executor = EXECUTOR.put(embassy::executor::Executor::new());

// provides the peripherals from the async first pac if you selected it
let dp = embassy_nrf::init(Default::default());

let green = gpio::Output::new(
// degrade just a typesystem hack to forget which pin it is so we can
// call it Anypin and make our function calls more generic
dp.P0_22.degrade(),
gpio::Level::High,
gpio::OutputDrive::Standard,
);

// spawn tasks
executor.run(|spawner| {
let _ = spawner.spawn(blinky_task(green));
let _ = spawner.spawn(display_task());
})
}

#[embassy::task]
async fn blinky_task(mut green: gpio::Output<'static, AnyPin>) {
loop {
green.set_high();
Timer::after(Duration::from_millis(300)).await;
green.set_low();
Timer::after(Duration::from_millis(1000)).await;
}
}

#[embassy::task]
pub async fn display_task() {
// Too lazy to pass all the pins and peripherals we need.
// Safety: Fragile but safe as long as pins and peripherals arent used
// anywhere else
let mut dp = unsafe { <embassy_nrf::Peripherals as embassy::util::Steal>::steal() };

let mut spim_irq = interrupt::take!(SPIM3);
spim_irq.set_priority(interrupt::Priority::P4);

let mut spim_config = spim::Config::default();
spim_config.frequency = spim::Frequency::M16;
let spim = spim::Spim::new_txonly(
&mut dp.SPI3,
&mut spim_irq,
&mut dp.P0_21,
&mut dp.P0_17,
spim_config,
);

let mut rst = Output::new(&mut dp.P0_16, Level::High, OutputDrive::Standard);
let dc = Output::new(&mut dp.P0_15, Level::High, OutputDrive::Standard);
let mut display = Ssd1331::new(spim, dc, DisplayRotation::Rotate0);
Timer::after(Duration::from_millis(1)).await;
rst.set_low();
Timer::after(Duration::from_millis(1)).await;
rst.set_high();
display.init().unwrap();

let (w, h) = display.dimensions();

let bmp =
Bmp::from_slice(include_bytes!("../../../assets/rust-pride.bmp")).expect("Failed to load BMP image");

let im: Image<Bmp<Rgb565>> = Image::new(&bmp, Point::zero());

// Position image in the center of the display
let moved = im.translate(Point::new(
(w as u32 - bmp.size().width) as i32 / 2,
(h as u32 - bmp.size().height) as i32 / 2,
));

moved.draw(&mut display).unwrap();

display.flush_async().await.unwrap();
// display.flush().unwrap();

// Block forever so the above drivers don't get dropped
pending::<()>().await;
}

#[panic_handler] // panicking behavior
fn panic(_: &core::panic::PanicInfo) -> ! {
loop {
cortex_m::asm::bkpt();
}
}
6 changes: 6 additions & 0 deletions ssd1331-examples/embassy-nrf/memory.x
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
MEMORY
{
/* NOTE 1 K = 1 KiBi = 1024 bytes */
FLASH : ORIGIN = 0x00000000, LENGTH = 1024K
RAM : ORIGIN = 0x20000000, LENGTH = 256K
}
6 changes: 6 additions & 0 deletions ssd1331-examples/embassy-nrf/rust-toolchain
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Before upgrading check that everything is available on all tier1 targets here:
# https://rust-lang.github.io/rustup-components-history
[toolchain]
channel = "nightly-2022-04-24"
components = [ "rust-src", "rustfmt" ]
targets = ["thumbv7em-none-eabi"]
File renamed without changes.
Loading