Demo using DMA to play recorded audio samples to the DAC (p18) on the mbed LPC1768 using the MODDMA library for the LPC1768.
main.cpp@1:a61a49613606, 2019-02-20 (annotated)
- Committer:
- 4180_1
- Date:
- Wed Feb 20 15:07:22 2019 +0000
- Revision:
- 1:a61a49613606
- Parent:
- 0:e4f991474a45
ver 0.1
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
4180_1 | 1:a61a49613606 | 1 | /* |
4180_1 | 1:a61a49613606 | 2 | * Demonstrates sending a flash buffer to the LPC1768 DAC using DMA |
4180_1 | 1:a61a49613606 | 3 | * from recorded audio data samples setup in flash memory |
4180_1 | 1:a61a49613606 | 4 | * Connect a speaker with a driver to Mbed pin 18. Main flashes LED1. |
4180_1 | 1:a61a49613606 | 5 | * LED3 and LED4 indicate which DMA (double) buffer is in use. |
4180_1 | 1:a61a49613606 | 6 | * https://os.mbed.com/users/4180_1/notebook/using-flash-to-play-audio-clips/ |
4180_1 | 1:a61a49613606 | 7 | * has additional info on using flash to store audio clips |
4180_1 | 1:a61a49613606 | 8 | */ |
4180_1 | 0:e4f991474a45 | 9 | #include "mbed.h" |
4180_1 | 1:a61a49613606 | 10 | //DMA library - based on DMA->DAC example4.h library code |
4180_1 | 1:a61a49613606 | 11 | #include "MODDMA.h" |
4180_1 | 1:a61a49613606 | 12 | //Cylon audio sample file in flash in special 32-bit format for DMA->DAC use |
4180_1 | 1:a61a49613606 | 13 | #include "DMADAC_cylonbyc.h" |
4180_1 | 0:e4f991474a45 | 14 | //see https://os.mbed.com/users/4180_1/notebook/using-flash-to-play-audio-clips/ |
4180_1 | 1:a61a49613606 | 15 | |
4180_1 | 1:a61a49613606 | 16 | DigitalOut led1(LED1); |
4180_1 | 1:a61a49613606 | 17 | DigitalOut led3(LED3); |
4180_1 | 1:a61a49613606 | 18 | DigitalOut led4(LED4); |
4180_1 | 1:a61a49613606 | 19 | |
4180_1 | 1:a61a49613606 | 20 | AnalogOut signal(p18); |
4180_1 | 1:a61a49613606 | 21 | |
4180_1 | 1:a61a49613606 | 22 | MODDMA dma; |
4180_1 | 1:a61a49613606 | 23 | MODDMA_Config *conf0, *conf1; |
4180_1 | 1:a61a49613606 | 24 | int DMA_size = 4095; //max DMA transfer size |
4180_1 | 1:a61a49613606 | 25 | int DMA_start_index = 0; //next starting address |
4180_1 | 1:a61a49613606 | 26 | int NUM_Remaining=0; //number of audio samples remaining to play |
4180_1 | 1:a61a49613606 | 27 | |
4180_1 | 1:a61a49613606 | 28 | void TC0_callback(void); |
4180_1 | 1:a61a49613606 | 29 | void TC1_callback(void); |
4180_1 | 1:a61a49613606 | 30 | void ERR0_callback(void); |
4180_1 | 1:a61a49613606 | 31 | void ERR1_callback(void); |
4180_1 | 1:a61a49613606 | 32 | |
4180_1 | 1:a61a49613606 | 33 | int main() |
4180_1 | 1:a61a49613606 | 34 | { |
4180_1 | 1:a61a49613606 | 35 | volatile int life_counter = 0; |
4180_1 | 0:e4f991474a45 | 36 | |
4180_1 | 1:a61a49613606 | 37 | // Prepare the GPDMA system for buffer0. |
4180_1 | 1:a61a49613606 | 38 | conf0 = new MODDMA_Config; |
4180_1 | 1:a61a49613606 | 39 | conf0 |
4180_1 | 1:a61a49613606 | 40 | ->channelNum ( MODDMA::Channel_0 ) |
4180_1 | 1:a61a49613606 | 41 | ->srcMemAddr ( (uint32_t) &sound_data) |
4180_1 | 1:a61a49613606 | 42 | ->dstMemAddr ( MODDMA::DAC ) |
4180_1 | 1:a61a49613606 | 43 | ->transferSize ( DMA_size ) |
4180_1 | 1:a61a49613606 | 44 | ->transferType ( MODDMA::m2p ) |
4180_1 | 1:a61a49613606 | 45 | ->dstConn ( MODDMA::DAC ) |
4180_1 | 1:a61a49613606 | 46 | ->attach_tc ( &TC0_callback ) |
4180_1 | 1:a61a49613606 | 47 | ->attach_err ( &ERR0_callback ) |
4180_1 | 1:a61a49613606 | 48 | ; // config end |
4180_1 | 1:a61a49613606 | 49 | // Prepare the GPDMA system for buffer1. |
4180_1 | 1:a61a49613606 | 50 | conf1 = new MODDMA_Config; |
4180_1 | 1:a61a49613606 | 51 | conf1 |
4180_1 | 1:a61a49613606 | 52 | ->channelNum ( MODDMA::Channel_1 ) |
4180_1 | 1:a61a49613606 | 53 | ->srcMemAddr ( (uint32_t) &sound_data[DMA_size] ) |
4180_1 | 1:a61a49613606 | 54 | ->dstMemAddr ( MODDMA::DAC ) |
4180_1 | 1:a61a49613606 | 55 | ->transferSize ( DMA_size ) |
4180_1 | 1:a61a49613606 | 56 | ->transferType ( MODDMA::m2p ) |
4180_1 | 1:a61a49613606 | 57 | ->dstConn ( MODDMA::DAC ) |
4180_1 | 1:a61a49613606 | 58 | ->attach_tc ( &TC1_callback ) |
4180_1 | 1:a61a49613606 | 59 | ->attach_err ( &ERR1_callback ) |
4180_1 | 1:a61a49613606 | 60 | ; // config end |
4180_1 | 0:e4f991474a45 | 61 | |
4180_1 | 1:a61a49613606 | 62 | // Calculating the transfer frequency: |
4180_1 | 1:a61a49613606 | 63 | // By default, the Mbed library sets the PCLK_DAC clock value |
4180_1 | 1:a61a49613606 | 64 | // to 24MHz. One complete sinewave cycle in each buffer is 360 |
4180_1 | 1:a61a49613606 | 65 | // points long. So, for a 1Hz wave we would need to transfer 360 |
4180_1 | 1:a61a49613606 | 66 | // values per second. That would be 24000000/360 which is approx |
4180_1 | 1:a61a49613606 | 67 | // 66,666. But that's no good! The count val is only 16bits in size |
4180_1 | 1:a61a49613606 | 68 | // so bare this in mind. If you need to go slower you will need to |
4180_1 | 1:a61a49613606 | 69 | // alter PCLK_DAC from CCLK/4 to CCLK/8. |
4180_1 | 1:a61a49613606 | 70 | LPC_DAC->DACCNTVAL = 2400; //around 11Khz audio sample rate |
4180_1 | 1:a61a49613606 | 71 | |
4180_1 | 1:a61a49613606 | 72 | // Prepare first configuration. |
4180_1 | 1:a61a49613606 | 73 | if (!dma.Prepare( conf0 )) { |
4180_1 | 1:a61a49613606 | 74 | error("Doh!"); |
4180_1 | 1:a61a49613606 | 75 | } |
4180_1 | 1:a61a49613606 | 76 | |
4180_1 | 1:a61a49613606 | 77 | // Begin (enable DMA and counter). Note, don't enable |
4180_1 | 1:a61a49613606 | 78 | // DBLBUF_ENA as we are using DMA double buffering. |
4180_1 | 1:a61a49613606 | 79 | LPC_DAC->DACCTRL |= (3UL << 2); |
4180_1 | 1:a61a49613606 | 80 | |
4180_1 | 1:a61a49613606 | 81 | while (1) { |
4180_1 | 1:a61a49613606 | 82 | // There's not a lot to do as DMA and interrupts are |
4180_1 | 1:a61a49613606 | 83 | // now handling the buffer transfers. So we'll just |
4180_1 | 1:a61a49613606 | 84 | // flash led1 to show the Mbed is alive and kicking. |
4180_1 | 1:a61a49613606 | 85 | if (life_counter++ > 1000000) { |
4180_1 | 1:a61a49613606 | 86 | led1 = !led1; // Show some sort of life. |
4180_1 | 1:a61a49613606 | 87 | life_counter = 0; |
4180_1 | 1:a61a49613606 | 88 | } |
4180_1 | 0:e4f991474a45 | 89 | } |
4180_1 | 0:e4f991474a45 | 90 | } |
4180_1 | 1:a61a49613606 | 91 | |
4180_1 | 1:a61a49613606 | 92 | // Configuration callback on TC |
4180_1 | 1:a61a49613606 | 93 | void TC0_callback(void) |
4180_1 | 1:a61a49613606 | 94 | { |
4180_1 | 1:a61a49613606 | 95 | |
4180_1 | 1:a61a49613606 | 96 | // Just show sending buffer0 complete. |
4180_1 | 1:a61a49613606 | 97 | led3 = !led3; |
4180_1 | 1:a61a49613606 | 98 | |
4180_1 | 1:a61a49613606 | 99 | // Get configuration pointer. |
4180_1 | 1:a61a49613606 | 100 | MODDMA_Config *config = dma.getConfig(); |
4180_1 | 1:a61a49613606 | 101 | |
4180_1 | 1:a61a49613606 | 102 | // Finish the DMA cycle by shutting down the channel. |
4180_1 | 1:a61a49613606 | 103 | dma.Disable( (MODDMA::CHANNELS)config->channelNum() ); |
4180_1 | 1:a61a49613606 | 104 | //Setup for next DMA chunk of audio data |
4180_1 | 1:a61a49613606 | 105 | DMA_start_index = DMA_start_index + DMA_size; |
4180_1 | 1:a61a49613606 | 106 | conf1->srcMemAddr ( (uint32_t) &sound_data[DMA_start_index]); |
4180_1 | 1:a61a49613606 | 107 | conf1->transferSize(DMA_size); |
4180_1 | 1:a61a49613606 | 108 | NUM_Remaining = NUM_ELEMENTS - DMA_start_index; |
4180_1 | 1:a61a49613606 | 109 | //Check for end of data and restart if needed |
4180_1 | 1:a61a49613606 | 110 | if (NUM_Remaining < DMA_size) { |
4180_1 | 1:a61a49613606 | 111 | conf1->transferSize(NUM_Remaining); |
4180_1 | 1:a61a49613606 | 112 | DMA_start_index = -DMA_size; |
4180_1 | 1:a61a49613606 | 113 | } |
4180_1 | 1:a61a49613606 | 114 | // re-config end |
4180_1 | 1:a61a49613606 | 115 | // Setup next DMA |
4180_1 | 1:a61a49613606 | 116 | // Prepare configuration. |
4180_1 | 1:a61a49613606 | 117 | if(!dma.Prepare( conf1 )) { |
4180_1 | 1:a61a49613606 | 118 | error("Bad DMA setup"); |
4180_1 | 1:a61a49613606 | 119 | led3=led4=1; |
4180_1 | 1:a61a49613606 | 120 | } |
4180_1 | 1:a61a49613606 | 121 | // Clear DMA IRQ flags. |
4180_1 | 1:a61a49613606 | 122 | if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq(); |
4180_1 | 1:a61a49613606 | 123 | } |
4180_1 | 1:a61a49613606 | 124 | |
4180_1 | 1:a61a49613606 | 125 | // Configuration callback on TC |
4180_1 | 1:a61a49613606 | 126 | void TC1_callback(void) |
4180_1 | 0:e4f991474a45 | 127 | { |
4180_1 | 1:a61a49613606 | 128 | |
4180_1 | 1:a61a49613606 | 129 | // Just show sending buffer1 complete. |
4180_1 | 1:a61a49613606 | 130 | led4 = !led4; |
4180_1 | 1:a61a49613606 | 131 | |
4180_1 | 1:a61a49613606 | 132 | // Get configuration pointer. |
4180_1 | 1:a61a49613606 | 133 | MODDMA_Config *config = dma.getConfig(); |
4180_1 | 1:a61a49613606 | 134 | |
4180_1 | 1:a61a49613606 | 135 | // Finish the DMA cycle by shutting down the channel. |
4180_1 | 1:a61a49613606 | 136 | dma.Disable( (MODDMA::CHANNELS)config->channelNum() ); |
4180_1 | 1:a61a49613606 | 137 | //Setup for next DMA chunk of audio data |
4180_1 | 1:a61a49613606 | 138 | DMA_start_index = DMA_start_index + DMA_size; |
4180_1 | 1:a61a49613606 | 139 | conf0->srcMemAddr ( (uint32_t) &sound_data[DMA_start_index]); |
4180_1 | 1:a61a49613606 | 140 | conf0->transferSize(DMA_size); |
4180_1 | 1:a61a49613606 | 141 | NUM_Remaining = NUM_ELEMENTS - DMA_start_index; |
4180_1 | 1:a61a49613606 | 142 | //Check for end of data and restart if needed |
4180_1 | 1:a61a49613606 | 143 | if (NUM_Remaining < DMA_size) { |
4180_1 | 1:a61a49613606 | 144 | conf0->transferSize(NUM_Remaining); |
4180_1 | 1:a61a49613606 | 145 | DMA_start_index = -DMA_size; |
4180_1 | 0:e4f991474a45 | 146 | } |
4180_1 | 1:a61a49613606 | 147 | // Swap to buffer0 |
4180_1 | 1:a61a49613606 | 148 | dma.Prepare( conf0 ); |
4180_1 | 1:a61a49613606 | 149 | |
4180_1 | 1:a61a49613606 | 150 | // Clear DMA IRQ flags. |
4180_1 | 1:a61a49613606 | 151 | if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq(); |
4180_1 | 0:e4f991474a45 | 152 | } |
4180_1 | 1:a61a49613606 | 153 | |
4180_1 | 1:a61a49613606 | 154 | // Configuration callback on Error |
4180_1 | 1:a61a49613606 | 155 | void ERR0_callback(void) |
4180_1 | 1:a61a49613606 | 156 | { |
4180_1 | 1:a61a49613606 | 157 | error("DMA 0 error"); |
4180_1 | 1:a61a49613606 | 158 | } |
4180_1 | 1:a61a49613606 | 159 | |
4180_1 | 1:a61a49613606 | 160 | // Configuration callback on Error |
4180_1 | 1:a61a49613606 | 161 | void ERR1_callback(void) |
4180_1 | 1:a61a49613606 | 162 | { |
4180_1 | 1:a61a49613606 | 163 | error("DMA 1 error"); |
4180_1 | 1:a61a49613606 | 164 | } |
4180_1 | 1:a61a49613606 | 165 |