Actions

Difference between revisions of "Driving the WS2811 at 800 kHz with an 8 MHz AVR"

From Just in Time

m (adding video)
(Attempt to correct syntax highlighting.)
 
(99 intermediate revisions by the same user not shown)
Line 1: Line 1:
WS2811 LED controllers are hot. [http://hackaday.com/?s=ws2811 HackaDay] has mentioned them three times in the last two months. Reason enough to order a WS2811 led string on [http://www.ebay.com/sch/i.html?LH_BIN=1&_sop=15&_nkw=ws2811+led+string&LH_PrefLoc=2 ebay] and start researching.
+
This page describes how to drive a WS2811 from an 8Mhz or 9.6Mhz AVR like an Atmel ATmega88, ATtiny2313 or ATtiny13 '''without added components''' such as an external oscillator. With only 30 instructions for the driver loop (110 bytes for the complete function), the driver code presented here is likely to be the [https://github.com/DannyHavenith/ws2811/blob/master/ws2811/ws2811_8.h#L49 shortest code] that delivers the exact timing (i.e. exactly 10 clock cycles for 1 byte, exactly 60 clock cycles for 6 bytes, etc.). With even less code a "sparse driver" for 9.6Mhz MCUs can drive long led strings with only a few bytes of buffer in RAM.
  
Normally I'd go straight to the datasheet and start working from there, but in this particular case the datasheets are [www.nooelec.com/files/WS2811.pdf not so very informative]. Luckily, the HackaDay links provide some excellent discussions. [http://bleaklow.com/2012/12/02/driving_the_ws2811_at_800khz_with_a_16mhz_avr.html This one] by Alan Burlison is especially helpful. That article not only explains in great detail why a library like FastSPI isn't guaranteed to work, but it comes with working code for a 16Mhz AVR that appears rock solid in its timing. Another good source was [http://www.instructables.com/id/My-response-to-the-WS2811-with-an-AVR-thing a post] by "Cunning_Fellow".
+
Of course, if you're creating a hardware project that controls more than 1 LED, you're going to have to demonstrate it with a Knight Rider LED sequence (which, I just learned, is actually called a [https://en.wikipedia.org/wiki/Glen_A._Larson Larson] scanner)... The sources for all the demonstrations in these videos can be found on [https://github.com/DannyHavenith/ws2811 github].
 +
{|
 +
|- valign="top"
 +
|colspan="3"|{{#ev:youtube|mP7vBw4UeME|400|left|Knight Rider on Steroids ([https://github.com/DannyHavenith/ws2811/blob/master/effects/chasers.hpp])}}
 +
|- valign="top"
 +
|{{#ev:youtube|MdX45a3OFS0|300||the [https://hackaday.io/project/9393-wheres-my-christmas-light/log/50423-the-most-simplest-is-the-most-easiest where's my christmas light] project}}
 +
|{{#ev:youtube|_wkTtAk2V_k|300||"Water Torture" or "Lava Drops" demo ([https://github.com/DannyHavenith/ws2811/blob/master/effects/water_torture.hpp source code], [[WS2811 "Water torture"|details]])}}
 +
|{{#ev:youtube|jAm7nVRvY_I|300||[[Driving a large WS2811 LED string with an ATtiny13 and nothing else|Special sparse driver]] allows an attiny13 to drive arbitrarily large LED strings from 64 bytes of memory}}
 +
|- valign="top"
 +
|{{#ev:youtube|mItPxAtkuVI|300||Flares demo on an attiny13 ([https://github.com/DannyHavenith/ws2811/blob/master/effects/flares.hpp source code])}}
 +
|{{#ev:youtube|VM84Q1vWSZE|300||Fireflies!}}
 +
|{{#ev:youtube|otCWPGf6O6w|300||Fire without flies}}
 +
|}
 +
<div style="clear: both"></div>
 +
==Download source code==
 +
The library is header-only. Using it is a matter of downloading the source code and including it in your AVR code. Example source code can be found [https://github.com/DannyHavenith/ws2811 here]. The code comes as an avr-eclipse project consisting for a large part of C++ demonstration code and the main driver function in assembly, in files [https://github.com/DannyHavenith/ws2811/blob/master/ws2811/ws2811_8.h ws2811_8.h] and [https://github.com/DannyHavenith/ws2811/blob/master/ws2811/ws2811_96.h ws2811_96.h] (for the 9.6Mhz version).
 +
 
 +
I don't recommend trying to understand the assembly code by reading these sources. How the code functions is described [[#Defining the puzzle|below]]. Usage information is in [[#Usage|the next section]]. The rest of this page describes the 8Mhz version. The 9.6Mhz code was added later, but is created in the same way.
 +
 
 +
'''New:''' If you'd like to see the assembly code in action, take a look at the [http://rurandom.org/avrgo_demo/ online AVR emulator]!
 +
[[File:Avrgo demo screenshot.png|400px|link=https://rurandom.org/avrgo_demo/]]
 +
 
 +
==Usage==
 +
You'll need the C++ compiler for this to work (turning ws2811.h into "pure C" is left as an exercise to the reader). I am told that this works just as good for an Arduino, but I haven't tested this myself. Remember that this code was written and optimized for 8Mhz and 9.6Mhz, it would run too fast on an 16Mhz Arduino. From the sources, you'll need files ''ws2811.h'', ''ws2811_8.h'', ''ws2811_96.h'' and ''rgb.h'', though you only include "ws2811.h". A simple example of how to use this code:
 +
 
 +
<syntaxhighlight lang="c++">
 +
#include <avr/io.h> // for _BV()
 +
 
 +
#define WS2811_PORT PORTD// ** your port here **
 +
#include "ws2811.h" // this will auto-select the 8Mhz or 9.6Mhz version
 +
 
 +
using ws2811::rgb;
 +
 
 +
namespace {
 +
  const int output_pin = 3;
 +
  rgb buffer[] = { rgb(255,255,255), rgb(0,0,255)};
 +
}
 +
 
 +
int main()
 +
{
 +
  // don't forget to configure your output pin,
 +
  // the library doesn't do that for you.
 +
  // in this example DDRD, because we're using PORTD.
 +
  DDRD = _BV( output_pin);
 +
 
 +
  // send the RGB-values in buffer via pin 3
 +
  // you can control up to 8 led strips from one AVR with this code, as long as they
 +
  // are connected to pins of the same port. Just
 +
  // provide the pin number that you want to send the values to here.
 +
  send( buffer, output_pin);
 +
 
 +
  // alternatively, if you don't statically know the size of the buffer
 +
  // or you have a pointer-to-rgb instead of an array-of-rgb.
 +
  send( buffer, sizeof buffer/ sizeof buffer[0], output_pin);
 +
  for(;;);
 +
}
 +
</syntaxhighlight>
 +
 
 +
==History==
 +
WS2811 LED controllers are hot. Projects using WS2811 (or WS2812, WS2812B or NeoPixel) LED strips have been featured on [http://hackaday.com/?s=ws2811 HackaDay] several times in the last few months. One feature showed how an AVR clocked at 16Mhz could send data at the required high rates. Inspired by this, I ordered an LED strip and 16Mhz oscillators from ebay. The LED strip arrived quickly, only the oscillators took weeks to arrive, which gave me plenty of time to think about the possibility of driving these led strips from an 8Mhz atmega88 without an external oscillator. With only 10 clock ticks per bit, this was going to be a challenge.
 +
 
 +
Normally I'd go straight to the datasheet and start working from there, but in this particular case the datasheets are [http://www.nooelec.com/files/WS2811.pdf not so very informative]. Luckily, the HackaDay links provide some excellent discussions. [http://bleaklow.com/2012/12/02/driving_the_ws2811_at_800khz_with_a_16mhz_avr.html This one] by Alan Burlison is especially helpful. That article not only explains in great detail why a library like FastSPI isn't guaranteed to work, but it comes with working code for a 16Mhz AVR that appears rock solid in its timing.
  
 
Small problem: I didn't have any 16Mhz crystals on stock, so I ordered a few, on ebay again and sat back for the 25 day shipping time to pass. 25 Days is a long time. The led strip had arrived and was sitting on my desk. 25 Days is a really long time. Maybe it could work off an AVR on its internal 8Mhz oscillator? It would be a lot of work. But 25 days is a very, very, long time.
 
Small problem: I didn't have any 16Mhz crystals on stock, so I ordered a few, on ebay again and sat back for the 25 day shipping time to pass. 25 Days is a long time. The led strip had arrived and was sitting on my desk. 25 Days is a really long time. Maybe it could work off an AVR on its internal 8Mhz oscillator? It would be a lot of work. But 25 days is a very, very, long time.
  
So, that is how I got to sit down and write my 8Mhz version of a WS2811@800Khz bit banger. The challenge is of course that I have 10 clock cycles for every bit, no more no less, and 80 cycles for every byte, no more no less. I wanted the timing to be as rock-steady as Alans, give-or-take the imprecise nature of the AVR internal oscillator.
+
So, that is how I got to sit down and write my 8Mhz version of a WS2811@800Khz bit banger. The challenge is of course that I have 10 clock cycles for every bit, no more no less, and 80 cycles for every byte, no more no less.