1 /***************************************************************************//**
2 * @file
3 * @brief Current Digital to Analog Converter (IDAC) 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_idac.h"
32 #if defined(IDAC_COUNT) && (IDAC_COUNT > 0)
33 #include "em_cmu.h"
34 #include "sl_assert.h"
35 #include "em_bus.h"
36
37 /***************************************************************************//**
38 * @addtogroup idac
39 * @{
40 ******************************************************************************/
41
42 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
43 /* Fix for errata IDAC_E101 - IDAC output current degradation */
44 #if defined(_SILICON_LABS_32B_SERIES_0) \
45 && (defined(_EFM32_ZERO_FAMILY) || defined(_EFM32_HAPPY_FAMILY))
46 #define ERRATA_FIX_IDAC_E101_EN
47 #endif
48 /** @endcond */
49
50 /*******************************************************************************
51 ************************** GLOBAL FUNCTIONS *******************************
52 ******************************************************************************/
53
54 /***************************************************************************//**
55 * @brief
56 * Initialize IDAC.
57 *
58 * @details
59 * Initializes IDAC according to the initialization structure parameter and
60 * sets the default calibration value stored in the DEVINFO structure.
61 *
62 * @note
63 * This function will disable IDAC prior to configuration.
64 *
65 * @param[in] idac
66 * A pointer to the IDAC peripheral register block.
67 *
68 * @param[in] init
69 * A pointer to the IDAC initialization structure.
70 ******************************************************************************/
IDAC_Init(IDAC_TypeDef * idac,const IDAC_Init_TypeDef * init)71 void IDAC_Init(IDAC_TypeDef *idac, const IDAC_Init_TypeDef *init)
72 {
73 uint32_t tmp;
74
75 EFM_ASSERT(IDAC_REF_VALID(idac));
76
77 tmp = (uint32_t)(init->prsSel);
78
79 tmp |= init->outMode;
80
81 if (init->enable) {
82 tmp |= IDAC_CTRL_EN;
83 }
84 if (init->prsEnable) {
85 #if defined(_IDAC_CTRL_OUTENPRS_MASK)
86 tmp |= IDAC_CTRL_OUTENPRS;
87 #else
88 tmp |= IDAC_CTRL_APORTOUTENPRS;
89 #endif
90 }
91 #if defined(_IDAC_CTRL_MAINOUTENPRS_MASK)
92 if (init->prsEnableMain) {
93 tmp |= IDAC_CTRL_MAINOUTENPRS;
94 }
95 #endif
96 if (init->sinkEnable) {
97 tmp |= IDAC_CTRL_CURSINK;
98 }
99
100 idac->CTRL = tmp;
101 }
102
103 /***************************************************************************//**
104 * @brief
105 * Enable/disable IDAC.
106 *
107 * @param[in] idac
108 * A pointer to the IDAC peripheral register block.
109 *
110 * @param[in] enable
111 * True to enable IDAC, false to disable.
112 ******************************************************************************/
IDAC_Enable(IDAC_TypeDef * idac,bool enable)113 void IDAC_Enable(IDAC_TypeDef *idac, bool enable)
114 {
115 EFM_ASSERT(IDAC_REF_VALID(idac));
116 BUS_RegBitWrite(&idac->CTRL, _IDAC_CTRL_EN_SHIFT, enable);
117 }
118
119 /***************************************************************************//**
120 * @brief
121 * Reset IDAC to the same state that it was in after a hardware reset.
122 *
123 * @param[in] idac
124 * A pointer to the IDAC peripheral register block.
125 ******************************************************************************/
IDAC_Reset(IDAC_TypeDef * idac)126 void IDAC_Reset(IDAC_TypeDef *idac)
127 {
128 EFM_ASSERT(IDAC_REF_VALID(idac));
129
130 #if defined(ERRATA_FIX_IDAC_E101_EN)
131 /* Fix for errata IDAC_E101 - IDAC output current degradation:
132 Instead of disabling, it will be put in its lowest power state (50 nA)
133 to avoid degradation over time. */
134
135 /* Make sure IDAC is enabled with a disabled output. */
136 idac->CTRL = _IDAC_CTRL_RESETVALUE | IDAC_CTRL_EN;
137
138 /* Set the lowest current (50 nA). */
139 idac->CURPROG = IDAC_CURPROG_RANGESEL_RANGE0
140 | (0x0 << _IDAC_CURPROG_STEPSEL_SHIFT);
141
142 /* Enable duty-cycling for all energy modes. */
143 idac->DUTYCONFIG = IDAC_DUTYCONFIG_DUTYCYCLEEN;
144 #else
145 idac->CTRL = _IDAC_CTRL_RESETVALUE;
146 idac->CURPROG = _IDAC_CURPROG_RESETVALUE;
147 idac->DUTYCONFIG = _IDAC_DUTYCONFIG_RESETVALUE;
148 #endif
149 #if defined (_IDAC_CAL_MASK)
150 idac->CAL = _IDAC_CAL_RESETVALUE;
151 #endif
152 }
153
154 /***************************************************************************//**
155 * @brief
156 * Enable/disable Minimal Output Transition mode.
157 *
158 * @param[in] idac
159 * A pointer to the IDAC peripheral register block.
160 *
161 * @param[in] enable
162 * True to enable Minimal Output Transition mode, false to disable.
163 ******************************************************************************/
IDAC_MinimalOutputTransitionMode(IDAC_TypeDef * idac,bool enable)164 void IDAC_MinimalOutputTransitionMode(IDAC_TypeDef *idac, bool enable)
165 {
166 EFM_ASSERT(IDAC_REF_VALID(idac));
167 BUS_RegBitWrite(&idac->CTRL, _IDAC_CTRL_MINOUTTRANS_SHIFT, enable);
168 }
169
170 /***************************************************************************//**
171 * @brief
172 * Set the current range of the IDAC output.
173 *
174 * @details
175 * This function sets the current range of the IDAC output. The function
176 * also updates the IDAC calibration register (IDAC_CAL) with the default
177 * calibration value from DEVINFO (factory calibration) corresponding to the
178 * specified range.
179 *
180 * @param[in] idac
181 * A pointer to the IDAC peripheral register block.
182 *
183 * @param[in] range
184 * The current range value.
185 ******************************************************************************/
IDAC_RangeSet(IDAC_TypeDef * idac,const IDAC_Range_TypeDef range)186 void IDAC_RangeSet(IDAC_TypeDef *idac, const IDAC_Range_TypeDef range)
187 {
188 uint32_t tmp;
189 #if defined(_IDAC_CURPROG_TUNING_MASK)
190 uint32_t diCal0;
191 uint32_t diCal1;
192 #endif
193
194 EFM_ASSERT(IDAC_REF_VALID(idac));
195 EFM_ASSERT(((uint32_t)range >> _IDAC_CURPROG_RANGESEL_SHIFT)
196 <= (_IDAC_CURPROG_RANGESEL_MASK >> _IDAC_CURPROG_RANGESEL_SHIFT));
197
198 #if defined (_IDAC_CAL_MASK)
199
200 /* Load proper calibration data depending on the selected range. */
201 switch ((IDAC_Range_TypeDef)range) {
202 case idacCurrentRange0:
203 idac->CAL = (DEVINFO->IDAC0CAL0 & _DEVINFO_IDAC0CAL0_RANGE0_MASK)
204 >> _DEVINFO_IDAC0CAL0_RANGE0_SHIFT;
205 break;
206 case idacCurrentRange1:
207 idac->CAL = (DEVINFO->IDAC0CAL0 & _DEVINFO_IDAC0CAL0_RANGE1_MASK)
208 >> _DEVINFO_IDAC0CAL0_RANGE1_SHIFT;
209 break;
210 case idacCurrentRange2:
211 idac->CAL = (DEVINFO->IDAC0CAL0 & _DEVINFO_IDAC0CAL0_RANGE2_MASK)
212 >> _DEVINFO_IDAC0CAL0_RANGE2_SHIFT;
213 break;
214 case idacCurrentRange3:
215 idac->CAL = (DEVINFO->IDAC0CAL0 & _DEVINFO_IDAC0CAL0_RANGE3_MASK)
216 >> _DEVINFO_IDAC0CAL0_RANGE3_SHIFT;
217 break;
218 }
219
220 tmp = idac->CURPROG & ~_IDAC_CURPROG_RANGESEL_MASK;
221 tmp |= (uint32_t)range;
222
223 #elif defined(_IDAC_CURPROG_TUNING_MASK)
224
225 /* Load calibration data depending on the selected range and sink/source mode */
226 /* TUNING (calibration) field in CURPROG register. */
227 EFM_ASSERT(idac == IDAC0);
228 diCal0 = DEVINFO->IDAC0CAL0;
229 diCal1 = DEVINFO->IDAC0CAL1;
230
231 tmp = idac->CURPROG & ~(_IDAC_CURPROG_TUNING_MASK
232 | _IDAC_CURPROG_RANGESEL_MASK);
233 if (idac->CTRL & IDAC_CTRL_CURSINK) {
234 switch (range) {
235 case idacCurrentRange0:
236 tmp |= ((diCal1 & _DEVINFO_IDAC0CAL1_SINKRANGE0TUNING_MASK)
237 >> _DEVINFO_IDAC0CAL1_SINKRANGE0TUNING_SHIFT)
238 << _IDAC_CURPROG_TUNING_SHIFT;
239 break;
240
241 case idacCurrentRange1:
242 tmp |= ((diCal1 & _DEVINFO_IDAC0CAL1_SINKRANGE1TUNING_MASK)
243 >> _DEVINFO_IDAC0CAL1_SINKRANGE1TUNING_SHIFT)
244 << _IDAC_CURPROG_TUNING_SHIFT;
245 break;
246
247 case idacCurrentRange2:
248 tmp |= ((diCal1 & _DEVINFO_IDAC0CAL1_SINKRANGE2TUNING_MASK)
249 >> _DEVINFO_IDAC0CAL1_SINKRANGE2TUNING_SHIFT)
250 << _IDAC_CURPROG_TUNING_SHIFT;
251 break;
252
253 case idacCurrentRange3:
254 tmp |= ((diCal1 & _DEVINFO_IDAC0CAL1_SINKRANGE3TUNING_MASK)
255 >> _DEVINFO_IDAC0CAL1_SINKRANGE3TUNING_SHIFT)
256 << _IDAC_CURPROG_TUNING_SHIFT;
257 break;
258 }
259 } else {
260 switch (range) {
261 case idacCurrentRange0:
262 tmp |= ((diCal0 & _DEVINFO_IDAC0CAL0_SOURCERANGE0TUNING_MASK)
263 >> _DEVINFO_IDAC0CAL0_SOURCERANGE0TUNING_SHIFT)
264 << _IDAC_CURPROG_TUNING_SHIFT;
265 break;
266
267 case idacCurrentRange1:
268 tmp |= ((diCal0 & _DEVINFO_IDAC0CAL0_SOURCERANGE1TUNING_MASK)
269 >> _DEVINFO_IDAC0CAL0_SOURCERANGE1TUNING_SHIFT)
270 << _IDAC_CURPROG_TUNING_SHIFT;
271 break;
272
273 case idacCurrentRange2:
274 tmp |= ((diCal0 & _DEVINFO_IDAC0CAL0_SOURCERANGE2TUNING_MASK)
275 >> _DEVINFO_IDAC0CAL0_SOURCERANGE2TUNING_SHIFT)
276 << _IDAC_CURPROG_TUNING_SHIFT;
277 break;
278
279 case idacCurrentRange3:
280 tmp |= ((diCal0 & _DEVINFO_IDAC0CAL0_SOURCERANGE3TUNING_MASK)
281 >> _DEVINFO_IDAC0CAL0_SOURCERANGE3TUNING_SHIFT)
282 << _IDAC_CURPROG_TUNING_SHIFT;
283 break;
284 }
285 }
286
287 tmp |= (uint32_t)range;
288
289 #else
290 #warning "IDAC calibration register definition unknown."
291 #endif
292
293 idac->CURPROG = tmp;
294 }
295
296 /***************************************************************************//**
297 * @brief
298 * Set the current step of the IDAC output.
299 *
300 * @param[in] idac
301 * A pointer to the IDAC peripheral register block.
302 *
303 * @param[in] step
304 * A step value for the IDAC output. A valid range is 0-31.
305 ******************************************************************************/
IDAC_StepSet(IDAC_TypeDef * idac,const uint32_t step)306 void IDAC_StepSet(IDAC_TypeDef *idac, const uint32_t step)
307 {
308 uint32_t tmp;
309
310 EFM_ASSERT(IDAC_REF_VALID(idac));
311 EFM_ASSERT(step <= (_IDAC_CURPROG_STEPSEL_MASK >> _IDAC_CURPROG_STEPSEL_SHIFT));
312
313 tmp = idac->CURPROG & ~_IDAC_CURPROG_STEPSEL_MASK;
314 tmp |= step << _IDAC_CURPROG_STEPSEL_SHIFT;
315
316 idac->CURPROG = tmp;
317 }
318
319 /***************************************************************************//**
320 * @brief
321 * Enable/disable the IDAC OUT pin.
322 *
323 * @warning
324 * This function will not enable/disable the IDAC OUT pin if APORTOUTENPRS is set
325 * because output enable will be controlled by PRS.
326 *
327 * @param[in] idac
328 * A pointer to the IDAC peripheral register block.
329 *
330 * @param[in] enable
331 * True to enable the IDAC OUT pin, false to disable.
332 ******************************************************************************/
IDAC_OutEnable(IDAC_TypeDef * idac,bool enable)333 void IDAC_OutEnable(IDAC_TypeDef *idac, bool enable)
334 {
335 EFM_ASSERT(IDAC_REF_VALID(idac));
336 #if defined(_IDAC_CTRL_OUTEN_MASK)
337 BUS_RegBitWrite(&idac->CTRL, _IDAC_CTRL_OUTEN_SHIFT, enable);
338 #else
339 BUS_RegBitWrite(&idac->CTRL, _IDAC_CTRL_APORTOUTEN_SHIFT, enable);
340 #endif
341 }
342
343 #if defined(_IDAC_CTRL_MAINOUTEN_MASK)
344 /***************************************************************************//**
345 * @brief
346 * Enable/disable the IDAC OUTPAD output.
347 *
348 * @note
349 * IDAC OUTPAD is not available on some EFR32xG1 series devices as well as on
350 * non-BGA125 EFR32MG12 devices either because of lack of that feature (former)
351 * or because OUTPAD pin is not available on other packages (latter).
352 *
353 * @warning
354 * This function will not enable/disable the IDAC OUTPAD pin if MAINOUTENPRS is set
355 * because output enable will be controlled by PRS.
356 *
357 * @param[in] idac
358 * A pointer to the IDAC peripheral register block.
359 *
360 * @param[in] enable
361 * True to enable the IDAC OUTPAD, false to disable.
362 ******************************************************************************/
IDAC_OutpadEnable(IDAC_TypeDef * idac,bool enable)363 void IDAC_OutpadEnable(IDAC_TypeDef *idac, bool enable)
364 {
365 EFM_ASSERT(IDAC_REF_VALID(idac));
366
367 BUS_RegBitWrite(&idac->CTRL, _IDAC_CTRL_MAINOUTEN_SHIFT, enable);
368 }
369 #endif
370
371 /** @} (end addtogroup idac) */
372
373 #endif /* defined(IDAC_COUNT) && (IDAC_COUNT > 0) */
374