1 /***************************************************************************//**
2  * @file
3  * @brief Incremental Analog to Digital Converter (IADC) Peripheral API
4  *******************************************************************************
5  * # License
6  * <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
7  *******************************************************************************
8  *
9  * SPDX-License-Identifier: Zlib
10  *
11  * The licensor of this software is Silicon Laboratories Inc.
12  *
13  * This software is provided 'as-is', without any express or implied
14  * warranty. In no event will the authors be held liable for any damages
15  * arising from the use of this software.
16  *
17  * Permission is granted to anyone to use this software for any purpose,
18  * including commercial applications, and to alter it and redistribute it
19  * freely, subject to the following restrictions:
20  *
21  * 1. The origin of this software must not be misrepresented; you must not
22  *    claim that you wrote the original software. If you use this software
23  *    in a product, an acknowledgment in the product documentation would be
24  *    appreciated but is not required.
25  * 2. Altered source versions must be plainly marked as such, and must not be
26  *    misrepresented as being the original software.
27  * 3. This notice may not be removed or altered from any source distribution.
28  *
29  ******************************************************************************/
30 
31 #include "em_iadc.h"
32 #if defined(IADC_COUNT) && (IADC_COUNT > 0)
33 
34 #include "sl_assert.h"
35 #include "em_cmu.h"
36 #include "sl_common.h"
37 #include <stddef.h>
38 
39 /***************************************************************************//**
40  * @addtogroup iadc IADC - Incremental ADC
41  * @brief Incremental Analog to Digital Converter (IADC) Peripheral API
42  * @details
43  *  This module contains functions to control the IADC peripheral of Silicon
44  *  Labs 32-bit MCUs and SoCs. The IADC is used to convert analog signals into a
45  *  digital representation.
46  * @{
47  ******************************************************************************/
48 
49 /*******************************************************************************
50  *******************************   DEFINES   ***********************************
51  ******************************************************************************/
52 
53 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
54 
55 // Validation of IADC register block pointer reference for assert statements.
56 #if (IADC_COUNT == 1)
57 #define IADC_REF_VALID(ref)    ((ref) == IADC0)
58 #elif (IADC_COUNT == 2)
59 #define IADC_REF_VALID(ref)    (((ref) == IADC0) || ((ref) == IADC1))
60 #endif
61 
62 // Max IADC clock rates
63 #define IADC_CLK_MAX_FREQ                   40000000UL
64 #define IADC_ANA_CLK_HIGH_SPEED_MAX_FREQ    20000000UL
65 #define IADC_ANA_CLK_NORMAL_MAX_FREQ        10000000UL
66 #define IADC_ANA_CLK_HIGH_ACCURACY_MAX_FREQ  5000000UL
67 #define IADC_ANA_CLK_MAX_FREQ(adcMode) (                          \
68     (adcMode) == iadcCfgModeNormal ? IADC_ANA_CLK_NORMAL_MAX_FREQ \
69     : IADC_ANA_CLK_HIGH_ACCURACY_MAX_FREQ                         \
70     )
71 
72 #define IADC_ROUND_D2I(n) (int)((n) < 0.0f ? ((n) - 0.5f) : ((n) + 0.5f))
73 
74 #define IADC0_SCANENTRIES IADC0_ENTRIES
75 #define IADC0_FIFOENTRIES 0x4UL
76 
77 #define IADC1_SCANENTRIES IADC1_ENTRIES
78 #define IADC1_FIFOENTRIES 0x4UL
79 
80 #define IADC_SCANENTRIES(iadc) (        \
81     (iadc) == IADC0 ? IADC0_SCANENTRIES \
82     : 0UL)
83 #define IADC_CONFIGNUM(iadc) (        \
84     (iadc) == IADC0 ? IADC0_CONFIGNUM \
85     : 0UL)
86 #define IADC_FIFOENTRIES(iadc) (        \
87     (iadc) == IADC0 ? IADC0_FIFOENTRIES \
88     : 0UL)
89 #define IADC_CMU_CLOCK(iadc) (       \
90     (iadc) == IADC0 ? cmuClock_IADC0 \
91     : cmuClock_IADC0)
92 
93 /** @endcond */
94 
95 /*******************************************************************************
96  ***************************   LOCAL FUNCTIONS   *******************************
97  ******************************************************************************/
98 
99 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
100 
IADC_disable(IADC_TypeDef * iadc)101 static void IADC_disable(IADC_TypeDef *iadc)
102 {
103 #if defined(IADC_STATUS_SYNCBUSY)
104   while ((iadc->STATUS & IADC_STATUS_SYNCBUSY) != 0U) {
105     // Wait for synchronization to finish before disable
106   }
107 #endif
108   iadc->EN_CLR = IADC_EN_EN;
109 #if defined(_IADC_EN_DISABLING_MASK)
110   while (IADC0->EN & _IADC_EN_DISABLING_MASK) {
111   }
112 #endif
113 }
114 
IADC_enable(IADC_TypeDef * iadc)115 static void IADC_enable(IADC_TypeDef *iadc)
116 {
117   iadc->EN_SET = IADC_EN_EN;
118 }
119 
IADC_ConvertRawDataToResult(uint32_t rawData,IADC_Alignment_t alignment)120 static IADC_Result_t IADC_ConvertRawDataToResult(uint32_t rawData,
121                                                  IADC_Alignment_t alignment)
122 {
123   IADC_Result_t result;
124 
125   switch (alignment) {
126     case iadcAlignRight12:
127 #if defined(IADC_SINGLEFIFOCFG_ALIGNMENT_RIGHT16)
128     case iadcAlignRight16:
129 #endif
130 #if defined(IADC_SINGLEFIFOCFG_ALIGNMENT_RIGHT20)
131     case iadcAlignRight20:
132 #endif
133       // Mask out ID and replace with sign extension
134       result.data = (rawData & 0x00FFFFFFUL)
135                     | ((rawData & 0x00800000UL) != 0x0UL ? 0xFF000000UL : 0x0UL);
136       // Mask out data and shift down
137       result.id   = (uint8_t)((rawData & 0xFF000000UL) >> 24);
138       break;
139 
140     case iadcAlignLeft12:
141 #if defined(IADC_SINGLEFIFOCFG_ALIGNMENT_RIGHT16)
142     case iadcAlignLeft16:
143 #endif
144 #if defined(IADC_SINGLEFIFOCFG_ALIGNMENT_RIGHT20)
145     case iadcAlignLeft20:
146 #endif
147       result.data = rawData & 0xFFFFFF00UL;
148       result.id   = (uint8_t)(rawData & 0x000000FFUL);
149       break;
150     default:
151       break;
152   }
153   return result;
154 }
155 
156 /** @endcond */
157 
158 /*******************************************************************************
159  **************************   GLOBAL FUNCTIONS   *******************************
160  ******************************************************************************/
161 
162 /***************************************************************************//**
163  * @brief
164  *   Initialize IADC.
165  *
166  * @details
167  *   Initializes common parts for both single conversion and scan sequence.
168  *   In addition, single and/or scan control configuration must be done, please
169  *   refer to @ref IADC_initSingle() and @ref IADC_initScan() respectively.
170  *
171  * @note
172  *   This function will stop any ongoing conversions.
173  *
174  * @param[in] iadc
175  *   Pointer to IADC peripheral register block.
176  *
177  * @param[in] init
178  *   Pointer to IADC initialization structure.
179  *
180  * @param[in] allConfigs
181  *   Pointer to structure holding all configs.
182  ******************************************************************************/
IADC_init(IADC_TypeDef * iadc,const IADC_Init_t * init,const IADC_AllConfigs_t * allConfigs)183 void IADC_init(IADC_TypeDef *iadc,
184                const IADC_Init_t *init,
185                const IADC_AllConfigs_t *allConfigs)
186 {
187   uint32_t tmp;
188   uint32_t config;
189   uint16_t wantedPrescale;
190   uint8_t srcClkPrescale;
191   uint32_t adcClkPrescale;
192   uint8_t timebase;
193   unsigned uiAnaGain;
194   uint16_t uiGainCAna;
195   IADC_CfgAdcMode_t adcMode;
196 #if defined(_IADC_CFG_ADCMODE_HIGHACCURACY)
197   float anaGain;
198   int anaGainRound;
199   float offsetAna;
200   float offset2;
201   int offsetLong;
202   int offsetAna1HiAccInt;
203   uint8_t osrValue;
204   float offsetAnaBase;
205   float gainSysHiAcc;
206   float refVoltage = 0;
207   // Over sampling ratio for high accuracy conversions
208   const float osrHiAcc[6] = { 16.0, 32.0, 64.0, 92.0, 128.0, 256.0 };
209 #endif
210 
211   EFM_ASSERT(IADC_REF_VALID(iadc));
212 
213   // Calculate min allowed SRC_CLK prescaler setting
214   srcClkPrescale = IADC_calcSrcClkPrescale(iadc, IADC_CLK_MAX_FREQ, 0);
215 
216   wantedPrescale = init->srcClkPrescale;
217   // Use wanted SRC_CLK prescaler setting instead if it is high enough
218   if (wantedPrescale >= srcClkPrescale) {
219     srcClkPrescale = wantedPrescale;
220   }
221 
222   IADC_disable(iadc);
223 
224   timebase = init->timebase;
225   if (timebase == 0) {
226     // Calculate timebase based on CMU_IADCCLKCTRL
227     timebase = IADC_calcTimebase(iadc, 0);
228   }
229 
230   tmp = (((uint32_t)(init->warmup) << _IADC_CTRL_WARMUPMODE_SHIFT)
231          & _IADC_CTRL_WARMUPMODE_MASK)
232         | (((uint32_t)(timebase) << _IADC_CTRL_TIMEBASE_SHIFT)
233            & _IADC_CTRL_TIMEBASE_MASK)
234         | (((uint32_t)(srcClkPrescale) << _IADC_CTRL_HSCLKRATE_SHIFT)
235            & _IADC_CTRL_HSCLKRATE_MASK);
236 
237   if (init->iadcClkSuspend0) {
238     tmp |= IADC_CTRL_ADCCLKSUSPEND0;
239   }
240   if (init->iadcClkSuspend1) {
241     tmp |= IADC_CTRL_ADCCLKSUSPEND1;
242   }
243   if (init->debugHalt) {
244     tmp |= IADC_CTRL_DBGHALT;
245   }
246   iadc->CTRL = tmp;
247 
248   iadc->TIMER = ((uint32_t) (init->timerCycles) << _IADC_TIMER_TIMER_SHIFT)
249                 & _IADC_TIMER_TIMER_MASK;
250 
251   iadc->CMPTHR = (((uint32_t) (init->greaterThanEqualThres) << _IADC_CMPTHR_ADGT_SHIFT)
252                   & _IADC_CMPTHR_ADGT_MASK)
253                  | (((uint32_t) (init->lessThanEqualThres) << _IADC_CMPTHR_ADLT_SHIFT)
254                     & _IADC_CMPTHR_ADLT_MASK);
255 
256   // Write configurations
257   for (config = 0; config < IADC_CONFIGNUM(iadc); config++) {
258     // Find min allowed ADC_CLK prescaler setting for given mode
259     adcMode = allConfigs->configs[config].adcMode;
260     wantedPrescale = allConfigs->configs[config].adcClkPrescale;
261     adcClkPrescale = IADC_calcAdcClkPrescale(iadc,
262                                              IADC_ANA_CLK_MAX_FREQ(adcMode),
263                                              0,
264                                              adcMode,
265                                              srcClkPrescale);
266 
267     // Use wanted ADC_CLK prescaler setting instead if it is high enough
268     adcClkPrescale = SL_MAX(adcClkPrescale, wantedPrescale);
269 
270     tmp = iadc->CFG[config].CFG & ~(_IADC_CFG_ADCMODE_MASK | _IADC_CFG_OSRHS_MASK
271                                     | _IADC_CFG_ANALOGGAIN_MASK | _IADC_CFG_REFSEL_MASK
272 #if defined(_IADC_CFG_DIGAVG_MASK)
273                                     | _IADC_CFG_DIGAVG_MASK
274 #endif
275                                     | _IADC_CFG_TWOSCOMPL_MASK
276 #if defined(_IADC_CFG_ADCMODE_HIGHACCURACY)
277                                     | _IADC_CFG_OSRHA_MASK
278 #endif
279                                     );
280     iadc->CFG[config].CFG = tmp
281                             | (((uint32_t)(adcMode) << _IADC_CFG_ADCMODE_SHIFT) & _IADC_CFG_ADCMODE_MASK)
282                             | (((uint32_t)(allConfigs->configs[config].osrHighSpeed) << _IADC_CFG_OSRHS_SHIFT)
283                                & _IADC_CFG_OSRHS_MASK)
284 #if defined(_IADC_CFG_ADCMODE_HIGHACCURACY)
285                             | (((uint32_t)(allConfigs->configs[config].osrHighAccuracy) << _IADC_CFG_OSRHA_SHIFT)
286                                & _IADC_CFG_OSRHA_MASK)
287 #endif
288                             | (((uint32_t)(allConfigs->configs[config].analogGain) << _IADC_CFG_ANALOGGAIN_SHIFT)
289                                & _IADC_CFG_ANALOGGAIN_MASK)
290                             | (((uint32_t)(allConfigs->configs[config].reference) << _IADC_CFG_REFSEL_SHIFT)
291                                & _IADC_CFG_REFSEL_MASK)
292 #if defined(_IADC_CFG_DIGAVG_MASK)
293                             | (((uint32_t)(allConfigs->configs[config].digAvg) << _IADC_CFG_DIGAVG_SHIFT)
294                                & _IADC_CFG_DIGAVG_MASK)
295 #endif
296                             | (((uint32_t)(allConfigs->configs[config].twosComplement) << _IADC_CFG_TWOSCOMPL_SHIFT)
297                                & _IADC_CFG_TWOSCOMPL_MASK);
298 
299     uiAnaGain = (iadc->CFG[config].CFG & _IADC_CFG_ANALOGGAIN_MASK) >> _IADC_CFG_ANALOGGAIN_SHIFT;
300     switch (uiAnaGain) {
301 #if defined(_IADC_CFG_ANALOGGAIN_ANAGAIN0P25)
302       case iadcCfgAnalogGain0P25x: // 0.25x
303 #endif
304       case iadcCfgAnalogGain0P5x: // 0.5x
305       case iadcCfgAnalogGain1x: // 1x
306         uiGainCAna = (uint16_t)((DEVINFO->IADC0GAIN0 & _DEVINFO_IADC0GAIN0_GAINCANA1_MASK) >> _DEVINFO_IADC0GAIN0_GAINCANA1_SHIFT);
307         break;
308       case iadcCfgAnalogGain2x: // 2x
309         uiGainCAna = (uint16_t)((DEVINFO->IADC0GAIN0 & _DEVINFO_IADC0GAIN0_GAINCANA2_MASK) >> _DEVINFO_IADC0GAIN0_GAINCANA2_SHIFT);
310         break;
311       case iadcCfgAnalogGain3x: // 3x
312         uiGainCAna = (uint16_t)((DEVINFO->IADC0GAIN1 & _DEVINFO_IADC0GAIN1_GAINCANA3_MASK) >> _DEVINFO_IADC0GAIN1_GAINCANA3_SHIFT);
313         break;
314       case iadcCfgAnalogGain4x: // 4x
315         uiGainCAna = (uint16_t)((DEVINFO->IADC0GAIN1 & _DEVINFO_IADC0GAIN1_GAINCANA4_MASK) >> _DEVINFO_IADC0GAIN1_GAINCANA4_SHIFT);
316         break;
317       default: // 1x
318         uiGainCAna = (uint16_t)((DEVINFO->IADC0GAIN0 & _DEVINFO_IADC0GAIN0_GAINCANA1_MASK) >> _DEVINFO_IADC0GAIN0_GAINCANA1_SHIFT);
319         break;
320     }
321 
322     // Gain and offset correction is applied according to adcMode and oversampling rate.
323     switch (adcMode) {
324       float offset;
325       uint32_t scale;
326       int iOffset, iOsr;
327       case iadcCfgModeNormal:
328 #if defined(_IADC_CFG_ADCMODE_HIGHSPEED)
329       case iadcCfgModeHighSpeed:
330 #endif
331         offset = 0.0f;
332         uiAnaGain = (iadc->CFG[config].CFG & _IADC_CFG_ANALOGGAIN_MASK) >> _IADC_CFG_ANALOGGAIN_SHIFT;
333         if ((uiAnaGain == _IADC_CFG_ANALOGGAIN_ANAGAIN0P5) || (uiAnaGain == _IADC_CFG_ANALOGGAIN_ANAGAIN1)) {
334           uiGainCAna = (uint16_t)(DEVINFO->IADC0GAIN0 & _DEVINFO_IADC0GAIN0_GAINCANA1_MASK);
335         } else if (uiAnaGain == _IADC_CFG_ANALOGGAIN_ANAGAIN2) {
336           uiGainCAna = (uint16_t)(DEVINFO->IADC0GAIN0 >> _DEVINFO_IADC0GAIN0_GAINCANA2_SHIFT);
337           if (adcMode == iadcCfgModeNormal) {
338             offset = (int16_t)(DEVINFO->IADC0NORMALOFFSETCAL0 >> _DEVINFO_IADC0NORMALOFFSETCAL0_OFFSETANA2NORM_SHIFT);
339           } else {
340             offset = (int16_t)(DEVINFO->IADC0HISPDOFFSETCAL0 >> _DEVINFO_IADC0HISPDOFFSETCAL0_OFFSETANA2HISPD_SHIFT);
341           }
342         } else if (uiAnaGain == _IADC_CFG_ANALOGGAIN_ANAGAIN3) {
343           uiGainCAna = (uint16_t)(DEVINFO->IADC0GAIN1 & _DEVINFO_IADC0GAIN1_GAINCANA3_MASK);
344           if (adcMode == iadcCfgModeNormal) {
345             offset = (int16_t)(DEVINFO->IADC0NORMALOFFSETCAL0 >> _DEVINFO_IADC0NORMALOFFSETCAL0_OFFSETANA2NORM_SHIFT) * 2;
346           } else {
347             offset = (int16_t)(DEVINFO->IADC0HISPDOFFSETCAL0 >> _DEVINFO_IADC0HISPDOFFSETCAL0_OFFSETANA2HISPD_SHIFT) * 2;
348           }
349         } else {
350           uiGainCAna = (uint16_t)(DEVINFO->IADC0GAIN1 >> _DEVINFO_IADC0GAIN1_GAINCANA4_SHIFT);
351           if (adcMode == iadcCfgModeNormal) {
352             offset = (int16_t)(DEVINFO->IADC0NORMALOFFSETCAL0 >> _DEVINFO_IADC0NORMALOFFSETCAL0_OFFSETANA2NORM_SHIFT) * 3;
353           } else {
354             offset = (int16_t)(DEVINFO->IADC0HISPDOFFSETCAL0 >> _DEVINFO_IADC0HISPDOFFSETCAL0_OFFSETANA2HISPD_SHIFT) * 3;
355           }
356         }
357 
358         // Set correct gain correction bitfields in scale variable.
359         tmp = (uint32_t)uiGainCAna & 0x9FFFU;
360         scale = tmp << _IADC_SCALE_GAIN13LSB_SHIFT;
361         if ((tmp & 0x8000U) != 0U) {
362           scale |= IADC_SCALE_GAIN3MSB;
363         }
364 
365         // Adjust offset according to selected OSR.
366         iOsr = 1U << (((iadc->CFG[config].CFG & _IADC_CFG_OSRHS_MASK) >> _IADC_CFG_OSRHS_SHIFT) + 1U);
367         if (iOsr == 2) {
368           if (adcMode == iadcCfgModeNormal) {
369             offset += (int16_t)(DEVINFO->IADC0NORMALOFFSETCAL0 & _DEVINFO_IADC0NORMALOFFSETCAL0_OFFSETANA1NORM_MASK);
370           } else {
371             offset += (int16_t)(DEVINFO->IADC0HISPDOFFSETCAL0 & _DEVINFO_IADC0HISPDOFFSETCAL0_OFFSETANA1HISPD_MASK);
372           }
373         } else {
374           if (adcMode == iadcCfgModeNormal) {
375             offset = (int16_t)(DEVINFO->IADC0NORMALOFFSETCAL1 & _DEVINFO_IADC0NORMALOFFSETCAL1_OFFSETANA3NORM_MASK) - offset;
376           } else {
377             offset += (int16_t)(DEVINFO->IADC0HISPDOFFSETCAL1 & _DEVINFO_IADC0HISPDOFFSETCAL1_OFFSETANA3HISPD_MASK) - offset;
378           }
379           offset /= iOsr / 2.0f;
380           offset += (int16_t)(DEVINFO->IADC0OFFSETCAL0 & _DEVINFO_IADC0OFFSETCAL0_OFFSETANABASE_MASK);
381         }
382 
383         // Compensate offset according to selected reference voltage.
384         if (allConfigs->configs[config].reference == iadcCfgReferenceInt1V2) {
385           // Internal reference voltage (VBGR) depends on the chip revision.
386           offset *= 1.25f / (IADC_getReferenceVoltage(allConfigs->configs[config].reference) / 1000.0f);
387         } else {
388           offset *= 1.25f / (allConfigs->configs[config].vRef / 1000.0f);
389         }
390 
391         // Compensate offset for systematic offset.
392         offset = (offset * 4.0f) + (640.0f * (256.0f / iOsr));
393 
394         // Apply gain error correction.
395         if (scale != 0x80000000U) {
396           offset = (uiGainCAna / 32768.0f) * (offset + 524288.0f) - 524288.0f;
397         }
398 
399         iOffset = IADC_ROUND_D2I(-offset);
400         // We only have 18 bits available for OFFSET in SCALE register.
401         // OFFSET is a 2nd complement number.
402         if (iOffset > 131071) {         // Positive overflow at 0x0001FFFF ?
403           scale |= 0x1FFFFU;
404         } else if (iOffset < -131072) { // Negative overflow at 0xFFFE0000 ?
405           scale |= 0x20000U;
406         } else {
407           scale |= (uint32_t)iOffset & 0x3FFFFU;
408         }
409         iadc->CFG[config].SCALE = scale;
410         break;
411 
412 #if defined(_IADC_CFG_ADCMODE_HIGHACCURACY)
413       case iadcCfgModeHighAccuracy:
414         // Get reference voltage in volts
415         refVoltage = IADC_getReferenceVoltage(allConfigs->configs[config].reference) / 1000.0f;
416 
417         // Get OSR from config register
418         osrValue = (iadc->CFG[config].CFG & _IADC_CFG_OSRHA_MASK) >> _IADC_CFG_OSRHA_SHIFT;
419 
420         // 1. Calculate gain correction
421         if (osrHiAcc[osrValue] == 92.0) {
422           // for OSR = 92, gainSysHiAcc = 0.957457
423           gainSysHiAcc = 0.957457;
424         } else {
425           // for OSR != 92, gainSysHiAcc = OSR/(OSR + 1)
426           gainSysHiAcc = osrHiAcc[osrValue] / (osrHiAcc[osrValue] + 1);
427         }
428         anaGain = (float) uiGainCAna / 32768.0f * gainSysHiAcc;
429         anaGainRound =  IADC_ROUND_D2I(32768.0f * anaGain);
430         IADC0->CFG[config].SCALE &= ~_IADC_SCALE_MASK;
431 
432         // Write GAIN3MSB
433         if ((uint32_t)anaGainRound & 0x8000) {
434           IADC0->CFG[config].SCALE |= IADC_SCALE_GAIN3MSB_GAIN100;
435         } else {
436           IADC0->CFG[config].SCALE |= IADC_SCALE_GAIN3MSB_GAIN011;
437         }
438         // Write GAIN13LSB
439         IADC0->CFG[config].SCALE |= ((uint32_t)anaGainRound & 0x1FFF) << _IADC_SCALE_GAIN13LSB_SHIFT;
440 
441         // Get offset value for high accuracy mode from DEVINFO
442         offsetAna1HiAccInt = (uint16_t)(DEVINFO->IADC0OFFSETCAL0 & _DEVINFO_IADC0OFFSETCAL0_OFFSETANA1HIACC_MASK)
443                              >> _DEVINFO_IADC0OFFSETCAL0_OFFSETANA1HIACC_SHIFT;
444 
445         // 2. OSR adjustment
446         // Get offset from DEVINFO
447         offsetAnaBase = (int16_t)(DEVINFO->IADC0OFFSETCAL0 & _DEVINFO_IADC0OFFSETCAL0_OFFSETANABASE_MASK)
448                         >> _DEVINFO_IADC0OFFSETCAL0_OFFSETANABASE_SHIFT;
449         offsetAna = offsetAnaBase + (offsetAna1HiAccInt) / (2 ^ osrValue);
450 
451         // 3. Reference voltage adjustment
452         offsetAna = (offsetAna) * (1.25f / refVoltage);
453 
454         // 4. Calculate final offset
455         offset2 = 262144.0f / osrHiAcc[osrValue] / (osrHiAcc[osrValue] + 1) + offsetAna * 4.0f + 524288.0f;
456         offset2 = (uiGainCAna / 32768.0f * (-1.0)) * offset2 + 524288.0f;
457         offsetLong = IADC_ROUND_D2I(offset2);
458 
459         // 5. Write offset to scale register
460         IADC0->CFG[config].SCALE |= (uint32_t)(offsetLong & _IADC_SCALE_OFFSET_MASK);
461         break;
462 #endif
463       default:
464         // Mode not supported.
465         EFM_ASSERT(false);
466         break;
467     }
468     iadc->CFG[config].SCHED = ((adcClkPrescale << _IADC_SCHED_PRESCALE_SHIFT)
469                                & _IADC_SCHED_PRESCALE_MASK);
470   }
471   IADC_enable(iadc);
472 }
473 
474 /***************************************************************************//**
475  * @brief
476  *   Initialize IADC scan sequence.
477  *
478  * @details
479  *   This function will configure scan mode and set up entries in the scan
480  *   table. The scan table mask can be updated by calling IADC_updateScanMask.
481  *
482  * @note
483  *   This function will stop any ongoing conversions.
484  *
485  * @note If an even numbered pin is selected for the positive input, the
486  *   negative input must use an odd numbered pin and vice versa.
487  *
488  * @param[in] iadc
489  *   Pointer to IADC peripheral register block.
490  *
491  * @param[in] init
492  *   Pointer to IADC initialization structure.
493  *
494  * @param[in] scanTable
495  *   Pointer to IADC scan table structure.
496  ******************************************************************************/
IADC_initScan(IADC_TypeDef * iadc,const IADC_InitScan_t * init,const IADC_ScanTable_t * scanTable)497 void IADC_initScan(IADC_TypeDef *iadc,
498                    const IADC_InitScan_t *init,
499                    const IADC_ScanTable_t *scanTable)
500 {
501   uint32_t i;
502   uint32_t tmp;
503   EFM_ASSERT(IADC_REF_VALID(iadc));
504 #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_3)
505   // Errata IADC_E305. Makes sure that DVL is equal or less than 7 entries.
506   EFM_ASSERT(init->dataValidLevel <= iadcFifoCfgDvl7);
507 #endif
508 
509   IADC_disable(iadc);
510 
511   iadc->SCANFIFOCFG = (((uint32_t) (init->alignment) << _IADC_SCANFIFOCFG_ALIGNMENT_SHIFT)
512                        & _IADC_SCANFIFOCFG_ALIGNMENT_MASK)
513                       | (init->showId ? IADC_SCANFIFOCFG_SHOWID : 0UL)
514                       | (((uint32_t) (init->dataValidLevel) << _IADC_SCANFIFOCFG_DVL_SHIFT)
515                          & _IADC_SCANFIFOCFG_DVL_MASK)
516                       | (init->fifoDmaWakeup ? IADC_SCANFIFOCFG_DMAWUFIFOSCAN : 0UL);
517 
518   // Clear bitfields for scan conversion in IADCn->TRIGGER and set new values
519   iadc->TRIGGER = (iadc->TRIGGER & ~(_IADC_TRIGGER_SCANTRIGSEL_MASK
520                                      | _IADC_TRIGGER_SCANTRIGACTION_MASK))
521                   | (((uint32_t) (init->triggerSelect) << _IADC_TRIGGER_SCANTRIGSEL_SHIFT)
522                      & _IADC_TRIGGER_SCANTRIGSEL_MASK)
523                   | (((uint32_t) (init->triggerAction) << _IADC_TRIGGER_SCANTRIGACTION_SHIFT)
524                      & _IADC_TRIGGER_SCANTRIGACTION_MASK);
525 
526   // Write scan table
527   for (i = 0; i < IADC_SCANENTRIES(iadc); i++) {
528     iadc->SCANTABLE[i].SCAN = (((uint32_t) (scanTable->entries[i].negInput) << _IADC_SCAN_PINNEG_SHIFT)
529                                & (_IADC_SCAN_PORTNEG_MASK | _IADC_SCAN_PINNEG_MASK))
530                               | (((uint32_t) (scanTable->entries[i].posInput) << _IADC_SCAN_PINPOS_SHIFT)
531                                  & (_IADC_SCAN_PORTPOS_MASK | _IADC_SCAN_PINPOS_MASK))
532                               | (((uint32_t) (scanTable->entries[i].configId) << _IADC_SCAN_CFG_SHIFT)
533                                  & _IADC_SCAN_CFG_MASK)
534                               | (scanTable->entries[i].compare ? IADC_SCAN_CMP : 0UL);
535   }
536 
537   IADC_enable(iadc);
538 
539   // Set scan mask
540   tmp = 0;
541   for (i = 0; i < IADC_SCANENTRIES(iadc); i++) {
542     if (scanTable->entries[i].includeInScan) {
543       tmp |= (1UL << i) << _IADC_MASKREQ_MASKREQ_SHIFT;
544     }
545   }
546   iadc->MASKREQ = tmp;
547 
548   if (init->start) {
549     IADC_command(iadc, iadcCmdStartScan);
550   }
551 }
552 
553 /***************************************************************************//**
554  * @brief
555  *   Initialize single IADC conversion.
556  *
557  * @details
558  *   This function will initialize the single conversion and configure the
559  *   single input selection.
560  *
561  * @note
562  *   This function will stop any ongoing conversions.
563  *
564  * @note If an even numbered pin is selected for the positive input, the
565  *   negative input must use an odd numbered pin and vice versa.
566  *
567  * @param[in] iadc
568  *   Pointer to IADC peripheral register block.
569  *
570  * @param[in] init
571  *   Pointer to IADC single initialization structure.
572  *
573  * @param[in] input
574  *   Pointer to IADC single input selection initialization structure.
575  ******************************************************************************/
IADC_initSingle(IADC_TypeDef * iadc,const IADC_InitSingle_t * init,const IADC_SingleInput_t * input)576 void IADC_initSingle(IADC_TypeDef *iadc,
577                      const IADC_InitSingle_t *init,
578                      const IADC_SingleInput_t *input)
579 {
580   EFM_ASSERT(IADC_REF_VALID(iadc));
581 #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_3)
582   // Errata IADC_E305. Makes sure that DVL is equal or less than 7 entries.
583   EFM_ASSERT(init->dataValidLevel <= iadcFifoCfgDvl7);
584 #endif
585   IADC_disable(iadc);
586 
587   iadc->SINGLEFIFOCFG = (((uint32_t) (init->alignment) << _IADC_SINGLEFIFOCFG_ALIGNMENT_SHIFT)
588                          & _IADC_SINGLEFIFOCFG_ALIGNMENT_MASK)
589                         | (init->showId ? IADC_SINGLEFIFOCFG_SHOWID : 0UL)
590                         | (((uint32_t) (init->dataValidLevel) << _IADC_SINGLEFIFOCFG_DVL_SHIFT)
591                            & _IADC_SINGLEFIFOCFG_DVL_MASK)
592                         | (init->fifoDmaWakeup ? IADC_SINGLEFIFOCFG_DMAWUFIFOSINGLE : 0UL);
593 
594   // Clear bitfields for single conversion in IADCn->TRIGGER and set new values
595   iadc->TRIGGER = (iadc->TRIGGER & ~(_IADC_TRIGGER_SINGLETRIGSEL_MASK
596                                      | _IADC_TRIGGER_SINGLETRIGACTION_MASK
597                                      | _IADC_TRIGGER_SINGLETAILGATE_MASK))
598                   | (((uint32_t) (init->triggerSelect) << _IADC_TRIGGER_SINGLETRIGSEL_SHIFT)
599                      & _IADC_TRIGGER_SINGLETRIGSEL_MASK)
600                   | (((uint32_t) (init->triggerAction) << _IADC_TRIGGER_SINGLETRIGACTION_SHIFT)
601                      & _IADC_TRIGGER_SINGLETRIGACTION_MASK)
602                   | (init->singleTailgate ? IADC_TRIGGER_SINGLETAILGATE : 0UL);
603 
604   IADC_updateSingleInput(iadc, input);
605 
606   IADC_enable(iadc);
607 
608   if (init->start) {
609     IADC_command(iadc, iadcCmdStartSingle);
610   }
611 }
612 
613 /***************************************************************************//**
614  * @brief
615  *   Update IADC single input selection.
616  *
617  * @details
618  *   This function updates the single input selection. The function can be
619  *   called while single and/or scan conversions are ongoing and the new input
620  *   configuration will take place on the next single conversion.
621  *
622  * @note If an even numbered pin is selected for the positive input, the
623  *   negative input must use an odd numbered pin and vice versa.
624  *
625  * @param[in] iadc
626  *   Pointer to IADC peripheral register block.
627  *
628  * @param[in] input
629  *   Pointer to single input selection structure.
630  ******************************************************************************/
IADC_updateSingleInput(IADC_TypeDef * iadc,const IADC_SingleInput_t * input)631 void IADC_updateSingleInput(IADC_TypeDef *iadc,
632                             const IADC_SingleInput_t *input)
633 {
634   bool enabled;
635 
636   EFM_ASSERT(IADC_REF_VALID(iadc));
637 
638   enabled = (iadc->EN & IADC_EN_EN) != 0UL;
639 
640   // IADCn->SINGLE has WSYNC type and can only be written while enabled
641   IADC_enable(iadc);
642 
643   iadc->SINGLE = (((uint32_t) (input->negInput) << _IADC_SINGLE_PINNEG_SHIFT)
644                   & (_IADC_SINGLE_PORTNEG_MASK | _IADC_SINGLE_PINNEG_MASK))
645                  | (((uint32_t) (input->posInput) << _IADC_SINGLE_PINPOS_SHIFT)
646                     & (_IADC_SINGLE_PORTPOS_MASK | _IADC_SINGLE_PINPOS_MASK))
647                  | (((uint32_t) (input->configId) << _IADC_SINGLE_CFG_SHIFT)
648                     & _IADC_SINGLE_CFG_MASK)
649                  | (input->compare ? IADC_SINGLE_CMP : 0UL);
650 
651   // Restore enabled state
652   if (!enabled) {
653     IADC_disable(iadc);
654   }
655 }
656 
657 /***************************************************************************//**
658  * @brief
659  *   Set mask of IADC scan table entries to include in scan.
660  *
661  * @details
662  *   Set mask of scan table entries to include in next scan. This function
663  *   can be called while scan conversions are ongoing, but the new scan mask
664  *   will take effect once the ongoing scan is completed.
665  *
666  * @param[in] iadc
667  *   Pointer to IADC peripheral register block.
668  *
669  * @param[in] mask
670  *   Mask of scan table entries to include in scan.
671  ******************************************************************************/
IADC_setScanMask(IADC_TypeDef * iadc,uint32_t mask)672 void IADC_setScanMask(IADC_TypeDef *iadc, uint32_t mask)
673 {
674   bool enabled;
675 
676   EFM_ASSERT(IADC_REF_VALID(iadc));
677 
678   EFM_ASSERT(mask <= ((1UL << IADC_SCANENTRIES(iadc)) - 1UL));
679 
680   enabled = (iadc->EN & IADC_EN_EN) != 0UL;
681 
682   // IADC must be enabled to update scan table mask
683   IADC_enable(iadc);
684 
685   iadc->MASKREQ = (mask << _IADC_MASKREQ_MASKREQ_SHIFT)
686                   & _IADC_MASKREQ_MASKREQ_MASK;
687 
688   // Restore enabled state
689   if (!enabled) {
690     IADC_disable(iadc);
691   }
692 }
693 
694 /***************************************************************************//**
695  * @brief
696  *   Add/update entry in scan table.
697  *
698  * @details
699  *   This function will update or add an entry in the scan table with a specific
700  *   ID.
701  *
702  * @note
703  *   This function will stop any ongoing conversions.
704  *
705  * @param[in] iadc
706  *   Pointer to IADC peripheral register block.
707  *
708  * @param[in] id
709  *   ID of scan table entry to add.
710  *
711  * @param[in] entry
712  *   Pointer to scan table entry structure.
713  ******************************************************************************/
IADC_updateScanEntry(IADC_TypeDef * iadc,uint8_t id,IADC_ScanTableEntry_t * entry)714 void IADC_updateScanEntry(IADC_TypeDef *iadc,
715                           uint8_t id,
716                           IADC_ScanTableEntry_t *entry)
717 {
718   bool enabled;
719 
720   EFM_ASSERT(IADC_REF_VALID(iadc));
721 
722   enabled = (iadc->EN & IADC_EN_EN) != 0UL;
723 
724   // IADC must be disabled to update scan table
725   IADC_disable(iadc);
726 
727   // Update entry in scan table
728   iadc->SCANTABLE[id].SCAN = (((uint32_t) (entry->negInput) << _IADC_SCAN_PINNEG_SHIFT)
729                               & (_IADC_SCAN_PORTNEG_MASK | _IADC_SCAN_PINNEG_MASK))
730                              | (((uint32_t) (entry->posInput) << _IADC_SCAN_PINPOS_SHIFT)
731                                 & (_IADC_SCAN_PORTPOS_MASK | _IADC_SCAN_PINPOS_MASK))
732                              | (((uint32_t) (entry->configId) << _IADC_SCAN_CFG_SHIFT)
733                                 & _IADC_SCAN_CFG_MASK)
734                              | (entry->compare ? IADC_SCAN_CMP : 0UL);
735 
736   // IADC must be enabled to update scan table mask
737   IADC_enable(iadc);
738 
739   if (entry->includeInScan) {
740     iadc->MASKREQ_SET = (1UL << (id & 0x1FUL)) << _IADC_MASKREQ_MASKREQ_SHIFT;
741   } else {
742     iadc->MASKREQ_CLR = (1UL << (id & 0x1FUL)) << _IADC_MASKREQ_MASKREQ_SHIFT;
743   }
744 
745   // Restore enabled state
746   if (!enabled) {
747     IADC_disable(iadc);
748   }
749 }
750 
751 /***************************************************************************//**
752  * @brief
753  *   Reset IADC to same state as after a HW reset.
754  *
755  * @param[in] iadc
756  *   Pointer to IADC peripheral register block.
757  ******************************************************************************/
IADC_reset(IADC_TypeDef * iadc)758 void IADC_reset(IADC_TypeDef *iadc)
759 {
760   uint32_t i;
761   EFM_ASSERT(IADC_REF_VALID(iadc));
762 
763   // Write all WSYNC registers to reset value while enabled
764   IADC_enable(iadc);
765 
766   // Stop conversions and timer, before resetting other registers.
767   iadc->CMD = IADC_CMD_SINGLESTOP | IADC_CMD_SCANSTOP | IADC_CMD_TIMERDIS;
768 
769   // Wait for all IADC operations to stop
770   while ((iadc->STATUS & (IADC_STATUS_CONVERTING
771                           | IADC_STATUS_SCANQUEUEPENDING
772                           | IADC_STATUS_SINGLEQUEUEPENDING
773                           | IADC_STATUS_TIMERACTIVE))
774          != 0UL) {
775   }
776 
777   // Reset all WSYNC registers
778   iadc->MASKREQ = _IADC_MASKREQ_RESETVALUE;
779   iadc->SINGLE  = _IADC_SINGLE_RESETVALUE;
780 
781   // Wait for SINGLE and MASQREQ writes to propagate to working registers
782   while ((iadc->STATUS & (IADC_STATUS_MASKREQWRITEPENDING
783                           | IADC_STATUS_SINGLEWRITEPENDING))
784          != 0UL) {
785   }
786 
787   // Pull from FIFOs until they are empty
788 
789   // Errata IADC_E305: Check SINGLEFIFOSTAT to make sure that SINGLEFIFO is getting emptied in case
790   // where STATUS register is incorrect.
791   while (((iadc->STATUS & IADC_STATUS_SINGLEFIFODV) != 0UL) || (iadc->SINGLEFIFOSTAT > 0)) {
792     (void) IADC_pullSingleFifoData(iadc);
793   }
794 
795   // Errata IADC_E305: check SCANFIFOSTAT to make sure that SCANFIFO is getting emptied in case
796   // where STATUS register is incorrect.
797   while (((iadc->STATUS & IADC_STATUS_SCANFIFODV) != 0UL) || (iadc->SCANFIFOSTAT > 0)) {
798     (void) IADC_pullScanFifoData(iadc);
799   }
800 
801   // Read data registers to clear data valid flags
802   (void) IADC_readSingleData(iadc);
803   (void) IADC_readScanData(iadc);
804 
805   // Write all WSTATIC registers to reset value while disabled
806   IADC_disable(iadc);
807 
808   // Reset all WSTATIC registers
809   iadc->CTRL            = _IADC_CTRL_RESETVALUE;
810   iadc->TIMER           = _IADC_TIMER_RESETVALUE;
811   iadc->TRIGGER         = _IADC_TRIGGER_RESETVALUE;
812 
813   iadc->CMPTHR          = _IADC_CMPTHR_RESETVALUE;
814   iadc->SINGLEFIFOCFG   = _IADC_SINGLEFIFOCFG_RESETVALUE;
815   iadc->SCANFIFOCFG     = _IADC_SCANFIFOCFG_RESETVALUE;
816 
817   for (i = 0; i < IADC_CONFIGNUM(iadc); i++) {
818     iadc->CFG[i].CFG    = _IADC_CFG_RESETVALUE;
819     iadc->CFG[i].SCALE  = _IADC_SCALE_RESETVALUE;
820     iadc->CFG[i].SCHED  = _IADC_SCHED_RESETVALUE;
821   }
822 
823   for (i = 0; i < IADC_SCANENTRIES(iadc); i++) {
824     iadc->SCANTABLE[i].SCAN = _IADC_SCAN_RESETVALUE;
825   }
826 
827   // Clear interrupt flags and disable interrupts
828   IADC_clearInt(iadc, _IADC_IF_MASK);
829   IADC_disableInt(iadc, _IADC_IEN_MASK);
830 }
831 
832 /***************************************************************************//**
833  * @brief
834  *   Calculate timebase value in order to get a timebase providing at least 1us.
835  *
836  * @param[in] iadc
837  *   Pointer to IADC peripheral register block.
838  *
839  * @param[in] cmuClkFreq Frequency in Hz of reference CLK_CMU_ADC clock. Set to 0 to
840  *   use currently defined CMU clock setting for the IADC.
841  *
842  * @return
843  *   Timebase value to use for IADC in order to achieve at least 1 us.
844  ******************************************************************************/
IADC_calcTimebase(IADC_TypeDef * iadc,uint32_t cmuClkFreq)845 uint8_t IADC_calcTimebase(IADC_TypeDef *iadc, uint32_t cmuClkFreq)
846 {
847   EFM_ASSERT(IADC_REF_VALID(iadc));
848 
849   if (cmuClkFreq == 0UL) {
850     cmuClkFreq = CMU_ClockFreqGet(IADC_CMU_CLOCK(iadc));
851 
852     // Just in case, make sure we get non-zero freq for below calculation
853     if (cmuClkFreq == 0UL) {
854       cmuClkFreq = 1;
855     }
856   }
857   // Determine number of ADCCLK cycle >= 1us
858   cmuClkFreq += 999999UL;
859   cmuClkFreq /= 1000000UL;
860 
861   // Convert to N+1 format
862   cmuClkFreq -= 1UL;
863 
864   // Limit to max allowed register setting
865   cmuClkFreq = SL_MIN(cmuClkFreq, (_IADC_CTRL_TIMEBASE_MASK >> _IADC_CTRL_TIMEBASE_SHIFT));
866 
867   // Return timebase value
868   return (uint8_t) cmuClkFreq;
869 }
870 
871 /***************************************************************************//**
872  * @brief
873  *   Calculate prescaler for CLK_SRC_ADC high speed clock
874  *
875  * @details
876  *   The IADC high speed clock is given by: CLK_SRC_ADC / (srcClkPrescaler + 1).
877  *
878  * @param[in] iadc
879  *   Pointer to IADC peripheral register block.
880  *
881  * @param[in] srcClkFreq CLK_SRC_ADC frequency wanted. The frequency will
882  *   automatically be adjusted to be within valid range according to reference
883  *   manual.
884  *
885  * @param[in] cmuClkFreq Frequency in Hz of reference CLK_CMU_ADC. Set to 0
886  *   to use currently defined CMU clock setting for the IADC.
887  *
888  * @return
889  *   Divider value to use for IADC in order to achieve a high speed clock value
890  *   <= @p srcClkFreq.
891  ******************************************************************************/
IADC_calcSrcClkPrescale(IADC_TypeDef * iadc,uint32_t srcClkFreq,uint32_t cmuClkFreq)892 uint8_t IADC_calcSrcClkPrescale(IADC_TypeDef *iadc,
893                                 uint32_t srcClkFreq,
894                                 uint32_t cmuClkFreq)
895 {
896   uint32_t ret;
897 
898   EFM_ASSERT(IADC_REF_VALID(iadc));
899   EFM_ASSERT(srcClkFreq);
900 
901   // Make sure wanted CLK_SRC_ADC clock is below max allowed frequency
902   srcClkFreq = SL_MIN(srcClkFreq, IADC_CLK_MAX_FREQ);
903 
904   // Use current CLK_CMU_ADC frequency?
905   if (cmuClkFreq == 0UL) {
906     cmuClkFreq = CMU_ClockFreqGet(IADC_CMU_CLOCK(iadc));
907   }
908 
909   ret = (cmuClkFreq + srcClkFreq - 1UL) / srcClkFreq;
910   if (ret != 0UL) {
911     ret--;
912   }
913 
914   // Limit to max allowed register setting
915   if (ret > _IADC_CTRL_HSCLKRATE_DIV4) {
916     ret = _IADC_CTRL_HSCLKRATE_DIV4;
917   }
918 
919   return (uint8_t)ret;
920 }
921 
922 /***************************************************************************//**
923  * @brief
924  *   Calculate prescaler for ADC_CLK clock.
925  *
926  * @details
927  *   The ADC_CLK is given by: CLK_SRC_ADC / (adcClkprescale + 1).
928  *
929  * @param[in] iadc
930  *   Pointer to IADC peripheral register block.
931  *
932  * @param[in] adcClkFreq  ADC_CLK frequency wanted. The frequency will
933  *   automatically be adjusted to be within valid range according to reference
934  *   manual.
935  *
936  * @param[in] cmuClkFreq Frequency in Hz of CLK_CMU_ADC Set to 0 to
937  *   use currently defined IADC clock setting (in CMU).
938  *
939  * @param[in] adcMode Mode for IADC config.
940  *
941  * @param[in] srcClkPrescaler Precaler setting for ADC_CLK
942  *
943  * @return
944  *   Divider value to use for IADC in order to achieve a ADC_CLK frequency
945  *   <= @p adcClkFreq.
946  ******************************************************************************/
IADC_calcAdcClkPrescale(IADC_TypeDef * iadc,uint32_t adcClkFreq,uint32_t cmuClkFreq,IADC_CfgAdcMode_t adcMode,uint8_t srcClkPrescaler)947 uint32_t IADC_calcAdcClkPrescale(IADC_TypeDef *iadc,
948                                  uint32_t adcClkFreq,
949                                  uint32_t cmuClkFreq,
950                                  IADC_CfgAdcMode_t adcMode,
951                                  uint8_t srcClkPrescaler)
952 {
953   uint32_t ret;
954   uint32_t resFreq;
955 
956   EFM_ASSERT(IADC_REF_VALID(iadc));
957   EFM_ASSERT(adcClkFreq);
958 
959   // Make sure wanted analog clock is below max allowed frequency for the given
960   // mode.
961   if (adcClkFreq > IADC_ANA_CLK_MAX_FREQ(adcMode)) {
962     adcClkFreq = IADC_ANA_CLK_MAX_FREQ(adcMode);
963   }
964 
965   // Use current CLK_CMU_ADC frequency?
966   if (cmuClkFreq == 0UL) {
967     resFreq = CMU_ClockFreqGet(IADC_CMU_CLOCK(iadc));
968   } else {
969     resFreq = cmuClkFreq;
970   }
971 
972   // Apply CLK_SRC_ADC prescaler
973   resFreq /= srcClkPrescaler + 1UL;
974 
975   ret = (resFreq + adcClkFreq - 1UL) / adcClkFreq;
976   if (ret != 0UL) {
977     ret--;
978   }
979 
980   // Limit to max allowed register setting
981   ret = SL_MIN(ret, (_IADC_SCHED_PRESCALE_MASK >> _IADC_SCHED_PRESCALE_SHIFT));
982 
983   return (uint16_t)ret;
984 }
985 
986 /***************************************************************************//**
987  * @brief
988  *   Pull result from single data FIFO. The result struct includes both the data
989  *   and the ID (0x20) if showId was set when initializing single mode.
990  *
991  * @note
992  *   Check data valid flag before calling this function.
993  *
994  * @param[in] iadc
995  *   Pointer to IADC peripheral register block.
996  *
997  * @return
998  *   Single conversion result struct holding data and id.
999  ******************************************************************************/
IADC_pullSingleFifoResult(IADC_TypeDef * iadc)1000 IADC_Result_t IADC_pullSingleFifoResult(IADC_TypeDef *iadc)
1001 {
1002   uint32_t alignment = (iadc->SINGLEFIFOCFG & _IADC_SINGLEFIFOCFG_ALIGNMENT_MASK)
1003                        >> _IADC_SINGLEFIFOCFG_ALIGNMENT_SHIFT;
1004   return IADC_ConvertRawDataToResult(iadc->SINGLEFIFODATA,
1005                                      (IADC_Alignment_t) alignment);
1006 }
1007 
1008 /***************************************************************************//**
1009  * @brief
1010  *   Read most recent single conversion result. The result struct includes both
1011  *   the data and the ID (0x20) if showId was set when initializing single mode.
1012  *   Calling this function will not affect the state of the single data FIFO.
1013  *
1014  * @note
1015  *   Check data valid flag before calling this function.
1016  *
1017  * @param[in] iadc
1018  *   Pointer to IADC peripheral register block.
1019  *
1020  * @return
1021  *   Single conversion result struct holding data and id.
1022  ******************************************************************************/
IADC_readSingleResult(IADC_TypeDef * iadc)1023 IADC_Result_t IADC_readSingleResult(IADC_TypeDef *iadc)
1024 {
1025   uint32_t alignment = (iadc->SINGLEFIFOCFG & _IADC_SINGLEFIFOCFG_ALIGNMENT_MASK)
1026                        >> _IADC_SINGLEFIFOCFG_ALIGNMENT_SHIFT;
1027   return IADC_ConvertRawDataToResult(iadc->SINGLEDATA,
1028                                      (IADC_Alignment_t) alignment);
1029 }
1030 
1031 /***************************************************************************//**
1032  * @brief
1033  *   Pull result from scan data FIFO. The result struct includes both the data
1034  *   and the ID (0x20) if showId was set when initializing scan entry.
1035  *
1036  * @note
1037  *   Check data valid flag before calling this function.
1038  *
1039  * @param[in] iadc
1040  *   Pointer to IADC peripheral register block.
1041  *
1042  * @return
1043  *   Scan conversion result struct holding data and id.
1044  ******************************************************************************/
IADC_pullScanFifoResult(IADC_TypeDef * iadc)1045 IADC_Result_t IADC_pullScanFifoResult(IADC_TypeDef *iadc)
1046 {
1047   uint32_t alignment = (iadc->SCANFIFOCFG & _IADC_SCANFIFOCFG_ALIGNMENT_MASK)
1048                        >> _IADC_SCANFIFOCFG_ALIGNMENT_SHIFT;
1049   return IADC_ConvertRawDataToResult(iadc->SCANFIFODATA,
1050                                      (IADC_Alignment_t) alignment);
1051 }
1052 
1053 /***************************************************************************//**
1054  * @brief
1055  *   Read most recent scan conversion result. The result struct includes both
1056  *   the data and the ID (0x20) if showId was set when initializing scan entry.
1057  *   Calling this function will not affect the state of the scan data FIFO.
1058  *
1059  * @note
1060  *   Check data valid flag before calling this function.
1061  *
1062  * @param[in] iadc
1063  *   Pointer to IADC peripheral register block.
1064  *
1065  * @return
1066  *   Scan conversion result struct holding data and id.
1067  ******************************************************************************/
IADC_readScanResult(IADC_TypeDef * iadc)1068 IADC_Result_t IADC_readScanResult(IADC_TypeDef *iadc)
1069 {
1070   uint32_t alignment = (iadc->SCANFIFOCFG & _IADC_SCANFIFOCFG_ALIGNMENT_MASK)
1071                        >> _IADC_SCANFIFOCFG_ALIGNMENT_SHIFT;
1072   return IADC_ConvertRawDataToResult(iadc->SCANDATA,
1073                                      (IADC_Alignment_t) alignment);
1074 }
1075 
1076 /***************************************************************************//**
1077  * @brief
1078  *   Get reference voltage selection.
1079  *
1080  * @param[in] reference
1081  *   IADC Reference selection.
1082  *
1083  * @return
1084  *   IADC reference voltage in millivolts.
1085  ******************************************************************************/
IADC_getReferenceVoltage(IADC_CfgReference_t reference)1086 uint32_t IADC_getReferenceVoltage(IADC_CfgReference_t reference)
1087 {
1088   uint32_t refVoltage = 0;
1089   // Get chip revision
1090   SYSTEM_ChipRevision_TypeDef chipRev;
1091   SYSTEM_ChipRevisionGet(&chipRev);
1092   switch (reference) {
1093     case iadcCfgReferenceInt1V2:
1094 #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
1095       if (chipRev.major == 2UL) {
1096         if (chipRev.minor == 0UL) {
1097           refVoltage = 1232;
1098         } else {
1099           refVoltage = 1180;
1100         }
1101       } else {
1102         refVoltage = 1210;
1103       }
1104 #else
1105       refVoltage = 1210;
1106 #endif
1107       break;
1108     case iadcCfgReferenceExt1V25:
1109       refVoltage = 1250;
1110       break;
1111 #if defined(_IADC_CFG_REFSEL_VREF2P5)
1112     case iadcCfgReferenceExt2V5:
1113       refVoltage = 2500;
1114       break;
1115 #endif
1116     case iadcCfgReferenceVddx:
1117       refVoltage = 3000;
1118       break;
1119     case iadcCfgReferenceVddX0P8Buf:
1120       refVoltage = 2400;
1121       break;
1122 #if defined(_IADC_CFG_REFSEL_VREFBUF)
1123     case iadcCfgReferenceBuf:
1124       refVoltage = 12500;
1125       break;
1126 #endif
1127 #if defined(_IADC_CFG_REFSEL_VREF0P8BUF)
1128     case iadcCfgReference0P8Buf:
1129       refVoltage = 1000;
1130       break;
1131 #endif
1132     default:
1133       EFM_ASSERT(false);
1134       break;
1135   }
1136 
1137   return refVoltage;
1138 }
1139 
1140 /** @} (end addtogroup iadc) */
1141 #endif /* defined(IADC_COUNT) && (IADC_COUNT > 0) */
1142