2828
2929using  namespace  arduino ; 
3030
31+ static  bool  dma_transfer_finished_cb (unsigned  int  channel, unsigned  int  sequenceNo, void  *userParam);
32+ 
3133AdcClass::AdcClass () :
32-   initialized(false ),
34+   initialized_single(false ),
35+   initialized_scan(false ),
36+   paused_transfer(false ),
3337  current_adc_pin(PD2),
3438  current_adc_reference(AR_VDD),
3539  current_read_resolution(this ->max_read_resolution_bits),
40+   user_onsampling_finished_callback(nullptr ),
3641  adc_mutex(nullptr )
3742{
3843  this ->adc_mutex  = xSemaphoreCreateMutexStatic (&this ->adc_mutex_buf );
3944  configASSERT (this ->adc_mutex );
4045}
4146
42- void  AdcClass::init  (PinName pin, uint8_t  reference)
47+ void  AdcClass::init_single  (PinName pin, uint8_t  reference)
4348{
4449  //  Set up the ADC pin as an input
4550  pinMode (pin, INPUT);
@@ -126,16 +131,180 @@ void AdcClass::init(PinName pin, uint8_t reference)
126131    }
127132  }
128133
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;
130295}
131296
132297uint16_t  AdcClass::get_sample (PinName pin)
133298{
134299  xSemaphoreTake (this ->adc_mutex , portMAX_DELAY);
135300
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 )) {
137306    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 );
139308  }
140309  //  Clear single done interrupt
141310  IADC_clearInt (IADC0, IADC_IF_SINGLEDONE);
@@ -162,18 +331,99 @@ void AdcClass::set_reference(uint8_t reference)
162331  }
163332  xSemaphoreTake (this ->adc_mutex , portMAX_DELAY);
164333  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+   }
166339  xSemaphoreGive (this ->adc_mutex );
167340}
168341
169- void  AdcClass::set_read_resolution (uint8_t  resolution) {
342+ void  AdcClass::set_read_resolution (uint8_t  resolution)
343+ {
170344  if  (resolution > this ->max_read_resolution_bits ) {
171345    this ->current_read_resolution  = this ->max_read_resolution_bits ;
172346    return ;
173347  }
174348  this ->current_read_resolution  = resolution;
175349}
176350
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+ 
177427const  IADC_PosInput_t AdcClass::GPIO_to_ADC_pin_map[64 ] = {
178428  //  Port A
179429  iadcPosInputPortAPin0,
0 commit comments