Demo using DMA to play recorded audio samples to the DAC (p18) on the mbed LPC1768 using the MODDMA library for the LPC1768.
Diff: main.cpp
- 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"); +} +