A while ago, I started a project with a new microcontroller board and wanted to use the on-board LED as an indicator. Naturally, I used the familiar digitalWrite(LED_BUILTIN, 1), loaded the program into the controller, and suddenly... Nothing happened!
Looking at the board I realized it was one of those fancy 3-colour LEDs, a WS2812. These are known as 'RGB' LEDs because they are red-green-blue, although some of them are green-red-blue just to confuse you.
The microcontroller was an ESP32, so it didn't take long to find the rgbLedWrite() method that's part of the ESP32 HAL (Hardware Application Layer) in file '..\cores\esp32\esp32-hal-rgb-led.c'. This uses the ESP32's on-chip 'Remote Control Transmitter' (RMT) to generate the signals for the LED with method rmtWrite(...). The problem is it only works for one LED. You could use rmtWriteRepeated() to write the same colour to multiple LEDs - which is OK for single-colour 'NEOPIXEL' style displays on 3D Printers. But what if you want to use the RMT for something else? Or you want a fancy colour animation?
If you need a multicoloured animation you can use an array of RMT control data. Each bit sent to the LEDs needs a 32-bit rmt_data_t value, and each LED needs 24 bits. So each LED needs 24 * 4 = 96 bytes. This cannot be reduced because the RMT data must be prepared in memory before it is sent.
There's a nice non-blocking method called rmtWriteAsync() which does not wait until it's all sent, so you can poll it - this is perfect. I've provided some code to do this below, which runs only on the ESP32. ESP32s have lots of RAM, so for ESP32 applications this is the best solution if you don't need the RMT for something else.
For other MCUs, or to free up the RMT and/or use less RAM, you can use the bit-bang version below which controls the output using nanosecond delays tuned to your CPU's speed. This uses just 4 bytes per LED instead of 96. It needs a fast processor, 64MHz or faster, because slower processors can't do the nanosecond delays. Versions for two MCU types are provided below (STM32, ESP32).
These LEDs are very bright. Each RGB colour has a one-byte brightness level of 0..255 (0x00..0xFF). At 0xFF it's too bright to look at, 0x0F is better if it's on the desk next to you. RGB values are 24-bits, usually stored as a 32-bit unsigned integer, 0x00rrggbb (0x00000000 .. 0x00FFFFFF).
If anyone is still awake, please read on...
57 Varieties
WS2812 LEDs can be bought as single SMD LEDs, on boards with a huge variety of shapes and sizes, or as flexible strips up to 5m long (that's 300 LEDs, so you'll need a very big 5V power supply). Aliexpress.com is a good place to go for these, if you can wait a couple of weeks for them to arrive.
All the arrays have the same 4-pin connections, 5V GND DI DO, the same connections are on each LED. 5V and GND are wired in parallel, DI and DO are wired in series. Some LED strips may be powered by 12V - the LEDs will be grouped and have SMD resistors to distribute the voltage.
Signal Timing
These LEDs need an 800KHz digital signal which has timed pulses to indicate '0' or '1' bits. '1' is high for 700ns (nanoseconds) and low for 350ns. '0' high for 350ns and low for 700ns. These timings are in nanoseconds, so they are very short. The LEDs have a data in pin (DI) and a data out pin (DO), so they can be chained together. You can connect the first DI pin directly to an output of your 3.3V or 5V microcontroller - see note about driving it with 3.3V below.
Below is the timing for a '1' bit (T1) and a '0' bit (T0). T1H and T0L (T1 High and T0 Low) are 700..800ns. T1L and T0H (T1 Low and T0 High) are 350..400nS. The specified timings vary a bit between different versions of the chip, but 700/350ns works well, and so does 800/400ns. The RMT example uses 800/400ns. Some chips will also run at 400KHz, so the delays are doubled and maybe you can use them with slower CPUs. (That's your homework.)
The DI and DO pins of all LEDs are chained together, so the serial data is passed down the chain to each LED until there is a break in the data of at least 50us microseconds. After this break, the LEDs assume the data is ready and will display the colour values they have received. Then they start waiting for new data.
5V Power
The LEDs need 5V DC at up to 60mA for each LED (20mA per colour), depending on the brightness. That's a worst case of 20A for a 5m LED array! So you will usually need a separate 5V power supply for the LEDs. If you have only one or two LEDs then you can use the MCU's 5V supply, which may come directly from the USB or may be passed though a regulator with current limitations, so check the data sheet. The DI data pin only takes a few microamps, so you don't need to worry about that.
NEOPIXEL LEDs for 3D printers often have an additional connector on the main board for an external 5V power supply.
5V DC at 20 Amps... The best ones have a fan. Power Factor Correction (PFC) is not needed because it's not an inductive load, so cheap PSUs can be used.
Q. Can you drive WS2812 5V LEDs directly from a 3.3V MCU output?
There's a lot of talk on Internut about this. The WS2812 data sheet says the minimum 'high' level is 0.7 x VDD, which is 3.5V. This implies that you cannot drive it reliably directly from a 3.3V MCU.
The max. current into the LED's DIN pin is +-1 microamp, which is tiny. This means it will not damage a 3.3V GPIO, even if the MCU does not have 5V-tolerant GPIOs. BUT DO NOT CONNECT IT to the 5V Data Out DO pin!
Will a 3.3V output be enough to drive the LED's DIN input which needs >= 3.5V?
The answer to that is YES, it is OK! I have never had a problem. The data sheet shows an absolute worst-case minimum switching voltage, which never occurs in reality (unless it's duff chip). I'm pretty sure they would not develop a product that could not be driven by a 3.3V MCU.
Tip! ESD Protection
Beware of Electrostatic Discharges. If mounting LEDs on a front panel or using NEOPIXEL LEDs with a 3D printer etc. ensure the MCU output has good ESD protection! If not, think about adding a suitable TVS diode to GND.
While working with these LEDs, I damaged an unprotected MCU output because I was wearing a fleece. A fleece is like a wearable Van der Graaf Generator or Wimshurst machine. For the same reason, never wear silk pyjamas (or a silk negligee) while working with delicate electronic equipment.
LED Animations
These LEDs can produce very bright and very spectacular displays. Any self-respecting alien would be proud to have these LEDs all over his saucer. And from the UAPs that I have observed, many of them already do.
This is the LED ring that's used for the 'SerialRGBLedRMT-Example.ino' sketch below.
Here is a genuine UAP that I filmed in my living room the other day...
It's not a saucer, it's an upside-down candle holder.
This code is for WS2811 driver chips and WS2812 RGB LEDs. It has an advantage over the other WS28xx libraries in that it is very small (100 or 200 lines, including the comments), making it easy to understand and modify. In comparison, the official Adafruit library is over 4000 lines of code, and probably does a lot of stuff that's rarely needed.
It has been tested with several 800KHz LED strips and matrices, and also on these 3D printer display boards with 3 x RGB LEDs driven by WS2811 chips:
•BIGTREETECH MINI 12864 V2.0
•MAKERBASE MKS MINI 12864 V3
Tip: For a single RGB LED on an ESP32, you do not need any of this code. Use the existing rgbLedWrite() method which uses the RMT:
This version allows the ESP32's on-chip RMT to be used to control multiple LEDs. ESP32s have lots of RAM, so 96 bytes per LED should not be a problem.
It has a non-blocking updateLedsAsync() method which returns immediately, leaving the RMT to do the data transfer in the background. In this case, poll it occasionally with ledsBusy() until it returns false.
#pragma once
/////////////////////////////////////////////////////////////////////
// RGB LED Driver for WS2811 driver chips or WS2812 LEDS
// Copyright (C) mumanchu + muman.ch, 2026.05.13
// All rights reversed
// https://github.com/mumanchu/SerialRGBLed
// https://muman.ch/muman/index.htm?muman-serial-rgb-leds.htm
//
// >>> THIS VERSION FOR ESP32 WITH REMOTE CONTROL TRANSMITTER RMT <<<
// Create a single 24-bit RGB value from three separate R G B values
#define RGB(r, g, b) \
(((ulong)(r & 0xff) << 16) + ((g & 0xff) << 8) + (b & 0xff))
#ifndef ESP32
#error This version of SerialRGBLed is only for the ESP32 with RMT
#else
class SerialRGBLed
{
uint rmtPin;
uint numberOfLeds;
bool grb; // true if GRB instead of RGB
uint rmtSize; // number of RMT symbols
rmt_data_t* rmtData = NULL;
static const rmt_data_t one;
static const rmt_data_t zero;
public:
bool begin(uint rmtPin, uint numberOfLeds, bool grb = false);
void clearLedData();
void setLedColor(uint led, ulong rgb);
void updateLeds();
void updateLedsAsync();
bool ledsBusy() { return !rmtTransmitCompleted(rmtPin); }
};
// Each bit needs a 32-bit RMT value
// { duration0, level0, duration1, level1 }
const rmt_data_t SerialRGBLed::one = { 8, 1, 4, 0 };
const rmt_data_t SerialRGBLed::zero = { 4, 1, 8, 0 };
// Call this once from setup()
bool SerialRGBLed::begin(uint rmtPin, uint numberOfLeds, bool grb)
{
if (rmtPin >= SOC_GPIO_PIN_COUNT)
return false;
if (!rmtInit(rmtPin, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000))
return false;
// 24 bits per LED, rmtSize is the number of RMT symbols
rmtSize = numberOfLeds * 24;
rmtData = (rmt_data_t*)malloc(rmtSize * sizeof(rmt_data_t));
if (rmtData == NULL)
return false;
this->rmtPin = rmtPin;
this->numberOfLeds = numberOfLeds;
this->grb = grb;
clearLedData();
return true;
}
// Clears the LED data array, all LEDs off
// it does not write the data to the display
void SerialRGBLed::clearLedData()
{
if (rmtData) {
// set all the RMT data to zero bits
rmt_data_t* p = rmtData;
for (uint i = 0; i < rmtSize; ++i)
*p++ = zero;
}
}
// Set the color for an led (0 .. numberOfLeds-1)
// ulong rgb : 24-bit standard RGB value, 0x00rrggbb
// Call updateLeds() to write the data to the LEDs
// Note: 'rgb' values are converted to 'gbr' according to the
// begin(, bool grb) parameter
void SerialRGBLed::setLedColor(uint led, ulong rgb)
{
if (rmtData == NULL || led >= numberOfLeds)
return;
// if the led is grb not rgb, then swap the red and green bytes
if (grb)
rgb = ((rgb >> 8) & 0xff00) + ((rgb << 8) & 0xff0000) + (rgb & 0xff);
// pointer to 24 bits of rmt_data_t values
rmt_data_t* p = rmtData + (led * 24);
// fill 24 x rmt_data_t values, MS bit 23 first
uint bitMask = 0x00800000;
while (bitMask) {
*p++ = (rgb & bitMask) ? one : zero;
bitMask >>= 1;
}
}
// Sends all data and returns once it has been sent (blocking)
// DO NOT CALL AGAIN FOR AT LEAST 100 MICROSECONDS
void SerialRGBLed::updateLeds()
{
if (rmtData)
rmtWrite(rmtPin, rmtData, rmtSize, RMT_WAIT_FOR_EVER);
}
// Starts sending data and returns immediately (non blocking)
// poll with ledsBusy() until it returns false
// DO NOT CALL AGAIN FOR AT LEAST 100 MICROSECONDS
void SerialRGBLed::updateLedsAsync()
{
if (rmtData)
rmtWriteAsync(rmtPin, rmtData, rmtSize);
}
#endif // #ifndef ESP32
Here's an example Sketch that uses SerialRGBLedRMT.h.
/////////////////////////////////////////////////////////////////////
// Example Sketch for SerialRGBLedRMT on ESP32
// This uses the ESP32's on-chip Remote Control Transmitter (RMT)
// Copyright (C) mumanchu & muman.ch, 2026.05.14
// All rights reversed
// https://github.com/mumanchu/SerialRGBLed
// https://muman.ch/muman/index.htm?muman-serial-rgb-leds.htm
#include "SerialRGBLedRMT.h"
SerialRGBLed leds;
// 24-led ring, as shown in the blog
#define NLEDS 24
void setup()
{
Serial.begin(115200);
delay(3000);
Serial.println("\n\rStarting...\n\r");
Serial.flush();
pinMode(LED_BUILTIN, OUTPUT);
if (!leds.begin(D0, NLEDS, true)) {
Serial.println("led.begin() failed");
Serial.flush();
while (1) yield();
}
}
void loop()
{
// 100ms scheduler
static ulong t1 = 0;
ulong t = millis();
if ((t - t1) >= 100) {
t1 = t;
// flash the onboard LED
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
// three rotating colours on a 24-led circular matrix
static int nled0 = 0;
static int nled1 = NLEDS / 4;
static int nled2 = NLEDS / 2;
static ulong color0 = 0x000f0000;
static ulong color1 = 0x00000f00;
static ulong color2 = 0x0000000f;
for (int i = 0; i < NLEDS; ++i) {
ulong color = 0;
if (i == nled0)
color = color0;
else if (i == nled1)
color = color1;
else if (i == nled2)
color = color2;
leds.setLedColor(i, color);
}
if (++nled0 == NLEDS)
nled0 = 0;
if (--nled1 < 0)
nled1 = NLEDS - 1;
if (++nled2 == NLEDS)
nled2 = 0;
leds.updateLeds();
}
}
The bit-bang versions work only on fast MCUs, 64MHz or faster, because the code is (mostly) in C++ it is not fast enough on old and slow MCUs. They use fast GPIO code which only works only on that specific processor.
Each LED or chip model has slightly different timing, but the chosen timing (700ns/350ns) should work for most 800KHz devices. For 400KHz devices, just modify the code to double the delays.
#pragma once
/////////////////////////////////////////////////////////////////////
// RGB LED Driver for WS2811 driver chips or WS2812 LEDS
// Copyright (C) mumanchu + muman.ch, 2026.05.13
// All rights reversed
// https://github.com/mumanchu/SerialRGBLed
// https://muman.ch/muman/index.htm?muman-serial-rgb-leds.htm
//
// >>> THIS VERSION FOR ESP32 <<<
// '#define MCU_FREQ_MHZ' before '#include "SerialRGBLedESP32.h"'
// see below for which MCU frequencies are supported
// the ESP32 normally runs at 240MHz
#ifndef MCU_FREQ_MHZ
#error MCU_FREQ_MHZ not defined
#endif
// Create a single 24-bit RGB value from three separate R G B values
#define RGB(r, g, b) \
(((ulong)(r & 0xff) << 16) + ((g & 0xff) << 8) + (b & 0xff))
#ifndef ESP32
#error This version of SerialRGBLed is only for the ESP32
#else
class SerialRGBLed
{
ulong* ledData = NULL;
uint numberOfLeds;
uint regBit;
uint ts_reg;
uint tc_reg;
bool grb;
public:
bool begin(uint dataPin, uint numberOfLeds, bool grb = false);
void clearLedData();
void setLedColor(uint led, ulong rgb);
void updateLeds() { updateLeds(ledData); }
void updateLeds(const ulong* data);
};
// Call this once from setup()
bool SerialRGBLed::begin(uint dataPin, uint numberOfLeds, bool grb)
{
if (dataPin >= SOC_GPIO_PIN_COUNT)
return false;
pinMode(dataPin, OUTPUT);
digitalWrite(dataPin, 0);
if (dataPin < 32) {
regBit = 1 << dataPin;
ts_reg = GPIO_OUT_W1TS_REG;
tc_reg = GPIO_OUT_W1TC_REG;
}
// some don't have more than 32 pins
#ifdef GPIO_OUT1_W1TS_REG
else {
regBit = 1 << (dataPin - 32);
ts_reg = GPIO_OUT1_W1TS_REG;
tc_reg = GPIO_OUT1_W1TC_REG;
}
#endif
uint size = numberOfLeds * sizeof(ulong);
ledData = (ulong*)malloc(size);
if (ledData == NULL)
return false;
clearLedData();
this->numberOfLeds = numberOfLeds;
this->grb = grb;
return true;
}
// Clears the LED data array, all LEDs off
// it does not write the data to the display
void SerialRGBLed::clearLedData()
{
if (ledData)
memset(ledData, 0, numberOfLeds * sizeof(ulong));
}
// Set the color for an led (0 .. numberOfLeds-1)
// ulong rgb : 24-bit standard RGB value, 0x00rrggbb
// Call updateLeds() to write the data to the LEDs
// Note: 'rgb' values are converted to 'gbr' according to the
// begin(, bool grb) parameter
void SerialRGBLed::setLedColor(uint led, ulong rgb)
{
if (led >= numberOfLeds)
return;
// if the led is grb not rgb, then swap the red and green bytes
if (grb) {
rgb = ((rgb >> 8) & 0x0000ff00) +
((rgb << 8) & 0x00ff0000) +
(rgb & 0x000000ff);
}
ledData[led] = rgb;
}
// Signal Timing
// This defines the approximate timing used for 800kHz chips and LEDs:
// 0 bit = 350ns high (T0H) + 700ns low (T0L)
// 1 bit = 700ns high (T1H) + 350ns low (T1L)
// total = 1250ns, for 800kHz chip
// Software 'nop' delays are used because the timing for a 'nop'
// instruction is reliable, depending only on the MCU clock speed.
#define NOP10 "nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; "
#define NOP50 NOP10 NOP10 NOP10 NOP10 NOP10
#define NOP5 "nop; nop; nop; nop; nop; "
#define NOP2 "nop; nop; "
// Define the number of NOP delays according to the MCU speed.
// There is no #define for the MCU speed because it can be set
// at run time by the clock configuration on many modern MCUs.
// The timing is not linear according to the MCU speed, so you
// must to use a 'scope to measure it and adjust the NOP count.
//TODO this is a lot of NOPs, maybe there's a better way...
#if (MCU_FREQ_MHZ == 80)
#define T0H NOP10 NOP10 NOP2
#define T0L NOP50
#define T1H NOP10 NOP10 NOP10 NOP10 NOP5
#define T1L NOP10 NOP5
#elif (MCU_FREQ_MHZ == 160)
#define T0H NOP50
#define T0L NOP50 NOP50 NOP5
#define T1H NOP50 NOP50
#define T1L NOP10 NOP10 NOP10 NOP10 NOP2
#elif (MCU_FREQ_MHZ == 240)
#define T0H NOP50 NOP10 NOP10 NOP10
#define T0L NOP50 NOP50 NOP50 NOP10 NOP2
#define T1H NOP50 NOP50 NOP50 NOP5
#define T1L NOP50 NOP10 NOP10
#else
#error Undefined MCU_FREQ_MHZ value
#endif
// Send data to all LEDs at 800KHz
// this takes a bit less than 30uS per LED
// NOTE: Leave at least a 300us between calls to give time for
// the 'reset' frame.
// ensure default optimization -Os for the software timing
__attribute__((optimize("-Os")))
void SerialRGBLed::updateLeds(const ulong* data)
{
if (ledData == NULL)
return;
// disable interrupts, interrupts mess up the software timing
noInterrupts();
// send data to all LEDs
// led data is 24 bits per led : 0x00rrggbb
// (or may be 0x00ggrrbb for other hardware)
for (uint led = 0; led < numberOfLeds; ++led) {
ulong color = data[led];
// send MS bit first
ulong bitMask = 1UL << 23;
// send 24 bits
while(bitMask) {
// output high
REG_WRITE(ts_reg, regBit);
// 0 or 1 bit?
bool b = (color & bitMask) == 0;
// delay according to the number of NOPs
if (b)
__asm volatile (T0H);
else
__asm volatile (T1H);
// output low
REG_WRITE(tc_reg, regBit);
// delay according to the number of NOPs
if (b)
__asm volatile (T0L);
else
__asm volatile (T1L);
// next bit
bitMask >>= 1;
}
}
interrupts();
}
#endif // #ifndef ESP32
#pragma once
/////////////////////////////////////////////////////////////////////
// RGB LED Driver for WS2811 driver chips or WS2812 LEDS
// Copyright (C) mumanchu + muman.ch, 2026.05.12
// All rights reversed
// https://github.com/mumanchu/SerialRGBLed
// https://muman.ch/muman/index.htm?muman-serial-rgb-leds.htm
// '#define MCU_FREQ_MHZ' before '#include "SerialRGBLed.h"'
// see below for which MCU frequencies are supported
#ifndef MCU_FREQ_MHZ
#error MCU_FREQ_MHZ not defined
#endif
// Create a single 24-bit RGB value from three separate R G B values
#define RGB(r, g, b) \
(((ulong)(r & 0xff) << 16) + ((g & 0xff) << 8) + (b & 0xff))
#ifndef STM32_CORE_VERSION
#error This version of SerialRGBLed is only for the STM32
#else
class SerialRGBLed
{
ulong* ledData = NULL;
uint numberOfLeds;
GPIO_TypeDef* port;
uint bitMask;
bool grb;
public:
bool begin(uint dataPin, uint numberOfLeds, bool grb = false);
void clearLedData();
void setLedColor(uint led, ulong rgb);
void updateLeds() { updateLeds(ledData); }
void updateLeds(const ulong* data);
};
// Call this once from setup()
bool SerialRGBLed::begin(uint dataPin, uint numberOfLeds, bool grb)
{
port = digitalPinToPort(dataPin);
if (port == NULL)
return false;
bitMask = digitalPinToBitMask(dataPin);
pinMode(dataPin, OUTPUT);
digitalWrite(dataPin, 0);
uint size = numberOfLeds * sizeof(ulong);
ledData = (ulong*)malloc(size);
if (ledData == NULL)
return false;
clearLedData();
this->numberOfLeds = numberOfLeds;
this->grb = grb;
return true;
}
// Clears the LED data array, all LEDs off
// it does not write the data to the display
void SerialRGBLed::clearLedData()
{
if (ledData)
memset(ledData, 0, numberOfLeds * sizeof(ulong));
}
// Set the color for an led (0 .. numberOfLeds-1)
// ulong rgb : 24-bit standard RGB value, 0x00rrggbb
// Call updateLeds() to write the data to the LEDs
// Note: 'rgb' values are converted to 'gbr' according to the
// begin(, bool grb) parameter
void SerialRGBLed::setLedColor(uint led, ulong rgb)
{
if (led >= numberOfLeds)
return;
// if the led is grb not rgb, then swap the red and green bytes
if (grb) {
rgb = ((rgb >> 8) & 0x0000ff00) +
((rgb << 8) & 0x00ff0000) +
(rgb & 0x000000ff);
}
ledData[led] = rgb;
}
// Signal Timing
// This defines the approximate timing used for 800kHz chips and LEDs:
// 0 bit = 350ns high (T0H) + 700ns low (T0L)
// 1 bit = 700ns high (T1H) + 350ns low (T1L)
// total = 1250ns, for 800kHz chip
// Software 'nop' delays are used because the timing for a 'nop'
// instruction is reliable, depending only on the MCU clock speed.
#define NOP10 "nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; "
#define NOP5 "nop; nop; nop; nop; nop; "
#define NOP2 "nop; nop; "
// Define the number of NOP delays according to the MCU speed.
// There is no #define for the MCU speed because it can be set
// at run time by the clock configuration on many modern MCUs.
// The timing is not linear according to the MCU speed, so you
// must to use a 'scope to measure it and adjust the NOP count
// in steps of 10 or 5 NOPs.
#if (MCU_FREQ_MHZ == 64 || MCU_FREQ_MHZ == 72)
// 64Mhz/72MHz STM32
#define T0H NOP10
#define T0L NOP10 NOP10
#define T1H NOP10 NOP10 NOP2
#define T1L ""
//TODO add more MCU speeds here
#elif (MCU_FREQ_MHZ == 168)
// 168MHz STM32
#define T0H NOP10 NOP10 NOP10 NOP10 NOP10 NOP5
#define T0L NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP5
#define T1H NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10
#define T1L NOP10 NOP10 NOP10 NOP10 NOP10
#else
#error Undefined MCU_FREQ_MHZ value
#endif
// Send data to all LEDs at 800KHz
// this takes a bit less than 30uS per LED
// NOTE: Leave at least a 300us between calls to give time for
// the 'reset' frame.
// ensure default optimization -Os for the software timing
__attribute__((optimize("-Os")))
void SerialRGBLed::updateLeds(const ulong* data)
{
if (ledData == NULL)
return;
// disable interrupts, interrupts mess up the software timing
noInterrupts();
// for fast digital outputs
ulong odr = port->ODR;
ulong odr0 = odr & ~bitMask;
ulong odr1 = odr | bitMask;
// send data to all LEDs
// led data is 24 bits per led : 0x00rrggbb
// (or may be 0x00ggrrbb for other hardware)
for (uint led = 0; led < numberOfLeds; ++led) {
ulong color = data[led];
// send MS bit first
ulong mask = 1UL << 23;
// send 24 bits
while(mask) {
// output high
port->ODR = odr1;
// 0 or 1 bit?
bool b = (color & mask) == 0;
// delay according to the number of NOPs
if (b)
__asm volatile (T0H);
else
__asm volatile (T1H);
// output low
port->ODR = odr0;
// delay according to the number of NOPs
if (b)
__asm volatile (T0L);
else
__asm volatile (T1L);
// next bit
mask >>= 1;
}
}
interrupts();
}
#endif // #ifndef STM32_CORE_VERSION
Data Sheets
These LEDs and driver chips are manufactured by Worldsemi. Their website always seems to download the data sheet onto your computer instead of opening it in the browser.
So here are links to the versions on the Adafruit website, because these will open in a new browser page.