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