Announcing the SSD1306 OLED display driver
As part of the weekly driver initiative, myself (@jamwaffles), @therealprof and @scowcron have been working on a Rust driver for the common as mud SSD1306-based OLED display modules. This little chip is found in the majority of inexpensive OLED display modules found on Ebay and AliExpress. It supports either an SPI or I2C interface, both of which the driver supports.
The driver currently supports two modes:
GraphicsMode
, a buffered mode for drawing text, shapes, pixels and imagesTerminalMode
, a bufferless mode to draw text to the display
The easiest way to get started with either mode is to use the Builder. Here's an example that connects over I2C and draws some shapes in GraphicsMode for the STM32F103:
extern crate cortex_m;
extern crate cortex_m_rt as rt;
extern crate panic_semihosting;
extern crate stm32f1xx_hal as hal;
use ;
use *;
use ;
use ;
use *;
use stm32;
use *;
use Builder;
!
!
First, we need to set up the I2C interface. This is pretty standard HAL boilerplate:
use ;
use *;
use ;
use ;
use *;
use stm32;
use *;
use Builder;
// ...
let dp = take.unwrap;
let mut flash = dp.FLASH.constrain;
let mut rcc = dp.RCC.constrain;
let clocks = rcc.cfgr.freeze;
let mut afio = dp.AFIO.constrain;
let mut gpiob = dp.GPIOB.split;
let scl = gpiob.pb8.into_alternate_open_drain;
let sda = gpiob.pb9.into_alternate_open_drain;
let i2c = i2c1;
You'll need to change this code to work with the device you're using. I'm running the I2C1 interface at 400KHz in the example above.
Next, let's create a display instance, initialise it and clear the display:
use *;
use Builder;
// ...
let mut disp: = new.connect_i2c.into;
disp.init.unwrap;
disp.flush.unwrap;
This is where we use the
Builder
pattern to construct
a driver that will talk to the display over I2C. By default, the builder returns a
RawMode
driver which isn't
very useful on it's own unless you just want to draw raw pixels. To be able to do more useful
things, we'll call .into()
which will convert the driver into a richer mode defined by the type of
disp
. In this case, we want to use GraphicsMode<_>
to be able to use all the goodness from the
embedded_graphics crate.
The last step is to initialise and clear the display with disp.init()
and disp.flush()
(graphics
mode has an empty display buffer by default).
Now we can draw some stuff to the display:
// Triangle
disp.draw;
disp.draw;
disp.draw;
// Square
disp.draw;
// Circle
disp.draw;
disp.flush.unwrap;
This will draw a triangle, square and circle in roughly the middle of the display.
Bufferless
Because GraphicsMode
is buffered, you need to call disp.flush()
to write the buffer to the
display. It also consumes 1KiB of RAM to hold the buffer which is quite a lot of memory for a µC!
Another supported mode is
TerminalMode
,
implemented by @therealprof. TerminalMode
is an unbuffered
character output mode that renders only text. It draws from left to right and top to bottom,
restarting in the top left corner. It uses a built-in 7x7 font on a fixed 8x8 pixel grid.
Aside from writing raw strings to the display, this mode also supports the core::fmt::Write
trait
so you can call any of the usual Rust output and formatting methods/macros on it. While useful, be
aware that doing so will add a lot of bloat to your binary.
Here's a small "Hello World!" example for the STM32F103, using the SSD1306 via I2C:
extern crate cortex_m;
extern crate cortex_m_rt as rt;
extern crate panic_semihosting;
extern crate stm32f1xx_hal as hal;
use Write;
use ExceptionFrame;
use ;
use ;
use *;
use stm32;
use *;
use Builder;
!
!
You can find the full example here.
There's currently no positioning or scrolling support beyond calling
clear()
,
but this mode provides a lighter alternative to a full, buffered GraphicsMode
.
Onwards
Please give the driver a try! There are a bunch of examples in the repo which should be a good starting point. They contain device-specific initialisation code, but the driver code itself is agnostic, so they should provide a good starting point. The driver should be pretty usable on most systems, but there's a plethora of hardware out there, some combinations of which might not work. Please open an issue if you find a bug or something missing from the crate. The crate is written in a way that makes it relatively easy to add new modes, so if you've got a great idea for one, please submit a PR!