Skip to content
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

Update to embedded-hal 1, merge fast-draw-improved #36

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
3 changes: 3 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ target_steps: &target_steps
- restore_cache:
key: v2-sh1106-{{ .Environment.CIRCLE_JOB }}-{{ checksum "Cargo.toml" }}
- run: rustup default ${RUST_VERSION:-stable}
# For docs gen
- run: rustup target add thumbv7m-none-eabi
- run: rustup target add thumbv7em-none-eabihf
- run: rustup component add rustfmt
- run: |
SYSROOT=$(rustc --print sysroot)
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,20 @@ and works with the [embedded-hal](crates.io/crates/embedded-hal) traits for maxi

## [Unreleased] - ReleaseDate

### Changed

- **(breaking)** [#28](https://github.com/jamwaffles/sh1106/pull/28) Upgrade MSRV to 1.50.0, add a
faster implementation of `DrawTarget::fill_solid`.
- **(breaking)** Switch to embedded-hal 1

## [0.5.0] - 2023-08-30

### Changed

- **(breaking)** [#34](https://github.com/jamwaffles/sh1106/pull/34) Upgrade to `embedded-graphics`
0.8 and `embedded-graphics-core` to 0.4.


## [0.4.0] - 2021-07-11

### Changed
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ targets = [ "thumbv7m-none-eabi", "thumbv7em-none-eabihf" ]
circle-ci = { repository = "jamwaffles/sh1106", branch = "master" }

[dependencies]
embedded-hal = "0.2.3"
embedded-hal = "1"
embedded-graphics-core = { version = "0.4.0", optional = true }

[dev-dependencies]
Expand Down
5 changes: 2 additions & 3 deletions build.sh
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
#!/bin/sh

set -e
set -ex

cargo build --target $TARGET --all-features --release

if [ -z $DISABLE_EXAMPLES ]; then
cargo build --target $TARGET --all-features --examples
fi

cargo test --lib --target x86_64-unknown-linux-gnu
cargo test --doc --target x86_64-unknown-linux-gnu
cargo deadlinks --ignore-fragments
192 changes: 192 additions & 0 deletions examples/bench-fill.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
//! Meant for development use only. Prints the time taken to draw a bunch of rectangles to the
//! display. Originally created to benchmark the `fill_solid` method.

#![no_std]
#![no_main]

use core::fmt::Write;
use embedded_graphics::{
geometry::Point,
mono_font::{ascii::FONT_6X10, MonoTextStyleBuilder},
pixelcolor::BinaryColor,
prelude::*,
primitives::{PrimitiveStyle, Rectangle},
text::{Baseline, Text},
};
use heapless::String;
use panic_semihosting as _;
use rtic::app;
use sh1106::{prelude::*, Builder};
use stm32f1xx_hal::{
gpio,
i2c::{BlockingI2c, DutyCycle, Mode},
pac::{self, I2C1},
prelude::*,
timer::{CountDownTimer, Event, Timer},
};

type Display = GraphicsMode<
I2cInterface<
BlockingI2c<
I2C1,
(
gpio::gpiob::PB8<gpio::Alternate<gpio::OpenDrain>>,
gpio::gpiob::PB9<gpio::Alternate<gpio::OpenDrain>>,
),
>,
>,
>;

#[inline(always)]
fn stopwatch<F>(f: F) -> u32
where
F: FnOnce() -> (),
{
let start: u32 = pac::DWT::cycle_count();
f();
let end: u32 = pac::DWT::cycle_count();
end.wrapping_sub(start)
}

#[app(device = stm32f1xx_hal::pac, peripherals = true)]
const APP: () = {
struct Resources {
display: Display,
timer: CountDownTimer<pac::TIM1>,
#[init(0)]
frame: u32,
}

#[init]
fn init(cx: init::Context) -> init::LateResources {
let dp = cx.device;
let mut cp = cx.core;

cp.DCB.enable_trace();
cp.DWT.enable_cycle_counter();

let mut flash = dp.FLASH.constrain();
let mut rcc = dp.RCC.constrain();

let clocks = rcc
.cfgr
.use_hse(8.mhz())
.sysclk(72.mhz())
.pclk1(36.mhz())
.freeze(&mut flash.acr);

let mut afio = dp.AFIO.constrain(&mut rcc.apb2);

let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);

let scl = gpiob.pb8.into_alternate_open_drain(&mut gpiob.crh);
let sda = gpiob.pb9.into_alternate_open_drain(&mut gpiob.crh);

let i2c = BlockingI2c::i2c1(
dp.I2C1,
(scl, sda),
&mut afio.mapr,
Mode::Fast {
frequency: 400_000.hz(),
duty_cycle: DutyCycle::Ratio2to1,
},
clocks,
&mut rcc.apb1,
1000,
10,
1000,
1000,
);

let mut display: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into();

display.init().unwrap();
display.flush().unwrap();

// Update framerate
let fps = 1;

let mut timer = Timer::tim1(dp.TIM1, &clocks, &mut rcc.apb2).start_count_down(fps.hz());

timer.listen(Event::Update);

// Init the static resources to use them later through RTIC
init::LateResources { timer, display }
}

#[idle()]
fn idle(_: idle::Context) -> ! {
loop {
// Fix default wfi() behaviour breaking debug probe
core::hint::spin_loop();
}
}

#[task(binds = TIM1_UP, resources = [display, timer, frame])]
fn update(cx: update::Context) {
let update::Resources {
display,
timer,
frame,
..
} = cx.resources;

display.clear();
display.flush().unwrap();

let center = display.bounding_box().center();

// Only bench time taken to draw rectangles
let time = stopwatch(|| {
for x in 0i32..64 {
// Square squares in center
Rectangle::with_center(center, Size::new(x as u32, x as u32))
.into_styled(PrimitiveStyle::with_fill(BinaryColor::On))
.draw(display)
.unwrap();
}

for x in 0i32..64 {
// Tall rectangles
Rectangle::with_center(Point::new(x * 5, 20), Size::new(4, x as u32))
.into_styled(PrimitiveStyle::with_fill(BinaryColor::On))
.draw(display)
.unwrap();

// Wide rectangles
Rectangle::with_center(Point::new(0, x * 2), Size::new(x as u32, 4))
.into_styled(PrimitiveStyle::with_fill(BinaryColor::On))
.draw(display)
.unwrap();
}
});

// Convert time to ms by dividing by sysclk * 1000
let time = time / 72_000;

let mut s: String<32> = String::new();

write!(s, "{}ms", time).ok();

let text_style = MonoTextStyleBuilder::new()
.font(&FONT_6X10)
.text_color(BinaryColor::On)
.background_color(BinaryColor::Off)
.build();

Text::with_baseline(&s, Point::zero(), text_style, Baseline::Top)
.draw(display)
.unwrap();

display.flush().unwrap();

*frame += 1;

// Clears the update flag
timer.clear_update_interrupt_flag();
}

extern "C" {
fn EXTI0();
}
};
31 changes: 18 additions & 13 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
//! ```

use core::marker::PhantomData;
use hal::{self, digital::v2::OutputPin};
use hal::{self, digital::OutputPin};

use crate::{
displayrotation::DisplayRotation,
Expand Down Expand Up @@ -116,7 +116,7 @@ impl Builder {
/// Finish the builder and use I2C to communicate with the display
pub fn connect_i2c<I2C, CommE>(self, i2c: I2C) -> DisplayMode<RawMode<I2cInterface<I2C>>>
where
I2C: hal::blocking::i2c::Write<Error = CommE>,
I2C: hal::i2c::I2c<Error = CommE>,
{
let properties = DisplayProperties::new(
I2cInterface::new(i2c, self.i2c_addr),
Expand All @@ -131,24 +131,21 @@ impl Builder {
/// If the Chip Select (CS) pin is not required, [`NoOutputPin`] can be used as a dummy argument
///
/// [`NoOutputPin`]: ./struct.NoOutputPin.html
pub fn connect_spi<SPI, DC, CS, CommE, PinE>(
pub fn connect_spi<SPI, DC, CommE, PinE>(
self,
spi: SPI,
dc: DC,
cs: CS,
) -> DisplayMode<RawMode<SpiInterface<SPI, DC, CS>>>
) -> DisplayMode<RawMode<SpiInterface<SPI, DC>>>
where
SPI: hal::blocking::spi::Transfer<u8, Error = CommE>
+ hal::blocking::spi::Write<u8, Error = CommE>,
SPI: hal::spi::SpiDevice<Error = CommE>,
DC: OutputPin<Error = PinE>,
CS: OutputPin<Error = PinE>,
{
let properties = DisplayProperties::new(
SpiInterface::new(spi, dc, cs),
SpiInterface::new(spi, dc),
self.display_size,
self.rotation,
);
DisplayMode::<RawMode<SpiInterface<SPI, DC, CS>>>::new(properties)
DisplayMode::<RawMode<SpiInterface<SPI, DC>>>::new(properties)
}
}

Expand All @@ -158,15 +155,17 @@ pub struct NoOutputPin<PinE = ()> {
_m: PhantomData<PinE>,
}

impl<PinE> NoOutputPin<PinE> {
impl<PinE: hal::digital::Error> NoOutputPin<PinE> {
/// Create a new instance of `NoOutputPin`
pub fn new() -> Self {
Self { _m: PhantomData }
}
}

impl<PinE> OutputPin for NoOutputPin<PinE> {
impl<PinE: hal::digital::Error> hal::digital::ErrorType for NoOutputPin<PinE> {
type Error = PinE;
}
impl<PinE: hal::digital::Error> OutputPin for NoOutputPin<PinE> {
fn set_low(&mut self) -> Result<(), PinE> {
Ok(())
}
Expand All @@ -178,9 +177,15 @@ impl<PinE> OutputPin for NoOutputPin<PinE> {
#[cfg(test)]
mod tests {
use super::NoOutputPin;
use embedded_hal::digital::v2::OutputPin;
use embedded_hal::digital::OutputPin;

#[derive(Debug)]
enum SomeError {}
impl hal::digital::Error for SomeError {
fn kind(&self) -> hal::digital::ErrorKind {
hal::digital::ErrorKind::Other
}
}

struct SomeDriver<P: OutputPin<Error = SomeError>> {
#[allow(dead_code)]
Expand Down
4 changes: 2 additions & 2 deletions src/interface/i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub struct I2cInterface<I2C> {

impl<I2C> I2cInterface<I2C>
where
I2C: hal::blocking::i2c::Write,
I2C: hal::i2c::I2c,
{
/// Create new sh1106 I2C interface
pub fn new(i2c: I2C, addr: u8) -> Self {
Expand All @@ -23,7 +23,7 @@ where

impl<I2C, CommE> DisplayInterface for I2cInterface<I2C>
where
I2C: hal::blocking::i2c::Write<Error = CommE>,
I2C: hal::i2c::I2c<Error = CommE>,
{
type Error = Error<CommE, ()>;

Expand Down
Loading