1
+
2
+ #include " samd21_mcu.h"
3
+ #include " ../hardware_api.h"
4
+
5
+
6
+ static bool freeRunning = false ;
7
+ static int _pinA, _pinB, _pinC;
8
+ static uint16_t a = 0xFFFF , b = 0xFFFF , c = 0xFFFF ; // updated by adcStopWithDMA when configured in freerunning mode
9
+ static SAMDCurrentSenseADCDMA instance;
10
+ /* *
11
+ * function reading an ADC value and returning the read voltage
12
+ *
13
+ * @param pinA - adc pin A
14
+ * @param pinB - adc pin B
15
+ * @param pinC - adc pin C
16
+ */
17
+ void _configureADCLowSide (const int pinA,const int pinB,const int pinC)
18
+ {
19
+ _pinA = pinA;
20
+ _pinB = pinB;
21
+ _pinC = pinC;
22
+ freeRunning = true ;
23
+ instance.init (pinA, pinB, pinC);
24
+
25
+ }
26
+ void _startADC3PinConversionLowSide ()
27
+ {
28
+ instance.startADCScan ();
29
+ }
30
+ /* *
31
+ * function reading an ADC value and returning the read voltage
32
+ *
33
+ * @param pinA - the arduino pin to be read (it has to be ADC pin)
34
+ */
35
+ float _readADCVoltageLowSide (const int pinA)
36
+ {
37
+ instance.readResults (a, b, c);
38
+
39
+ if (pinA == _pinA)
40
+ return instance.toVolts (a);
41
+ if (pinA == _pinB)
42
+ return instance.toVolts (b);
43
+ if (pinA == _pinC)
44
+ return instance.toVolts (c);
45
+
46
+ return NAN;
47
+ }
48
+
49
+ /* *
50
+ * function syncing the Driver with the ADC for the LowSide Sensing
51
+ */
52
+ void _driverSyncLowSide ()
53
+ {
54
+ SIMPLEFOC_SAMD_DEBUG_SERIAL.println (F (" TODO! _driverSyncLowSide() is not implemented" ));
55
+ instance.startADCScan ();
56
+ // TODO: hook with PWM interrupts
57
+ }
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+ // Credit: significant portions of this code were pulled from Paul Gould's git https://github.com/gouldpa/FOC-Arduino-Brushless
69
+
70
+ static void adcStopWithDMA (void );
71
+ static void adcStartWithDMA (void );
72
+
73
+ /* *
74
+ * @brief ADC sync wait
75
+ * @retval void
76
+ */
77
+ static __inline__ void ADCsync () __attribute__((always_inline, unused));
78
+ static void ADCsync () {
79
+ while (ADC->STATUS .bit .SYNCBUSY == 1 ); // Just wait till the ADC is free
80
+ }
81
+
82
+ // ADC DMA sequential free running (6) with Interrupts /////////////////
83
+
84
+ SAMDCurrentSenseADCDMA * SAMDCurrentSenseADCDMA::getHardwareAPIInstance ()
85
+ {
86
+
87
+ return &instance;
88
+ }
89
+
90
+ SAMDCurrentSenseADCDMA::SAMDCurrentSenseADCDMA ()
91
+ {
92
+ }
93
+
94
+ void SAMDCurrentSenseADCDMA::init (int pinA, int pinB, int pinC, int pinAREF, float voltageAREF, uint32_t adcBits, uint32_t channelDMA)
95
+ {
96
+ this ->pinA = pinA;
97
+ this ->pinB = pinB;
98
+ this ->pinC = pinC;
99
+ this ->pinAREF = pinAREF;
100
+ this ->channelDMA = channelDMA;
101
+ this ->voltageAREF = voltageAREF;
102
+ this ->maxCountsADC = 1 << adcBits;
103
+ countsToVolts = ( voltageAREF / maxCountsADC );
104
+
105
+ initPins ();
106
+ initADC ();
107
+ initDMA ();
108
+ startADCScan (); // so we have something to read next time we call readResults()
109
+ }
110
+
111
+
112
+ void SAMDCurrentSenseADCDMA::startADCScan (){
113
+ adcToDMATransfer (adcBuffer + oneBeforeFirstAIN, BufferSize);
114
+ adcStartWithDMA ();
115
+ }
116
+
117
+ bool SAMDCurrentSenseADCDMA::readResults (uint16_t & a, uint16_t & b, uint16_t & c){
118
+ if (ADC->CTRLA .bit .ENABLE )
119
+ return false ;
120
+ uint32_t ainA = g_APinDescription[pinA].ulADCChannelNumber ;
121
+ uint32_t ainB = g_APinDescription[pinB].ulADCChannelNumber ;
122
+ a = adcBuffer[ainA];
123
+ b = adcBuffer[ainB];
124
+ if (_isset (pinC))
125
+ {
126
+ uint32_t ainC = g_APinDescription[pinC].ulADCChannelNumber ;
127
+ c = adcBuffer[ainC];
128
+ }
129
+ return true ;
130
+ }
131
+
132
+
133
+ float SAMDCurrentSenseADCDMA::toVolts (uint16_t counts) {
134
+ return counts * countsToVolts;
135
+ }
136
+
137
+ void SAMDCurrentSenseADCDMA::initPins (){
138
+
139
+ pinMode (pinAREF, INPUT);
140
+ pinMode (pinA, INPUT);
141
+ pinMode (pinB, INPUT);
142
+
143
+ uint32_t ainA = g_APinDescription[pinA].ulADCChannelNumber ;
144
+ uint32_t ainB = g_APinDescription[pinB].ulADCChannelNumber ;
145
+ firstAIN = min (ainA, ainB);
146
+ lastAIN = max (ainA, ainB);
147
+ if ( _isset (pinC) )
148
+ {
149
+ uint32_t ainC = g_APinDescription[pinC].ulADCChannelNumber ;
150
+ pinMode (pinC, INPUT);
151
+ firstAIN = min (firstAIN, ainC);
152
+ lastAIN = max (lastAIN, ainC);
153
+ }
154
+
155
+ oneBeforeFirstAIN = firstAIN - 1 ; // hack to discard noisy first readout
156
+ BufferSize = lastAIN - oneBeforeFirstAIN + 1 ;
157
+
158
+ }
159
+
160
+ void SAMDCurrentSenseADCDMA::initADC (){
161
+
162
+ analogRead (pinA); // do some pin init pinPeripheral()
163
+ analogRead (pinB); // do some pin init pinPeripheral()
164
+ analogRead (pinC); // do some pin init pinPeripheral()
165
+
166
+ ADC->CTRLA .bit .ENABLE = 0x00 ; // Disable ADC
167
+ ADCsync ();
168
+ // ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTVCC0_Val; // 2.2297 V Supply VDDANA
169
+ ADC->INPUTCTRL .bit .GAIN = ADC_INPUTCTRL_GAIN_1X_Val; // Gain select as 1X
170
+ // ADC->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_DIV2_Val; // default
171
+ ADC->REFCTRL .bit .REFSEL = ADC_REFCTRL_REFSEL_AREFA;
172
+ // ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTVCC0;
173
+ ADCsync (); // ref 31.6.16
174
+
175
+ /*
176
+ Bits 19:16 – INPUTSCAN[3:0]: Number of Input Channels Included in Scan
177
+ This register gives the number of input sources included in the pin scan. The number of input sources included is
178
+ INPUTSCAN + 1. The input channels included are in the range from MUXPOS + INPUTOFFSET to MUXPOS +
179
+ INPUTOFFSET + INPUTSCAN.
180
+ The range of the scan mode must not exceed the number of input channels available on the device.
181
+ Bits 4:0 – MUXPOS[4:0]: Positive Mux Input Selection
182
+ These bits define the Mux selection for the positive ADC input. Table 32-14 shows the possible input selections. If
183
+ the internal bandgap voltage or temperature sensor input channel is selected, then the Sampling Time Length bit
184
+ group in the SamplingControl register must be written.
185
+ Table 32-14. Positive Mux Input Selection
186
+ MUXPOS[4:0] Group configuration Description
187
+ 0x00 PIN0 ADC AIN0 pin
188
+ 0x01 PIN1 ADC AIN1 pin
189
+ 0x02 PIN2 ADC AIN2 pin
190
+ 0x03 PIN3 ADC AIN3 pin
191
+ 0x04 PIN4 ADC AIN4 pin
192
+ 0x05 PIN5 ADC AIN5 pin
193
+ 0x06 PIN6 ADC AIN6 pin
194
+ 0x07 PIN7 ADC AIN7 pin
195
+ 0x08 PIN8 ADC AIN8 pin
196
+ 0x09 PIN9 ADC AIN9 pin
197
+ 0x0A PIN10 ADC AIN10 pin
198
+ 0x0B PIN11 ADC AIN11 pin
199
+ 0x0C PIN12 ADC AIN12 pin
200
+ 0x0D PIN13 ADC AIN13 pin
201
+ 0x0E PIN14 ADC AIN14 pin
202
+ 0x0F PIN15 ADC AIN15 pin
203
+ 0x10 PIN16 ADC AIN16 pin
204
+ 0x11 PIN17 ADC AIN17 pin
205
+ 0x12 PIN18 ADC AIN18 pin
206
+ 0x13 PIN19 ADC AIN19 pin
207
+ 0x14-0x17 Reserved
208
+ 0x18 TEMP Temperature reference
209
+ 0x19 BANDGAP Bandgap voltage
210
+ 0x1A SCALEDCOREVCC 1/4 scaled core supply
211
+ 0x1B SCALEDIOVCC 1/4 scaled I/O supply
212
+ 0x1C DAC DAC output
213
+ 0x1D-0x1F Reserved
214
+ */
215
+ ADC->INPUTCTRL .bit .MUXPOS = oneBeforeFirstAIN;
216
+ ADCsync ();
217
+ ADC->INPUTCTRL .bit .INPUTSCAN = lastAIN; // so the adc will scan from oneBeforeFirstAIN to lastAIN (inclusive)
218
+ ADCsync ();
219
+ ADC->INPUTCTRL .bit .INPUTOFFSET = 0 ; // input scan cursor
220
+ ADCsync ();
221
+ ADC->AVGCTRL .reg = 0x00 ; // no averaging
222
+ ADC->SAMPCTRL .reg = 0x05 ; ; // sample length in 1/2 CLK_ADC cycles, see GCLK_ADC and ADC_CTRLB_PRESCALER_DIV16
223
+ // according to the specsheet: f_GCLK_ADC ADC input clock frequency 48 MHz, so same as fCPU
224
+ ADCsync ();
225
+ ADC->CTRLB .reg = ADC_CTRLB_PRESCALER_DIV16 | ADC_CTRLB_FREERUN | ADC_CTRLB_RESSEL_12BIT;
226
+ ADCsync ();
227
+ }
228
+
229
+ volatile dmacdescriptor wrb[12 ] __attribute__ ((aligned (16 )));
230
+
231
+ void SAMDCurrentSenseADCDMA::initDMA () {
232
+ // probably on by default
233
+ PM->AHBMASK .reg |= PM_AHBMASK_DMAC ;
234
+ PM->APBBMASK .reg |= PM_APBBMASK_DMAC ;
235
+ NVIC_EnableIRQ ( DMAC_IRQn ) ;
236
+ DMAC->BASEADDR .reg = (uint32_t )descriptor_section;
237
+ DMAC->WRBADDR .reg = (uint32_t )wrb;
238
+ DMAC->CTRL .reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN (0xf );
239
+ }
240
+
241
+
242
+ void SAMDCurrentSenseADCDMA::adcToDMATransfer (void *rxdata, uint32_t hwords) {
243
+
244
+ DMAC->CHID .reg = DMAC_CHID_ID (channelDMA);
245
+ DMAC->CHCTRLA .reg &= ~DMAC_CHCTRLA_ENABLE;
246
+ DMAC->CHCTRLA .reg = DMAC_CHCTRLA_SWRST;
247
+ DMAC->SWTRIGCTRL .reg &= (uint32_t )(~(1 << channelDMA));
248
+
249
+ DMAC->CHCTRLB .reg = DMAC_CHCTRLB_LVL (0 )
250
+ | DMAC_CHCTRLB_TRIGSRC (ADC_DMAC_ID_RESRDY)
251
+ | DMAC_CHCTRLB_TRIGACT_BEAT;
252
+ DMAC->CHINTENSET .reg = DMAC_CHINTENSET_MASK ; // enable all 3 interrupts
253
+ descriptor.descaddr = 0 ;
254
+ descriptor.srcaddr = (uint32_t ) &ADC->RESULT .reg ;
255
+ descriptor.btcnt = hwords;
256
+ descriptor.dstaddr = (uint32_t )rxdata + hwords*2 ; // end address
257
+ descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_HWORD | DMAC_BTCTRL_DSTINC | DMAC_BTCTRL_VALID;
258
+ memcpy (&descriptor_section[channelDMA],&descriptor, sizeof (dmacdescriptor));
259
+
260
+ // start channel
261
+ DMAC->CHID .reg = DMAC_CHID_ID (channelDMA);
262
+ DMAC->CHCTRLA .reg |= DMAC_CHCTRLA_ENABLE;
263
+ }
264
+
265
+
266
+ int iii = 0 ;
267
+
268
+ void adcStopWithDMA (void ){
269
+ ADCsync ();
270
+ ADC->CTRLA .bit .ENABLE = 0x00 ;
271
+ // ADCsync();
272
+ // if(iii++ % 1000 == 0)
273
+ // {
274
+ // SIMPLEFOC_SAMD_DEBUG_SERIAL.print(a);
275
+ // SIMPLEFOC_SAMD_DEBUG_SERIAL.print(" :: ");
276
+ // SIMPLEFOC_SAMD_DEBUG_SERIAL.print(b);
277
+ // SIMPLEFOC_SAMD_DEBUG_SERIAL.print(" :: ");
278
+ // SIMPLEFOC_SAMD_DEBUG_SERIAL.print(c);
279
+ // SIMPLEFOC_SAMD_DEBUG_SERIAL.println("yo!");
280
+ // }
281
+
282
+
283
+ }
284
+
285
+ void adcStartWithDMA (void ){
286
+ ADCsync ();
287
+ ADC->INPUTCTRL .bit .INPUTOFFSET = 0 ;
288
+ ADCsync ();
289
+ ADC->SWTRIG .bit .FLUSH = 1 ;
290
+ ADCsync ();
291
+ ADC->CTRLA .bit .ENABLE = 0x01 ;
292
+ ADCsync ();
293
+ }
294
+
295
+ void DMAC_Handler () {
296
+ uint8_t active_channel;
297
+ active_channel = DMAC->INTPEND .reg & DMAC_INTPEND_ID_Msk; // get channel number
298
+ DMAC->CHID .reg = DMAC_CHID_ID (active_channel);
299
+ adcStopWithDMA ();
300
+ DMAC->CHINTFLAG .reg = DMAC_CHINTENCLR_TCMPL; // clear
301
+ DMAC->CHINTFLAG .reg = DMAC_CHINTENCLR_TERR;
302
+ DMAC->CHINTFLAG .reg = DMAC_CHINTENCLR_SUSP;
303
+
304
+ }
0 commit comments