Ned Konz / Mbed 2 deprecated Multi_WS2811_test

Dependencies:   Multi_WS2811 mbed MMA8451Q

Fork of WS2811 by Heroic Robotics

Revision:
20:b9d76e567637
Parent:
17:b4e9d8f4baa9
Child:
21:4541da183397
--- 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;
+}