Tomas Hübner
/
DMANeoPixel
Programming an array of NeoPixels using the GPDMA for maximum performance.
Revision 1:b6ae9e61d764, committed 2014-12-06
- 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()); +} +