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.

Files at this revision

API Documentation at this revision

Comitter:
Mischa
Date:
Sun Dec 29 01:00:30 2013 +0000
Commit message:
First version.

Changed in this revision

DMAFuncGen.cpp Show annotated file Show diff for this revision Revisions of this file
DMAFuncGen.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DMAFuncGen.cpp	Sun Dec 29 01:00:30 2013 +0000
@@ -0,0 +1,120 @@
+#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.");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DMAFuncGen.h	Sun Dec 29 01:00:30 2013 +0000
@@ -0,0 +1,63 @@
+#ifndef MBED_DMAFUNCGEN_H
+#define MBED_DMAFUNCGEN_H
+
+#include "mbed.h"
+#include "MODDMA.h"
+
+//! DMAFuncGen class generates a waveform on Analog output using Direct Memory Access alone.
+class DMAFuncGen {
+public:
+    /// Create DMAFucGen instance
+    /// @param dma The dma to use.
+    /// @param channel The dma channel to use for transferring data to the D/A converter.
+    DMAFuncGen(MODDMA& dma, MODDMA::CHANNELS channel);
+
+    /// Number of data points in the waveform. Must be set by client.
+    int buffer_size;
+    /// Buffer for the binary data that will be transferred by DMA. Must be allocated by client.
+    uint32_t* buffer;
+
+    /// Set waveform value for a specified data point.
+    /// @param idx The index of the data point in #buffer, must be in range 0..#buffer_size-1.
+    /// @param x The value for the D/A, in the range 0..65535.
+    void set(int idx, uint16_t x);
+    /// Get waveform value for a specified data point
+    /// @param idx The index of the data point in the #buffer, in the range 0..#buffer_size-1.
+    /// @returns The value for the data point, in the range 0..65535.
+    uint16_t operator[](const uint16_t idx);
+    
+    /// Connect the D/A converter pin (enable output)
+    void Connect();
+    /// Disconnect the D/A converter pin (pin becomes tri-state)
+    void Disconnect();
+
+    /// Reference to the DMA instance   
+    MODDMA& dma;
+    
+    /// Set up the dma.
+    /// Must set #buffer and #buffer_size first.
+    void Setup(void);
+    
+    /// Set waveform frequency
+    /// @note Must set #buffer_size first.
+    /// @param f Frequency of a complete cycle of the waveform (in Hz). 
+    void SetFrequency(float f);
+    /// Read the actual waveform frequency that will be generated.
+    float Frequency();
+    
+    /// Start generating the waveform (start dma)
+    void Start();
+    /// Stop generating the waveform (but waits for the current cycle to complete)
+    void Stop();
+
+private:
+    MODDMA_Config conf;
+    MODDMA_LLI lli;
+
+    /// ISR routine, called on Terminal Count
+    void TC_callback(void);
+    /// ISR routine, called on dma ERRor
+    void ERR_callback(void);
+};
+
+#endif