After the previous breakthrough which allows me to program the ATTiny10 and a small delay caused by a wonderful sunny Ibiza holiday, it’s time to get the WS2812 2020 leds working on this wonderful small microcontroller.
To check if I was able to control pin 4 of the ATTiny10, I hooked up my Rigol oscilloscope.
So far, so good. I was able to send some pulses. Unfortunately, the WS2812 leds have a pretty strict timing protocol. Since the leds are controlled over one wire, and don’t have a clock pin, the data is transferred by a strict timed pulse pattern.
I could go into depth on how these data pulses are built up, but back in 2014 Josh Levine wrote an in depth blog post on how to control the WS2812. This post helped me a lot to figure out what data I needed to send.
To make sure the generated pulses are at the required speed to transfer a bit of data, I needed to fall back to raw assembly code. If this scares you, don’t worry. It was my first dive in assembly as well.
Eventually I ended up with the following function to send a bit of data to the WS2812:
void sendBit( bool bitVal ) {
if (bitVal) {
asm volatile (
"sbi %[port], %[bit] \n"
"nop \n nop \n nop \n nop \n nop \n nop \n"
"cbi %[port], %[bit] \n"
"nop \n "
::
[port] "I" (_SFR_IO_ADDR(PORTB)),
[bit] "I" (PIN)
);
} else {
asm volatile (
"sbi %[port], %[bit] \n"
"nop \n "
"cbi %[port], %[bit] \n"
"nop \n nop \n nop \n nop \n nop \n nop \n"
::
[port] "I" (_SFR_IO_ADDR(PORTB)),
[bit] "I" (PIN)
);
}
}
It might look a bit scary at first, but it simply turns on the correct pin using the sbi
command, waits for a specific amount of operations by using nop
(no operation), then turns the pin off using the cbi
command, and waits again using the nop
command.
The difference between sending a 1 or 0 is the duration of the on and off state:
- 1: on for 6 processor operations. off for 1 operation.
- 0: on for 1 processor operations. off for 6 operation.
I figured out these durations by measuring the pulses with my scope, and comparing them to te required durations. In other words: a lot of trail and error.
The WS2812 pixels expect 24 bits per pixel: 8 bits green, 8 bits red, 8 bits blue.
And after a lot of fiddeling, I managed to turn the WS2812 on. Bright green light confirmed my sketchy code worked.
But there was one weird issue: the led didn’t respond to the first 24 bits I sent, but to the second 24 bits. In other words: I needed to address led 1, if I wanted to control led 0.
To see if it was just this specific WS2812 that behaved weirdly, I created my own “strip” of WS2812 2020 leds using a piece of 1.27mm breakout board. This way I could hook up 4 more WS2812s to my ATTiny10.
And than the mystery got even bigger: it turned out the WS2812 2020s I was using skipped every 24 bits I was sending. To confirm this, I hooked up a standard strip of WS2812s (5050) in parallel with the 2020s. And indeed, the 2020 pixels seem to skip every 1 pixel index, as seen in the animation above.
I checked to see if this was caused by my own bit banging, or caused by the ATTiny10. But when I hooked them up to a simple Arduino Nano using the Adafruit Neopixel library, the results were the same.
Maybe I just have a faulty batch of 2020s, or I just discovered a glitch in the Matrix. Who knows.
Anyway, since my end goal was to just control the tiny 2020 I soldered directly to a ATTiny10, I could simply address pixel 0 and 1 by sending the 24 bits of data twice. A dirty workaround, but if it works, it works. A few extra lines of wheel code allow me to show a cool color fading effect.
It works … Awesome!
The combination of the AtTiny10 and the WS2812 2020 gives me a lot of cool ideas. I’m sure I can find something tiny on my dad’s model train tracks to illuminate!
If you are interested in the full code I used, check out the gist on GitHub. Keep in mind that this is specifically written for the AtTiny10, so it might not work with other AtTiny models due to different timings.
And if you have any idea why the WS2812 2020s skip every 24 bits, let me know in the comments down below and win the internet!
PS Ibiza, hoop to see you again soon!