Driving the First WS2816 LED With AVR Assembly

by BoboacaC in Teachers > K

42 Views, 0 Favorites, 0 Comments

Driving the First WS2816 LED With AVR Assembly

led.jpg

WS2812/WS2816 LEDs are individually addressable RGB LEDs. Each LED is controlled via a single data line using a specific timing protocol.

This example shows how to light the first LED in full white using direct AVR assembly, without any library. It’s a great way to understand how the protocol works at the cycle-accurate level.

Supplies

1.jpg

Arduino Nano/Uno (ATmega328p)

1 WS2812 or WS2816 LED

Jumper wires

Optional: 470Ω resistor on the data line for signal stability

Understanding the WS2812/WS2816 Protocol

5.jpg


  1. T0H = high time for a “0”
  2. T1H = high time for a “1”
  3. Reset = low > 50 µs to latch data into LED
⚠️ Timing is critical: if HIGH/LOW durations are off, the LED will misinterpret the data.

The Delay Loop Code


1️⃣ The delay loop code


ld_loop:
nop ; 1 CPU cycle (~62.5 ns at 16 MHz)
nop ; 1 CPU cycle (~62.5 ns)
dec r16 ; decrement r16 (~1 cycle)
brne ld_loop ; branch if r16 ≠ 0 (~2 cycles if taken, 1 if not)
ret ; return (~4 cycles)
  1. CPU frequency: 16 MHz → 1 cycle = 62.5 ns
  2. nop = 1 cycle
  3. dec = 1 cycle
  4. brne = 2 cycles when branch is taken, 1 cycle when not
  5. ret = 4 cycles (only at the end of the loop)


How It Creates HIGH/LOW Time

Example for HIGH: r16 = 14

  1. Each iteration of the loop while r16 ≠ 0 lasts approximately:

nop + nop + dec + brne (taken) = 1 + 1 + 1 + 2 = 5 cycles
  1. Number of iterations = 14
  2. Total time = 14 × 5 cycles × 62.5 ns ≈ 4.375 µs

⚠️ In practice, the exact timing is empirically tuned, so using 14 for HIGH and 12 for LOW gives ≈0.7 µs HIGH and ≈0.6 µs LOW at 16 MHz.

WS2816 Bit Timing Principle


WS2816 reads a bit based on the HIGH pulse duration:

  1. ~0.7 µs HIGH → bit “1”
  2. ~0.35 µs HIGH → bit “0”

The LOW period completes the bit and ensures a total bit period of ~1.25 µs, which is required by WS2816.


Example: Sending a “1” Bit


| Action | Pin | Delay (r16) | Approx. Time |
| -------- | --- | ----------- | ------------ |
| Set HIGH | PB0 | 14 | 0.7 µs |
| Set LOW | PB0 | 12 | 0.6 µs |


  1. Total time per bit ≈ 1.3 µs, which is within WS2816’s spec (1.05 – 1.45 µs)

Repeating this 24 times sends one LED in full white.

The Avr Asm Code

Driving WS281x LEDs directly with AVR Assembly arduino uno addressing strip led



; first LED on ws2816 high white

.org 0
rjmp init ; reset vector -> jump to program start

.org 0x68 ; (arbitrary start address depending on flash map)
init:
sbi 4,0 ; set bit in I/O register: DDRB0 = 1 (set PB0 as output)

loop:

ldi r17,24 ; number of bits to send = 24 (1 LED x 24 bits)
sus:
; --- BIT "1" routine ---
; total time per bit = 1.25 µs ± 250 ns (valid range 1.05 – 1.45 µs)

sbi 5,0 ; set pin high = start of bit
ldi r16,14 ; delay for HIGH period (~700 ns, range 500–850 ns)
rcall ld_loop ; wait loop for HIGH

cbi 5,0 ; clear pin low = end of HIGH
ldi r16,12 ; delay for LOW period (~600 ns, range 450–1000 ns)
rcall ld_loop ; wait loop for LOW

dec r17 ; decrement bit counter
brne sus ; if not zero, send next bit

; --- Reset frame ---
; needed to latch data into WS2816 LEDs
rcall ld_loop ; here used as reset delay (>50 µs)
rjmp loop ; repeat the process forever

; ----------------------------------------------------
; Subroutine: ld_loop
; Provides a simple delay
; Each iteration ≈ 50 ns (at 16 MHz)
; Input: r16 = loop count
; ----------------------------------------------------
ld_loop:
nop ; no operation (~62.5 ns each at 16 MHz)
nop
dec r16
brne ld_loop ; repeat until r16 = 0
ret


Visit https://costycnc.github.io/avr-compiler-js, paste the existing code, then compile and upload it to the Arduino Nano. WebSerial is used, so no additional libraries or IDE are required.