Test program for my Multi_WS2811 library that started out as a fork of heroic/WS2811. My library uses hardware DMA on the FRDM-KL25Z to drive up to 16 strings of WS2811 or WS2812 LEDs in parallel.
Dependencies: Multi_WS2811 mbed MMA8451Q
Fork of WS2811 by
NOTE: I have accidentally pushed changes for another fork of this program that I used in the recent Georgetown Carnival Power Tool Races. When I get some time, I will restore the test program to its original glory.
You can see my power tool racer (Nevermore's Revenge) here

This tests my FRDM-KL25Z multi-string WS2811/WS2812 library. It uses the accelerometer to change the rainbow phase on two strings of LEDs as well as the touch sense to change brightness.
A video of this program in operation is here.
Here is the library that I developed to run the LEDs:
Import libraryMulti_WS2811
Library allowing up to 16 strings of 60 WS2811 or WS2812 LEDs to be driven from a single FRDM-KL25Z board. Uses hardware DMA to do a full 800 KHz rate without much CPU burden.
WS2811.cpp
- Committer:
- bikeNomad
- Date:
- 2013-12-21
- Revision:
- 20:b9d76e567637
- Parent:
- 17:b4e9d8f4baa9
- Child:
- 21:4541da183397
File content as of revision 20:b9d76e567637:
// 800 KHz WS2811 driver, kinda.
//
// Parameterized and modified to use soft SPI.
// Jas Strong <jasmine@electronpusher.org>
// Modified to use hard SPI by Ned Konz <ned@bike-nomad.com>
/*****************************************************************************/
#include "LedStrip.h"
#include "WS2811.h"
extern void dump_spi_settings(SPI_Type const *spi);
extern Serial pc;
extern DigitalOut debugOut;
static const unsigned DMA_MUX_SRC_SPI0_Transmit = 17;
// const unsigned DMA_MUX_SRC_SPI1_Transmit = 19;
static const unsigned dmaWriteChannel = 0;
static const unsigned dmaXmitMuxSrc = DMA_MUX_SRC_SPI0_Transmit;
static volatile bool dma_done = false;
// 12.8 MHz => 800KHz bit rate (1.25 usec/byte)
WS2811::WS2811(int n, SPI_Type *_spi, PinName _mosi, PinName sclk) :
LedStrip(n),
spi(_spi),
mosi(_mosi)
{
SPI spitemp(_mosi, NC, sclk);
spitemp.format(8,3);
spitemp.frequency(800e3 * 16 * 2); // 12 MHz (48MHz/60) => 750KHz rate (1.33 usec/byte)
//Enable DMA clocking
SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK; // Enable clock to DMA mux
SIM->SCGC7 |= SIM_SCGC7_DMA_MASK; // Enable clock to DMA
// reset DMAMUX
DMAMUX0->CHCFG[dmaWriteChannel] = 0;
DMAMUX0->CHCFG[dmaWriteChannel] = DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(dmaXmitMuxSrc);
// Enable DMA features within the SPI registers
spi->C1 |= SPI_C1_SPTIE_MASK | // enable transmit-interrupt
SPI_C1_MSTR_MASK;
}
/*
* These chips use a one-wire protocol based on a sort of NRZ signalling- jas.
* Spec is 1.25usec +/- 600nsec => 650nsec to 1850nsec
*/
void WS2811::startDMA()
{
DMA0->DMA[dmaWriteChannel].DSR_BCR = DMA_DSR_BCR_DONE_MASK; // clear/reset DMA status
DMA0->DMA[dmaWriteChannel].SAR = (uint32_t)(void*)dmaBytes; // set source address
DMA0->DMA[dmaWriteChannel].DAR = (uint32_t)(void*)&(spi->D); // set dest address: SPI0_Data register
DMA0->DMA[dmaWriteChannel].DSR_BCR |= DMA_DSR_BCR_BCR_MASK & sizeof(dmaBytes); // length of transfer
DMA0->DMA[dmaWriteChannel].DCR = DMA_DCR_EINT_MASK | // enable interrupt on end of transfer
DMA_DCR_ERQ_MASK |
DMA_DCR_SINC_MASK |
DMA_DCR_SSIZE(0x01) |
DMA_DCR_DSIZE(0x01) |
// DMA_DCR_START_MASK |
DMA_DCR_D_REQ_MASK; // clear ERQ on end of transfer
dump_spi_settings(spi);
debugOut = 1;
while (!(spi->S & SPI_S_SPTEF_MASK))
__NOP();
spi->D = dmaBytes[0];
dma_done = false;
spi->C2 |= SPI_C2_TXDMAE_MASK;
// wait until done
// while (!(DMA0->DMA[dmaWriteChannel].DSR_BCR & DMA_DSR_BCR_DONE_MASK))
while (!dma_done)
__NOP();
spi->C2 &= ~SPI_C2_TXDMAE_MASK;
debugOut = 0;
dump_spi_settings(spi);
}
void WS2811::writePixel(uint8_t *p)
{
writeByte(*p++, dmaBytes + 0);
writeByte(*p++, dmaBytes + 16);
writeByte(*p, dmaBytes + 32);
// printf("DMA Bytes:\r\n");
// for (int i = 0; i < sizeof(dmaBytes); i++)
// printf(" %02x", dmaBytes[i]);
// printf("\r\n");
startDMA();
}
void WS2811::writeByte(uint8_t byte, uint8_t *dest)
{
for (uint8_t mask = 0x80; mask; mask >>= 1) {
if (mask & byte)
*dest++ = 0xff; // 8 high
else
*dest++ = 0xe0; // 3 high, 5 low
*dest++ = 0x00; // 8 more low
}
}
void WS2811::begin(void)
{
blank();
show();
}
void WS2811::blank(void)
{
memset(pixels, 0x00, numPixelBytes());
}
void WS2811::show(void)
{
uint16_t i, n = numPixels(); // 3 bytes per LED
uint8_t *p = pixels;
while (guardtime.read_us() < 50)
__NOP();
for (i=0; i<n; i++ ) {
writePixel(p);
pc.printf("%d> ", i);
pc.getc();
p += 3;
}
guardtime.reset();
}
extern "C" void DMA0IntHandler()
{
DMA0->DMA[dmaWriteChannel].DSR_BCR = DMA_DSR_BCR_DONE_MASK; // clear/reset DMA status
dma_done = true;
}
Ned Konz


Generic WS2811/WS2812