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.

Files at this revision

API Documentation at this revision

Comitter:
bikeNomad
Date:
Sat Dec 21 04:32:21 2013 +0000
Parent:
19:600deef36348
Child:
21:4541da183397
Commit message:
still trying to get DMA to work

Changed in this revision

WS2811.cpp Show annotated file Show diff for this revision Revisions of this file
WS2811.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- a/WS2811.cpp	Fri Dec 06 06:58:12 2013 +0000
+++ b/WS2811.cpp	Sat Dec 21 04:32:21 2013 +0000
@@ -7,52 +7,107 @@
 
 #include "LedStrip.h"
 #include "WS2811.h"
+extern void dump_spi_settings(SPI_Type const *spi);
+extern Serial pc;
+extern DigitalOut debugOut;
 
-WS2811::WS2811(int n, PinName mosi, PinName sclk) :
+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(mosi, NC, sclk)
+    spi(_spi),
+    mosi(_mosi)
 {
-#ifdef TARGET_KL25Z
-    spi.format(8, 3);
-#else
-    spi.format(16, 3);
-#endif
-    spi.frequency(800e3 * 16);  // 12.8 MHz => 800KHz bit rate
+    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.
+ * These chips use a one-wire protocol based on a sort of NRZ signalling- jas.
+ * Spec is 1.25usec +/- 600nsec => 650nsec to 1850nsec
  */
 
-inline void WS2811::writebit(bool bit)
+
+void WS2811::startDMA()
 {
-    if (bit) {
-#ifdef TARGET_KL25Z
-        spi.write(0xff);    // 8 high
-        spi.write(0x00);    // 8 low
-#else
-        spi.write(0xff00);
-#endif
-    } else {
-#ifdef TARGET_KL25Z
-        spi.write(0xe0);    // 3 high, 5 low
-        spi.write(0x00);    // +8 low
-#else
-        spi.write(0xe000);
-#endif
-    }
+    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::write(uint8_t byte)
+void WS2811::writePixel(uint8_t *p)
 {
-    writebit(byte & 0x80);
-    writebit(byte & 0x40);
-    writebit(byte & 0x20);
-    writebit(byte & 0x10);
-    writebit(byte & 0x08);
-    writebit(byte & 0x04);
-    writebit(byte & 0x02);
-    writebit(byte & 0x01);
+    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)
@@ -66,17 +121,25 @@
     memset(pixels, 0x00, numPixelBytes());
 }
 
+
 void WS2811::show(void)
 {
-    uint16_t i, nl3 = numPixelBytes(); // 3 bytes per LED
+    uint16_t i, n = numPixels(); // 3 bytes per LED
+    uint8_t *p = pixels;
     while (guardtime.read_us() < 50)
-        /* spin */;
-    __disable_irq();
-    for (i=0; i<nl3; i++ ) {
-        write(pixels[i]);
+        __NOP();
+    for (i=0; i<n; i++ ) {
+        writePixel(p);
+        pc.printf("%d> ", i);
+        pc.getc();
+        p += 3;
     }
-    __enable_irq();
     guardtime.reset();
 }
 
+extern "C" void DMA0IntHandler()
+{
+    DMA0->DMA[dmaWriteChannel].DSR_BCR = DMA_DSR_BCR_DONE_MASK;     // clear/reset DMA status
+    dma_done = true;
+}
 
--- a/WS2811.h	Fri Dec 06 06:58:12 2013 +0000
+++ b/WS2811.h	Sat Dec 21 04:32:21 2013 +0000
@@ -16,27 +16,35 @@
 #define MBED_WS2811_H
 
 #ifdef TARGET_KL25Z
+// SPI0 max frequency = fBUS/2 = 24MHz
+// SPI1 max frequency = fSYS/2 = 12MHz
+// but watch out for SPI1 errata using DMA!
+// SPI0: PTA16, PTA17, PTC6, PTC7, PTD2, PTD3
+// SPI1: PTB16, PTB17, PTD6, PTD7, PTE1, PTE3
 #define MOSI_DEFAULT PTD2
-#define SCLK_DEFAULT PTC5
+// SPI0: PTA15, PTC5, PTD1/ LED_BLUE
+// SPI1: PTB11, PTD5, PTE2
+#define SCLK_DEFAULT PTD1
 #else
 #endif
 
 class WS2811 : public LedStrip
 {
 public:
-    WS2811(int n, PinName mosi = MOSI_DEFAULT, PinName sclk = SCLK_DEFAULT);
+    WS2811(int n, SPI_Type *_spi, PinName mosi = MOSI_DEFAULT, PinName sclk = SCLK_DEFAULT);
     virtual void begin(void);
     virtual void show(void);
     virtual void blank(void);
 
 private:
-    SPI spi;            // to do actual communication
+    SPI_Type *spi;
+    PinName mosi;
+    uint8_t dmaBytes[16*3]; // enough for DMA'ing 1 RGB LED
 
     Timer guardtime;
-    uint32_t bogocal;
 
-    void write(uint8_t byte);
-    void writebit(bool bit);
-    void celldelay(void);
+    void writePixel(uint8_t *p);
+    void writeByte(uint8_t byte, uint8_t *dest);
+    void startDMA();
 };
 #endif
\ No newline at end of file
--- a/main.cpp	Fri Dec 06 06:58:12 2013 +0000
+++ b/main.cpp	Sat Dec 21 04:32:21 2013 +0000
@@ -2,23 +2,71 @@
 #include "WS2811.h"
 #include "Colors.h"
 
+const unsigned nLEDs = 60*4;
+
+const PinName dataOutput = PTD2;
+const PinName sckOutput = PTD1;
+SPI_Type *spiPort = SPI0;
+
+DigitalOut debugOut(PTD3);
+Serial pc(USBTX, USBRX);
+
+// @brief sets different colors in each of the LEDs of a strip
+// @param strip the light strip
+// @param sat saturation, 0.0 - 1.0
+// @param brite brightness, 0.0 - 1.0
+// @param hueShift shift, 0.0 - 1.0 is equivalent to 0 - 360 degrees
+void showRainbow(WS2811 &strip, float sat, float brite, float hueShift)
+{
+    unsigned nLEDs = strip.numPixels();
+    for (unsigned i = 0; i < nLEDs; i++) {
+        uint8_t r, g, b;
+        float hue = ((float)i / (float)nLEDs) + hueShift;
+        HSBtoRGB(hue, sat, brite, &r, &g, &b);
+        strip.setPixelColor(i, LedStrip::Color(r, g, b));
+    }
+    strip.begin();
+}
+void showTestPattern(WS2811 &strip)
+{
+    unsigned nLEDs = strip.numPixels();
+    for (unsigned i = 0; i < nLEDs; i++) {
+        strip.setPixelColor(i, LedStrip::Color(0xff,0,0x55));
+    }
+    strip.show();
+}
+void dump_spi_settings(SPI_Type const *spi)
+{
+    pc.printf("SPI C1=%#02x, C2=%#02x, BR=%d, S=%#02x, D=%#02x, M=%02x\r\n", spi->C1, spi->C2, spi->BR, spi->S, spi->D, spi->M);
+    pc.printf("DMA DSR_BCR=%#08lx, SAR=%p, DAR=%p, DCR=%#08lx\r\n",
+              (unsigned long)DMA0->DMA[0].DSR_BCR,
+              DMA0->DMA[0].SAR,
+              DMA0->DMA[0].DAR,
+              DMA0->DMA[0].DCR);
+}
+
 int main(void)
 {
-    const unsigned nLEDs = 60*4;
+    pc.baud(115200);
+    pc.printf("LEDs: %d\r\n", nLEDs);
+    debugOut = 0;
 
-    //                NLEDs  MOSI SCK
-    WS2811 lightStrip(nLEDs, PTD2, PTD3);
+    //                NLEDs  MOSI        SCK
+    WS2811 lightStrip(nLEDs, spiPort, dataOutput, sckOutput);
+
+    lightStrip.begin();
+    dump_spi_settings(spiPort);
 
-    float sat = 1.0;
-    float brite = 0.25;
-    for (float hueShift = 0.0; hueShift < 10.0; hueShift += 1.0 / 20) {
-        for (unsigned i = 0; i < nLEDs; i++) {
-            uint8_t r, g, b;
-            float hue = ((float)i / (float)nLEDs) + hueShift;
-            HSBtoRGB(hue, sat, brite, &r, &g, &b);
-            lightStrip.setPixelColor(i, LedStrip::Color(r, g, b));
-        }
-        lightStrip.begin();
-        wait(0.5);
-    }
+    // float sat = 1.0;
+    // float brite = 0.25;
+    // float hueShift = 0.0;
+    // float hueShiftIncrement = 1.0 / (360.0 / 30.0);
+    for (;;) {
+        // debugOut = 1;
+        // showRainbow(lightStrip, sat, brite, hueShift);
+        showTestPattern(lightStrip);
+
+        // debugOut = 0;
+        // hueShift += hueShiftIncrement;
+   }
 }
\ No newline at end of file
--- a/mbed.bld	Fri Dec 06 06:58:12 2013 +0000
+++ b/mbed.bld	Sat Dec 21 04:32:21 2013 +0000
@@ -1,1 +1,1 @@
-http://mbed.org/users/mbed_official/code/mbed/builds/9c8f0e3462fb
\ No newline at end of file
+http://mbed.org/users/mbed_official/code/mbed/builds/dc225afb6914
\ No newline at end of file