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 "em_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 (init->sinkEnable) {
92 tmp |= IDAC_CTRL_CURSINK;
93 }
94
95 idac->CTRL = tmp;
96 }
97
98 /***************************************************************************//**
99 * @brief
100 * Enable/disable IDAC.
101 *
102 * @param[in] idac
103 * A pointer to the IDAC peripheral register block.
104 *
105 * @param[in] enable
106 * True to enable IDAC, false to disable.
107 ******************************************************************************/
IDAC_Enable(IDAC_TypeDef * idac,bool enable)108 void IDAC_Enable(IDAC_TypeDef *idac, bool enable)
109 {
110 EFM_ASSERT(IDAC_REF_VALID(idac));
111 BUS_RegBitWrite(&idac->CTRL, _IDAC_CTRL_EN_SHIFT, enable);
112 }
113
114 /***************************************************************************//**
115 * @brief
116 * Reset IDAC to the same state that it was in after a hardware reset.
117 *
118 * @param[in] idac
119 * A pointer to the IDAC peripheral register block.
120 ******************************************************************************/
IDAC_Reset(IDAC_TypeDef * idac)121 void IDAC_Reset(IDAC_TypeDef *idac)
122 {
123 EFM_ASSERT(IDAC_REF_VALID(idac));
124
125 #if defined(ERRATA_FIX_IDAC_E101_EN)
126 /* Fix for errata IDAC_E101 - IDAC output current degradation:
127 Instead of disabling, it will be put in its lowest power state (50 nA)
128 to avoid degradation over time. */
129
130 /* Make sure IDAC is enabled with a disabled output. */
131 idac->CTRL = _IDAC_CTRL_RESETVALUE | IDAC_CTRL_EN;
132
133 /* Set the lowest current (50 nA). */
134 idac->CURPROG = IDAC_CURPROG_RANGESEL_RANGE0
135 | (0x0 << _IDAC_CURPROG_STEPSEL_SHIFT);
136
137 /* Enable duty-cycling for all energy modes. */
138 idac->DUTYCONFIG = IDAC_DUTYCONFIG_DUTYCYCLEEN;
139 #else
140 idac->CTRL = _IDAC_CTRL_RESETVALUE;
141 idac->CURPROG = _IDAC_CURPROG_RESETVALUE;
142 idac->DUTYCONFIG = _IDAC_DUTYCONFIG_RESETVALUE;
143 #endif
144 #if defined (_IDAC_CAL_MASK)
145 idac->CAL = _IDAC_CAL_RESETVALUE;
146 #endif
147 }
148
149 /***************************************************************************//**
150 * @brief
151 * Enable/disable Minimal Output Transition mode.
152 *
153 * @param[in] idac
154 * A pointer to the IDAC peripheral register block.
155 *
156 * @param[in] enable
157 * True to enable Minimal Output Transition mode, false to disable.
158 ******************************************************************************/
IDAC_MinimalOutputTransitionMode(IDAC_TypeDef * idac,bool enable)159 void IDAC_MinimalOutputTransitionMode(IDAC_TypeDef *idac, bool enable)
160 {
161 EFM_ASSERT(IDAC_REF_VALID(idac));
162 BUS_RegBitWrite(&idac->CTRL, _IDAC_CTRL_MINOUTTRANS_SHIFT, enable);
163 }
164
165 /***************************************************************************//**
166 * @brief
167 * Set the current range of the IDAC output.
168 *
169 * @details
170 * This function sets the current range of the IDAC output. The function
171 * also updates the IDAC calibration register (IDAC_CAL) with the default
172 * calibration value from DEVINFO (factory calibration) corresponding to the
173 * specified range.
174 *
175 * @param[in] idac
176 * A pointer to the IDAC peripheral register block.
177 *
178 * @param[in] range
179 * The current range value.
180 ******************************************************************************/
IDAC_RangeSet(IDAC_TypeDef * idac,const IDAC_Range_TypeDef range)181 void IDAC_RangeSet(IDAC_TypeDef *idac, const IDAC_Range_TypeDef range)
182 {
183 uint32_t tmp;
184 #if defined(_IDAC_CURPROG_TUNING_MASK)
185 uint32_t diCal0;
186 uint32_t diCal1;
187 #endif
188
189 EFM_ASSERT(IDAC_REF_VALID(idac));
190 EFM_ASSERT(((uint32_t)range >> _IDAC_CURPROG_RANGESEL_SHIFT)
191 <= (_IDAC_CURPROG_RANGESEL_MASK >> _IDAC_CURPROG_RANGESEL_SHIFT));
192
193 #if defined (_IDAC_CAL_MASK)
194
195 /* Load proper calibration data depending on the selected range. */
196 switch ((IDAC_Range_TypeDef)range) {
197 case idacCurrentRange0:
198 idac->CAL = (DEVINFO->IDAC0CAL0 & _DEVINFO_IDAC0CAL0_RANGE0_MASK)
199 >> _DEVINFO_IDAC0CAL0_RANGE0_SHIFT;
200 break;
201 case idacCurrentRange1:
202 idac->CAL = (DEVINFO->IDAC0CAL0 & _DEVINFO_IDAC0CAL0_RANGE1_MASK)
203 >> _DEVINFO_IDAC0CAL0_RANGE1_SHIFT;
204 break;
205 case idacCurrentRange2:
206 idac->CAL = (DEVINFO->IDAC0CAL0 & _DEVINFO_IDAC0CAL0_RANGE2_MASK)
207 >> _DEVINFO_IDAC0CAL0_RANGE2_SHIFT;
208 break;
209 case idacCurrentRange3:
210 idac->CAL = (DEVINFO->IDAC0CAL0 & _DEVINFO_IDAC0CAL0_RANGE3_MASK)
211 >> _DEVINFO_IDAC0CAL0_RANGE3_SHIFT;
212 break;
213 }
214
215 tmp = idac->CURPROG & ~_IDAC_CURPROG_RANGESEL_MASK;
216 tmp |= (uint32_t)range;
217
218 #elif defined(_IDAC_CURPROG_TUNING_MASK)
219
220 /* Load calibration data depending on the selected range and sink/source mode */
221 /* TUNING (calibration) field in CURPROG register. */
222 EFM_ASSERT(idac == IDAC0);
223 diCal0 = DEVINFO->IDAC0CAL0;
224 diCal1 = DEVINFO->IDAC0CAL1;
225
226 tmp = idac->CURPROG & ~(_IDAC_CURPROG_TUNING_MASK
227 | _IDAC_CURPROG_RANGESEL_MASK);
228 if (idac->CTRL & IDAC_CTRL_CURSINK) {
229 switch (range) {
230 case idacCurrentRange0:
231 tmp |= ((diCal1 & _DEVINFO_IDAC0CAL1_SINKRANGE0TUNING_MASK)
232 >> _DEVINFO_IDAC0CAL1_SINKRANGE0TUNING_SHIFT)
233 << _IDAC_CURPROG_TUNING_SHIFT;
234 break;
235
236 case idacCurrentRange1:
237 tmp |= ((diCal1 & _DEVINFO_IDAC0CAL1_SINKRANGE1TUNING_MASK)
238 >> _DEVINFO_IDAC0CAL1_SINKRANGE1TUNING_SHIFT)
239 << _IDAC_CURPROG_TUNING_SHIFT;
240 break;
241
242 case idacCurrentRange2:
243 tmp |= ((diCal1 & _DEVINFO_IDAC0CAL1_SINKRANGE2TUNING_MASK)
244 >> _DEVINFO_IDAC0CAL1_SINKRANGE2TUNING_SHIFT)
245 << _IDAC_CURPROG_TUNING_SHIFT;
246 break;
247
248 case idacCurrentRange3:
249 tmp |= ((diCal1 & _DEVINFO_IDAC0CAL1_SINKRANGE3TUNING_MASK)
250 >> _DEVINFO_IDAC0CAL1_SINKRANGE3TUNING_SHIFT)
251 << _IDAC_CURPROG_TUNING_SHIFT;
252 break;
253 }
254 } else {
255 switch (range) {
256 case idacCurrentRange0:
257 tmp |= ((diCal0 & _DEVINFO_IDAC0CAL0_SOURCERANGE0TUNING_MASK)
258 >> _DEVINFO_IDAC0CAL0_SOURCERANGE0TUNING_SHIFT)
259 << _IDAC_CURPROG_TUNING_SHIFT;
260 break;
261
262 case idacCurrentRange1:
263 tmp |= ((diCal0 & _DEVINFO_IDAC0CAL0_SOURCERANGE1TUNING_MASK)
264 >> _DEVINFO_IDAC0CAL0_SOURCERANGE1TUNING_SHIFT)
265 << _IDAC_CURPROG_TUNING_SHIFT;
266 break;
267
268 case idacCurrentRange2:
269 tmp |= ((diCal0 & _DEVINFO_IDAC0CAL0_SOURCERANGE2TUNING_MASK)
270 >> _DEVINFO_IDAC0CAL0_SOURCERANGE2TUNING_SHIFT)
271 << _IDAC_CURPROG_TUNING_SHIFT;
272 break;
273
274 case idacCurrentRange3:
275 tmp |= ((diCal0 & _DEVINFO_IDAC0CAL0_SOURCERANGE3TUNING_MASK)
276 >> _DEVINFO_IDAC0CAL0_SOURCERANGE3TUNING_SHIFT)
277 << _IDAC_CURPROG_TUNING_SHIFT;
278 break;
279 }
280 }
281
282 tmp |= (uint32_t)range;
283
284 #else
285 #warning "IDAC calibration register definition unknown."
286 #endif
287
288 idac->CURPROG = tmp;
289 }
290
291 /***************************************************************************//**
292 * @brief
293 * Set the current step of the IDAC output.
294 *
295 * @param[in] idac
296 * A pointer to the IDAC peripheral register block.
297 *
298 * @param[in] step
299 * A step value for the IDAC output. A valid range is 0-31.
300 ******************************************************************************/
IDAC_StepSet(IDAC_TypeDef * idac,const uint32_t step)301 void IDAC_StepSet(IDAC_TypeDef *idac, const uint32_t step)
302 {
303 uint32_t tmp;
304
305 EFM_ASSERT(IDAC_REF_VALID(idac));
306 EFM_ASSERT(step <= (_IDAC_CURPROG_STEPSEL_MASK >> _IDAC_CURPROG_STEPSEL_SHIFT));
307
308 tmp = idac->CURPROG & ~_IDAC_CURPROG_STEPSEL_MASK;
309 tmp |= step << _IDAC_CURPROG_STEPSEL_SHIFT;
310
311 idac->CURPROG = tmp;
312 }
313
314 /***************************************************************************//**
315 * @brief
316 * Enable/disable the IDAC OUT pin.
317 *
318 * @param[in] idac
319 * A pointer to the IDAC peripheral register block.
320 *
321 * @param[in] enable
322 * True to enable the IDAC OUT pin, false to disable.
323 ******************************************************************************/
IDAC_OutEnable(IDAC_TypeDef * idac,bool enable)324 void IDAC_OutEnable(IDAC_TypeDef *idac, bool enable)
325 {
326 EFM_ASSERT(IDAC_REF_VALID(idac));
327 #if defined(_IDAC_CTRL_OUTEN_MASK)
328 BUS_RegBitWrite(&idac->CTRL, _IDAC_CTRL_OUTEN_SHIFT, enable);
329 #else
330 BUS_RegBitWrite(&idac->CTRL, _IDAC_CTRL_APORTOUTEN_SHIFT, enable);
331 #endif
332 }
333
334 /** @} (end addtogroup idac) */
335
336 #endif /* defined(IDAC_COUNT) && (IDAC_COUNT > 0) */
337