-
Notifications
You must be signed in to change notification settings - Fork 82
/
pico_ws2812_led.rs
219 lines (185 loc) · 6.74 KB
/
pico_ws2812_led.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
//! # Pico WS2812 RGB LED Example
//!
//! Drives 3 WS2812 LEDs connected directly to the Raspberry Pi Pico.
//! This assumes you drive the Raspberry Pi Pico via USB power, so that VBUS
//! delivers the 5V and at least enough amperes to drive the LEDs.
//!
//! For a more large scale and longer strips you should use an extra power
//! supply for the LED strip (or know what you are doing ;-) ).
//!
//! The example also comes with an utility function to calculate the colors
//! from HSV color space. It also limits the brightness a bit to save a
//! few milliamperes - be careful if you increase the strip length you will
//! quickly get into power consumption of multiple amperes.
//!
//! The example assumes you connected the data input to pin 6 of the
//! Raspberry Pi Pico, which is GPIO4 of the rp2040. Here is a circuit
//! diagram that shows the assumed setup:
//!
//! ```text
//! _______________ /----------------------\
//! |+5V /---\ +5V|----/ _|USB|_ |
//! |DO <-|LED|<- DI|-\ |1 R 40|-VBUS-/
//! |GND \---/ GND|--+---\ |2 P 39|
//! """"""""""""""" | \-GND-|3 38|
//! | |4 P 37|
//! | |5 I 36|
//! \------GP4-|6 C |
//! |7 O |
//! | |
//! .........
//! |20 21|
//! """""""
//! Symbols:
//! - (+) crossing lines, not connected
//! - (o) connected lines
//! ```
//!
//! See the `Cargo.toml` file for Copyright and license details.
#![no_std]
#![no_main]
// The macro for our start-up function
use rp_pico::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 rp_pico::hal::prelude::*;
// A shorter alias for the Peripheral Access Crate, which provides low-level
// register access
use rp_pico::hal::pac;
// Import the Timer for Ws2812:
use rp_pico::hal::timer::Timer;
// A shorter alias for the Hardware Abstraction Layer, which provides
// higher-level drivers.
use rp_pico::hal;
// PIOExt for the split() method that is needed to bring
// PIO0 into useable form for Ws2812:
use rp_pico::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(
rp_pico::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 = rp_pico::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().to_Hz());
// 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, &clocks);
// 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);
// Instanciate a Ws2812 LED strip:
let mut ws = Ws2812::new(
// Use pin 6 on the Raspberry Pi Pico (which is GPIO4 of the rp2040 chip)
// for the LED data output:
pins.gpio4.into_function(),
&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,
)
}