diff --git a/Cargo.toml b/Cargo.toml index 8ec105d..51942b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members = [ "boards/adafruit-trinkey-qt2040", "boards/pimoroni-pico-explorer", "boards/pimoroni-pico-lipo-16mb", + "boards/pimoroni-plasma-2040", "boards/pimoroni-tiny2040", "boards/rp-pico", "boards/solderparty-rp2040-stamp", diff --git a/boards/pimoroni-plasma-2040/.gitignore b/boards/pimoroni-plasma-2040/.gitignore new file mode 100644 index 0000000..ff47c2d --- /dev/null +++ b/boards/pimoroni-plasma-2040/.gitignore @@ -0,0 +1,11 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk diff --git a/boards/pimoroni-plasma-2040/Cargo.toml b/boards/pimoroni-plasma-2040/Cargo.toml new file mode 100644 index 0000000..a962e6e --- /dev/null +++ b/boards/pimoroni-plasma-2040/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "pimoroni-plasma-2040" +version = "0.1.0" +authors = ["Jordan Williams ", "The rp-rs Developers"] +edition = "2018" +homepage = "https://github.com/rp-rs/rp-hal/tree/main/boards/pimoroni-plasma-2040" +description = "Board Support Package for the Pimoroni Plasma 2040" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rp-rs/rp-hal.git" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cortex-m = "0.7.2" +rp2040-boot2 = { version = "0.2.0", optional = true } +rp2040-hal = { path = "../../rp2040-hal", version = "0.4.0"} +cortex-m-rt = { version = "0.7", optional = true } +embedded-time = "0.12.0" + +[dev-dependencies] +panic-halt= "0.2.0" +embedded-hal ="0.2.5" +smart-leds = "0.3.0" +ws2812-pio = { git = "https://github.com/ithinuel/ws2812-pio-rs", rev = "fd6b6604d65a66242b52ccf7f24a95ca325991dd" } + +defmt = "0.2.0" +defmt-rtt = "0.2.0" + +[features] +default = ["boot2", "rt"] +boot2 = ["rp2040-boot2"] +rt = ["cortex-m-rt","rp2040-hal/rt"] diff --git a/boards/pimoroni-plasma-2040/README.md b/boards/pimoroni-plasma-2040/README.md new file mode 100644 index 0000000..6ab31f2 --- /dev/null +++ b/boards/pimoroni-plasma-2040/README.md @@ -0,0 +1,100 @@ +# [pimoroni-plasma-2040] - Board Support for the [Pimoroni Plasma 2040] + +You should include this crate if you are writing code that you want to run on +a [Pimoroni Plasma 2040] - Swathe everything in rainbows with this all-in-one, USB-C powered controller for WS2812/Neopixel and APA102/Dotstar addressable LED strip. + +This crate includes the [rp2040-hal], but also configures each pin of the +RP2040 chip according to how it is connected up on the Pimoroni Plasma 2040. + +[Pimoroni Plasma 2040]: https://shop.pimoroni.com/products/plasma-2040 +[pimoroni-plasma-2040]: https://github.com/rp-rs/rp-hal/tree/main/boards/pimoroni-plasma-2040 +[rp2040-hal]: https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal +[Raspberry Silicon RP2040]: https://www.raspberrypi.org/products/rp2040/ + +## Using + +To use this crate, your `Cargo.toml` file should contain: + +```toml +pimoroni-plasma-2040 = "0.1.0" +``` + +In your program, you will need to call `pimoroni_plasma_2040::Pins::new` to create +a new `Pins` structure. This will set up all the GPIOs for any on-board +devices. See the [examples](./examples) folder for more details. + +## Examples + +### General Instructions + +To compile an example, clone the _rp-hal_ repository and run: + +```console +rp-hal/boards/pimoroni-plasma-2040 $ cargo build --release --example +``` + +You will get an ELF file called +`./target/thumbv6m-none-eabi/release/examples/`, where the `target` +folder is located at the top of the _rp-hal_ repository checkout. Normally +you would also need to specify `--target=thumbv6m-none-eabi` but when +building examples from this git repository, that is set as the default. + +If you want to convert the ELF file to a UF2 and automatically copy it to the +USB drive exported by the RP2040 bootloader, simply boot your board into +bootloader mode and run: + +```console +rp-hal/boards/pimoroni-plasma-2040 $ cargo run --release --example +``` + +If you get an error about not being able to find `elf2uf2-rs`, try: + +```console +$ cargo install elf2uf2-rs +``` + +then try repeating the `cargo run` command above. + +### [pimoroni_plasma_2040_blinky](./examples/pimoroni_plasma_2040_blinky.rs) + +Flashes the Plasma 2040's three on-board LEDs in sequence. + +### [pimoroni_plasma_2040_ws2812_led](./examples/pimoroni_plasma_2040_ws2812_led.rs) + +Drives 3 WS2812 LEDs connected directly to the Pimoroni Plasma 2040 via its onboard terminal block. + +## Contributing + +Contributions are what make the open source community such an amazing place to +be learn, inspire, and create. Any contributions you make are **greatly +appreciated**. + +The steps are: + +1. Fork the Project by clicking the 'Fork' button at the top of the page. +2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) +3. Make some changes to the code or documentation. +4. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) +5. Push to the Feature Branch (`git push origin feature/AmazingFeature`) +6. Create a [New Pull Request](https://github.com/rp-rs/rp-hal/pulls) +7. An admin will review the Pull Request and discuss any changes that may be required. +8. Once everyone is happy, the Pull Request can be merged by an admin, and your work is part of our project! + +## Code of Conduct + +Contribution to this crate is organized under the terms of the [Rust Code of +Conduct][CoC], and the maintainer of this crate, the [rp-rs team], promises +to intervene to uphold that code of conduct. + +[CoC]: CODE_OF_CONDUCT.md +[rp-rs team]: https://github.com/orgs/rp-rs/teams/rp-rs + +## License + +The contents of this repository are dual-licensed under the _MIT OR Apache +2.0_ License. That means you can chose either the MIT license or the +Apache-2.0 license when you re-use this code. See `MIT` or `APACHE2.0` for more +information on each specific license. + +Any submissions to this project (e.g. as Pull Requests) must be made available +under these terms. diff --git a/boards/pimoroni-plasma-2040/examples/pimoroni_plasma_2040_blinky.rs b/boards/pimoroni-plasma-2040/examples/pimoroni_plasma_2040_blinky.rs new file mode 100644 index 0000000..9082f76 --- /dev/null +++ b/boards/pimoroni-plasma-2040/examples/pimoroni_plasma_2040_blinky.rs @@ -0,0 +1,70 @@ +//! Blinks the 3 colour LEDs on a Pimoroni Plasma 2040 in sequence +#![no_std] +#![no_main] + +use cortex_m_rt::entry; +use defmt::*; +use defmt_rtt as _; +use embedded_hal::digital::v2::OutputPin; +use embedded_time::fixed_point::FixedPoint; +use panic_halt as _; + +use pimoroni_plasma_2040 as bsp; + +use bsp::hal::{ + clocks::{init_clocks_and_plls, Clock}, + pac, + sio::Sio, + watchdog::Watchdog, +}; + +#[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); + + let clocks = init_clocks_and_plls( + bsp::XOSC_CRYSTAL_FREQ, + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut watchdog, + ) + .ok() + .unwrap(); + + let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().integer()); + + let pins = bsp::Pins::new( + pac.IO_BANK0, + pac.PADS_BANK0, + sio.gpio_bank0, + &mut pac.RESETS, + ); + + let mut led_green = pins.led_green.into_push_pull_output(); + let mut led_red = pins.led_red.into_push_pull_output(); + let mut led_blue = pins.led_blue.into_push_pull_output(); + led_green.set_high().unwrap(); + led_red.set_high().unwrap(); + led_blue.set_high().unwrap(); + + loop { + led_green.set_low().unwrap(); + delay.delay_ms(500); + led_green.set_high().unwrap(); + led_blue.set_low().unwrap(); + delay.delay_ms(500); + led_blue.set_high().unwrap(); + led_red.set_low().unwrap(); + delay.delay_ms(500); + led_red.set_high().unwrap(); + } +} + +// End of file diff --git a/boards/pimoroni-plasma-2040/examples/pimoroni_plasma_2040_ws2812_led.rs b/boards/pimoroni-plasma-2040/examples/pimoroni_plasma_2040_ws2812_led.rs new file mode 100644 index 0000000..fc63880 --- /dev/null +++ b/boards/pimoroni-plasma-2040/examples/pimoroni_plasma_2040_ws2812_led.rs @@ -0,0 +1,186 @@ +//! # Pimoroni Plasma 2040 WS2812 RGB LED Example +//! +//! Drives 3 WS2812 LEDs connected directly to the Pimoroni Plasma 2040 via its terminal block. +//! Derived from the [pico_ws2812_led](../../rp-pico/examples/pico_ws2812_led.rs) example for the Raspberry Pi Pico. + +#![no_std] +#![no_main] + +// The macro for our start-up function +use cortex_m_rt::entry; + +// Ensure we halt the program on panic (if we don't mention this crate it won't +// be linked) +use panic_halt as _; + +// Pull in any important traits +use pimoroni_plasma_2040::hal::prelude::*; + +// Embed the `Hz` function/trait: +use embedded_time::rate::*; + +// A shorter alias for the Peripheral Access Crate, which provides low-level +// register access +use pimoroni_plasma_2040::hal::pac; + +// Import the Timer for Ws2812: +use pimoroni_plasma_2040::hal::timer::Timer; + +// A shorter alias for the Hardware Abstraction Layer, which provides +// higher-level drivers. +use pimoroni_plasma_2040::hal; + +// PIOExt for the split() method that is needed to bring +// PIO0 into useable form for Ws2812: +use pimoroni_plasma_2040::hal::pio::PIOExt; + +// Import useful traits to handle the ws2812 LEDs: +use smart_leds::{brightness, SmartLedsWrite, RGB8}; + +// Import the actual crate to handle the Ws2812 protocol: +use ws2812_pio::Ws2812; + +// Currently 3 consecutive LEDs are driven by this example +// to keep the power draw compatible with USB: +const STRIP_LEN: usize = 3; + +#[entry] +fn main() -> ! { + // Grab our singleton objects + let mut pac = pac::Peripherals::take().unwrap(); + let core = pac::CorePeripherals::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( + pimoroni_plasma_2040::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 = pimoroni_plasma_2040::Pins::new( + pac.IO_BANK0, + pac.PADS_BANK0, + sio.gpio_bank0, + &mut pac.RESETS, + ); + + // Setup a delay for the LED blink signals: + let mut frame_delay = + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().integer()); + + // Import the `sin` function for a smooth hue animation from the + // Pico rp2040 ROM: + let sin = hal::rom_data::float_funcs::fsin::ptr(); + + // Create a count down timer for the Ws2812 instance: + let timer = Timer::new(pac.TIMER, &mut pac.RESETS); + + // Split the PIO state machine 0 into individual objects, so that + // Ws2812 can use it: + let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS); + + // Instantiate a Ws2812 LED strip: + let mut ws = Ws2812::new( + pins.data.into_mode(), + &mut pio, + sm0, + clocks.peripheral_clock.freq(), + timer.count_down(), + ); + + let mut leds: [RGB8; STRIP_LEN] = [(0, 0, 0).into(); STRIP_LEN]; + let mut t = 0.0; + + // Bring down the overall brightness of the strip to not blow + // the USB power supply: every LED draws ~60mA, RGB means 3 LEDs per + // ws2812 LED, for 3 LEDs that would be: 3 * 3 * 60mA, which is + // already 540mA for just 3 white LEDs! + let strip_brightness = 64u8; // Limit brightness to 64/256 + + // Slow down timer by this factor (0.1 will result in 10 seconds): + let animation_speed = 0.1; + + loop { + for (i, led) in leds.iter_mut().enumerate() { + // An offset to give 3 consecutive LEDs a different color: + let hue_offs = match i % 3 { + 1 => 0.25, + 2 => 0.5, + _ => 0.0, + }; + + let sin_11 = sin((t + hue_offs) * 2.0 * core::f32::consts::PI); + // Bring -1..1 sine range to 0..1 range: + let sin_01 = (sin_11 + 1.0) * 0.5; + + let hue = 360.0 * sin_01; + let sat = 1.0; + let val = 1.0; + + let rgb = hsv2rgb_u8(hue, sat, val); + *led = rgb.into(); + } + + // Here the magic happens and the `leds` buffer is written to the + // ws2812 LEDs: + ws.write(brightness(leds.iter().copied(), strip_brightness)) + .unwrap(); + + // Wait a bit until calculating the next frame: + frame_delay.delay_ms(16); // ~60 FPS + + // Increase the time counter variable and make sure it + // stays inbetween 0.0 to 1.0 range: + t += (16.0 / 1000.0) * animation_speed; + while t > 1.0 { + t -= 1.0; + } + } +} + +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 < 60.0 { + (c, x, 0.0) + } else if hue < 120.0 { + (x, c, 0.0) + } else if hue < 180.0 { + (0.0, c, x) + } else if hue < 240.0 { + (0.0, x, c) + } else if hue < 300.0 { + (x, 0.0, c) + } else { + (c, 0.0, x) + }; + (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, + ) +} diff --git a/boards/pimoroni-plasma-2040/src/lib.rs b/boards/pimoroni-plasma-2040/src/lib.rs new file mode 100644 index 0000000..fa7c020 --- /dev/null +++ b/boards/pimoroni-plasma-2040/src/lib.rs @@ -0,0 +1,75 @@ +#![no_std] + +pub extern crate rp2040_hal as hal; + +#[cfg(feature = "rt")] +extern crate cortex_m_rt; + +#[cfg(feature = "rt")] +pub use cortex_m_rt::entry; + +/// The linker will place this boot block at the start of our program image. We +/// need this to help the ROM bootloader get our code up and running. +#[cfg(feature = "boot2")] +#[link_section = ".boot2"] +#[no_mangle] +#[used] +pub static BOOT2_FIRMWARE: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +pub use hal::pac; + +hal::bsp_pins!( + /// GPIO 0 is connected to I2C0_SDA + Gpio0 { name: i2c0_sda }, + /// GPIO 1 is connected to I2C0_SCL + Gpio1 { name: i2c0_scl }, + /// GPIO 2 is connected to I2C1_SDA + Gpio2 { name: i2c1_sda }, + /// GPIO 3 is connected to I2C1_SCL + Gpio3 { name: i2c1_scl }, + Gpio4 { name: gpio4 }, + Gpio5 { name: gpio5 }, + /// GPIO 12 is connected to button A, active low + Gpio12 { name: button_a }, + /// GPIO 13 is connected to button B, active low + Gpio13 { name: button_b }, + /// GPIO 14 is connected to CLK for APA102 only + Gpio14 { name: clk }, + /// GPIO 15 is connected to DAT for Apa102 and Ws2812 + Gpio15 { name: data }, + /// GPIO 16 is red LED, active low + Gpio16 { name: led_red }, + /// GPIO 17 is green LED, active low + Gpio17 { name: led_green }, + /// GPIO 18 is blue LED, active low + Gpio18 { name: led_blue }, + /// GPIO 19 is I2C_INT + Gpio19 { name: i2c_int }, + /// GPIO 20 is I2C_SDA + Gpio20 { + name: i2c_sda, + aliases: { FunctionI2C: Sda } + }, + /// GPIO 21 is I2C_SCL + Gpio21 { + name: i2c_scl, + aliases: { FunctionI2C: Scl } + }, + /// GPIO 23 is connected to the USER_SW, the BOOT button, active low + Gpio23 { name: user_sw }, + /// GPIO 26 is connected to ADC0 + Gpio26 { name: adc0 }, + /// GPIO 27 is connected to ADC1 + Gpio27 { name: adc1 }, + /// GPIO 28 is connected to ADC2 + Gpio28 { name: adc2 }, + /// GPIO 29 is connected to ADC3 which is used for low side current sensing + Gpio29 { + name: current_sense, + }, +); + +pub const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; + +pub const ADC_GAIN: u32 = 50; +pub const SHUNT_RESISTOR: f32 = 0.015; diff --git a/boards/pimoroni-tiny2040/README.md b/boards/pimoroni-tiny2040/README.md index d5f075d..4d8a2d6 100644 --- a/boards/pimoroni-tiny2040/README.md +++ b/boards/pimoroni-tiny2040/README.md @@ -7,7 +7,7 @@ This crate includes the [rp2040-hal], but also configures each pin of the RP2040 chip according to how it is connected up on the Tiny2040. [Pimoroni Tiny2040]: https://shop.pimoroni.com/products/tiny-2040 -[rp-pico]: https://github.com/rp-rs/rp-hal/tree/main/boards/rp-pico +[pimoroni-tiny2040]: https://github.com/rp-rs/rp-hal/tree/main/boards/pimoroni-tiny2040 [rp2040-hal]: https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal [Raspberry Silicon RP2040]: https://www.raspberrypi.org/products/rp2040/ diff --git a/boards/pimoroni-tiny2040/examples/tiny2040_blinky.rs b/boards/pimoroni-tiny2040/examples/tiny2040_blinky.rs index bb7cd73..759d0ea 100644 --- a/boards/pimoroni-tiny2040/examples/tiny2040_blinky.rs +++ b/boards/pimoroni-tiny2040/examples/tiny2040_blinky.rs @@ -26,7 +26,6 @@ fn main() -> ! { let mut watchdog = Watchdog::new(pac.WATCHDOG); let sio = Sio::new(pac.SIO); - // External high-speed crystal on the pico board is 12Mhz let clocks = init_clocks_and_plls( bsp::XOSC_CRYSTAL_FREQ, pac.XOSC, diff --git a/boards/rp-pico/examples/pico_ws2812_led.rs b/boards/rp-pico/examples/pico_ws2812_led.rs index 430b75e..b2c1f1f 100644 --- a/boards/rp-pico/examples/pico_ws2812_led.rs +++ b/boards/rp-pico/examples/pico_ws2812_led.rs @@ -119,7 +119,7 @@ fn main() -> ! { // Import the `sin` function for a smooth hue animation from the // Pico rp2040 ROM: - let sin = rp_pico::hal::rom_data::float_funcs::fsin::ptr(); + let sin = hal::rom_data::float_funcs::fsin::ptr(); // Create a count down timer for the Ws2812 instance: let timer = Timer::new(pac.TIMER, &mut pac.RESETS);