Browse Source

Made an example for the SSD1306 OLED display

master
Weird Constructor 1 year ago
parent
commit
7383747983
  1. 19
      ssd1306_oled_display/.cargo/config
  2. 16
      ssd1306_oled_display/.gitignore
  3. 93
      ssd1306_oled_display/Cargo.toml
  4. 31
      ssd1306_oled_display/build.rs
  5. 13
      ssd1306_oled_display/memory.x
  6. 150
      ssd1306_oled_display/src/main.rs

19
ssd1306_oled_display/.cargo/config

@ -0,0 +1,19 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
runner = "probe-run-rp --chip RP2040"
rustflags = [
"-C", "linker=flip-link",
"-C", "link-arg=--nmagic",
"-C", "link-arg=-Tlink.x",
"-C", "link-arg=-Tdefmt.x",
# Code-size optimizations.
# trap unreachable can save a lot of space, but requires nightly compiler.
# uncomment the next line if you wish to enable it
# "-Z", "trap-unreachable=no",
"-C", "inline-threshold=5",
"-C", "no-vectorize-loops",
]
[build]
target = "thumbv6m-none-eabi"

16
ssd1306_oled_display/.gitignore vendored

@ -0,0 +1,16 @@
**/*.rs.bk
.#*
.gdb_history
Cargo.lock
target/
# editor files
.vscode/*
!.vscode/*.md
!.vscode/*.svd
!.vscode/launch.json
!.vscode/tasks.json
!.vscode/extensions.json
!.vscode/settings.json
*.history

93
ssd1306_oled_display/Cargo.toml

@ -0,0 +1,93 @@
[package]
authors = ["Weird Constructor"]
edition = "2018"
readme = "README.md"
name = "rp2040-ws2812-led-cube"
version = "0.1.0"
resolver = "2"
[dependencies]
cortex-m = "0.7.3"
cortex-m-rt = "0.7.0"
embedded-hal = { version = "0.2.5", features=["unproven"] }
embedded-time = "0.12.0"
defmt = "0.2.0"
defmt-rtt = "0.2.0"
panic-probe = { version = "0.2.0", features = ["print-defmt"] }
#rp2040-hal = { git = "https://github.com/rp-rs/rp-hal", branch="main", features=["rt"] }
rp2040-boot2 = { git = "https://github.com/rp-rs/rp2040-boot2-rs", branch="main" }
pico = { git = "https://github.com/rp-rs/rp-hal.git", branch="main" }
ssd1306 = "0.7.0"
embedded-graphics = "0.7.1"
nb = "1.0"
[features]
default = [
"defmt-default",
]
defmt-default = []
defmt-trace = []
defmt-debug = []
defmt-info = []
defmt-warn = []
defmt-error = []
# cargo build/run
[profile.dev]
codegen-units = 1
debug = 2
debug-assertions = true
incremental = false
opt-level = 3
overflow-checks = true
# cargo build/run --release
[profile.release]
codegen-units = 1
debug = 2
debug-assertions = false
incremental = false
lto = 'fat'
opt-level = 3
overflow-checks = false
# do not optimize proc-macro crates = faster builds from scratch
[profile.dev.build-override]
codegen-units = 8
debug = false
debug-assertions = false
opt-level = 0
overflow-checks = false
[profile.release.build-override]
codegen-units = 8
debug = false
debug-assertions = false
opt-level = 0
overflow-checks = false
# cargo test
[profile.test]
codegen-units = 1
debug = 2
debug-assertions = true
incremental = false
opt-level = 3
overflow-checks = true
# cargo test --release
[profile.bench]
codegen-units = 1
debug = 2
debug-assertions = false
incremental = false
lto = 'fat'
opt-level = 3
[dependencies.num-traits]
version = "0.2.14"
default-features = false

31
ssd1306_oled_display/build.rs

@ -0,0 +1,31 @@
//! This build script copies the `memory.x` file from the crate root into
//! a directory where the linker can always find it at build time.
//! For many projects this is optional, as the linker always searches the
//! project root directory -- wherever `Cargo.toml` is. However, if you
//! are using a workspace or have a more complicated build setup, this
//! build script becomes required. Additionally, by requesting that
//! Cargo re-run the build script whenever `memory.x` is changed,
//! updating `memory.x` ensures a rebuild of the application with the
//! new memory settings.
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() {
// Put `memory.x` in our output directory and ensure it's
// on the linker search path.
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("memory.x"))
.unwrap()
.write_all(include_bytes!("memory.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
// By default, Cargo will re-run a build script whenever
// any file in the project changes. By specifying `memory.x`
// here, we ensure the build script is only re-run when
// `memory.x` is changed.
println!("cargo:rerun-if-changed=memory.x");
}

13
ssd1306_oled_display/memory.x

@ -0,0 +1,13 @@
MEMORY {
BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100
FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100
RAM : ORIGIN = 0x20000000, LENGTH = 256K
}
SECTIONS {
/* ### Boot loader */
.boot2 ORIGIN(BOOT2) :
{
KEEP(*(.boot2));
} > BOOT2
} INSERT BEFORE .text;

150
ssd1306_oled_display/src/main.rs

@ -0,0 +1,150 @@
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use defmt::*;
use defmt_rtt as _;
use embedded_time::duration::*;
use embedded_time::rate::Extensions;
use embedded_hal::timer::CountDown;
use panic_probe as _;
use embedded_hal::adc::OneShot;
use embedded_hal::blocking::i2c::Write;
use pico::hal::{
pac,
clocks::{Clock, init_clocks_and_plls},
sio::Sio,
adc::Adc,
gpio,
i2c::I2C,
watchdog::Watchdog,
timer::Timer,
};
use embedded_graphics::{
mono_font::{ascii::FONT_9X18_BOLD, MonoTextStyleBuilder},
pixelcolor::BinaryColor,
prelude::*,
text::{Baseline, Text},
};
use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306};
use pico::hal::pio::PIOExt;
#[link_section = ".boot2"]
#[used]
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080;
struct FmtBuf {
buf: [u8; 64],
ptr: usize,
}
impl FmtBuf {
fn new() -> Self {
Self { buf: [0; 64], ptr: 0 }
}
fn as_str(&self) -> &str {
unsafe {
core::str::from_utf8_unchecked(&self.buf[0..self.ptr])
}
}
}
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(())
}
}
#[entry]
fn main() -> ! {
info!("Program start");
let mut pac = pac::Peripherals::take().unwrap();
let _core = pac::CorePeripherals::take().unwrap();
let mut watchdog = Watchdog::new(pac.WATCHDOG);
let sio = Sio::new(pac.SIO);
// External high-speed crystal on the pico board is 12Mhz
let external_xtal_freq_hz = 12_000_000u32;
let clocks =
init_clocks_and_plls(
external_xtal_freq_hz,
pac.XOSC,
pac.CLOCKS,
pac.PLL_SYS,
pac.PLL_USB,
&mut pac.RESETS,
&mut watchdog)
.ok()
.unwrap();
let pins = 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::<gpio::FunctionI2C>();
let scl_pin = pins.gpio17.into_mode::<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 mut i2c = I2C::i2c0(
pac.I2C0,
sda_pin,
scl_pin,
400.kHz(),
&mut pac.RESETS,
clocks.peripheral_clock,
);
let interface = I2CDisplayInterface::new(i2c);
let mut display =
Ssd1306::new(interface, DisplaySize128x64, DisplayRotation::Rotate0)
.into_buffered_graphics_mode();
display.init().unwrap();
let text_style = MonoTextStyleBuilder::new()
.font(&FONT_9X18_BOLD)
.text_color(BinaryColor::On)
.build();
let timer = Timer::new(pac.TIMER, &mut pac.RESETS);
let mut delay = timer.count_down();
let mut count = 0;
loop {
let mut buf = FmtBuf::new();
core::fmt::write(&mut buf, format_args!("counter: {}", count));
count += 1;
display.clear();
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();
delay.start(500.milliseconds());
let _ = nb::block!(delay.wait());
}
}
Loading…
Cancel
Save