Programming an array of NeoPixels using the GPDMA for maximum performance.

Dependencies:   MODDMA mbed

Files at this revision

API Documentation at this revision

Comitter:
tohu
Date:
Sat Dec 06 10:45:10 2014 +0000
Parent:
0:26e4dc5b5a7d
Child:
2:1f2f547a9991
Commit message:
Ickefungerande f?rstaversion

Changed in this revision

example4.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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/example4.h	Sat Dec 06 10:45:10 2014 +0000
@@ -0,0 +1,145 @@
+/*
+ * Demonstrates sending a buffer repeatedly to the DAC using DMA.
+ * Connect an oscilloscope to Mbed pin 18. This example doesn't
+ * output anything else (nothing on any serial ports).
+ */
+#include "mbed.h"
+#include "MODDMA.h"
+
+// Make the buffer size match the number of degrees
+// in a circle since we are going to output a sinewave.
+#define BUFFER_SIZE 360
+
+// Set DAC output power mode.
+#define DAC_POWER_MODE  (1 << 16)
+
+DigitalOut led1(LED1);
+DigitalOut led3(LED3);
+DigitalOut led4(LED4);
+
+int buffer[2][BUFFER_SIZE];
+
+AnalogOut signal(p18);
+
+MODDMA dma;
+MODDMA_Config *conf0, *conf1;
+
+void TC0_callback(void);
+void ERR0_callback(void);
+
+void TC1_callback(void);
+void ERR1_callback(void);
+
+int main() {
+    volatile int life_counter = 0;
+    
+    
+    // Prepare the GPDMA system for buffer0.
+    conf0 = new MODDMA_Config;
+    conf0
+     ->channelNum    ( MODDMA::Channel_0 )
+     ->srcMemAddr    ( (uint32_t) &buffer[0] )
+     ->dstMemAddr    ( MODDMA::DAC )
+     ->transferSize  ( 360 )
+     ->transferType  ( MODDMA::m2p )
+     ->dstConn       ( MODDMA::DAC )
+     ->attach_tc     ( &TC0_callback )
+     ->attach_err    ( &ERR0_callback )     
+    ; // config end
+    
+    
+    // Prepare the GPDMA system for buffer1.
+    conf1 = new MODDMA_Config;
+    conf1
+     ->channelNum    ( MODDMA::Channel_1 )
+     ->srcMemAddr    ( (uint32_t) &buffer[1] )
+     ->dstMemAddr    ( MODDMA::DAC )
+     ->transferSize  ( 360 )
+     ->transferType  ( MODDMA::m2p )
+     ->dstConn       ( MODDMA::DAC )
+     ->attach_tc     ( &TC1_callback )
+     ->attach_err    ( &ERR1_callback )     
+    ; // config end
+
+    
+    // Calculating the transfer frequency:
+    // By default, the Mbed library sets the PCLK_DAC clock value
+    // to 24MHz. One complete sinewave cycle in each buffer is 360
+    // points long. So, for a 1Hz wave we would need to transfer 360
+    // values per second. That would be 24000000/360 which is approx
+    // 66,666. But that's no good! The count val is only 16bits in size
+    // so bare this in mind. If you need to go slower you will need to
+    // alter PCLK_DAC from CCLK/4 to CCLK/8.
+    // For our demo we are going to have the sinewave run at 1kHz.
+    // That's 24000000/360000 which is approx 66. Experimentation
+    // however showed 65 to get closer to 1kHz (on my Mbed and scope 
+    // at least).
+    LPC_DAC->DACCNTVAL = 65; // 6500 for 10Hz
+
+    // Prepare first configuration.
+    if (!dma.Prepare( conf0 )) {
+        error("Doh!");
+    }
+    
+    // Begin (enable DMA and counter). Note, don't enable
+    // DBLBUF_ENA as we are using DMA double buffering.
+    LPC_DAC->DACCTRL |= (3UL << 2);
+    
+    while (1) { 
+        // There's not a lot to do as DMA and interrupts are
+        // now handling the buffer transfers. So we'll just
+        // flash led1 to show the Mbed is alive and kicking.
+        if (life_counter++ > 1000000) {
+            led1 = !led1; // Show some sort of life.
+            life_counter = 0;
+        }
+    } 
+}
+
+// Configuration callback on TC
+void TC0_callback(void) {
+    
+    // Just show sending buffer0 complete.
+    led3 = !led3; 
+        
+    // Get configuration pointer.
+    MODDMA_Config *config = dma.getConfig();
+    
+    // Finish the DMA cycle by shutting down the channel.
+    dma.Disable( (MODDMA::CHANNELS)config->channelNum() );
+   
+    // Swap to buffer1
+    dma.Prepare( conf1 );
+
+    // Clear DMA IRQ flags.
+    if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq(); 
+}
+
+// Configuration callback on Error
+void ERR0_callback(void) {
+    error("Oh no! My Mbed EXPLODED! :( Only kidding, go find the problem");
+}
+
+// Configuration callback on TC
+void TC1_callback(void) {
+    
+    // Just show sending buffer1 complete.
+    led4 = !led4; 
+        
+    // Get configuration pointer.
+    MODDMA_Config *config = dma.getConfig();
+    
+    // Finish the DMA cycle by shutting down the channel.
+    dma.Disable( (MODDMA::CHANNELS)config->channelNum() );
+    
+    // Swap to buffer0
+    dma.Prepare( conf0 );
+    
+    // Clear DMA IRQ flags.
+    if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq(); 
+}
+
+// Configuration callback on Error
+void ERR1_callback(void) {
+    error("Oh no! My Mbed EXPLODED! :( Only kidding, go find the problem");
+}
--- a/main.cpp	Sat Dec 06 07:39:42 2014 +0000
+++ b/main.cpp	Sat Dec 06 10:45:10 2014 +0000
@@ -1,10 +1,24 @@
 #include "mbed.h"
+#include "MODDMA.h"
 
 Serial pc(USBTX, USBRX);
 
-DigitalOut myled(LED1);
+DigitalOut led1(LED1);
+DigitalOut led2(LED2);
+DigitalOut led3(LED3);
+DigitalOut led4(LED4);
+
+
+#define IMAGE_SIZE 120
 
-uint8_t  smiley [120] = {
+// Set DAC output power mode.
+#define DAC_POWER_MODE  (1 << 16)
+
+//
+// IMAGE AND RENDER BUFFER
+//
+
+uint8_t  smiley [IMAGE_SIZE] = {
     0x0,0x0,0x1F,0x0,0x0,0x1F,0x0,0x0,0x1F,0x0,0x0,0x1F,0x0,0x0,0x1F,
     0x0,0x0,0x3E,0xFF,0xFF,0x0,0xFF,0xFF,0x0,0xFF,0xFF,0x0,0x0,0x0,0x3E,
     0xFF,0xFF,0x0,0x0,0x0,0x0,0xFF,0xFF,0x0,0x0,0x0,0x0,0xFF,0xFF,0x0,
@@ -15,51 +29,157 @@
     0x0,0x0,0xFF,0x0,0x0,0xFF,0x0,0x0,0xFF,0x0,0x0,0xFF,0x0,0x0,0xFF
 };
 
-uint8_t renderBuffer[360];
+uint8_t renderBuffer[IMAGE_SIZE*3];
+
+void copyImageToRender(uint8_t *image, uint8_t *buffer, size_t len);
+
+//
+// DMA STUFF
+//
 
-void copyImageToRender(uint8_t *image, uint8_t *buffer, size_t len)
-{
-    Timer timer;
-    
-    timer.start();
-    
-    int outIndex;
-    
-    for(int i = 0; i < len; i++)
-    {
-        outIndex = i*3;        
-        buffer[outIndex] = 0x92; // 10010010
-        buffer[outIndex] |= image[i]&128>>1;
-        buffer[outIndex] |= image[i]&64>>3;
-        buffer[outIndex] |= image[i]&32>>5;
-        
-        buffer[outIndex+1] = 0x49; // 01001001
-        buffer[outIndex+1] |= image[i]&16<<1;
-        buffer[outIndex+1] |= image[i]&8>>1;
-        
-        buffer[outIndex+1] = 0x24; // 00100100
-        buffer[outIndex+1] |= image[i]&4<<5;
-        buffer[outIndex+1] |= image[i]&2<<3;
-        buffer[outIndex+1] |= image[i]&1<<1;
-    }
-    
-    timer.stop();
-    
-    pc.printf("Time spend copying image: %d us\n", timer.read_us());
-}
+MODDMA dma;
+MODDMA_Config *dmaConfig;
+
+void TC_callback(void);
+void ERR_callback(void);
+
+bool flgCompleted = false;
+bool flgFailed = false;
 
 int main()
 {
     // Setup the serial port to print out results.
     pc.baud(115200);
 
-    while(1) {
-        
-        copyImageToRender(smiley, renderBuffer, 120);
+    led1 = false;
+    led2 = false;
+    led3 = false;
+    led4 = false;
+
+    // Set up the image to render
+    copyImageToRender(smiley, renderBuffer, IMAGE_SIZE);
+
+    // SET SPI OUTPUT PINS
+    LPC_PINCON->PINSEL1 |= 3; // SSEL0
+    LPC_PINCON->PINSEL1 |= (3<<2); // MISO0
+    LPC_PINCON->PINSEL1 |= (3<<4); // MOSI0
+
+    // Configure the DMA
+    dmaConfig = new MODDMA_Config;
+    dmaConfig
+    ->channelNum    ( MODDMA::Channel_0 )
+    ->srcMemAddr    ( (uint32_t) renderBuffer )
+    ->dstMemAddr    ( MODDMA::SSP0_Tx )
+    ->transferSize  ( IMAGE_SIZE*3 )
+    ->transferType  ( MODDMA::m2p )
+    ->dstConn       ( MODDMA::SSP0_Tx )
+    ->attach_tc     ( &TC_callback )
+    ->attach_err    ( &ERR_callback )
+    ;
+    
+    // Set up SSP0 (SPI)
+    LPC_GPDMA->DMACSoftSReq = 0xC;
+    
+    NVIC_EnableIRQ(DMA_IRQn);
         
-        myled = 1;
-        wait(0.2);
-        myled = 0;
-        wait(0.2);
+    // SET THE CLOCK AND POWER THE SSP0 UP
+    LPC_SC->PCLKSEL0 &= ~(2<<10); // PCLK_SSP0 = CCLK/2 (96MHz)
+    LPC_SSP0->CPSR = 10; // CPSDVSR = 10 (9600kHz)
+    // PCLK/(PSR*(SCR+1))
+    LPC_SSP0->CR0 =  (3<<8) | 7;  // SCR = 1 (2400kHz), CPOL = low, SPI frame format, 8 bits per frame
+    // Start the transfer
+    LPC_SSP0->DMACR |= 3; // TX/RXDMAE - enable transmit FIFO for DMA
+    
+    if(!dma.Prepare(dmaConfig)) {
+        error("Failed to prepare dma configuration!");
+    }
+    
+    dma.Enable(dmaConfig);
+    
+    
+    LPC_SSP0->CR1 = 2; // Enable the SSP0
+    
+    while(1) {
+
+        if(!flgCompleted)
+        {
+            led1 = !led1;
+        }
+        else
+        {
+            led2 = !led2;
+        }
+        if(flgFailed)
+        {
+            wait(0.1);            
+            led2 = !led2;
+        }
+
+        wait(1);
+        led4 = !led4;     
     }
 }
+
+void TC_callback(void)
+{
+    //
+    // TODO: FIX THIS UP!!!!
+    // DISMANTLE THE SSP0 (SPI)
+    //
+    
+    LPC_SSP0->CR1 = 0; // Diable the SSP0    ?
+    
+    // Just show sending buffer0 complete.
+    led3 = !led3; 
+        
+    // Get configuration pointer.
+    MODDMA_Config *config = dma.getConfig();
+    
+    // Finish the DMA cycle by shutting down the channel.
+    dma.haltAndWaitChannelComplete( (MODDMA::CHANNELS)config->channelNum());
+    dma.Disable( (MODDMA::CHANNELS)config->channelNum() );
+     
+    flgCompleted = true;
+    
+    // Clear DMA IRQ flags.
+    if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq();
+    if (dma.irqType() == MODDMA::ErrIrq) dma.clearErrIrq();
+}
+
+void ERR_callback(void)
+{
+    error("Something failed!!!!");
+    
+    flgFailed = true;
+}
+
+void copyImageToRender(uint8_t *image, uint8_t *buffer, size_t len)
+{
+    Timer timer;
+
+    timer.start();
+
+    int outIndex;
+
+    for(int i = 0; i < len; i++) {
+
+        outIndex = i*3;
+
+        // 10010010
+        buffer[outIndex] = 0x92 | image[i]&128>>1 | image[i]&64>>3 | image[i]&32>>5;
+        //buffer[outIndex] |= image[i]&128>>1 | image[i]&64>>3 | image[i]&32>>5;
+
+        // 01001001
+        buffer[outIndex+1] = 0x49 | image[i]&16<<1 | image[i]&8>>1;
+        //buffer[outIndex+1] |= image[i]&16<<1 | image[i]&8>>1;
+
+        // 00100100
+        buffer[outIndex+2] = 0x24 |image[i]&4<<5 | image[i]&2<<3 | image[i]&1<<1;
+        //buffer[outIndex+2] |= image[i]&4<<5 | image[i]&2<<3 | image[i]&1<<1;
+    }
+
+    timer.stop();
+
+    pc.printf("Time spend copying image: %d us\n", timer.read_us());
+}
+