Add the Pimoroni Plasma 2040 board (#337)

* Add the Pimoroni Plasma 2040 board

This PR adds the board support package and a simple example.
The example just blinks the on-board RGB LED.

An example should be added for using the board to control an LED strip.
This should probably use smart-leds with the associated PIO driver.

An example or functionality should be added for the current sensor.

* Rename LED data line from dat to data to match schematic

* Add an example for driving WS2812 LEDs

This is pretty much a copy-paste of the awesome pico_ws2812_led example.

* Remove reference in README to rp-pico

* Remove reference to pico board clock speed

I have removed this in the Plasma 2040 repository and where I copied it from, tiny2040_blinky.

* Remove redundant namespace

* Add self-reference in README to the current board's GitHub README

Fix the erroneous link in the pimoroni-tiny2040 README from which I copied.
This commit is contained in:
Jordan Williams 2022-05-11 20:17:11 -05:00 committed by GitHub
parent 38692dfcb9
commit cf86e08749
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 477 additions and 3 deletions

View file

@ -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",

11
boards/pimoroni-plasma-2040/.gitignore vendored Normal file
View file

@ -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

View file

@ -0,0 +1,32 @@
[package]
name = "pimoroni-plasma-2040"
version = "0.1.0"
authors = ["Jordan Williams <jordan@jwillikers.com>", "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"]

View file

@ -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 <name>
```
You will get an ELF file called
`./target/thumbv6m-none-eabi/release/examples/<name>`, 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 <name>
```
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.

View file

@ -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

View file

@ -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,
)
}

View file

@ -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;

View file

@ -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/

View file

@ -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,

View file

@ -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);