jisakson3

Dependencies:   MODDMA mbed

Revision:
0:c1a4c1e9618c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SignalGenDAC.cpp	Mon Dec 05 02:01:53 2022 +0000
@@ -0,0 +1,200 @@
+//
+// Signal Generate DAC Driver
+//
+// Derived from AN10917: Memory to DAC data transfers using the LPC1700's DMA
+// 
+
+#include "SignalGenDAC.h"
+
+#define PI 3.14159      // for the sine-wave
+
+/// The linked list structure used to control the DMA transfer
+///
+typedef struct {
+    uint32_t  source;     /// start of source area
+    uint32_t  destination;/// start of destination area
+    uint32_t  next;       /// address of next strLLI in chain
+    uint32_t  control;    /// DMACCxControl register
+} LinkListItem_t;    
+
+/// The DACsignal memory, which is DMA sent to the DAC to play the waveform,
+/// produced by scaling the VoltSignal array
+///
+float VoltSignal[SIGNAL_MEM_ENTRIES] __attribute__ ((section("AHBSRAM0")));
+signed long DACsignal[SIGNAL_MEM_ENTRIES] __attribute__ ((section("AHBSRAM0")));
+
+/// The linked list item record, used by the DMA engine to decide how to play
+///
+LinkListItem_t llio __attribute__ ((section("AHBSRAM0")));
+
+
+SignalGenDAC::SignalGenDAC(PinName _aout, float _minV, float _maxV) :
+    minV(_minV), maxV(_maxV) {
+    aout = new AnalogOut(_aout);
+}
+
+SignalGenDAC::~SignalGenDAC() {
+}
+
+void SignalGenDAC::Start(bool oneShot) {
+    printf("Start(%d) w/%d samples\r\n", oneShot ? 1 : 0, numSamples);
+    isOn = (oneShot) ? false : true;
+    
+    llio.source = (uint32_t)DACsignal;
+    llio.destination = (uint32_t)&LPC_DAC->DACR;
+    llio.next = (uint32_t)&llio;
+    llio.control = (1<<26) | (2<<21) | (2<<18) | numSamples;
+
+    LPC_SC->PCONP |= (1<<29);
+
+    /* Enable GPDMA  and sync logic */
+    LPC_GPDMA->DMACConfig        = 1;
+    LPC_GPDMA->DMACSync          = (1<<6); 
+
+    /* Load DMA Channel0 */
+    LPC_GPDMACH0->DMACCSrcAddr   = (uint32_t)DACsignal;
+    LPC_GPDMACH0->DMACCDestAddr  = (uint32_t)&LPC_DAC->DACR;
+    LPC_GPDMACH0->DMACCLLI       = (oneShot) ? 0 : (uint32_t)&llio;
+
+    int playSampleCount = numSamples + oneShot;
+    LPC_GPDMACH0->DMACCControl = playSampleCount  // transfer size (0 - 11) = 64
+                  | (0 << 12)         // source burst size (12 - 14) = 1
+                  | (0 << 15)         // destination burst size (15 - 17) = 1
+                  | (2 << 18)         // source width (18 - 20) = 32 bit
+                  | (2 << 21)         // destination width (21 - 23) = 32 bit
+                  | (0 << 24)         // source AHB select (24) = AHB 0
+                  | (0 << 25)         // destination AHB select (25) = AHB 0
+                  | (1 << 26)         // source increment (26) = increment
+                  | (0 << 27)         // destination increment (27) = no increment
+                  | (0 << 28)         // mode select (28) = access in user mode
+                  | (0 << 29)         // (29) = access not bufferable
+                  | (0 << 30)         // (30) = access not cacheable
+                  | (0 << 31);        // terminal count interrupt disabled
+
+    LPC_GPDMACH0->DMACCConfig = 1
+                  | (0 << 1)          // source peripheral (1 - 5) = none
+                  | (7 << 6)          // destination peripheral (6 - 10) = DAC
+                  | (1 << 11)         // flow control (11 - 13) = mem to per
+                  | (0 << 14)         // (14) = mask out error interrupt
+                  | (0 << 15)         // (15) = mask out terminal count interrupt
+                  | (0 << 16)         // (16) = no locked transfers
+                  | (0 << 18);        // (27) = no HALT
+
+    /* DACclk = 25 MHz, so 10 usec interval */
+    LPC_DAC->DACCNTVAL = 18;               // 16-bit reload value
+    /* DMA, timer running, dbuff */
+    LPC_DAC->DACCTRL = 
+          1<<3          // DMA_ENA dma burst is enabled
+        | 1<<2          // CNT_ENA Timeout couner is enabled
+        | 1<<1;         // DBLBUF_ENA double-buffering enabled
+}
+
+void SignalGenDAC::Stop(void) {
+    printf("Stop()\r\n");
+    LPC_GPDMACH0->DMACCLLI       = 0;
+    while (LPC_GPDMACH0->DMACCConfig & 1)
+        wait_ms(1);
+    aout->write(offset / maxV);
+    isOn = false;
+}
+
+
+void SignalGenDAC::PrepareWaveform(SG_Waveform mode, float _frequency, float _dutycycle, float _voltage, float _offset) {
+    int x, dcCount, firstQtr, lastQtr;
+    frequency = _frequency;
+    dutycycle = _dutycycle;
+    voltage = _voltage;
+    offset = _offset;
+    float upp = rangelimit(offset + voltage/2, minV, maxV);
+    float mid = rangelimit(offset, minV, maxV);
+    float low = rangelimit(offset - voltage/2, minV, maxV);
+    float v;
+    numSamples = 128;    // Ideally, compute this based on the frequency for good resolution
+    dcCount = dutycycle/100.0 * numSamples;
+    firstQtr = dcCount / 2;
+    lastQtr = dcCount + (numSamples - dcCount)/2;
+    
+    // Set the timebased based on the frequency
+    if (isOn) {
+        Stop();
+    }
+    printf("Generate wave for mode: %d\r\n", mode);
+    switch (mode) {
+        case SG_SINE:
+            for (x=0; x<numSamples; x++) {
+                if (x < dcCount) {
+                    v = offset + voltage/2 * sin(x * 1 * PI / dcCount);
+                } else {
+                    v = offset - voltage/2 * sin((x - dcCount) * 1 * PI / (numSamples-dcCount));
+                }
+                v = rangelimit(v, minV, maxV);
+                VoltSignal[x] = v;
+            }
+            VoltSignal[numSamples] = rangelimit(offset, minV, maxV);
+            break;
+        case SG_SQUARE:
+            for (x=0; x<numSamples; x++) {
+                if (0 && x == 0) {
+                    v = rangelimit(offset, minV, maxV);                    
+                } else if (x < dcCount) {
+                    v = rangelimit(offset + voltage/2, minV, maxV);
+                } else {
+                    v = rangelimit(offset - voltage/2, minV, maxV);
+                }
+                VoltSignal[x] = v;
+            }
+            VoltSignal[numSamples] = rangelimit(offset, minV, maxV);
+            break;
+        case SG_TRIANGLE:
+            for (x=0; x<numSamples; x++) {
+                if (x < firstQtr) {
+                    v = voltage/2 * (float)x/(firstQtr);
+                    v += offset;
+                } else if (x < dcCount) {
+                    v = voltage/2 * (float)x/(firstQtr);
+                    v = voltage - (v - offset);
+                } else if (x < lastQtr) {
+                    v = voltage * (float)(x - dcCount)/(numSamples - dcCount);
+                    v = offset - v;
+                } else {
+                    v = voltage * (float)(x - dcCount)/(numSamples - dcCount);
+                    v = v + offset - voltage;
+                }
+                VoltSignal[x] = v;
+            }
+            VoltSignal[numSamples] = rangelimit(offset, minV, maxV);
+            break;
+        case SG_SAWTOOTH:
+            for (x=0; x<numSamples; x++) {
+                if (x < dcCount) {
+                    v = offset - voltage/2 + (float)x/dcCount * voltage/2;
+                } else {
+                    v = offset + (float)(x - dcCount)/(numSamples - dcCount) *  voltage/2;
+                }
+                v = rangelimit(v, minV, maxV);
+                VoltSignal[x] = v;
+            }
+            VoltSignal[numSamples] = rangelimit(offset, minV, maxV);
+            break;
+        case SG_USER:
+            break;
+    }
+    //printf("DAC Data %3.2f %3.2f\r\n", voltage, offset);
+    for (x=0; x<=numSamples; x++) {
+        DACsignal[x] = ((uint16_t)(VoltSignal[x]/maxV * 1023) << 6);
+        printf("%3d, %5.3f, %d\r\n", x, VoltSignal[x], DACsignal[x]);
+    }
+    if (!isOn) {
+        aout->write(offset / maxV);
+    }
+}
+
+float SignalGenDAC::rangelimit(float value, float min, float max) {
+    if (value < min)
+        return min;
+    else if (value > max)
+        return max;
+    else
+        return value;
+}
+