Carson Brown
/
AFG_project
Waveform generator
SignalGenDAC.cpp@0:c1a4c1e9618c, 22 months ago (annotated)
- Committer:
- jisakson3
- Date:
- Mon Dec 05 02:01:53 2022 +0000
- Revision:
- 0:c1a4c1e9618c
- Child:
- 1:2fa202e02c48
V1.0
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
jisakson3 | 0:c1a4c1e9618c | 1 | // |
jisakson3 | 0:c1a4c1e9618c | 2 | // Signal Generate DAC Driver |
jisakson3 | 0:c1a4c1e9618c | 3 | // |
jisakson3 | 0:c1a4c1e9618c | 4 | // Derived from AN10917: Memory to DAC data transfers using the LPC1700's DMA |
jisakson3 | 0:c1a4c1e9618c | 5 | // |
jisakson3 | 0:c1a4c1e9618c | 6 | |
jisakson3 | 0:c1a4c1e9618c | 7 | #include "SignalGenDAC.h" |
jisakson3 | 0:c1a4c1e9618c | 8 | |
jisakson3 | 0:c1a4c1e9618c | 9 | #define PI 3.14159 // for the sine-wave |
jisakson3 | 0:c1a4c1e9618c | 10 | |
jisakson3 | 0:c1a4c1e9618c | 11 | /// The linked list structure used to control the DMA transfer |
jisakson3 | 0:c1a4c1e9618c | 12 | /// |
jisakson3 | 0:c1a4c1e9618c | 13 | typedef struct { |
jisakson3 | 0:c1a4c1e9618c | 14 | uint32_t source; /// start of source area |
jisakson3 | 0:c1a4c1e9618c | 15 | uint32_t destination;/// start of destination area |
jisakson3 | 0:c1a4c1e9618c | 16 | uint32_t next; /// address of next strLLI in chain |
jisakson3 | 0:c1a4c1e9618c | 17 | uint32_t control; /// DMACCxControl register |
jisakson3 | 0:c1a4c1e9618c | 18 | } LinkListItem_t; |
jisakson3 | 0:c1a4c1e9618c | 19 | |
jisakson3 | 0:c1a4c1e9618c | 20 | /// The DACsignal memory, which is DMA sent to the DAC to play the waveform, |
jisakson3 | 0:c1a4c1e9618c | 21 | /// produced by scaling the VoltSignal array |
jisakson3 | 0:c1a4c1e9618c | 22 | /// |
jisakson3 | 0:c1a4c1e9618c | 23 | float VoltSignal[SIGNAL_MEM_ENTRIES] __attribute__ ((section("AHBSRAM0"))); |
jisakson3 | 0:c1a4c1e9618c | 24 | signed long DACsignal[SIGNAL_MEM_ENTRIES] __attribute__ ((section("AHBSRAM0"))); |
jisakson3 | 0:c1a4c1e9618c | 25 | |
jisakson3 | 0:c1a4c1e9618c | 26 | /// The linked list item record, used by the DMA engine to decide how to play |
jisakson3 | 0:c1a4c1e9618c | 27 | /// |
jisakson3 | 0:c1a4c1e9618c | 28 | LinkListItem_t llio __attribute__ ((section("AHBSRAM0"))); |
jisakson3 | 0:c1a4c1e9618c | 29 | |
jisakson3 | 0:c1a4c1e9618c | 30 | |
jisakson3 | 0:c1a4c1e9618c | 31 | SignalGenDAC::SignalGenDAC(PinName _aout, float _minV, float _maxV) : |
jisakson3 | 0:c1a4c1e9618c | 32 | minV(_minV), maxV(_maxV) { |
jisakson3 | 0:c1a4c1e9618c | 33 | aout = new AnalogOut(_aout); |
jisakson3 | 0:c1a4c1e9618c | 34 | } |
jisakson3 | 0:c1a4c1e9618c | 35 | |
jisakson3 | 0:c1a4c1e9618c | 36 | SignalGenDAC::~SignalGenDAC() { |
jisakson3 | 0:c1a4c1e9618c | 37 | } |
jisakson3 | 0:c1a4c1e9618c | 38 | |
jisakson3 | 0:c1a4c1e9618c | 39 | void SignalGenDAC::Start(bool oneShot) { |
jisakson3 | 0:c1a4c1e9618c | 40 | printf("Start(%d) w/%d samples\r\n", oneShot ? 1 : 0, numSamples); |
jisakson3 | 0:c1a4c1e9618c | 41 | isOn = (oneShot) ? false : true; |
jisakson3 | 0:c1a4c1e9618c | 42 | |
jisakson3 | 0:c1a4c1e9618c | 43 | llio.source = (uint32_t)DACsignal; |
jisakson3 | 0:c1a4c1e9618c | 44 | llio.destination = (uint32_t)&LPC_DAC->DACR; |
jisakson3 | 0:c1a4c1e9618c | 45 | llio.next = (uint32_t)&llio; |
jisakson3 | 0:c1a4c1e9618c | 46 | llio.control = (1<<26) | (2<<21) | (2<<18) | numSamples; |
jisakson3 | 0:c1a4c1e9618c | 47 | |
jisakson3 | 0:c1a4c1e9618c | 48 | LPC_SC->PCONP |= (1<<29); |
jisakson3 | 0:c1a4c1e9618c | 49 | |
jisakson3 | 0:c1a4c1e9618c | 50 | /* Enable GPDMA and sync logic */ |
jisakson3 | 0:c1a4c1e9618c | 51 | LPC_GPDMA->DMACConfig = 1; |
jisakson3 | 0:c1a4c1e9618c | 52 | LPC_GPDMA->DMACSync = (1<<6); |
jisakson3 | 0:c1a4c1e9618c | 53 | |
jisakson3 | 0:c1a4c1e9618c | 54 | /* Load DMA Channel0 */ |
jisakson3 | 0:c1a4c1e9618c | 55 | LPC_GPDMACH0->DMACCSrcAddr = (uint32_t)DACsignal; |
jisakson3 | 0:c1a4c1e9618c | 56 | LPC_GPDMACH0->DMACCDestAddr = (uint32_t)&LPC_DAC->DACR; |
jisakson3 | 0:c1a4c1e9618c | 57 | LPC_GPDMACH0->DMACCLLI = (oneShot) ? 0 : (uint32_t)&llio; |
jisakson3 | 0:c1a4c1e9618c | 58 | |
jisakson3 | 0:c1a4c1e9618c | 59 | int playSampleCount = numSamples + oneShot; |
jisakson3 | 0:c1a4c1e9618c | 60 | LPC_GPDMACH0->DMACCControl = playSampleCount // transfer size (0 - 11) = 64 |
jisakson3 | 0:c1a4c1e9618c | 61 | | (0 << 12) // source burst size (12 - 14) = 1 |
jisakson3 | 0:c1a4c1e9618c | 62 | | (0 << 15) // destination burst size (15 - 17) = 1 |
jisakson3 | 0:c1a4c1e9618c | 63 | | (2 << 18) // source width (18 - 20) = 32 bit |
jisakson3 | 0:c1a4c1e9618c | 64 | | (2 << 21) // destination width (21 - 23) = 32 bit |
jisakson3 | 0:c1a4c1e9618c | 65 | | (0 << 24) // source AHB select (24) = AHB 0 |
jisakson3 | 0:c1a4c1e9618c | 66 | | (0 << 25) // destination AHB select (25) = AHB 0 |
jisakson3 | 0:c1a4c1e9618c | 67 | | (1 << 26) // source increment (26) = increment |
jisakson3 | 0:c1a4c1e9618c | 68 | | (0 << 27) // destination increment (27) = no increment |
jisakson3 | 0:c1a4c1e9618c | 69 | | (0 << 28) // mode select (28) = access in user mode |
jisakson3 | 0:c1a4c1e9618c | 70 | | (0 << 29) // (29) = access not bufferable |
jisakson3 | 0:c1a4c1e9618c | 71 | | (0 << 30) // (30) = access not cacheable |
jisakson3 | 0:c1a4c1e9618c | 72 | | (0 << 31); // terminal count interrupt disabled |
jisakson3 | 0:c1a4c1e9618c | 73 | |
jisakson3 | 0:c1a4c1e9618c | 74 | LPC_GPDMACH0->DMACCConfig = 1 |
jisakson3 | 0:c1a4c1e9618c | 75 | | (0 << 1) // source peripheral (1 - 5) = none |
jisakson3 | 0:c1a4c1e9618c | 76 | | (7 << 6) // destination peripheral (6 - 10) = DAC |
jisakson3 | 0:c1a4c1e9618c | 77 | | (1 << 11) // flow control (11 - 13) = mem to per |
jisakson3 | 0:c1a4c1e9618c | 78 | | (0 << 14) // (14) = mask out error interrupt |
jisakson3 | 0:c1a4c1e9618c | 79 | | (0 << 15) // (15) = mask out terminal count interrupt |
jisakson3 | 0:c1a4c1e9618c | 80 | | (0 << 16) // (16) = no locked transfers |
jisakson3 | 0:c1a4c1e9618c | 81 | | (0 << 18); // (27) = no HALT |
jisakson3 | 0:c1a4c1e9618c | 82 | |
jisakson3 | 0:c1a4c1e9618c | 83 | /* DACclk = 25 MHz, so 10 usec interval */ |
jisakson3 | 0:c1a4c1e9618c | 84 | LPC_DAC->DACCNTVAL = 18; // 16-bit reload value |
jisakson3 | 0:c1a4c1e9618c | 85 | /* DMA, timer running, dbuff */ |
jisakson3 | 0:c1a4c1e9618c | 86 | LPC_DAC->DACCTRL = |
jisakson3 | 0:c1a4c1e9618c | 87 | 1<<3 // DMA_ENA dma burst is enabled |
jisakson3 | 0:c1a4c1e9618c | 88 | | 1<<2 // CNT_ENA Timeout couner is enabled |
jisakson3 | 0:c1a4c1e9618c | 89 | | 1<<1; // DBLBUF_ENA double-buffering enabled |
jisakson3 | 0:c1a4c1e9618c | 90 | } |
jisakson3 | 0:c1a4c1e9618c | 91 | |
jisakson3 | 0:c1a4c1e9618c | 92 | void SignalGenDAC::Stop(void) { |
jisakson3 | 0:c1a4c1e9618c | 93 | printf("Stop()\r\n"); |
jisakson3 | 0:c1a4c1e9618c | 94 | LPC_GPDMACH0->DMACCLLI = 0; |
jisakson3 | 0:c1a4c1e9618c | 95 | while (LPC_GPDMACH0->DMACCConfig & 1) |
jisakson3 | 0:c1a4c1e9618c | 96 | wait_ms(1); |
jisakson3 | 0:c1a4c1e9618c | 97 | aout->write(offset / maxV); |
jisakson3 | 0:c1a4c1e9618c | 98 | isOn = false; |
jisakson3 | 0:c1a4c1e9618c | 99 | } |
jisakson3 | 0:c1a4c1e9618c | 100 | |
jisakson3 | 0:c1a4c1e9618c | 101 | |
jisakson3 | 0:c1a4c1e9618c | 102 | void SignalGenDAC::PrepareWaveform(SG_Waveform mode, float _frequency, float _dutycycle, float _voltage, float _offset) { |
jisakson3 | 0:c1a4c1e9618c | 103 | int x, dcCount, firstQtr, lastQtr; |
jisakson3 | 0:c1a4c1e9618c | 104 | frequency = _frequency; |
jisakson3 | 0:c1a4c1e9618c | 105 | dutycycle = _dutycycle; |
jisakson3 | 0:c1a4c1e9618c | 106 | voltage = _voltage; |
jisakson3 | 0:c1a4c1e9618c | 107 | offset = _offset; |
jisakson3 | 0:c1a4c1e9618c | 108 | float upp = rangelimit(offset + voltage/2, minV, maxV); |
jisakson3 | 0:c1a4c1e9618c | 109 | float mid = rangelimit(offset, minV, maxV); |
jisakson3 | 0:c1a4c1e9618c | 110 | float low = rangelimit(offset - voltage/2, minV, maxV); |
jisakson3 | 0:c1a4c1e9618c | 111 | float v; |
jisakson3 | 0:c1a4c1e9618c | 112 | numSamples = 128; // Ideally, compute this based on the frequency for good resolution |
jisakson3 | 0:c1a4c1e9618c | 113 | dcCount = dutycycle/100.0 * numSamples; |
jisakson3 | 0:c1a4c1e9618c | 114 | firstQtr = dcCount / 2; |
jisakson3 | 0:c1a4c1e9618c | 115 | lastQtr = dcCount + (numSamples - dcCount)/2; |
jisakson3 | 0:c1a4c1e9618c | 116 | |
jisakson3 | 0:c1a4c1e9618c | 117 | // Set the timebased based on the frequency |
jisakson3 | 0:c1a4c1e9618c | 118 | if (isOn) { |
jisakson3 | 0:c1a4c1e9618c | 119 | Stop(); |
jisakson3 | 0:c1a4c1e9618c | 120 | } |
jisakson3 | 0:c1a4c1e9618c | 121 | printf("Generate wave for mode: %d\r\n", mode); |
jisakson3 | 0:c1a4c1e9618c | 122 | switch (mode) { |
jisakson3 | 0:c1a4c1e9618c | 123 | case SG_SINE: |
jisakson3 | 0:c1a4c1e9618c | 124 | for (x=0; x<numSamples; x++) { |
jisakson3 | 0:c1a4c1e9618c | 125 | if (x < dcCount) { |
jisakson3 | 0:c1a4c1e9618c | 126 | v = offset + voltage/2 * sin(x * 1 * PI / dcCount); |
jisakson3 | 0:c1a4c1e9618c | 127 | } else { |
jisakson3 | 0:c1a4c1e9618c | 128 | v = offset - voltage/2 * sin((x - dcCount) * 1 * PI / (numSamples-dcCount)); |
jisakson3 | 0:c1a4c1e9618c | 129 | } |
jisakson3 | 0:c1a4c1e9618c | 130 | v = rangelimit(v, minV, maxV); |
jisakson3 | 0:c1a4c1e9618c | 131 | VoltSignal[x] = v; |
jisakson3 | 0:c1a4c1e9618c | 132 | } |
jisakson3 | 0:c1a4c1e9618c | 133 | VoltSignal[numSamples] = rangelimit(offset, minV, maxV); |
jisakson3 | 0:c1a4c1e9618c | 134 | break; |
jisakson3 | 0:c1a4c1e9618c | 135 | case SG_SQUARE: |
jisakson3 | 0:c1a4c1e9618c | 136 | for (x=0; x<numSamples; x++) { |
jisakson3 | 0:c1a4c1e9618c | 137 | if (0 && x == 0) { |
jisakson3 | 0:c1a4c1e9618c | 138 | v = rangelimit(offset, minV, maxV); |
jisakson3 | 0:c1a4c1e9618c | 139 | } else if (x < dcCount) { |
jisakson3 | 0:c1a4c1e9618c | 140 | v = rangelimit(offset + voltage/2, minV, maxV); |
jisakson3 | 0:c1a4c1e9618c | 141 | } else { |
jisakson3 | 0:c1a4c1e9618c | 142 | v = rangelimit(offset - voltage/2, minV, maxV); |
jisakson3 | 0:c1a4c1e9618c | 143 | } |
jisakson3 | 0:c1a4c1e9618c | 144 | VoltSignal[x] = v; |
jisakson3 | 0:c1a4c1e9618c | 145 | } |
jisakson3 | 0:c1a4c1e9618c | 146 | VoltSignal[numSamples] = rangelimit(offset, minV, maxV); |
jisakson3 | 0:c1a4c1e9618c | 147 | break; |
jisakson3 | 0:c1a4c1e9618c | 148 | case SG_TRIANGLE: |
jisakson3 | 0:c1a4c1e9618c | 149 | for (x=0; x<numSamples; x++) { |
jisakson3 | 0:c1a4c1e9618c | 150 | if (x < firstQtr) { |
jisakson3 | 0:c1a4c1e9618c | 151 | v = voltage/2 * (float)x/(firstQtr); |
jisakson3 | 0:c1a4c1e9618c | 152 | v += offset; |
jisakson3 | 0:c1a4c1e9618c | 153 | } else if (x < dcCount) { |
jisakson3 | 0:c1a4c1e9618c | 154 | v = voltage/2 * (float)x/(firstQtr); |
jisakson3 | 0:c1a4c1e9618c | 155 | v = voltage - (v - offset); |
jisakson3 | 0:c1a4c1e9618c | 156 | } else if (x < lastQtr) { |
jisakson3 | 0:c1a4c1e9618c | 157 | v = voltage * (float)(x - dcCount)/(numSamples - dcCount); |
jisakson3 | 0:c1a4c1e9618c | 158 | v = offset - v; |
jisakson3 | 0:c1a4c1e9618c | 159 | } else { |
jisakson3 | 0:c1a4c1e9618c | 160 | v = voltage * (float)(x - dcCount)/(numSamples - dcCount); |
jisakson3 | 0:c1a4c1e9618c | 161 | v = v + offset - voltage; |
jisakson3 | 0:c1a4c1e9618c | 162 | } |
jisakson3 | 0:c1a4c1e9618c | 163 | VoltSignal[x] = v; |
jisakson3 | 0:c1a4c1e9618c | 164 | } |
jisakson3 | 0:c1a4c1e9618c | 165 | VoltSignal[numSamples] = rangelimit(offset, minV, maxV); |
jisakson3 | 0:c1a4c1e9618c | 166 | break; |
jisakson3 | 0:c1a4c1e9618c | 167 | case SG_SAWTOOTH: |
jisakson3 | 0:c1a4c1e9618c | 168 | for (x=0; x<numSamples; x++) { |
jisakson3 | 0:c1a4c1e9618c | 169 | if (x < dcCount) { |
jisakson3 | 0:c1a4c1e9618c | 170 | v = offset - voltage/2 + (float)x/dcCount * voltage/2; |
jisakson3 | 0:c1a4c1e9618c | 171 | } else { |
jisakson3 | 0:c1a4c1e9618c | 172 | v = offset + (float)(x - dcCount)/(numSamples - dcCount) * voltage/2; |
jisakson3 | 0:c1a4c1e9618c | 173 | } |
jisakson3 | 0:c1a4c1e9618c | 174 | v = rangelimit(v, minV, maxV); |
jisakson3 | 0:c1a4c1e9618c | 175 | VoltSignal[x] = v; |
jisakson3 | 0:c1a4c1e9618c | 176 | } |
jisakson3 | 0:c1a4c1e9618c | 177 | VoltSignal[numSamples] = rangelimit(offset, minV, maxV); |
jisakson3 | 0:c1a4c1e9618c | 178 | break; |
jisakson3 | 0:c1a4c1e9618c | 179 | case SG_USER: |
jisakson3 | 0:c1a4c1e9618c | 180 | break; |
jisakson3 | 0:c1a4c1e9618c | 181 | } |
jisakson3 | 0:c1a4c1e9618c | 182 | //printf("DAC Data %3.2f %3.2f\r\n", voltage, offset); |
jisakson3 | 0:c1a4c1e9618c | 183 | for (x=0; x<=numSamples; x++) { |
jisakson3 | 0:c1a4c1e9618c | 184 | DACsignal[x] = ((uint16_t)(VoltSignal[x]/maxV * 1023) << 6); |
jisakson3 | 0:c1a4c1e9618c | 185 | printf("%3d, %5.3f, %d\r\n", x, VoltSignal[x], DACsignal[x]); |
jisakson3 | 0:c1a4c1e9618c | 186 | } |
jisakson3 | 0:c1a4c1e9618c | 187 | if (!isOn) { |
jisakson3 | 0:c1a4c1e9618c | 188 | aout->write(offset / maxV); |
jisakson3 | 0:c1a4c1e9618c | 189 | } |
jisakson3 | 0:c1a4c1e9618c | 190 | } |
jisakson3 | 0:c1a4c1e9618c | 191 | |
jisakson3 | 0:c1a4c1e9618c | 192 | float SignalGenDAC::rangelimit(float value, float min, float max) { |
jisakson3 | 0:c1a4c1e9618c | 193 | if (value < min) |
jisakson3 | 0:c1a4c1e9618c | 194 | return min; |
jisakson3 | 0:c1a4c1e9618c | 195 | else if (value > max) |
jisakson3 | 0:c1a4c1e9618c | 196 | return max; |
jisakson3 | 0:c1a4c1e9618c | 197 | else |
jisakson3 | 0:c1a4c1e9618c | 198 | return value; |
jisakson3 | 0:c1a4c1e9618c | 199 | } |
jisakson3 | 0:c1a4c1e9618c | 200 |