jisakson3

Dependencies:   MODDMA mbed

Files at this revision

API Documentation at this revision

Comitter:
jisakson3
Date:
Mon Dec 05 02:01:53 2022 +0000
Commit message:
V1.0

Changed in this revision

MODDMA.lib Show annotated file Show diff for this revision Revisions of this file
SignalGenDAC.cpp Show annotated file Show diff for this revision Revisions of this file
SignalGenDAC.h Show annotated file Show diff for this revision Revisions of this file
SignalGenDefs.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MODDMA.lib	Mon Dec 05 02:01:53 2022 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/AjK/code/MODDMA/#97a16bf2ff43
--- /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;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SignalGenDAC.h	Mon Dec 05 02:01:53 2022 +0000
@@ -0,0 +1,96 @@
+//
+// Signal Generator DAC Driver
+//
+// Derived from AN10917: Memory to DAC data transfers using the LPC1700's DMA
+//
+// 
+#ifndef SIGNALGENDAC_H
+#define SIGNALGENDAC_H
+
+#include "mbed.h"
+
+#include "SignalGenDefs.h"       // access the waveform mode data type
+
+
+#define SIGNAL_MEM_ENTRIES 2048     // size of the DAC buffer
+
+/// The Signal Generator DAC Driver
+///
+/// This class provides the interface to first configure the DAC hardware characteristics,
+/// and then to define and control the DAC output.
+///
+/// A choice of waveforms is available (Sine, Square, Triangle, Sawtooth, and User Defined.
+///
+/// @todo add support for User Defined waveform.
+///
+/// @code
+/// SignalGenDAC g_signal;            // defaults to LPC1768 mbed module (p18 and 3.3v)
+/// 
+/// g_signal.PrepareWaveform(SG_SINE, 1000, 50, 2.2, 1.5);
+/// g_signal.Start();
+/// wait_ms(1000);
+/// g_signal.Stop();
+/// @endcode
+///
+class SignalGenDAC {
+
+public:
+
+    /// Constructor, which is used to define the hardware
+    /// 
+    /// The default parameters are based on the mbed LPC1768 micro, which has
+    /// AnalogOut on p18 and uses a 3.3v supply for the A/D reference.
+    ///
+    /// @param[in] aout is the analog output pin
+    /// @param[in] minV is based on the A/D low reference voltage (default 0.0)
+    /// @param[in] maxV is based on the A/D high reference voltage (default 3.3)
+    ///
+    SignalGenDAC(PinName aout = p18, float minV = 0.0, float maxV = 3.3);
+
+    /// Destructor
+    ///
+    ~SignalGenDAC();
+
+    /// Create the waveform in the private memory buffer that is used to DMA to the DAC
+    ///
+    /// @param[in] mode defines the waveform: Sine, Square, Triangle, Sawtooth, User
+    /// @param[in] frequency defines the desired frequency
+    /// @param[in] dutycycle defined the duty cycle of the waveform to be created. The value
+    ///             is range limited to 5 to 95 (representing 5 to 95 %).
+    /// @param[in] voltage is the peak-to-peak voltage, and it range limited to 0 to 3.0.
+    /// @param[in] offset is the offset voltage, and is range limited to 0 to 3.0.
+    ///
+    void PrepareWaveform(SG_Waveform mode, float frequency, float dutycycle, float voltage, float offset);
+
+    /// Start the signal, in either a oneshot, or continuous mode.
+    ///
+    /// @param[in] oneShot defaults false, which causes continuous mode. 
+    ///             When set true, one cycle is produced.
+    ///
+    void Start(bool oneShot = false);
+
+    /// Stop the signal, if it is running.
+    ///
+    void Stop(void);
+
+    /// Determine if the signal is running.
+    ///
+    /// @returns true if the signal is running.
+    ///
+    bool isRunning(void) { return isOn; }
+
+private:
+    bool isOn;              // tracks whether the signal is on or off
+    AnalogOut * aout;
+    float frequency;        // signal parameters
+    float dutycycle;
+    float voltage;
+    float offset;
+    float minV;             // Based on the A/D hardware
+    float maxV;             // Based on the A/D hardware
+    /// range limit a value.
+    float rangelimit(float value, float min, float max);
+    int numSamples;         // private container for number of samples
+};
+
+#endif // SIGNALGENDAC_H
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SignalGenDefs.h	Mon Dec 05 02:01:53 2022 +0000
@@ -0,0 +1,21 @@
+
+#ifndef SIGNALGENDEFS_H
+#define SIGNALGENDEFS_H
+
+/// Signal Generator Modes
+///
+/// This defines the modes. However, SG_KEYPAD is not an mode,
+/// it is a proprietary mechanism used for displaying the keypad, and
+/// is not intended to be used by the application.
+///
+typedef enum {
+    SG_SINE,        ///< Sine wave
+    SG_SQUARE,      ///< Square wave
+    SG_TRIANGLE,    ///< Triangle wave
+    SG_SAWTOOTH,    ///< Sawtooth
+    SG_USER,        ///< User defined waveform
+    SG_KEYPAD,      ///< This is an internal value, not for applications
+    SG_START,       ///< This is the start/stop/pulse button
+} SG_Waveform;
+
+#endif // SIGNALGENDEFS_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Mon Dec 05 02:01:53 2022 +0000
@@ -0,0 +1,31 @@
+
+
+// confirmed includes
+#include "mbed.h"
+#include "MODDMA.h"
+
+// testing includes
+#include "SignalGenDAC.h"
+#include "SignalGenDefs.h"
+
+#define PI 3.14159
+
+int main() {
+    RawSerial pc(USBTX, USBRX);
+    
+    SignalGenDAC signal;
+    signal.PrepareWaveform(SG_SINE, 100, 50, 3.0, 1.5);
+    wait(1.0);
+    
+    while (1) {
+        signal.Start();
+        wait(5.0);
+        signal.Stop();
+        wait(1.0);
+    }
+}
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Mon Dec 05 02:01:53 2022 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/mbed_official/code/mbed/builds/65be27845400
\ No newline at end of file