Add an example for using the SSD1306 I2C display driver crate with the Raspberry Pi Pico

This commit is contained in:
Weird Constructor 2022-01-12 05:07:33 +01:00
parent 0cb171ffc8
commit 2f418bbf8f
2 changed files with 187 additions and 0 deletions

View file

@ -31,6 +31,8 @@ heapless = "0.7.9"
embedded-sdmmc = { git = "https://github.com/rust-embedded-community/embedded-sdmmc-rs.git" } embedded-sdmmc = { git = "https://github.com/rust-embedded-community/embedded-sdmmc-rs.git" }
smart-leds = "0.3.0" smart-leds = "0.3.0"
ws2812-pio = { git = "https://github.com/ithinuel/ws2812-pio-rs", rev = "4f0d81e594ea9934f9c4c38ed9824ad0cce4ebb5" } ws2812-pio = { git = "https://github.com/ithinuel/ws2812-pio-rs", rev = "4f0d81e594ea9934f9c4c38ed9824ad0cce4ebb5" }
ssd1306 = "0.7.0"
embedded-graphics = "0.7.1"
defmt = "0.2.0" defmt = "0.2.0"
defmt-rtt = "0.2.0" defmt-rtt = "0.2.0"

View file

@ -0,0 +1,185 @@
//! # Pico (monochome) OLED Display with SSD1306 Driver Example
//!
//! See the `Cargo.toml` file for Copyright and licence details.
#![no_std]
#![no_main]
// The macro for our start-up function
use cortex_m_rt::entry;
// Time handling traits:
use embedded_time::duration::*;
use embedded_time::rate::Extensions;
// CountDown timer for the counter on the display:
use embedded_hal::timer::CountDown;
// Ensure we halt the program on panic (if we don't mention this crate it won't
// be linked)
use panic_halt as _;
// A shorter alias for the Peripheral Access Crate, which provides low-level
// register access
use rp_pico::hal::pac;
// A shorter alias for the Hardware Abstraction Layer, which provides
// higher-level drivers.
use rp_pico::hal;
// For in the graphics drawing utilities like the font
// and the drawing routines:
use embedded_graphics::{
mono_font::{ascii::FONT_9X18_BOLD, MonoTextStyleBuilder},
pixelcolor::BinaryColor,
prelude::*,
text::{Baseline, Text},
};
// The display driver:
use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306};
/// Entry point to our bare-metal application.
///
/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
/// as soon as all global variables are initialised.
///
/// The function configures the RP2040 peripherals,
/// gets a handle on the I2C peripheral,
/// initializes the SSD1306 driver, initializes the text builder
/// and then draws some text on the display.
#[entry]
fn main() -> ! {
// Grab our singleton objects
let mut pac = pac::Peripherals::take().unwrap();
// Set up the watchdog driver - needed by the clock setup code
let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
// Configure the clocks
//
// The default is to generate a 125 MHz system clock
let clocks =
hal::clocks::init_clocks_and_plls(
rp_pico::XOSC_CRYSTAL_FREQ,
pac.XOSC,
pac.CLOCKS,
pac.PLL_SYS,
pac.PLL_USB,
&mut pac.RESETS,
&mut watchdog)
.ok()
.unwrap();
// The single-cycle I/O block controls our GPIO pins
let sio = hal::Sio::new(pac.SIO);
// Set the pins up according to their function on this particular board
let pins = rp_pico::Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS,
);
// Configure two pins as being I²C, not GPIO
let sda_pin = pins.gpio16.into_mode::<hal::gpio::FunctionI2C>();
let scl_pin = pins.gpio17.into_mode::<hal::gpio::FunctionI2C>();
// Create the I²C drive, using the two pre-configured pins. This will fail
// at compile time if the pins are in the wrong mode, or if this I²C
// peripheral isn't available on these pins!
let i2c = hal::I2C::i2c0(
pac.I2C0,
sda_pin,
scl_pin,
400.kHz(),
&mut pac.RESETS,
clocks.peripheral_clock,
);
// Create the I²C display interface:
let interface = I2CDisplayInterface::new(i2c);
// Create a driver instance and initialize:
let mut display =
Ssd1306::new(interface, DisplaySize128x64, DisplayRotation::Rotate0)
.into_buffered_graphics_mode();
display.init().unwrap();
// Create a text style for drawing the font:
let text_style = MonoTextStyleBuilder::new()
.font(&FONT_9X18_BOLD)
.text_color(BinaryColor::On)
.build();
let timer = hal::Timer::new(pac.TIMER, &mut pac.RESETS);
let mut delay = timer.count_down();
let mut count = 0;
let mut buf = FmtBuf::new();
loop {
buf.reset();
// Format some text into a static buffer:
core::fmt::write(&mut buf, format_args!("counter: {}", count)).unwrap();
count += 1;
// Empty the display:
display.clear();
// Draw 3 lines of text:
Text::with_baseline("Hello world!", Point::zero(), text_style, Baseline::Top)
.draw(&mut display)
.unwrap();
Text::with_baseline("Hello Rust!", Point::new(0, 16), text_style, Baseline::Top)
.draw(&mut display)
.unwrap();
Text::with_baseline(buf.as_str(), Point::new(0, 32), text_style, Baseline::Top)
.draw(&mut display)
.unwrap();
display.flush().unwrap();
// Wait a bit:
delay.start(500.milliseconds());
let _ = nb::block!(delay.wait());
}
}
/// This is a very simple buffer to pre format a short line of text.
struct FmtBuf {
buf: [u8; 64],
ptr: usize,
}
impl FmtBuf {
fn new() -> Self {
Self {
buf: [0; 64],
ptr: 0
}
}
fn reset(&mut self) {
self.ptr = 0;
}
fn as_str(&self) -> &str {
core::str::from_utf8(&self.buf[0..self.ptr]).unwrap()
}
}
impl core::fmt::Write for FmtBuf {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
let len = s.len();
self.buf[self.ptr..(self.ptr + len)].copy_from_slice(s.as_bytes());
self.ptr += len;
Ok(())
}
}
// End of file