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