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 Heroic Robotics

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

/media/uploads/bikeNomad/img_0482.jpg

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;
}