Simple 8x8 LED Matrix controller which interfaces with a Processing GUI over serial to display sketches

Dependencies:   Multi_WS2811 mbed

Fork of Multi_WS2811_test by Ned Konz

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