Jacob Isakson
/
AFG_project
jisakson3
Revision 0:c1a4c1e9618c, committed 24 months ago
- Comitter:
- jisakson3
- Date:
- Mon Dec 05 02:01:53 2022 +0000
- Commit message:
- V1.0
Changed in this revision
diff -r 000000000000 -r c1a4c1e9618c MODDMA.lib --- /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
diff -r 000000000000 -r c1a4c1e9618c SignalGenDAC.cpp --- /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; +} +
diff -r 000000000000 -r c1a4c1e9618c SignalGenDAC.h --- /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
diff -r 000000000000 -r c1a4c1e9618c SignalGenDefs.h --- /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
diff -r 000000000000 -r c1a4c1e9618c main.cpp --- /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); + } +} + + + + +
diff -r 000000000000 -r c1a4c1e9618c mbed.bld --- /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