Skip to main content

Crate rustyfarian_avr_ws2812

Crate rustyfarian_avr_ws2812 

Source
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

AspectWs2812Spi (SPI prerendered)[Ws2812BitBang] (cycle-counted asm)
Cargo featurealways availablebitbang (opt-in)
Statusworks on tolerant stripsrecommended; hardware-validated
Hardwareany AVR with SPI peripheralATmega328P @ 16 MHz
Pinthe SPI MOSI pinany pin on PORTB / PORTC / PORTD
WS2812 timingT0H = 500 ns, T1H = 1500 ns (relies on chip tolerance)T0H = 250 ns, T1H = 812 ns (in WS2812B spec)
Interruptscaller wraps write in avr_device::interrupt::freewrapped internally — timing is mandatory
Other peripheralsSPI bus is owned by the driveronly 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();
});

[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-bang RainbowEffect, the recommended demo (src/main.rs)
  • just flash-avr-bitbang-demo — bit-bang PulseEffect red 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§

Ws2812Spi
WS2812 LED driver using SPI prerendered encoding (no_std, embedded-hal 1.0).

Enums§

SpiError
Errors that can occur during WS2812 SPI operations.

Functions§

spi_buffer_size
Returns the total SPI buffer size for num_leds LEDs (data + reset bytes).