rust-embedded-community / ssd1306 Goto Github PK
View Code? Open in Web Editor NEWSSD1306 OLED driver
License: Apache License 2.0
SSD1306 OLED driver
License: Apache License 2.0
Building any example that uses i2c on both nightly-2018-05-10
and nightly-2018-04-18
results in the following errors. I am not sure if it is issue with ssd1306
, my apologies if it is with another crate.
error[E0277]: the trait bound `blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>: hal::prelude::Write` is not satisfied
--> examples/graphics_i2c.rs:60:52
|
60 | let mut disp: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into();
| ^^^^^^^^^^^ the trait `hal::prelude::Write` is not implemented for `blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>`
error[E0277]: the trait bound `blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>: hal::prelude::Write` is not satisfied
--> examples/graphics_i2c.rs:60:69
|
60 | let mut disp: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into();
| ^^^^ the trait `hal::prelude::Write` is not implemented for `blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>`
|
= note: required because of the requirements on the impl of `ssd1306::interface::DisplayInterface` for `ssd1306::interface::I2cInterface<blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>>`
error[E0277]: the trait bound `blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>: hal::prelude::Write` is not satisfied
--> examples/graphics_i2c.rs:60:19
|
60 | let mut disp: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into();
| ^^^^^^^^^^^^^^^ the trait `hal::prelude::Write` is not implemented for `blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>`
|
= note: required because of the requirements on the impl of `ssd1306::interface::DisplayInterface` for `ssd1306::interface::I2cInterface<blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>>`
= note: required by `ssd1306::mode::GraphicsMode`
error[E0599]: no method named `init` found for type `ssd1306::mode::GraphicsMode<ssd1306::interface::I2cInterface<blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>>>` in the current scope
--> examples/graphics_i2c.rs:62:10
|
62 | disp.init().unwrap();
| ^^^^
|
= note: the method `init` exists but the following trait bounds were not satisfied:
`ssd1306::interface::I2cInterface<blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>> : ssd1306::interface::DisplayInterface`
At leas the three of us have some code lying around trying to accomplish different things with this little display. This repository was offered up as a forum for putting together an SSD1306 driver we can all work with at rust-embedded/wg#39.
I think we should lay down what our various goals are and decide the best path forward here.
RTFM was renamed to RTIC. To avoid deprecation warnings, we should update references to it.
[target.thumbv7m-none-eabi]
runner = [ "arm-none-eabi-gdb.exe", "-iex", "set auto-load safe-path ." ]
rustflags = [
"-C", "link-arg=-Tlink.x",
"-C", "linker=arm-none-eabi-ld",
"-Z", "linker-flavor=ld",
"-Z", "thinlto=no",
]
If this file exists it should be useful and hopefully not contain host specific information.
I have this display from adafruit. It's a ssd1305 but in adafruit's circuit python tutorials they use a ssd1306 driver. I ran out of memory programming the chip in python so I'm switching to rust and found this library.
In python I had to define the display size as 132x64, despite being advertised as 128x32. With this display size the top left pixel is 32x4. If I use a display size of 128x32 then every other line is disabled (looks like an old crt).
I see the same behavior with your library, if I use a TerminalMode driver with DisplaySize::Display128x32 then every other line is disabled. If I use 128x64 the display is much better, each character looks correct, but the text starts off screen. The last columns of the display also do not work, it will show random pixels when the display is initialized and they will not change or clear.
Display132x64
?display.properties.set_display_area
but it isn't public, is there a way to set that?ssd1306
in use (if applicable): 0.3.0I believe it would be useful to add an example with the SSD1306 being used to visualize something that is updated with interrupts, e.g. a counter, timer or similar. As the F0xx crate specifically requires the critical sections to be used in peripherals setup, it is not easy to adapt existing examples made for F1 or F4 boards.
I tried to re-use and adapt the code for blinking LEDs with interrupts from the F0xx-HAL crate, but I don't know how to approach it. In case of LEDs it seems quite straightforward: they are made global variables, wrapped in Mutex. How should this be done for the display: is it the SDA and SCL pins, as well as I2C and RCC that have to be global, or is there a way to make the display instance global, while everything else is set up within the Critical Section? If the latter is true, what should the type be, and what needs to be inside mutex?
Here's my non-working code that gives an idea of what the problem is. https://github.com/nebelgrau77/STM32F0-SSD1306-IRQ_DRAFT
This is a working example on STM32F411: it is easier with that crate, as the peripherals don't need to be defined within CS:
https://github.com/nebelgrau77/STM32F4-OLED-IRQ_timecounter
I believe such example would be useful for any kind of project that needs to use interrupts AND a display, e.g. involving some ADC readings, clocks and such.
Hello,
I'm very sorry, but I am having trouble using cortex_m_rtfm and an I2C SSD1306. I have it working in a non-RTFM program, and the only examples of using your library with RTFM are with an SPI interface. I keep getting variant not found
and proc macro panicked
errors in app!
.
How would I write the same blinky example using I2C?
Thanks! :)
Hello,
I am only able to get release builds to function if opt-level = 0
or opt-level = 1
is added to Cargo.toml. If it is not added there are no errors during building and a much smaller binary is produced, but nothing is displayed. I have tested this with my own project, and both the blinky
and graphics
examples. I have not tested on any I2C displays.
I'm probably doing something stupid and normally I'd be fine just using opt-level = 1
and calling it a day, but unfortunately the binaries are too large for my MCU.
Thanks! :)
Edit: I was wrong to say nothing is displayed; every 4 or 5 resets sometimes the program works and stuff is displayed, but cutting power and resetting again results in no image. Debug binaries always work regardless of power loss and frequency of resets.
Second edit: I realise it is probably just the display's internal cache retaining data written with debug binaries
I'm having the exact same issue with a lot of my projects. It's due to crates using an outdated version of proc-macro2
. Most use 0.3.8 but the issues are fixed on the latest. I think the solution is a whole lot of PRs with the latest version, but its hard to replicate locally (each broken crate requires cloning then making dependancies point to a local directory).
Based on this metric I believe the linked line may be incorrect.
If that's supposed to be height, I'm happy to fix it, otherwise please provide an explanation why it's width.
ssd1306
in use (if applicable): 0.3.0-alpha.2I was trying to print some text out but with the code below I not able to see anything into the display. I've tried to change che I2C pin, frequency etc; I didn't spend much time debugging because I don't know if the problem is related to the stm32l432kc
implementation, with the ssd1306
or not. How can I test this issue?
//! Reads data from a gyuvl53l0x sensor
#![no_main]
#![no_std]
extern crate cortex_m;
extern crate cortex_m_rt as rt;
extern crate stm32l4xx_hal as hal;
extern crate ssd1306;
use cortex_m_semihosting::hprintln;
use crate::hal::prelude::*;
use crate::hal::stm32l4::stm32l4x2;
use crate::hal::i2c::I2c;
use crate::rt::entry;
use core::panic::PanicInfo;
use ssd1306::{mode::GraphicsMode, Builder, interface::I2cInterface};
use embedded_graphics::pixelcolor::BinaryColor;
use embedded_graphics::fonts::{Font6x8, Font12x16};
use embedded_graphics::prelude::*;
use embedded_graphics::Drawing;
#[entry]
fn main() -> ! {
hprintln!("Hello, world!").unwrap();
let dp = stm32l4x2::Peripherals::take().unwrap();
let mut flash = dp.FLASH.constrain();
let mut rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.freeze(&mut flash.acr);
let mut gpioa = dp.GPIOA.split(&mut rcc.ahb2);
let mut gpiob = dp.GPIOB.split(&mut rcc.ahb2);
let mut led = gpiob.pb3.into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper);
let mut scl = gpioa
.pa9
.into_open_drain_output(&mut gpioa.moder, &mut gpioa.otyper);
scl.internal_pull_up(&mut gpioa.pupdr, true);
let scl = scl.into_af4(&mut gpioa.moder, &mut gpioa.afrh);
let mut sda = gpioa
.pa10
.into_open_drain_output(&mut gpioa.moder, &mut gpioa.otyper);
sda.internal_pull_up(&mut gpioa.pupdr, true);
let sda = sda.into_af4(&mut gpioa.moder, &mut gpioa.afrh);
hprintln!("Start configuring I2C").unwrap();
let i2c = I2c::i2c1(dp.I2C1, (scl, sda), 400.khz(), clocks, &mut rcc.apb1r1);
hprintln!("Start configuring SSD1306").unwrap();
let mut disp: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into();
disp.init().unwrap();
disp.flush().unwrap();
let (a, b) = disp.get_dimensions();
hprintln!("{:#?}", a).unwrap();
hprintln!("{:#?}", b).unwrap();
disp.draw(
Font12x16::render_str("rust")
.stroke(Some(BinaryColor::On))
.translate(Point::new(10, 10))
.into_iter(),
);
hprintln!("b").unwrap();
disp.flush().unwrap();
led.set_high().unwrap();
hprintln!("c").unwrap();
loop {}
}
#[panic_handler]
#[no_mangle]
pub fn panic(_info: &PanicInfo) -> ! {
loop{}
}
test case dependencies
[package]
authors = ["Luca Zulian <[email protected]>"]
edition = "2018"
readme = "README.md"
name = "robot"
version = "0.1.1"
[dependencies]
cortex-m = "0.6.1"
cortex-m-rtfm = "0.4.3"
panic-halt = "0.2.0"
alloc-cortex-m = "0.3.5"
embedded-hal = "0.2.3"
generic-array = "0.13.2"
nb = "0.1.0"
cortex-m-semihosting = "0.3.5"
gyuvl53l0x = "0.1.3"
ssd1306 = "0.3.0-alpha.2"
embedded-graphics = "0.6.0-alpha.2"
[dependencies.stm32l4xx-hal]
git = "https://github.com/stm32-rs/stm32l4xx-hal"
branch = "master"
features = ["stm32l4x2", "rt"]
[dependencies.cortex-m-rt]
version = "0.6.10"
features = ["device"]
[dependencies.cast]
default-features = false
version = "0.2.2"
[dependencies.safe-transmute]
version = "0.10.1"
default-features = false
[[bin]]
name = "robot"
test = false
bench = false
[profile.release]
codegen-units = 1 # better optimizations
debug = false # symbols are nice and they don't increase the size on Flash
lto = true # better optimizations
panic = "abort"
opt-level = 's'
[profile.dev]
panic = "abort"
Can someone explain me how to "solve" or debug this kind of issue?
On top of rotations a display mirror/flip should also be settable. I propose adding a DisplayFlip
trait that allows opt-in flipping both horizontally and vertically.
0.2.6 is still to be released but won't need more than a changelog entry to describe non-breaking changes. 0.3.0 will upgrade embedded-graphics to 0.6.0 so will be a much more substantial change. It should have either a blog post, or a migration guide, or a link to the embedded-graphics migration guide created for it.
TerminalMode
does not treat newline characters in any special way. Instead, they hit this case which just renders an empty bitmap for that character. I think that the TerminalMode
would be much more useful if it recognised \n
and skipped to the beginning of the next line.
I've been able to draw fonts and such with embedded-graphics v0.4.5 with this code:
use embedded_graphics::prelude::*;
use embedded_graphics::fonts;
use ssd1306::prelude::*;
use ssd1306::Builder;
#[entry]
fn main() -> ! {
// ...snip...
let mut disp: GraphicsMode<_> = Builder::new()
.with_size(DisplaySize::Display128x32)
.connect_i2c(i2c)
.into();
disp.init().unwrap();
disp.flush().unwrap();
disp.draw(
fonts::Font12x16::render_str("Hello Rust!")
.into_iter(),
);
disp.flush().unwrap();
but once I update to latest embedded-graphics, I get the following error:
error[E0599]: no method named `draw` found for type `ssd1306::mode::graphics::GraphicsMode<ssd1306::interface::i2c::I2cInterface<hal::sercom::I2CMaster3>>` in the current scope
--> src/main.rs:55:10
|
55 | disp.draw(
| ^^^^
|
= help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
|
14 | use embedded_graphics::Drawing;
|
Is this expected? I can't easily see why this is breaking in the first place. Explicitly calling use embedded_graphics::Drawing
doesn't seem to fix it for me.
Open a PR to add this crate to the list of drivers when it's ready.
I am trying to use this library in my project with little success. When I compile my code as an example it works without issue, but in a standalone project I get the following error:
error[E0599]: no method named ``draw`` found for type ``ssd1306::SSD1306<ssd1306::interface::I2cInterface<hal::i2c::I2c<hal::<unnamed>::I2C1, (hal::gpio::gpiob::PB6<hal::gpio::Alternate<hal::gpio::OpenDrain>>, hal::gpio::gpiob::PB7<hal::gpio::Alternate<hal::gpio::OpenDrain>>)>>>`` in the current scope --> src/main.rs:62:10 | 62 | disp.draw(Font6x8::render_str("Hello world!", (0, 0)).into_iter()); | ^^^^
error[E0599]: no method named ``draw`` found for type ``ssd1306::SSD1306<ssd1306::interface::I2cInterface<hal::i2c::I2c<hal::<unnamed>::I2C1, (hal::gpio::gpiob::PB6<hal::gpio::Alternate<hal::gpio::OpenDrain>>, hal::gpio::gpiob::PB7<hal::gpio::Alternate<hal::gpio::OpenDrain>>)>>>`` in the current scope --> src/main.rs:63:10 | 63 | disp.draw(Font6x8::render_str("Hello Rust!", (0, 16)).into_iter()); | ^^^^
Any idea what it could be?
Thanks!
send_data()
should be using less memory. @scowcron's implementation uses buffered chunks which might be a good way to go. Adding the initial 0x40
to the beginning of the buffer makes using another buffer a necessity at the moment.
Spinoff from #2 (comment):
So thinking a bit more about this. I think we can introduce display modes by separating the current buffer initialisation from the interface initialisation. Then we could use different types for the different modes and into_...() functions to turn the initialised display interface into a specific output interface.
I'd have called @jamwaffles egfx, mine char and @scowcron pixbuf.
Comments?
After #19, there should be a way of giving the driver a different length display buffer depending on the size of the screen.
@scowcron @therealprof I've made a release milestone so there's a short-term(-ish) goal to work towards. I've already assigned a couple of issues to it. I'd like to collect a set of features/requirements from you (and anybody else reading this!) as a set of issues with the Enhancement tag and the Release 0.1
milestone. Please add any desired features/bugs/requirements to the issue list so we can get something published :D
It would also be nice to be published in the embedded WG newsletter to get some people using this driver.
From #27:
Rotation acts really funny for 90 and 270 degrees, it basically mirrors the characters and changes from l-t-r to r-t-l mode with character mode.
This is probably an artifact of me only testing an image (of the Rust logo). I'll do some more thorough testing taking into account LTR/RTL direction. I think it's fine for graphics where entire display is refreshed at once, but char mode will depend on fill/scroll directions.
I just tried to build the examples using a fresh clone of the repo and got a number of errors (escalated from warnings due to https://github.com/jamwaffles/ssd1306/blob/master/src/lib.rs#L160)
error: use of deprecated item 'hal::digital::OutputPin::set_low': Deprecated because the methods cannot return errors. Users should use the traits in digital::v2.
It sounds like the solution is to just update to the latest traits, so I did that in my local copy and got the examples to build. I can create a PR, but right now I'm just throwing away the result from the pin state changes (which I guess effectively matches the existing behaviour). If more thorough error handling seems appropriate I'm open to that.
This should be a good test to exercise the driver. There's still the outstanding question of how we organise examples but having a variety should help people get up and running quickly.
As reported in #47.
In TerminalMode
, the text isn't rotated properly in these two rotations. Instead, it's just drawn backwards for 90 degrees:
And like this for 270 degrees:
0 and 180 degrees work fine.
Is it possible to power off the display using command 0xAE? This would enable saving power and OLED life in long-running applications.
I'm pretty sure it's currently broken. There's a good solution in embedded-graphics/embedded-graphics#160 which could also be used here.
(copypasta from rust-embedded-community/sh1106#12)
The display buffer has a fixed length. There should be a guard that just does a noop if the pixel coordinates are out of bounds. I think this is better than returning a Result::Err
because it allows the user more freedom in what they (try and) show on the display.
There are quite a few TODO
s scattered around the codebase about adding various items to a prelude to make using the crate easier. Should be easy to add a few things so that people can just add use ssd1306::prelude::*
.
Travis is too slow. Cc rust-embedded-community/sh1106#12.
ssd1306
in use (if applicable): master branch (or "v0.3.0-alpha.2")Fail to compile this project because embedded-graphics
removed Drawing
trait.
cargo build --example image_i2c
Compiling ssd1306 v0.3.0-alpha.2 (/Users/pronvis/rust/xxx/ssd1306)
error[E0432]: unresolved import `self::embedded_graphics::Drawing`
--> src/mode/graphics.rs:267:5
|
267 | Drawing,
| ^^^^^^^ no `Drawing` in the root
Either “0111100” (0x3C) or “0111101” (0x3D), can be selected as the slave address of SSD1306. We're hard-coded for 0x3C.
ssd1306
in use (if applicable): Master branch of github.Can not determin type of display interface, I want to use display in critical sections, But not working, what can I do about it ,Thanks!
......
type DISP = ssd1306::mode::graphics::GraphicsMode<
stm32f1xx_hal::i2c::BlockingI2c<
stm32f1::stm32f103::I2C1,
(
stm32f1xx_hal::gpio::gpiob::PB8<stm32f1xx_hal::gpio::Alternate<OpenDrain>>,
stm32f1xx_hal::gpio::gpiob::PB9<stm32f1xx_hal::gpio::Alternate<OpenDrain>>,
),
>,
>;
......
let i2c = BlockingI2c::i2c1(
device.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 interface = I2CDIBuilder::new().init(i2c);
// The working code.
// let mut disp: GraphicsMode<_> = Builder::new().connect(interface).into();
// **Code Not Working.
let mut disp: DISP = Builder::new().connect(interface).into();
error[E0277]: the trait bound `ssd1306::mode::graphics::GraphicsMode<stm32f1xx_hal::i2c::BlockingI2c<stm32f1::stm32f103::I2C1, (stm32f1xx_hal::gpio::gpiob::PB8<stm32f1xx_hal::gpio::Alternate<stm32f1xx_hal::gpio::OpenDrain>>, stm32f1xx_hal::gpio::gpiob::PB9<stm32f1xx_hal::gpio::Alternate<stm32f1xx_hal::gpio::OpenDrain>>)>>: ssd1306::mode::displaymode::DisplayModeTrait<impl display_interface::WriteOnlyDataCommand, ssd1306::displaysize::DisplaySize128x64>` is not satisfied
--> src\main.rs:183:60
|
183 | let mut disp: DISP = Builder::new().connect(interface).into();
| ^^^^ the trait `ssd1306::mode::displaymode::DisplayModeTrait<impl display_interface::WriteOnlyDataCommand, ssd1306::displaysize::DisplaySize128x64>` is not implemented for `ssd1306::mode::graphics::GraphicsMode<stm32f1xx_hal::i2c::BlockingI2c<stm32f1::stm32f103::I2C1, (stm32f1xx_hal::gpio::gpiob::PB8<stm32f1xx_hal::gpio::Alternate<stm32f1xx_hal::gpio::OpenDrain>>, stm32f1xx_hal::gpio::gpiob::PB9<stm32f1xx_hal::gpio::Alternate<stm32f1xx_hal::gpio::OpenDrain>>)>>`
|
= help: the following implementations were found:
<ssd1306::mode::graphics::GraphicsMode<DI, DSIZE> as ssd1306::mode::displaymode::DisplayModeTrait<DI, DSIZE>>
Uncomment this line and add some basic docs for everything.
I've got a blog at wapl.es (repo here) which would be a good home for a release blog post. Would anybody like to write/host one? If not, I'll write it and tag @scowcron and @therealprof in a PR.
There's IMHO not to much point in testing 4 variants of ARM-M which only differ in minor aspects. Maybe throw in a std
target and a different no_std
one for good measure instead?
Looking at the documentation for the Builder module, it states I can do the following:
use ssd1306::prelude::*;
use ssd1306::Builder;
fn main() {
let mut disp: GraphicsMode<_> = Builder::new()
.connect_i2c(i2c)
.with_size(DisplaySize::Display128x32)
.into();
}
However, I get the following error:
error[E0599]: no method named `with_size` found for type `ssd1306::mode::displaymode::DisplayMode<ssd1306::mode::raw::RawMode<ssd1306::interface::i2c::I2cInterface<hal::sercom::I2CMaster3>>>` in the current scope
--> src/main.rs:49:10
|
49 | .with_size(DisplaySize::Display128x32)
| ^^^^^^^^^
The documentation should be fixed and enable testing on them so this doesn't happen again
Hey, I'm new to rust and wondering if this driver can also work with the stm32f7xx-hal
https://github.com/stm32-rs/stm32f7xx-hal/
It exposes a similar BlockingI2c
struct: https://github.com/stm32-rs/stm32f7xx-hal/blob/471ca4de7026612b28f419b10f0ec0ef77813071/src/i2c.rs but I'm struggling to glue things together.
To me it seems that the st32f7xx-hal isn't particularly stable and is a WIP. Maybe it would be better to not use it for now. Is it possible to use this library without it?
Thanks for any assistance
Even the simplest blinky example compiles down to this horrorshow on ARMv7M:
File .text Size Name
0.0% 0.6% 74B [10 Others]
2.6% 79.4% 10.1KiB ssd1306::command::Command::send
0.2% 6.9% 900B blinky::init
0.1% 3.3% 424B cortex_m_rt::reset_handler
0.1% 2.3% 300B <ssd1306::mode::graphics::GraphicsMode<DI>>::flush
0.0% 1.3% 166B __aeabi_memset4
0.0% 1.2% 152B blinky::draw_square
0.0% 1.1% 142B cortex_m_rtfm::atomic
0.0% 1.0% 134B <ssd1306::mode::graphics::GraphicsMode<DI>>::set_pixel
0.0% 0.8% 100B memset
0.0% 0.7% 96B memcpy
0.0% 0.6% 80B blinky::idle
0.0% 0.2% 30B blinky::main
0.0% 0.1% 10B __aeabi_memset
0.0% 0.1% 10B core::result::unwrap_failed
0.0% 0.1% 10B core::panicking::panic_bounds_check
0.0% 0.1% 10B SVCALL
0.0% 0.1% 10B MEM_MANAGE
0.0% 0.1% 10B DEBUG_MONITOR
0.0% 0.1% 10B PENDSV
0.0% 0.1% 10B USAGE_FAULT
3.3% 100.0% 12.7KiB .text section size, the file size is 387.9KiB
Obviously the compiler fails to see through the nature of the call to generate straight forward output.
It's not as close as bad on ARMv6M but a trivial test application easily doubles in size mostly due to that one function:
File .text Size Name
0.2% 13.5% 320B [35 Others]
0.4% 27.3% 644B ssd1306::command::Command::send
0.2% 15.6% 368B main
0.2% 14.9% 352B <ssd1306::properties::DisplayProperties<DI>>::init_column_mode
0.1% 6.1% 144B <ssd1306::properties::DisplayProperties<DI>>::set_rotation
0.1% 5.1% 120B <ssd1306::interface::i2c::I2cInterface<I2C> as ssd1306::interface::DisplayInterface>::send_data
0.1% 4.2% 100B <stm32f042_hal::i2c::I2c<stm32f042::I2C1, PINS> as embedded_hal::blocking::i2c::Write>::write
0.0% 3.6% 84B <ssd1306::mode::character::CharacterMode<DI> as core::fmt::Write>::write_str
0.0% 2.7% 64B cortex_m_rt::reset_handler
0.0% 1.8% 42B <ssd1306::interface::i2c::I2cInterface<I2C> as ssd1306::interface::DisplayInterface>::send_command
0.0% 0.8% 18B __aeabi_memcpy
0.0% 0.6% 14B __aeabi_memset
0.0% 0.5% 12B __aeabi_memclr
0.0% 0.4% 10B core::result::unwrap_failed
0.0% 0.4% 10B core::panicking::panic_bounds_check
0.0% 0.4% 10B core::slice::slice_index_order_fail
0.0% 0.4% 10B core::slice::slice_index_len_fail
0.0% 0.4% 10B PVD
0.0% 0.4% 10B TIM1_BRK_UP_TRG_COM
0.0% 0.4% 10B DMA_CH4_5_6_7
0.0% 0.4% 10B EXTI0_1
I'm just being lazy now. The driver shold expose the result of transactions to the end user.
warning: unused `core::result::Result` which must be used
--> src\interface\i2c.rs:23:9
|
23 | self.i2c.write(0x3c, &[0, cmd]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: #[warn(unused_must_use)] on by default
warning: unused `core::result::Result` which must be used
--> src\interface\i2c.rs:39:9
|
39 | self.i2c.write(0x3c, &writebuf[0..buf.len() + 1]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: unused `core::result::Result` which must be used
--> src\interface\spi.rs:29:9
|
29 | self.spi.write(&[cmd]);
| ^^^^^^^^^^^^^^^^^^^^^^^
warning: unused `core::result::Result` which must be used
--> src\interface\spi.rs:38:9
|
38 | self.spi.write(&buf);
| ^^^^^^^^^^^^^^^^^^^^^
The release of this crate depends on embedded_graphics being available on crates.io.
Currently each communication interface uses their own interface specific Error type which is passed down all the way to the caller as associated type which requires knowledge about the interface type being used and hence making that knowledge available throughout the driver. However I don't see why the caller would be interested in handling interface (and even HAL implementation) specific errors since that is a lot of work without any particular benefit. In order to get rid of the complexity I would suggest to rewrite the error to a new custom error type.
Would it be easy to extend this driver to also support the ssh1106 oled driver chip?
The ssh1106 is very similar to the ssd1306 being popular of raspberry pi breakouts.
There are several C and python driver libraries that support both:
https://github.com/durydevelop/arduino-lib-oled
https://luma-oled.readthedocs.io/en/latest/
ssd1306
in use (if applicable): 0.4.0
Several uppercase characters in terminal mode are bold, inconsistent with the rest. The characters in question are DLORW
, which use 2-3 pixel font instead of 1 pixel like the rest of the characters.
let interface = I2CDIBuilder::new().init(i2c);
let mut disp: TerminalMode<_> = Builder::new().connect(interface).into();
disp.init().unwrap();
disp.clear().unwrap();
for c in "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|".chars() {
disp.print_char(c);
}
ssd1306
in use (if applicable): 0.3.0-alpha.2Randomly gets NACK back over I2C. I didn't spend much time debugging, I was trying to get a little Rust logo to bounce back and forth between sides of the screen and noted that it would get about half way before a NACK i2c error.
I hacked this together to tinker with rust, rtfm, and this sweet little display so perhaps I just did something wrong. I did look at the i2c signaling on my scope and it looked clean.
//#![deny(warnings)]
#![no_std]
#![no_main]
use core::convert::TryInto;
use panic_semihosting as _;
use cortex_m::iprintln;
use rtfm::cyccnt::{Instant, U32Ext as _ };
use stm32f4xx_hal::{prelude::*, i2c::I2c, gpio::{Alternate, AF4, gpiob::{PB8, PB9}}, stm32};
use embedded_graphics::{prelude::*, pixelcolor::BinaryColor};
use ssd1306::{prelude::*, Builder as SSD1306Builder};
const PERIOD: u32 = 1_000_000;
#[rtfm::app(device = stm32f4::stm32f446, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)]
const APP: () = {
struct Resources {
#[init(0)]
n: u32,
itm: cortex_m::peripheral::ITM,
disp: GraphicsMode<ssd1306::interface::i2c::I2cInterface<stm32f4xx_hal::i2c::I2c<stm32f4::stm32f446::I2C1, (stm32f4xx_hal::gpio::gpiob::PB8<stm32f4xx_hal::gpio::Alternate<stm32f4xx_hal::gpio::AF4>>, stm32f4xx_hal::gpio::gpiob::PB9<stm32f4xx_hal::gpio::Alternate<stm32f4xx_hal::gpio::AF4>>)>>>,
im: Image<'static, BinaryColor>,
}
#[init(schedule = [tick])]
fn init(mut cx: init::Context) -> init::LateResources {
// Initialize (enable) the monotonic timer (CYCCNT)
cx.core.DCB.enable_trace();
// required on devices that software lock the DWT (e.g. STM32F7)
unsafe { cx.core.DWT.lar.write(0xC5ACCE55) }
cx.core.DWT.enable_cycle_counter();
let mut itm = cx.core.ITM;
// semantically, the monotonic timer is frozen at time "zero" during `init`
let now = cx.start; // the start time of the system
// Schedule `tick` to run 8e6 cycles (clock cycles) in the future
cx.schedule.tick(now + PERIOD.cycles()).unwrap();
//iprintln!(&mut itm.stim[0], "init @ {:?}", now);
let rcc = cx.device.RCC.constrain();
let clocks = rcc.cfgr.sysclk(48.mhz()).freeze();
let gpiob = cx.device.GPIOB.split();
let scl = gpiob.pb8.into_alternate_af4();
let sda = gpiob.pb9.into_alternate_af4();
let i2c = I2c::i2c1(
cx.device.I2C1,
(scl, sda),
400.khz(),
clocks
);
let mut disp: GraphicsMode<_> = SSD1306Builder::new().connect_i2c(i2c).into();
let im: Image<BinaryColor> = Image::new(include_bytes!("./rust.raw"), 64, 64);
disp.init().unwrap();
disp.flush().unwrap();
//disp.clear();
//disp.flush().unwrap();
disp.draw(im.into_iter());
disp.flush().unwrap();
init::LateResources {
itm: itm,
disp: disp,
im: im,
}
}
#[task(schedule = [tick], resources = [itm, n, im, disp])]
fn tick(cx: tick::Context) {
let now = Instant::now();
*cx.resources.n += 1;
let x_coord: i32 = (*cx.resources.n % 128).try_into().unwrap();
cx.resources.disp.draw(cx.resources.im.translate(Point::new(x_coord, 0)).into_iter());
cx.resources.disp.flush().unwrap();
cx.schedule.tick(cx.scheduled + PERIOD.cycles()).unwrap();
//iprintln!(&mut cx.resources.itm.stim[0], "tick(scheduled = {:?}, now = {:?})", cx.scheduled, now);
}
extern "C" {
fn UART4();
}
};
test case dependencies
[dependencies]
cortex-m = "0.6.1"
cortex-m-rt = "0.6.10"
cortex-m-semihosting = "0.3.3"
cortex-m-rtfm = "0.5.0"
panic-semihosting = "0.5.3"
embedded-graphics = "0.6.0-alpha.2"
ssd1306 = "0.3.0-alpha.2"
stm32f4xx-hal = {version = "0.6.0", features = ["stm32f446"]}
stm32f4 = {version = "0.8.0", features = [ "stm32f446", "rt" ]}
It should be possible to set the rotation to one of 4 settings (0º, 90º, 180º, 270º). The builder should have an option, something like with_rotation()
which accepts an enum.
We should have some kind of CI process!
At minimum, right now I think it should:
rustfmt
and fail if any code changes to keep code clean and consistentcargo test
to make sure the examples build**because there aren't any tests at the moment...
Anything else?
This crate is purely a driver, so should not require embeddeed_graphics as a hard dependency. It should be feature gated instead for those that do want to use it.
Also, where should examples that used embedded_graphics go? Should they stay in this crate or be moved to e_g?
ssd1306
in use: 0.4I'm experimenting with sending images over serial to the board. I'm using the .draw
method like in the white noise example,
sending the serial RX buffer to the display. I noticed that the bits of the bytes are written top to bottom, but then the bytes are in horizontal order. So basically for each byte I get 8 vertical pixels until the end of the row, and so on. Which is different from what I imagined :) I understand this is the the default page mode? How can I change it to Horizontal, or in other words, how do I use init_with_mode
or change_mode
?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.