Last active
February 6, 2025 19:37
-
-
Save ahadjeres/087f2cd7e65a2e43b176fb384c361571 to your computer and use it in GitHub Desktop.
Example Code for the NRF52xx Using Embassy and the neoPixel WS2812
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![no_std] | |
#![no_main] | |
use defmt::*; | |
use defmt_rtt as _; // For printing logs to RTT | |
use panic_probe as _; // For panic handling | |
use embassy_executor::Spawner; | |
use embassy_nrf::pwm::{ | |
Config as PwmConfig, Prescaler, SequenceLoad, SequencePwm, SequenceConfig, | |
SingleSequencer, SingleSequenceMode, | |
}; | |
use embassy_time::{Duration, Timer}; | |
// ---------------------------------------------------------------------- | |
// Adjust these for your setup | |
// ---------------------------------------------------------------------- | |
// How many WS2812 LEDs do you have? | |
const NUM_LEDS: usize = 8; | |
// Each LED has 24 data bits => total bits for all = 24 * NUM_LEDS | |
const TOTAL_BITS: usize = 24 * NUM_LEDS; | |
// "0" => ~7/20 high => ~0.44µs | |
// "1" => ~13/20 high => ~0.81µs | |
// | |
// The high bit (0x8000) in each u16 sets the PWM polarity | |
// (active high first, then the remainder of the cycle is low). | |
const T0H: u16 = 0x8000 | 7; | |
const T1H: u16 = 0x8000 | 13; | |
// We'll store each bit's 16-bit value in a static buffer. | |
static mut WS2812_BUF: [u16; TOTAL_BITS] = [0; TOTAL_BITS]; | |
// Simple color struct for R, G, B | |
#[derive(Clone, Copy)] | |
struct Color { | |
r: u8, | |
g: u8, | |
b: u8, | |
} | |
// ---------------------------------------------------------------------- | |
// Fill the WS2812_BUF array with T0H/T1H for each bit of each LED's G,R,B | |
// ---------------------------------------------------------------------- | |
fn ws2812_fill(colors: &[Color]) { | |
// WS2812 wants bits in the order G(8), R(8), B(8) per LED | |
let mut bit_idx = 0; | |
for &c in colors { | |
let (g, r, b) = (c.g, c.r, c.b); | |
// G | |
for i in 0..8 { | |
let bit = (g & (1 << (7 - i))) != 0; | |
unsafe { WS2812_BUF[bit_idx] = if bit { T1H } else { T0H }; } | |
bit_idx += 1; | |
} | |
// R | |
for i in 0..8 { | |
let bit = (r & (1 << (7 - i))) != 0; | |
unsafe { WS2812_BUF[bit_idx] = if bit { T1H } else { T0H }; } | |
bit_idx += 1; | |
} | |
// B | |
for i in 0..8 { | |
let bit = (b & (1 << (7 - i))) != 0; | |
unsafe { WS2812_BUF[bit_idx] = if bit { T1H } else { T0H }; } | |
bit_idx += 1; | |
} | |
} | |
} | |
// ---------------------------------------------------------------------- | |
// Write out the static buffer exactly once using the PWM | |
// ---------------------------------------------------------------------- | |
async fn ws2812_show( | |
pwm: &mut SequencePwm<'static, embassy_nrf::peripherals::PWM0>, | |
) { | |
// Create a sequence config without an extra end_delay | |
// (We skip any explicit "reset" time.) | |
let seq_cfg = SequenceConfig::default(); | |
let seq = unsafe { SingleSequencer::new(pwm, &WS2812_BUF, seq_cfg) }; | |
if let Err(e) = seq.start(SingleSequenceMode::Times(1)) { | |
defmt::warn!("PWM sequence start error: {:?}", e); | |
} | |
Timer::after(Duration::from_millis(50)).await; | |
} | |
// ---------------------------------------------------------------------- | |
// The main entry: set up PWM, then animate the LEDs | |
// ---------------------------------------------------------------------- | |
#[embassy_executor::main] | |
async fn main(_spawner: Spawner) { | |
let p = embassy_nrf::init(Default::default()); | |
// 1) Configure PWM @ 16 MHz, with max_duty = 20 => 1.25µs total bit time | |
let mut config = PwmConfig::default(); | |
config.sequence_load = SequenceLoad::Common; // single channel | |
config.prescaler = Prescaler::Div1; // 16 MHz | |
config.max_duty = 20; // 1.25 µs | |
let mut pwm = SequencePwm::new_1ch(p.PWM0, p.P0_17, config).unwrap(); | |
info!("Starting WS2812 (no extra reset) demo..."); | |
// 2) We'll have an array of 8 "Color"s | |
let mut leds = [Color {r:0, g:0, b:0}; NUM_LEDS]; | |
// A simple "breathing" effect on the Blue channel. | |
// We'll fade brightness from 0..=200, then back down repeatedly. | |
let mut brightness: u8 = 0; | |
let mut direction_up = true; | |
loop { | |
// Update all LED colors | |
for c in leds.iter_mut() { | |
// For example, keep R=0, G=0, vary B | |
c.r = brightness; | |
c.g = 0; | |
c.b = brightness; | |
} | |
// Fill the static array, then start the PWM | |
ws2812_fill(&leds); | |
ws2812_show(&mut pwm).await; | |
// Increment or decrement brightness | |
if direction_up { | |
brightness = brightness.saturating_add(5); | |
if brightness >= 200 { | |
direction_up = false; | |
} | |
} else { | |
brightness = brightness.saturating_sub(5); | |
if brightness == 0 { | |
direction_up = true; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment