28
28
29
29
using namespace arduino ;
30
30
31
+ static bool dma_transfer_finished_cb (unsigned int channel, unsigned int sequenceNo, void *userParam);
32
+
31
33
AdcClass::AdcClass () :
32
- initialized(false ),
34
+ initialized_single(false ),
35
+ initialized_scan(false ),
36
+ paused_transfer(false ),
33
37
current_adc_pin(PD2),
34
38
current_adc_reference(AR_VDD),
35
39
current_read_resolution(this ->max_read_resolution_bits),
40
+ user_onsampling_finished_callback(nullptr ),
36
41
adc_mutex(nullptr )
37
42
{
38
43
this ->adc_mutex = xSemaphoreCreateMutexStatic (&this ->adc_mutex_buf );
39
44
configASSERT (this ->adc_mutex );
40
45
}
41
46
42
- void AdcClass::init (PinName pin, uint8_t reference)
47
+ void AdcClass::init_single (PinName pin, uint8_t reference)
43
48
{
44
49
// Set up the ADC pin as an input
45
50
pinMode (pin, INPUT);
@@ -126,16 +131,180 @@ void AdcClass::init(PinName pin, uint8_t reference)
126
131
}
127
132
}
128
133
129
- this ->initialized = true ;
134
+ this ->initialized_scan = false ;
135
+ this ->initialized_single = true ;
136
+ }
137
+
138
+ void AdcClass::init_scan (PinName pin, uint8_t reference)
139
+ {
140
+ // Set up the ADC pin as an input
141
+ pinMode (pin, INPUT);
142
+
143
+ // Create ADC init structs with default values
144
+ IADC_Init_t init = IADC_INIT_DEFAULT;
145
+ IADC_AllConfigs_t all_configs = IADC_ALLCONFIGS_DEFAULT;
146
+ IADC_InitScan_t init_scan = IADC_INITSCAN_DEFAULT;
147
+
148
+ // Scan table structure
149
+ IADC_ScanTable_t scanTable = IADC_SCANTABLE_DEFAULT;
150
+
151
+ // Enable IADC0, GPIO and PRS clock branches
152
+ CMU_ClockEnable (cmuClock_IADC0, true );
153
+ CMU_ClockEnable (cmuClock_GPIO, true );
154
+ CMU_ClockEnable (cmuClock_PRS, true );
155
+
156
+ // Shutdown between conversions to reduce current
157
+ init.warmup = iadcWarmupNormal;
158
+
159
+ // Set the HFSCLK prescale value here
160
+ init.srcClkPrescale = IADC_calcSrcClkPrescale (IADC0, 20000000 , 0 );
161
+
162
+ IADC_CfgReference_t sl_adc_reference;
163
+ uint32_t sl_adc_vref;
164
+
165
+ // Set the voltage reference
166
+ switch (reference) {
167
+ case AR_INTERNAL1V2:
168
+ sl_adc_reference = iadcCfgReferenceInt1V2;
169
+ sl_adc_vref = 1200 ;
170
+ break ;
171
+
172
+ case AR_EXTERNAL_1V25:
173
+ sl_adc_reference = iadcCfgReferenceExt1V25;
174
+ sl_adc_vref = 1250 ;
175
+ break ;
176
+
177
+ case AR_VDD:
178
+ sl_adc_reference = iadcCfgReferenceVddx;
179
+ sl_adc_vref = 3300 ;
180
+ break ;
181
+
182
+ case AR_08VDD:
183
+ sl_adc_reference = iadcCfgReferenceVddX0P8Buf;
184
+ sl_adc_vref = 2640 ;
185
+ break ;
186
+
187
+ default :
188
+ return ;
189
+ }
190
+
191
+ // Set the voltage reference
192
+ all_configs.configs [0 ].reference = sl_adc_reference;
193
+ all_configs.configs [0 ].vRef = sl_adc_vref;
194
+ all_configs.configs [0 ].osrHighSpeed = iadcCfgOsrHighSpeed2x;
195
+ all_configs.configs [0 ].analogGain = iadcCfgAnalogGain1x;
196
+
197
+ /*
198
+ * CLK_SRC_ADC must be prescaled by some value greater than 1 to
199
+ * derive the intended CLK_ADC frequency.
200
+ * Based on the default 2x oversampling rate (OSRHS)...
201
+ * conversion time = ((4 * OSRHS) + 2) / fCLK_ADC
202
+ * ...which results in a maximum sampling rate of 833 ksps with the
203
+ * 2-clock input multiplexer switching time is included.
204
+ */
205
+ all_configs.configs [0 ].adcClkPrescale = IADC_calcAdcClkPrescale (IADC0,
206
+ 10000000 ,
207
+ 0 ,
208
+ iadcCfgModeNormal,
209
+ init.srcClkPrescale );
210
+
211
+ // Reset the ADC
212
+ IADC_reset (IADC0);
213
+
214
+ // Only configure the ADC if it is not already running
215
+ if (IADC0->CTRL == _IADC_CTRL_RESETVALUE) {
216
+ IADC_init (IADC0, &init, &all_configs);
217
+ }
218
+
219
+ // Assign the input pin
220
+ uint32_t pin_index = pin - PIN_NAME_MIN;
221
+
222
+ // Trigger continuously once scan is started
223
+ init_scan.triggerAction = iadcTriggerActionContinuous;
224
+ // Set the SCANFIFODVL flag when scan FIFO holds 2 entries
225
+ // The interrupt associated with the SCANFIFODVL flag in the IADC_IF register is not used
226
+ init_scan.dataValidLevel = iadcFifoCfgDvl1;
227
+ // Enable DMA wake-up to save the results when the specified FIFO level is hit
228
+ init_scan.fifoDmaWakeup = true ;
229
+
230
+ scanTable.entries [0 ].posInput = GPIO_to_ADC_pin_map[pin_index];
231
+ scanTable.entries [0 ].includeInScan = true ;
232
+
233
+ // Initialize scan
234
+ IADC_initScan (IADC0, &init_scan, &scanTable);
235
+ IADC_enableInt (IADC0, IADC_IEN_SCANTABLEDONE);
236
+
237
+ // Allocate the analog bus for ADC0 inputs
238
+ // Port C and D are handled together
239
+ // Even and odd pins on the same port have a different register value
240
+ bool pin_is_even = (pin % 2 == 0 );
241
+ if (pin >= PD0 || pin >= PC0) {
242
+ if (pin_is_even) {
243
+ GPIO->CDBUSALLOC |= GPIO_CDBUSALLOC_CDEVEN0_ADC0;
244
+ } else {
245
+ GPIO->CDBUSALLOC |= GPIO_CDBUSALLOC_CDODD0_ADC0;
246
+ }
247
+ } else if (pin >= PB0) {
248
+ if (pin_is_even) {
249
+ GPIO->BBUSALLOC |= GPIO_BBUSALLOC_BEVEN0_ADC0;
250
+ } else {
251
+ GPIO->BBUSALLOC |= GPIO_BBUSALLOC_BODD0_ADC0;
252
+ }
253
+ } else {
254
+ if (pin_is_even) {
255
+ GPIO->ABUSALLOC |= GPIO_ABUSALLOC_AEVEN0_ADC0;
256
+ } else {
257
+ GPIO->ABUSALLOC |= GPIO_ABUSALLOC_AODD0_ADC0;
258
+ }
259
+ }
260
+
261
+ this ->initialized_single = false ;
262
+ this ->initialized_scan = true ;
263
+ }
264
+
265
+ sl_status_t AdcClass::init_dma (uint32_t *buffer, uint32_t size)
266
+ {
267
+ sl_status_t status;
268
+ if (!this ->initialized_scan ) {
269
+ return SL_STATUS_NOT_INITIALIZED;
270
+ }
271
+
272
+ // Initialize DMA with default parameters
273
+ DMADRV_Init ();
274
+
275
+ // Allocate DMA channel
276
+ status = DMADRV_AllocateChannel (&this ->dma_channel , NULL );
277
+ if (status != ECODE_EMDRV_DMADRV_OK) {
278
+ return SL_STATUS_FAIL;
279
+ }
280
+
281
+ // Trigger LDMA transfer on IADC scan completion
282
+ LDMA_TransferCfg_t transferCfg = LDMA_TRANSFER_CFG_PERIPHERAL (ldmaPeripheralSignal_IADC0_IADC_SCAN);
283
+
284
+ /*
285
+ * Set up a linked descriptor to save scan results to the
286
+ * user-specified buffer. By linking the descriptor to itself
287
+ * (the last argument is the relative jump in terms of the number of
288
+ * descriptors), transfers will run continuously.
289
+ */
290
+ #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
291
+ this ->ldma_descriptor = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_P2M_WORD (&(IADC0->SCANFIFODATA ), buffer, size, 0 );
292
+
293
+ DMADRV_LdmaStartTransfer ((int )this ->dma_channel , &transferCfg, &this ->ldma_descriptor , dma_transfer_finished_cb, NULL );
294
+ return SL_STATUS_OK;
130
295
}
131
296
132
297
uint16_t AdcClass::get_sample (PinName pin)
133
298
{
134
299
xSemaphoreTake (this ->adc_mutex , portMAX_DELAY);
135
300
136
- if (!this ->initialized || pin != this ->current_adc_pin ) {
301
+ if (this ->initialized_scan ) {
302
+ this ->scan_stop ();
303
+ }
304
+
305
+ if (!this ->initialized_single || (pin != this ->current_adc_pin )) {
137
306
this ->current_adc_pin = pin;
138
- this ->init (this ->current_adc_pin , this ->current_adc_reference );
307
+ this ->init_single (this ->current_adc_pin , this ->current_adc_reference );
139
308
}
140
309
// Clear single done interrupt
141
310
IADC_clearInt (IADC0, IADC_IF_SINGLEDONE);
@@ -162,18 +331,99 @@ void AdcClass::set_reference(uint8_t reference)
162
331
}
163
332
xSemaphoreTake (this ->adc_mutex , portMAX_DELAY);
164
333
this ->current_adc_reference = reference;
165
- this ->init (this ->current_adc_pin , this ->current_adc_reference );
334
+ if (this ->initialized_single ) {
335
+ this ->init_single (this ->current_adc_pin , this ->current_adc_reference );
336
+ } else if (this ->initialized_scan ) {
337
+ this ->init_scan (this ->current_adc_pin , this ->current_adc_reference );
338
+ }
166
339
xSemaphoreGive (this ->adc_mutex );
167
340
}
168
341
169
- void AdcClass::set_read_resolution (uint8_t resolution) {
342
+ void AdcClass::set_read_resolution (uint8_t resolution)
343
+ {
170
344
if (resolution > this ->max_read_resolution_bits ) {
171
345
this ->current_read_resolution = this ->max_read_resolution_bits ;
172
346
return ;
173
347
}
174
348
this ->current_read_resolution = resolution;
175
349
}
176
350
351
+ sl_status_t AdcClass::scan_start (PinName pin, uint32_t *buffer, uint32_t size, void (*user_onsampling_finished_callback)())
352
+ {
353
+ sl_status_t status = SL_STATUS_FAIL;
354
+ xSemaphoreTake (this ->adc_mutex , portMAX_DELAY);
355
+
356
+ if ((!this ->initialized_scan && !this ->initialized_single ) || (pin != this ->current_adc_pin )) {
357
+ // Initialize in scan mode
358
+ this ->current_adc_pin = pin;
359
+ this ->user_onsampling_finished_callback = user_onsampling_finished_callback;
360
+ this ->init_scan (this ->current_adc_pin , this ->current_adc_reference );
361
+ status = this ->init_dma (buffer, size);
362
+ } else if (this ->initialized_scan && this ->paused_transfer ) {
363
+ // Resume DMA transfer if paused
364
+ status = DMADRV_ResumeTransfer (this ->dma_channel );
365
+ this ->paused_transfer = false ;
366
+ } else if (this ->initialized_single ) {
367
+ // Initialize in scan mode if it was initialized in single mode
368
+ this ->deinit ();
369
+ this ->current_adc_pin = pin;
370
+ this ->user_onsampling_finished_callback = user_onsampling_finished_callback;
371
+ this ->init_scan (this ->current_adc_pin , this ->current_adc_reference );
372
+ status = this ->init_dma (buffer, size);
373
+ } else {
374
+ xSemaphoreGive (this ->adc_mutex );
375
+ return status;
376
+ }
377
+
378
+ // Start the conversion and wait for results
379
+ IADC_command (IADC0, iadcCmdStartScan);
380
+
381
+ xSemaphoreGive (this ->adc_mutex );
382
+ return status;
383
+ }
384
+
385
+ void AdcClass::scan_stop ()
386
+ {
387
+ // Pause sampling
388
+ DMADRV_PauseTransfer (this ->dma_channel );
389
+ this ->paused_transfer = true ;
390
+ }
391
+
392
+ void AdcClass::deinit ()
393
+ {
394
+ // Stop sampling
395
+ DMADRV_StopTransfer (this ->dma_channel );
396
+
397
+ // Free resources
398
+ DMADRV_FreeChannel (this ->dma_channel );
399
+
400
+ // Reset the ADC
401
+ IADC_reset (IADC0);
402
+
403
+ this ->initialized_scan = false ;
404
+ this ->initialized_single = false ;
405
+ this ->current_adc_pin = PIN_NAME_NC;
406
+ }
407
+
408
+ void AdcClass::handle_dma_finished_callback ()
409
+ {
410
+ if (!this ->user_onsampling_finished_callback ) {
411
+ return ;
412
+ }
413
+
414
+ this ->user_onsampling_finished_callback ();
415
+ }
416
+
417
+ bool dma_transfer_finished_cb (unsigned int channel, unsigned int sequenceNo, void *userParam)
418
+ {
419
+ (void )channel;
420
+ (void )sequenceNo;
421
+ (void )userParam;
422
+
423
+ ADC.handle_dma_finished_callback ();
424
+ return false ;
425
+ }
426
+
177
427
const IADC_PosInput_t AdcClass::GPIO_to_ADC_pin_map[64 ] = {
178
428
// Port A
179
429
iadcPosInputPortAPin0,
0 commit comments