Function generator using the DAC output, using DMA alone.
This library has been inspired by MODDMA example4.h.
Some differences compared with the MODDMA example: 1) this DMAFuncGen class can work using dma alone (no ISR required, although there is a callback routine included if you would like to synchronize something to the function generator), and 2) there is only one buffer (not two).
It is intended for the LPC1768. It has been tested and seems to work fine.
For a demo program, see
Import programDACDMAfuncgenlib
Generate a sine wave on the Analog Output using DMA alone.
DMAFuncGen.cpp@0:337ad0fe7734, 2013-12-29 (annotated)
- Committer:
- Mischa
- Date:
- Sun Dec 29 01:00:30 2013 +0000
- Revision:
- 0:337ad0fe7734
First version.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Mischa | 0:337ad0fe7734 | 1 | #include "assert.h" |
Mischa | 0:337ad0fe7734 | 2 | #include "DMAFuncGen.h" |
Mischa | 0:337ad0fe7734 | 3 | |
Mischa | 0:337ad0fe7734 | 4 | //DigitalOut led1(LED1); |
Mischa | 0:337ad0fe7734 | 5 | |
Mischa | 0:337ad0fe7734 | 6 | DMAFuncGen::DMAFuncGen(MODDMA& dma, MODDMA::CHANNELS channel) : dma(dma) { |
Mischa | 0:337ad0fe7734 | 7 | conf.channelNum( channel ); |
Mischa | 0:337ad0fe7734 | 8 | buffer_size = 0; |
Mischa | 0:337ad0fe7734 | 9 | buffer = NULL; |
Mischa | 0:337ad0fe7734 | 10 | } |
Mischa | 0:337ad0fe7734 | 11 | |
Mischa | 0:337ad0fe7734 | 12 | #define DAC_POWER_MODE (0 << 16) // DAC output power mode. |
Mischa | 0:337ad0fe7734 | 13 | |
Mischa | 0:337ad0fe7734 | 14 | uint16_t DMAFuncGen::operator[](const uint16_t idx) { |
Mischa | 0:337ad0fe7734 | 15 | assert(buffer!=NULL); |
Mischa | 0:337ad0fe7734 | 16 | assert(idx<buffer_size); |
Mischa | 0:337ad0fe7734 | 17 | return buffer[idx] & 0xFFC0; |
Mischa | 0:337ad0fe7734 | 18 | } |
Mischa | 0:337ad0fe7734 | 19 | |
Mischa | 0:337ad0fe7734 | 20 | void DMAFuncGen::set(int idx, uint16_t x) { |
Mischa | 0:337ad0fe7734 | 21 | // V = (x>>6) × ((VrefP - VrefN)/1024) + VrefN; VrefN = 0V; VrefP = 3.3V; |
Mischa | 0:337ad0fe7734 | 22 | buffer[idx] = DAC_POWER_MODE | (x & 0xFFC0); |
Mischa | 0:337ad0fe7734 | 23 | } |
Mischa | 0:337ad0fe7734 | 24 | |
Mischa | 0:337ad0fe7734 | 25 | void DMAFuncGen::Connect() { |
Mischa | 0:337ad0fe7734 | 26 | // Connect DAC to pin |
Mischa | 0:337ad0fe7734 | 27 | LPC_PINCON->PINSEL1 &= ~(3UL <<20); |
Mischa | 0:337ad0fe7734 | 28 | LPC_PINCON->PINSEL1 |= 2UL <<20; //AOUT |
Mischa | 0:337ad0fe7734 | 29 | LPC_PINCON->PINMODE1 &= ~(3UL <<20); |
Mischa | 0:337ad0fe7734 | 30 | LPC_PINCON->PINMODE1 |= (2UL <<20); // neither pull-up nor pull-down |
Mischa | 0:337ad0fe7734 | 31 | LPC_PINCON->PINMODE_OD0 &= ~(1UL <<26); // normal (not open drain) mode |
Mischa | 0:337ad0fe7734 | 32 | } |
Mischa | 0:337ad0fe7734 | 33 | |
Mischa | 0:337ad0fe7734 | 34 | void DMAFuncGen::Disconnect() { |
Mischa | 0:337ad0fe7734 | 35 | LPC_PINCON->PINSEL1 &= ~(3UL <<20); |
Mischa | 0:337ad0fe7734 | 36 | LPC_PINCON->PINSEL1 |= 1UL <<20; //AD0.3, input |
Mischa | 0:337ad0fe7734 | 37 | LPC_PINCON->PINMODE1 &= ~(3UL <<20); |
Mischa | 0:337ad0fe7734 | 38 | LPC_PINCON->PINMODE1 |= (2UL <<20); // neither pull-up nor pull-down |
Mischa | 0:337ad0fe7734 | 39 | LPC_PINCON->PINMODE_OD0 &= ~(1UL <<26); // normal (not open drain) mode |
Mischa | 0:337ad0fe7734 | 40 | } |
Mischa | 0:337ad0fe7734 | 41 | |
Mischa | 0:337ad0fe7734 | 42 | void DMAFuncGen::Setup(void) { |
Mischa | 0:337ad0fe7734 | 43 | // Prepare the GPDMA system. |
Mischa | 0:337ad0fe7734 | 44 | lli.srcAddr( (uint32_t) buffer ); |
Mischa | 0:337ad0fe7734 | 45 | lli.dstAddr( (uint32_t) &LPC_DAC->DACR ); |
Mischa | 0:337ad0fe7734 | 46 | lli.nextLLI( (uint32_t) &lli); |
Mischa | 0:337ad0fe7734 | 47 | lli.control( dma.CxControl_TransferSize(buffer_size) |
Mischa | 0:337ad0fe7734 | 48 | | dma.CxControl_SBSize((uint32_t)MODDMA::_1) |
Mischa | 0:337ad0fe7734 | 49 | | dma.CxControl_DBSize((uint32_t)MODDMA::_1) |
Mischa | 0:337ad0fe7734 | 50 | | dma.CxControl_SWidth((uint32_t)MODDMA::word) |
Mischa | 0:337ad0fe7734 | 51 | | dma.CxControl_DWidth((uint32_t)MODDMA::word) |
Mischa | 0:337ad0fe7734 | 52 | | dma.CxControl_SI() |
Mischa | 0:337ad0fe7734 | 53 | | dma.CxControl_I() ); |
Mischa | 0:337ad0fe7734 | 54 | |
Mischa | 0:337ad0fe7734 | 55 | conf.srcMemAddr ( (uint32_t) buffer ) |
Mischa | 0:337ad0fe7734 | 56 | ->dstMemAddr ( MODDMA::DAC ) // unnecessary? |
Mischa | 0:337ad0fe7734 | 57 | ->transferSize ( buffer_size ) |
Mischa | 0:337ad0fe7734 | 58 | ->transferType ( MODDMA::m2p ) |
Mischa | 0:337ad0fe7734 | 59 | ->dstConn ( MODDMA::DAC ) |
Mischa | 0:337ad0fe7734 | 60 | ->dmaLLI ( (uint32_t) &lli ) |
Mischa | 0:337ad0fe7734 | 61 | ->attach_tc ( this, &DMAFuncGen::TC_callback ) |
Mischa | 0:337ad0fe7734 | 62 | ->attach_err ( this, &DMAFuncGen::ERR_callback ); |
Mischa | 0:337ad0fe7734 | 63 | |
Mischa | 0:337ad0fe7734 | 64 | // Setup dma. |
Mischa | 0:337ad0fe7734 | 65 | if (!dma.Setup( &conf )) { |
Mischa | 0:337ad0fe7734 | 66 | error("Unexpected error during DMA.Setup(conf)"); |
Mischa | 0:337ad0fe7734 | 67 | } |
Mischa | 0:337ad0fe7734 | 68 | } |
Mischa | 0:337ad0fe7734 | 69 | |
Mischa | 0:337ad0fe7734 | 70 | float DMAFuncGen::Frequency() { |
Mischa | 0:337ad0fe7734 | 71 | float f; |
Mischa | 0:337ad0fe7734 | 72 | f=SystemCoreClock; |
Mischa | 0:337ad0fe7734 | 73 | const int divisors[4] = {4,1,2,8}; |
Mischa | 0:337ad0fe7734 | 74 | f/=divisors[((LPC_SC->PCLKSEL0) >> 22) & 0x03]; |
Mischa | 0:337ad0fe7734 | 75 | f/=buffer_size; |
Mischa | 0:337ad0fe7734 | 76 | f/=LPC_DAC->DACCNTVAL; |
Mischa | 0:337ad0fe7734 | 77 | return f; |
Mischa | 0:337ad0fe7734 | 78 | } |
Mischa | 0:337ad0fe7734 | 79 | |
Mischa | 0:337ad0fe7734 | 80 | void DMAFuncGen::SetFrequency(float f) { |
Mischa | 0:337ad0fe7734 | 81 | // Set the transfer frequency. |
Mischa | 0:337ad0fe7734 | 82 | float cntval; |
Mischa | 0:337ad0fe7734 | 83 | cntval = SystemCoreClock/(f*buffer_size); |
Mischa | 0:337ad0fe7734 | 84 | const int PCLK_DAC[4] = {1,2,0,3}; |
Mischa | 0:337ad0fe7734 | 85 | int i=0; |
Mischa | 0:337ad0fe7734 | 86 | int divisor=1; |
Mischa | 0:337ad0fe7734 | 87 | while (((cntval/divisor)>65535) && (i<4)) {divisor*=2; i++; } |
Mischa | 0:337ad0fe7734 | 88 | if (i==4) { divisor=8; i=3; } |
Mischa | 0:337ad0fe7734 | 89 | LPC_SC->PCLKSEL0 &= ~(3UL<<22); |
Mischa | 0:337ad0fe7734 | 90 | LPC_SC->PCLKSEL0 |= ((uint32_t) PCLK_DAC[i])<<22; |
Mischa | 0:337ad0fe7734 | 91 | LPC_DAC->DACCNTVAL = cntval/divisor; |
Mischa | 0:337ad0fe7734 | 92 | } |
Mischa | 0:337ad0fe7734 | 93 | |
Mischa | 0:337ad0fe7734 | 94 | void DMAFuncGen::Start() { |
Mischa | 0:337ad0fe7734 | 95 | dma.Enable( &conf ); |
Mischa | 0:337ad0fe7734 | 96 | |
Mischa | 0:337ad0fe7734 | 97 | // Enable DMA, and TC interrupt |
Mischa | 0:337ad0fe7734 | 98 | LPC_DAC->DACCTRL = 3UL<<2; // DMA_ENA set, DMA_CNT set |
Mischa | 0:337ad0fe7734 | 99 | } |
Mischa | 0:337ad0fe7734 | 100 | |
Mischa | 0:337ad0fe7734 | 101 | void DMAFuncGen::Stop() { |
Mischa | 0:337ad0fe7734 | 102 | dma.haltAndWaitChannelComplete( (MODDMA::CHANNELS) conf.channelNum() ); |
Mischa | 0:337ad0fe7734 | 103 | LPC_DAC->DACCTRL = 0UL<<2; |
Mischa | 0:337ad0fe7734 | 104 | } |
Mischa | 0:337ad0fe7734 | 105 | |
Mischa | 0:337ad0fe7734 | 106 | // Configuration callback on TC |
Mischa | 0:337ad0fe7734 | 107 | void DMAFuncGen::TC_callback(void) { |
Mischa | 0:337ad0fe7734 | 108 | |
Mischa | 0:337ad0fe7734 | 109 | // Just show sending buffer complete. |
Mischa | 0:337ad0fe7734 | 110 | // led1 = !led1; |
Mischa | 0:337ad0fe7734 | 111 | |
Mischa | 0:337ad0fe7734 | 112 | // Clear DMA IRQ flags. |
Mischa | 0:337ad0fe7734 | 113 | if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq(); |
Mischa | 0:337ad0fe7734 | 114 | } |
Mischa | 0:337ad0fe7734 | 115 | |
Mischa | 0:337ad0fe7734 | 116 | // Configuration callback on Error |
Mischa | 0:337ad0fe7734 | 117 | void DMAFuncGen::ERR_callback(void) { |
Mischa | 0:337ad0fe7734 | 118 | error("Unexpected error - DMA error callback."); |
Mischa | 0:337ad0fe7734 | 119 | } |
Mischa | 0:337ad0fe7734 | 120 |