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

main.cpp

Committer:
4180_1
Date:
2019-02-20
Revision:
1:a61a49613606
Parent:
0:e4f991474a45

File content as of revision 1:a61a49613606:

/*
 * 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"
//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/

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;

    // 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

    // 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;
        }
    }
}

// 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)
{

    // 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");
}