Friday, 15 November 2013

Adafruit Neopixel Ring - example assembly source code for Atmel ATTiny85

These new NeoPixel rings from adafruit are really very good.  A little strange to operate perhaps (they are like a shift register but they shift by re-transmitting downstream.  Or to put it another way you would send the pixels as if they were drawn out on a TV screen (which is not the way a shift register works if you think about it).

Initially, I wrote some PASM code for a Parallax Propeller (P8X32A) chip to make some interesting colour patterns appear on the ring.  This was quite easy with the aid of a Saleae Logic probe.

But the problem with the Propeller is that it's a very expensive chip and is COMPLETE overkill for making some fancy patterns on a bunch of LEDs.  No, instead I wanted to use a smaller chip to run the LEDs from.

Enter the ATTiny85 [PDF].

Long story short, I've written an very simple ATTiny85 demo assembly code program for showing a pattern on the NeoPixel Ring.  It shows a stationary pattern of 5 reds, 5 greens, and 5 blues, all of increasing brightness, and a single white LED showing an equal mix of the three colours.

Here's a pic of the code in action...

Please forgive the brightness of the LEDs in the picture.  In reality they are in fact displayed at a deliberately low intensity, because the LEDs themselves are very bright indeed when forced to maximum brightness.

The code shown below was written for the ATTiny85 and was compiled using Atmel Studio (version 6.1.2562) and I used a AVRISP MkII programmer device to install the software into the chip using the 6-pin programming connector.

VITAL INFO: You'll need to get the fuses right for this, I've highlighted the important bits...

SOURCE CODE Here's the (public domain) code: ring.asm

Load up Atmel Studio and start a new Assembly language project and select the ATTiny85 as your target chip.  Then replace the default assembly program with the code I have given here.

Hit F7 and it should hopefully compile without any errors.  Then open the programming menu and configure the Fuse options as you see above.  Then once you've set the fuses properly click the "Program" button to send the new fuse config to the chip.  Next click the "Memories" option from the list on the left side of the dialog and then program your chip with the "*.hex" file that was created.  You might have to use the "..." button next to the filename dropdown box in order to find the hex file.

All being well you should be able to hit the "Program" button on the Memories tab and the chip attached to your AVRISP MKII programmmer will now contain the firmware to generate the NeoPixel ring test pattern.

Connecting the ATTiny85 to the NeoPixel Ring:
  • ATTiny Pin 8 goes to 5Volts
  • ATTiny Pin 4 goes to GND
  • ATTiny Pin 5 (PB0) goes to the "Data IN" pin on the NeoPixel ring.  Also power the ring from 5V and the same GND rail.

If you have any problems, please let me know and I'll try to fix my hastily written instructions ASAP. :)

I hope this helps some of you wanting a fast and simple intro to using ATtiny85s to drive the Neopixels without having to rely on some awful heap of crummy arduino imitation libraries to get the job done.


Please note that the WS2812 datasheet is WRONG!!!  Please use the Adafruit timings table instead, my code uses the adafruit timings and they do work well...
Load this page and scroll down to the "Writing Your Own Library" section and notice the differences in the timings table.  This is important!

Good luck and happy hacking. :)

You might find that other fuse settings work for you, the important thing to know is that the code I have provided is designed to work with an internally generated 16MHz system clock.  If you want to you can always just use an external 16MHz clock and choose your own fuses as you wish.  The code itself just deals with shovelling bits around.  The only time-sensitive part of the assembly code given here is the sub-routine labelled "send8bits". In fact you might want to simply convert the "send8bits" function into an Interrupt Service Routine and let it run every 60Hz or so. That would let the rest of your code run without any particular timing limitations - at a leisurely pace if you will :)


  1. Very cool. Coincidentally I *just* started playing with a WS2812B using a Tiny a few days ago. I love the minimalist approach of going with an ATtiny, just enough MCU for so many projects. :) I use them so often I built a breakout for a few of the Tiny families because I kept breadboarding them all the time and got sick of trying to remember what AVR ISP wires go where :) I'm selling them now on Tindie, even. I did some assembly on the Tiny13 and had a blast with that so it is great to see an asm project for the t85 here. The code looks nice and straightforward. I'm sure it'll help others find beautiful minimalism. :) So, just so I understand, the timing difference is because you're using the 8MHz internal RC clock instead of a 16MHz external xtal? Thanks --Michael

  2. Hi Michael,

    Yeah, the fuses I'm using in the "Device Programming" dialog are to configure the internal 16MHz CPU clock. I think this is the fastest that the ATTiny85 can go without any external components. Turns out that is just fast enough to get pretty darn close to the ideal signal timings required by the WS2812's 800KHz "protocol".

    I like the products on your Tindie store, the prices are great. The "Autonomous Rover Baseboard" looks awesome.

  3. I find that the source code for this project compiles and runs on my attiny85 with a 12 pixel ring... I modified the code to deal with the fact that I am using avra assembler and also to use the 12 pixel ring. The problem is, when I download the code to the t85 with my buspirate, I can see the patterns immediately. However, if I reset the attiny85, the pattern no longer shows up. Maybe has to do with startup time of the processor and the timing protocol of the ring.

  4. To add, I programmed what you wrote... to an attiny85 using a beaglebone black with Ubuntu installed.

  5. Try one of the other "sut_cksel" fuses that has a longer start-up time.

    Failing that try a 600uF elecrolytic cap over the power rails on the pixel chain.