Expand description
WS2812 (NeoPixel) LED driver for AVR with two backends.
Both backends share the same &[RGB8]-based public API and run every
ferriswheel effect unchanged.
All animation logic stays in ferriswheel / bunting; these drivers are
thin hardware wrappers.
§Choosing a Backend
| Aspect | Ws2812Spi (SPI prerendered) | [Ws2812BitBang] (cycle-counted asm) |
|---|---|---|
| Cargo feature | always available | bitbang (opt-in) |
| Status | works on tolerant strips | recommended; hardware-validated |
| Hardware | any AVR with SPI peripheral | ATmega328P @ 16 MHz |
| Pin | the SPI MOSI pin | any pin on PORTB / PORTC / PORTD |
| WS2812 timing | T0H = 500 ns, T1H = 1500 ns (relies on chip tolerance) | T0H = 250 ns, T1H = 812 ns (in WS2812B spec) |
| Interrupts | caller wraps write in avr_device::interrupt::free | wrapped internally — timing is mandatory |
| Other peripherals | SPI bus is owned by the driver | only the chosen GPIO pin is owned |
ADR 007 records the empirical evidence: a strip that works correctly on the ESP32 RMT drivers produced stable white-ish output and chain leakage on the SPI prerendered backend across both genuine and clone Arduino Nanos. The bit-bang backend renders correctly on the same hardware.
Use the SPI backend if your strip is known to tolerate the encoding’s
out-of-spec T1H, or if you need other peripherals to keep operating during the LED
write. Use the bit-bang backend everywhere else.
§SPI Prerendered Backend
Ws2812Spi drives WS2812/NeoPixel LEDs over SPI using the prerendered encoding
from bunting — 4 SPI bits per WS2812 bit,
12 SPI bytes per LED. The encoding is byte-for-byte compatible with
ws2812-spi v0.5.1’s prerendered module.
§Buffer sizing
Const-generic buffer [u8; N] where N = spi_data_len(num_leds) + SPI_RESET_BYTES_2MHZ.
Use spi_buffer_size to compute N at compile time:
use rustyfarian_avr_ws2812::spi_buffer_size;
const N: usize = spi_buffer_size(8); // 8-LED ring§SPI clock configuration
The SPI peripheral must be configured at 2 MHz before the first call to
Ws2812Spi::write. On a 16 MHz ATmega328P, use a clock prescaler of ÷8.
§Interrupt safety
The SPI backend is not interrupt-safe by itself; the caller wraps each
write in a critical section:
avr_device::interrupt::free(|_| {
ws.write(&colors).unwrap();
});§Example
use rustyfarian_avr_ws2812::{Ws2812Spi, spi_buffer_size};
use rgb::RGB8;
const NUM_LEDS: usize = 8;
const N: usize = spi_buffer_size(NUM_LEDS);
let mut ws: Ws2812Spi<_, N> = Ws2812Spi::new(spi_bus);
let colors = [RGB8::new(255, 0, 0); NUM_LEDS];
avr_device::interrupt::free(|_| {
ws.write(&colors).unwrap();
});§Bit-Bang Backend (recommended)
[Ws2812BitBang] uses cycle-counted inline asm! to drive any GPIO pin in low
I/O space (PORTB / PORTC / PORTD on ATmega328P) at WS2812-spec timing.
The write method wraps the asm loop in avr_device::interrupt::free internally —
the caller does not need to add a critical section.
Enable the bitbang feature in Cargo.toml:
rustyfarian-avr-ws2812 = { version = "0.1", features = ["bitbang"] }§Pin selection
The driver is generic over the port-register address ([ports::PORTB], [ports::PORTC],
[ports::PORTD]) and the pin bit number (0–7). Both are compile-time constants so
the asm uses single-instruction sbi/cbi operations.
§Interrupt safety
Cycle-accurate WS2812 timing requires interrupts to stay disabled for the full frame
window (≈ 30 µs per LED). The driver does this internally; user code remains free of
interrupt::free boilerplate. The millis() timer and serial UART will lose ticks
during the write window — standard tradeoff documented in docs/avr-getting-started.md.
§Example
use rustyfarian_avr_ws2812::{ports, Ws2812BitBang};
use rgb::RGB8;
let pin = pins.d11.into_output();
let mut driver: Ws2812BitBang<_, { ports::PORTB }, 3> = Ws2812BitBang::new(pin);
let colors = [RGB8::new(8, 0, 0); 10];
driver.write(&colors).ok(); // no `interrupt::free` needed — handled inside§Runnable Examples
Standalone, flashable Arduino Nano examples live at
examples/avr-nano-rainbow/
in the workspace root (separate AVR toolchain, target, and arduino-hal git dependency).
See docs/avr-getting-started.md
for wiring, toolchain setup, and just recipes:
just flash-avr-example— bit-bangRainbowEffect, the recommended demo (src/main.rs)just flash-avr-bitbang-demo— bit-bangPulseEffectred breath (bin/bitbang_demo)just flash-avr-spi-rainbow— SPI prerendered comparison, diagnostic only (bin/spi_rainbow)just flash-avr-bitbang-spike— frozen low-level reference, no driver crate (bin/bitbang_spike)
Structs§
- Ws2812
Spi - WS2812 LED driver using SPI prerendered encoding (
no_std,embedded-hal1.0).
Enums§
- SpiError
- Errors that can occur during WS2812 SPI operations.
Functions§
- spi_
buffer_ size - Returns the total SPI buffer size for
num_ledsLEDs (data + reset bytes).