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