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 images
TerminalMode, 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
The last step is to initialise and clear the display with
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.
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
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
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
but this mode provides a lighter alternative to a full, buffered
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!