6 changed files with 479 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,95 @@
|
||||
[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" |
||||
|
||||
embedded-sdmmc = { git = "https://github.com/rust-embedded-community/embedded-sdmmc-rs.git" } |
||||
|
||||
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" } |
||||
|
||||
smart-leds = "0.3.0" |
||||
ws2812-pio = { path = "ws2812-pio-rs" } |
||||
|
||||
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,305 @@
|
||||
#![no_std] |
||||
#![no_main] |
||||
|
||||
use cortex_m_rt::entry; |
||||
use defmt::*; |
||||
use defmt_rtt as _; |
||||
use embedded_time::duration::*; |
||||
use embedded_time::rate::*; |
||||
use embedded_hal::timer::CountDown; |
||||
use embedded_hal::digital::v2::OutputPin; |
||||
use panic_probe as _; |
||||
|
||||
use embedded_sdmmc; |
||||
|
||||
use rp2040_hal as rphal; |
||||
|
||||
use embedded_hal::adc::OneShot; |
||||
use pico::hal::{ |
||||
pac, |
||||
clocks::{Clock, init_clocks_and_plls}, |
||||
sio::Sio, |
||||
adc::Adc, |
||||
watchdog::Watchdog, |
||||
timer::Timer, |
||||
}; |
||||
|
||||
|
||||
use pico::hal::pio::PIOExt; |
||||
//use rp2040_hal::pio::PIOExt;
|
||||
use smart_leds::{brightness, SmartLedsWrite, RGB8}; |
||||
use ws2812_pio::Ws2812; |
||||
|
||||
#[link_section = ".boot2"] |
||||
#[used] |
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; |
||||
|
||||
const LEN : usize = 302; |
||||
|
||||
struct MovingAvg { |
||||
buf: [u16; 30], |
||||
ptr: u16, |
||||
} |
||||
|
||||
impl MovingAvg { |
||||
pub fn new() -> Self { |
||||
Self { |
||||
buf: [0xFFFF; 30], |
||||
ptr: 0, |
||||
} |
||||
} |
||||
|
||||
pub fn next(&mut self, input: u16) -> u16 { |
||||
self.buf[self.ptr as usize] = input; |
||||
self.ptr = ((self.ptr as usize + 1) % self.buf.len()) as u16; |
||||
let mut sum : usize = 0; |
||||
for b in &self.buf { |
||||
sum += *b as usize; |
||||
} |
||||
(sum / self.buf.len()) as u16 |
||||
} |
||||
} |
||||
|
||||
pub fn hsv2rgb(hue: f32, sat: f32, val: f32) -> (f32, f32, f32) { |
||||
let c = val * sat; |
||||
let v = (hue / 60.0) % 2.0 - 1.0; |
||||
let v = if v < 0.0 { -v } else { v }; |
||||
let x = c * (1.0 - v); |
||||
let m = val - c; |
||||
let (r_, g_, b_) = |
||||
if hue >= 0.0 && hue < 60.0 { |
||||
(c, x, 0.0) |
||||
} else if hue >= 60.0 && hue < 120.0 { |
||||
(x, c, 0.0) |
||||
} else if hue >= 120.0 && hue < 180.0 { |
||||
(0.0, c, x) |
||||
} else if hue >= 180.0 && hue < 240.0 { |
||||
(0.0, x, c) |
||||
} else if hue >= 240.0 && hue < 300.0 { |
||||
(x, 0.0, c) |
||||
} else { // if hue >= 300.0 && hue < 360.0 {
|
||||
(c, 0.0, x) |
||||
}; |
||||
// println!("in: h={}, s={}, v={}, r:{}, g:{}, b: {}", hue, sat, val,
|
||||
// (r_ + m) * 255.0,
|
||||
// (g_ + m) * 255.0,
|
||||
// (b_ + m) * 255.0);
|
||||
(r_ + m, g_ + m, b_ + m) |
||||
} |
||||
|
||||
pub fn hsv2rgb_u8(h: f32, s: f32, v: f32) -> (u8, u8, u8) { |
||||
let r = hsv2rgb(h, s, v); |
||||
( |
||||
(r.0 * 255.0) as u8, |
||||
(r.1 * 255.0) as u8, |
||||
(r.2 * 255.0) as u8 |
||||
) |
||||
} |
||||
|
||||
pub struct DummyTimesource { |
||||
} |
||||
|
||||
impl DummyTimesource { |
||||
pub fn new() -> Self { |
||||
Self { |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl embedded_sdmmc::TimeSource for DummyTimesource { |
||||
fn get_timestamp(&self) -> embedded_sdmmc::Timestamp { |
||||
embedded_sdmmc::Timestamp { |
||||
year_since_1970: 0, |
||||
zero_indexed_month: 0, |
||||
zero_indexed_day: 0, |
||||
hours: 0, |
||||
minutes: 0, |
||||
seconds: 0, |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[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, |
||||
); |
||||
|
||||
let timer = Timer::new(pac.TIMER, &mut pac.RESETS); |
||||
let mut delay = timer.count_down(); |
||||
|
||||
let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS); |
||||
let mut ws = Ws2812::new( |
||||
pins.gpio16.into_mode(), |
||||
&mut pio, |
||||
sm0, |
||||
clocks.peripheral_clock.freq(), |
||||
timer.count_down(), |
||||
); |
||||
|
||||
let _spi_sclk = pins.gpio2.into_mode::<rphal::gpio::FunctionSpi>(); |
||||
let _spi_mosi = pins.gpio3.into_mode::<rphal::gpio::FunctionSpi>(); |
||||
let _spi_miso = pins.gpio4.into_mode::<rphal::gpio::FunctionSpi>(); |
||||
let spi_cs = pins.gpio5.into_push_pull_output(); |
||||
let spi = rphal::spi::Spi::<_, _, 8>::new(pac.SPI0); |
||||
|
||||
// Exchange the uninitialised SPI driver for an initialised one
|
||||
let mut spi = spi.init( |
||||
&mut pac.RESETS, |
||||
clocks.peripheral_clock.freq(), |
||||
16_000_000u32.Hz(), |
||||
&embedded_hal::spi::MODE_0, |
||||
); |
||||
|
||||
let mut sdspi = embedded_sdmmc::SdMmcSpi::new(spi, spi_cs); |
||||
let block = sdspi.acquire().unwrap(); |
||||
info!("Aquire BlockDevice ok!"); |
||||
|
||||
let mut cont = |
||||
embedded_sdmmc::Controller::new(block, DummyTimesource::new()); |
||||
info!("Init SD card..."); |
||||
|
||||
info!("OK!\nCard size..."); |
||||
match cont.device().card_size_bytes() { |
||||
Ok(size) => info!("card size={}", size), |
||||
Err(e) => info!("Err: 1"), |
||||
} |
||||
|
||||
info!("Volume 0..."); |
||||
match cont.get_volume(embedded_sdmmc::VolumeIdx(0)) { |
||||
Ok(mut v) => { |
||||
info!("VOl!"); |
||||
match cont.open_root_dir(&v) { |
||||
Ok(dir) => { |
||||
info!("Root!"); |
||||
cont.iterate_dir(&v, &dir, |ent| { |
||||
use core::fmt; |
||||
info!("/{}.{}", |
||||
core::str::from_utf8(ent.name.base_name()).unwrap(), |
||||
core::str::from_utf8(ent.name.extension()).unwrap()); |
||||
}); |
||||
|
||||
let mut file = |
||||
cont.open_file_in_dir( |
||||
&mut v, &dir, "O.TST", |
||||
embedded_sdmmc::filesystem::Mode::ReadOnly).unwrap(); |
||||
let mut buf = [0u8; 32]; |
||||
let nr = cont.read(&mut v, &mut file, &mut buf).unwrap(); |
||||
info!("READ {} bytes: {}", nr, buf); |
||||
cont.close_file(&v, file); |
||||
|
||||
let mut file = |
||||
cont.open_file_in_dir( |
||||
&mut v, &dir, "O.TST", |
||||
// embedded_sdmmc::filesystem::Mode::ReadWriteCreateOrAppend).unwrap();
|
||||
embedded_sdmmc::filesystem::Mode::ReadWriteCreateOrTruncate).unwrap(); |
||||
cont.write(&mut v, &mut file, b"foobar123\n").unwrap(); |
||||
cont.close_file(&v, file); |
||||
}, |
||||
Err(e) => { |
||||
info!("Err: Root {}", defmt::Debug2Format(&e)); |
||||
}, |
||||
} |
||||
}, |
||||
Err(e) => info!("Err: {}", defmt::Debug2Format(&e)), |
||||
} |
||||
|
||||
cont.free(); |
||||
|
||||
|
||||
let mut adc = Adc::new(pac.ADC, &mut pac.RESETS); |
||||
let mut adc_pin_0 = pins.gpio26.into_floating_input(); |
||||
|
||||
|
||||
let mut n: u8 = 0; |
||||
let mut leds_off : [RGB8; LEN] = [(0,0,0).into(); LEN]; |
||||
let mut leds : [RGB8; LEN] = [(0,0,0).into(); LEN]; |
||||
|
||||
for i in 0..LEN { |
||||
leds[i] = (255, 128, 64).into(); |
||||
} |
||||
// leds[149] = (255, 255, 255).into();
|
||||
// leds[70] = (255, 0, 255).into();
|
||||
// leds[100] = (255, 0, 0).into();
|
||||
|
||||
let colors : [RGB8; 3] = [ |
||||
hsv2rgb_u8(0.0, 1.0, 1.0).into(), |
||||
(0, 255, 0).into(), |
||||
(0, 0, 255).into(), |
||||
]; |
||||
|
||||
let amperes = 8.0; |
||||
let all_on_amp = (LEN as f32 * 3.0 * 60.0) / 1000.0; |
||||
|
||||
let vbrightness = ((amperes / all_on_amp) * 255.0) as u8; |
||||
info!("brightness={} / 255", vbrightness); |
||||
ws.write(brightness(leds_off.iter().copied(), vbrightness)).unwrap(); |
||||
|
||||
// for i in 0..LEN {
|
||||
// leds[i] = colors[1];
|
||||
// }
|
||||
// info!("LEDS={}", leds.len());
|
||||
|
||||
let mut cnt = 0; |
||||
|
||||
let mut j = 0; |
||||
|
||||
let mut led_pin = pins.led.into_push_pull_output(); |
||||
|
||||
loop { |
||||
cnt += 1; |
||||
if cnt > 400 { |
||||
cnt = 0; |
||||
} |
||||
|
||||
let clr = hsv2rgb_u8((cnt as f32 / 400.0) * 360.0, 0.0, 1.0); |
||||
// info!("[{}] clr : {}", cnt, clr);
|
||||
for i in 0..LEN { |
||||
leds[i] = clr.into(); |
||||
} |
||||
|
||||
ws.write(brightness(leds.iter().copied(), vbrightness)).unwrap(); |
||||
delay.start(16.milliseconds()); |
||||
let _ = nb::block!(delay.wait()); |
||||
|
||||
info!("on!"); |
||||
led_pin.set_high().unwrap(); |
||||
delay.start(500.milliseconds()); |
||||
let _ = nb::block!(delay.wait()); |
||||
info!("off!"); |
||||
led_pin.set_low().unwrap(); |
||||
delay.start(500.milliseconds()); |
||||
let _ = nb::block!(delay.wait()); |
||||
} |
||||
} |
||||
|
||||
pub fn rot(slice: &mut [RGB8]) { |
||||
let first = slice[0]; |
||||
for i in 1..slice.len() { |
||||
slice[i - 1] = slice[i]; |
||||
} |
||||
slice[slice.len() - 1] = first; |
||||
} |
Loading…
Reference in new issue