Demo using DMA to play recorded audio samples to the DAC (p18) on the mbed LPC1768 using the MODDMA library for the LPC1768.

Dependencies:   mbed MODDMA

Revision:
1:a61a49613606
Parent:
0:e4f991474a45
--- a/main.cpp	Sat Oct 21 20:53:36 2017 +0000
+++ b/main.cpp	Wed Feb 20 15:07:22 2019 +0000
@@ -1,37 +1,165 @@
+/*
+ * Demonstrates sending a flash buffer to the LPC1768 DAC using DMA
+ * from recorded audio data samples setup in flash memory
+ * Connect a speaker with a driver to Mbed pin 18. Main flashes LED1.
+ * LED3 and LED4 indicate which DMA (double) buffer is in use.
+ * https://os.mbed.com/users/4180_1/notebook/using-flash-to-play-audio-clips/
+ * has additional info on using flash to store audio clips
+ */
 #include "mbed.h"
-AnalogOut speaker(p18);
-Ticker sampletick;
-DigitalOut myled(LED1);
-//Plays Audio Clip using Array in Flash
-//
-//setup const array in flash with audio values 
-//from free wav file conversion tool at
-//http://ccgi.cjseymour.plus.com/wavtocode/wavtocode.htm
+//DMA library - based on DMA->DAC example4.h library code
+#include "MODDMA.h"
+//Cylon audio sample file in flash in special 32-bit format for DMA->DAC use
+#include "DMADAC_cylonbyc.h"
 //see https://os.mbed.com/users/4180_1/notebook/using-flash-to-play-audio-clips/
-#include "cylonbyc.h"
+
+DigitalOut led1(LED1);
+DigitalOut led3(LED3);
+DigitalOut led4(LED4);
+
+AnalogOut signal(p18);
+
+MODDMA dma;
+MODDMA_Config *conf0, *conf1;
+int DMA_size = 4095; //max DMA transfer size
+int DMA_start_index = 0; //next starting address
+int NUM_Remaining=0; //number of audio samples remaining to play
+
+void TC0_callback(void);
+void TC1_callback(void);
+void ERR0_callback(void);
+void ERR1_callback(void);
+
+int main()
+{
+    volatile int life_counter = 0;
 
-#define sample_freq 11025.0
-//get and set the frequency from wav conversion tool GUI
-int i=0;
+    // Prepare the GPDMA system for buffer0.
+    conf0 = new MODDMA_Config;
+    conf0
+    ->channelNum    ( MODDMA::Channel_0 )
+    ->srcMemAddr    ( (uint32_t) &sound_data)
+    ->dstMemAddr    ( MODDMA::DAC )
+    ->transferSize  ( DMA_size )
+    ->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) &sound_data[DMA_size] )
+    ->dstMemAddr    ( MODDMA::DAC )
+    ->transferSize  ( DMA_size )
+    ->transferType  ( MODDMA::m2p )
+    ->dstConn       ( MODDMA::DAC )
+    ->attach_tc     ( &TC1_callback )
+    ->attach_err    ( &ERR1_callback )
+    ; // config end
 
-//interrupt routine to play next audio sample from array in flash
-void audio_sample ()
-{
-    speaker.write_u16(sound_data[i]);
-    i++;
-    if (i>= NUM_ELEMENTS) {
-        i = 0;
-        sampletick.detach();
-        myled = 0;
+    // 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.
+    LPC_DAC->DACCNTVAL = 2400; //around 11Khz audio sample rate
+
+    // 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;
+        }
     }
 }
-int main()
+
+// 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() );
+    //Setup for next DMA chunk of audio data
+    DMA_start_index = DMA_start_index + DMA_size;
+    conf1->srcMemAddr    ( (uint32_t) &sound_data[DMA_start_index]);
+    conf1->transferSize(DMA_size);
+    NUM_Remaining = NUM_ELEMENTS - DMA_start_index;
+    //Check for end of data and restart if needed
+    if (NUM_Remaining < DMA_size) {
+        conf1->transferSize(NUM_Remaining);
+        DMA_start_index = -DMA_size;
+    }
+    // re-config end
+    // Setup next DMA
+    // Prepare configuration.
+    if(!dma.Prepare( conf1 )) {
+        error("Bad DMA setup");
+        led3=led4=1;
+    }
+    // Clear DMA IRQ flags.
+    if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq();
+}
+
+// Configuration callback on TC
+void TC1_callback(void)
 {
-    while(1) {
-        myled = 1;
-//use a ticker to play send next audio sample value to D/A
-        sampletick.attach(&audio_sample, 1.0 / sample_freq);
-//can do other things while audio plays with timer interrupts
-        wait(10.0);
+
+    // 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() );
+        //Setup for next DMA chunk of audio data
+    DMA_start_index = DMA_start_index + DMA_size;
+    conf0->srcMemAddr    ( (uint32_t) &sound_data[DMA_start_index]);
+    conf0->transferSize(DMA_size);
+    NUM_Remaining = NUM_ELEMENTS - DMA_start_index;
+    //Check for end of data and restart if needed
+    if (NUM_Remaining < DMA_size) {
+        conf0->transferSize(NUM_Remaining);
+        DMA_start_index = -DMA_size;
     }
+    // Swap to buffer0
+    dma.Prepare( conf0 );
+
+    // Clear DMA IRQ flags.
+    if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq();
 }
+
+// Configuration callback on Error
+void ERR0_callback(void)
+{
+    error("DMA 0 error");
+}
+
+// Configuration callback on Error
+void ERR1_callback(void)
+{
+    error("DMA 1 error");
+}
+