Skip to content

Instantly share code, notes, and snippets.

@ahadjeres
Last active February 6, 2025 19:37
Show Gist options
  • Save ahadjeres/087f2cd7e65a2e43b176fb384c361571 to your computer and use it in GitHub Desktop.
Save ahadjeres/087f2cd7e65a2e43b176fb384c361571 to your computer and use it in GitHub Desktop.
Example Code for the NRF52xx Using Embassy and the neoPixel WS2812
#![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