diff --git a/rtic_ws2812_pio/src/main.rs b/rtic_ws2812_pio/src/main.rs index fdd553e..546b382 100644 --- a/rtic_ws2812_pio/src/main.rs +++ b/rtic_ws2812_pio/src/main.rs @@ -246,6 +246,7 @@ mod app { )] fn timer_irq(mut c: timer_irq::Context) { let clr : RGB8 = (255, 0, 255).into(); + info!("Sched!"); c.shared.ws.lock(|ws| ws.write([clr].iter().copied()).unwrap()); diff --git a/ws2812_bl_ctrl/Cargo.toml b/ws2812_bl_ctrl/Cargo.toml new file mode 100644 index 0000000..7d671f3 --- /dev/null +++ b/ws2812_bl_ctrl/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" } +rp-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 diff --git a/ws2812_bl_ctrl/build.rs b/ws2812_bl_ctrl/build.rs new file mode 100644 index 0000000..d534cc3 --- /dev/null +++ b/ws2812_bl_ctrl/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"); +} diff --git a/ws2812_bl_ctrl/memory.x b/ws2812_bl_ctrl/memory.x new file mode 100644 index 0000000..0596611 --- /dev/null +++ b/ws2812_bl_ctrl/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; \ No newline at end of file diff --git a/ws2812_bl_ctrl/res/breadboard_setup_hc_05.jpg b/ws2812_bl_ctrl/res/breadboard_setup_hc_05.jpg new file mode 100755 index 0000000..3e30961 Binary files /dev/null and b/ws2812_bl_ctrl/res/breadboard_setup_hc_05.jpg differ diff --git a/ws2812_bl_ctrl/src/main.rs b/ws2812_bl_ctrl/src/main.rs new file mode 100644 index 0000000..0ae3e75 --- /dev/null +++ b/ws2812_bl_ctrl/src/main.rs @@ -0,0 +1,258 @@ +#![no_std] +#![no_main] + +use cortex_m_rt::entry; +use defmt::*; +use defmt_rtt as _; +use embedded_time::duration::*; +use embedded_hal::timer::CountDown; +use panic_probe as _; + +use embedded_hal::adc::OneShot; +use rp_pico::hal::{ + pac, + clocks::{Clock, init_clocks_and_plls}, + sio::Sio, + adc::Adc, + watchdog::Watchdog, + timer::Timer, +}; + +use rp_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 = 100; + +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 + ) +} + +#[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 = rp_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.gpio18.into_mode(), + &mut pio, + sm0, + clocks.peripheral_clock.freq(), + timer.count_down(), + ); + + let uart_pins = ( + // UART TX (characters sent from RP2040) on pin 1 (GPIO0) + pins.gpio16.into_mode::(), + // UART RX (characters received by RP2040) on pin 2 (GPIO1) + pins.gpio17.into_mode::(), + ); + + let mut uart = rp_pico::hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS) + .enable( + rp_pico::hal::uart::common_configs::_9600_8_N_1, + clocks.peripheral_clock.into(), + ) + .unwrap(); + +// 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]; +// +// 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; +// +// 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(); +// } +// +// } + + let max_ma = 500.0f32; + let mut leds : [RGB8; LEN] = [(0,0,0).into(); LEN]; + + let real_len = 9; + loop { + if uart.uart_is_readable() { + let mut buf = [0u8; 100]; + if let Ok(len) = uart.read_raw(&mut buf) { + let s = core::str::from_utf8(&buf[0..len]).unwrap(); + info!("Recv: [{}]", s); + } + + if buf[0] == b'r' { + for l in leds.iter_mut() { + *l = hsv2rgb_u8(0.0, 1.0, 1.0).into(); + } + } else if buf[0] == b'y' { + for l in leds.iter_mut() { + *l = hsv2rgb_u8(60.0, 1.0, 1.0).into(); + } + } else if buf[0] == b'w' { + for l in leds.iter_mut() { + *l = hsv2rgb_u8(0.0, 0.0, 1.0).into(); + } + } else { + for l in leds.iter_mut() { + *l = hsv2rgb_u8(90.0, 1.0, 1.0).into(); + } + } + + let vbrightness = limit_to_milliamp(&leds[0..real_len], max_ma); + info!("Bright={}", vbrightness); + + ws.write(brightness(leds.iter().copied(), vbrightness)).unwrap(); + } + delay.start(16.milliseconds()); + let _ = nb::block!(delay.wait()); + } +} + +/// Returns the brightness from 0 to 255. +pub fn limit_to_milliamp(leds: &[RGB8], max_milliamp: f32) -> u8 { + let mut sum_ma : f32 = 0.0; + + for l in leds.iter() { + sum_ma += (l.r as f32 / 255.0) * 60.0; + sum_ma += (l.g as f32 / 255.0) * 60.0; + sum_ma += (l.b as f32 / 255.0) * 60.0; + } + + let factor = max_milliamp / sum_ma; + if factor > 1.0 { 255u8 } + else { (255.0 * factor) as u8 } +} + +//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; +//} diff --git a/ws2812_bl_ctrl/ws2812-pio-rs b/ws2812_bl_ctrl/ws2812-pio-rs new file mode 160000 index 0000000..37ade6d --- /dev/null +++ b/ws2812_bl_ctrl/ws2812-pio-rs @@ -0,0 +1 @@ +Subproject commit 37ade6dd937c62c2b9c0b0fe212c291fe5f33317