6 changed files with 322 additions and 0 deletions
@ -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" |
@ -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 |
@ -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 |
@ -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"); |
||||
} |
@ -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; |
@ -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…
Reference in new issue