Function generator using the DAC output, using DMA alone.

Dependents:   DACDMAfuncgenlib

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

Committer:
Mischa
Date:
2013-12-29
Revision:
0:337ad0fe7734

File content as of revision 0:337ad0fe7734:

#include "assert.h"
#include "DMAFuncGen.h"

//DigitalOut led1(LED1);

DMAFuncGen::DMAFuncGen(MODDMA& dma, MODDMA::CHANNELS channel) : dma(dma) {
    conf.channelNum( channel );
    buffer_size = 0;
    buffer = NULL;
}

#define DAC_POWER_MODE  (0 << 16)  // DAC output power mode.

uint16_t DMAFuncGen::operator[](const uint16_t idx) {
    assert(buffer!=NULL);
    assert(idx<buffer_size);
    return buffer[idx] & 0xFFC0;
}

void DMAFuncGen::set(int idx, uint16_t x) {
    // V = (x>>6) × ((VrefP - VrefN)/1024) + VrefN; VrefN = 0V; VrefP = 3.3V;
    buffer[idx] = DAC_POWER_MODE | (x & 0xFFC0);
}

void DMAFuncGen::Connect() {
    // Connect DAC to pin
    LPC_PINCON->PINSEL1 &= ~(3UL <<20);
    LPC_PINCON->PINSEL1 |= 2UL <<20; //AOUT
    LPC_PINCON->PINMODE1 &= ~(3UL <<20);
    LPC_PINCON->PINMODE1 |= (2UL <<20);  // neither pull-up nor pull-down
    LPC_PINCON->PINMODE_OD0 &= ~(1UL <<26);  // normal (not open drain) mode
}
 
void DMAFuncGen::Disconnect() {
    LPC_PINCON->PINSEL1 &= ~(3UL <<20);
    LPC_PINCON->PINSEL1 |= 1UL <<20; //AD0.3, input
    LPC_PINCON->PINMODE1 &= ~(3UL <<20);
    LPC_PINCON->PINMODE1 |= (2UL <<20);  // neither pull-up nor pull-down
    LPC_PINCON->PINMODE_OD0 &= ~(1UL <<26);  // normal (not open drain) mode
}

void DMAFuncGen::Setup(void) {
    // Prepare the GPDMA system.
    lli.srcAddr( (uint32_t) buffer );
    lli.dstAddr( (uint32_t) &LPC_DAC->DACR );
    lli.nextLLI( (uint32_t) &lli);
    lli.control( dma.CxControl_TransferSize(buffer_size)
               | dma.CxControl_SBSize((uint32_t)MODDMA::_1) 
               | dma.CxControl_DBSize((uint32_t)MODDMA::_1) 
               | dma.CxControl_SWidth((uint32_t)MODDMA::word) 
               | dma.CxControl_DWidth((uint32_t)MODDMA::word) 
               | dma.CxControl_SI() 
               | dma.CxControl_I() );

    conf.srcMemAddr  ( (uint32_t) buffer )
      ->dstMemAddr   ( MODDMA::DAC )  // unnecessary?
      ->transferSize ( buffer_size )
      ->transferType ( MODDMA::m2p )
      ->dstConn      ( MODDMA::DAC )
      ->dmaLLI       ( (uint32_t) &lli )
      ->attach_tc    ( this, &DMAFuncGen::TC_callback ) 
      ->attach_err   ( this, &DMAFuncGen::ERR_callback );

    // Setup dma.
    if (!dma.Setup( &conf )) {
        error("Unexpected error during DMA.Setup(conf)");
    }
}        

float DMAFuncGen::Frequency() {
    float f;
    f=SystemCoreClock;
    const int divisors[4] = {4,1,2,8};
    f/=divisors[((LPC_SC->PCLKSEL0) >> 22) & 0x03];
    f/=buffer_size;
    f/=LPC_DAC->DACCNTVAL;
    return f;
}

void DMAFuncGen::SetFrequency(float f) {
    // Set the transfer frequency.
    float cntval;
    cntval = SystemCoreClock/(f*buffer_size);
    const int PCLK_DAC[4] = {1,2,0,3}; 
    int i=0;
    int divisor=1;
    while (((cntval/divisor)>65535) && (i<4)) {divisor*=2; i++; }
    if (i==4) { divisor=8; i=3; }
    LPC_SC->PCLKSEL0 &= ~(3UL<<22);
    LPC_SC->PCLKSEL0 |=  ((uint32_t) PCLK_DAC[i])<<22;
    LPC_DAC->DACCNTVAL = cntval/divisor;
}

void DMAFuncGen::Start() {
    dma.Enable( &conf );
    
    // Enable DMA, and TC interrupt
    LPC_DAC->DACCTRL = 3UL<<2; // DMA_ENA set, DMA_CNT set
}

void DMAFuncGen::Stop() {
    dma.haltAndWaitChannelComplete( (MODDMA::CHANNELS) conf.channelNum() );
    LPC_DAC->DACCTRL = 0UL<<2;
}

// Configuration callback on TC
void DMAFuncGen::TC_callback(void) {
    
    // Just show sending buffer complete.
    // led1 = !led1; 

    // Clear DMA IRQ flags.
    if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq(); 
}

// Configuration callback on Error
void DMAFuncGen::ERR_callback(void) {
    error("Unexpected error - DMA error callback.");
}