I am trying to control a strip of SK6812 RGBW leds from an arduino nano, and I need some help sending the color data to the strip. I would like to write this code myself, rather than using the AdaFruit neopixel library that most people seem to use. The problem is in sending the signal fast enough.
I checked the datasheet for the leds, and they use a total time of 1.25+-0.6 µS to send a bit. For a 0 it's 0.3 µS high 0.9 µS low, and for a 1 it's 0.6 µS high 0.6 µS low (all times +- 0.15 µS). My first thought was to use a PWM signal with a variable duty cycle, updated with a timer overflow interrupt every 1.2 (or 1.25?) µS. I got this to work on an ordinary led, blinking it at variable duty cycles with a period of around 1 s so I could see if it was working. However, after doing the math, I see that a clock cycle on a 16MHz chip is 0.0625 µS so that a 1.25 µS window is only 20 clock cycles. According to Nick Gammon's page on interrupts, "an ISR using the ISR define will take you 2.625 µS to execute, plus whatever the code itself does." Thus, I will not have enough time to update my PWM if its period is only 1.25 µS.
Can anybody tell me a better way to do this? I'm sure it's possible, since people report success with the library using these leds, but I can't find much of anything about it on the forums. I'm still learning my way around the arduino board, and I am trying to do this from the ground up. Is there a way to send data this fast in general? Gammon's page suggested using a "tight loop" for timings less than 1 µS, but I am a bit confused as to how to produce a loop with such precise timing.
Yes use a libiary, either the Adafruit one or the fastLED one support this sort of protocol even thought it might not be mentioned as a specific supported device, try what they have to offer.
I'm sure that would work, but it isn't what I'm trying to do. I'm interested in learning how to achieve fast and precise signal timing in general, and I don't want to rely on third party libraries. An example of some c++ code that does something similar would be great, if anybody out there has done this before. As far as I can tell, the libraries use assembly language for this task. Since I don't know assembly language (yet), I can't really follow it. If assembly is required for this sort of thing and c++ won't work, that would be good to know too.
I asked this question in the LEDs section, since I have some concrete data from the SK6812 datasheet to work with for the timings. The question is really more about how to send the data with the proper timings than how to get the LEDs to turn on. I was hoping someone had succeeded in doing this already and could give me some pointers.
Haha good point! I'll probably end up just learning how to read assembly well enough to copy and paste the right code from the library. Just as a matter of personal preference, I hate using libraries when I don't know what they actually do. The fact that this part of the code is in assembly is a big stumbling block for me. Thanks again for pointing me in the right direction!
kpdonahue85:
Haha good point! I'll probably end up just learning how to read assembly well enough to copy and paste the right code from the library. Just as a matter of personal preference, I hate using libraries when I don't know what they actually do. The fact that this part of the code is in assembly is a big stumbling block for me. Thanks again for pointing me in the right direction!
I have the same impulse - but using libraries is generally good programming practice - outside of embedded code, it's largely unavoidable, and in professional programming settings, it's generally required (because programmers' own implementations may include non-obvious bugs, potentially compromising security - the common libraries have been used and tested by thousands of programmers, so the corner cases and security flaws have mostly been caught). So while it's often good practice to take a look at the implementation of the library when working on microcontrollers, where there can be conflict over specific hardware resources, you should still use the libraries (or make your own library when one can't be found), and not make a habit of reinventing the wheel. In programming desktop systems, you rarely end up looking at library code.
you should still use the libraries (or make your own library when one can't be found), and not make a habit of reinventing the wheel
Yeah, my programmer friends tell me the same thing. I try to take this advice most of the time, and I don't usually dig too deep into the libraries if I'm working on something designed to run on a desktop. With the arduino though, I think it's worth a look at the libraries sometimes. For example, if there are a lot of long delay() commands hidden in there that would slow down the other routines.
I think I'm going to reinvent the wheel on this one, since my goal here is pretty simple. Essentially, I want to take this "blink using assembly" tutorial and modify it to run over a period of 20 clock cycles (actually this would even simplify the tutorial's timer loop a bit.) In the loop, I want to read the color data for the LEDs and write to the OCRnx register to change the PWM duty cycle. I'd say I could get this done in under 20 cycles (hence it won't take many asm commands to write).
The only issue I'm still having is how to pass the data from the c++ code into the assembly code. I looked this up for passing simple integer types, but my concern is that it won't be this easy to pass an array. If I'm reading the datasheet correctly, the ATMega328P has 256 registers of 1 byte each. Since I need to pass a 32 bit integer to each LED, at most I could fit 64 of these into the registers (in reality much fewer since some registers are already in use.) So I'm guessing I need an assembly routine that can accept a pointer to an array (the color data) as its argument, and then find the color data in SRAM and read it into the registers.
Does this sound like a feasible approach?
Edit: On second thought, it's probably not necessary to read the SRAM into the other registers. It could be accessed directly.
That's a perfect starting point, thanks so much! As far as I know, the only difference between the RGB and the RGBW types is that it takes an extra byte of data to talk to the RGBWs. I'll take a look at your code and see what I can do with it. I'm just starting to learn assembly, so this code will be extremely helpful