1 /***************************************************************************//**
2 * @file
3 * @brief Digital to Analog Converter (VDAC) 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_vdac.h"
32 #if defined(VDAC_COUNT) && (VDAC_COUNT > 0)
33 #include "em_cmu.h"
34
35 /***************************************************************************//**
36 * @addtogroup vdac
37 * @{
38 ******************************************************************************/
39
40 /*******************************************************************************
41 ******************************* DEFINES ***********************************
42 ******************************************************************************/
43
44 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
45
46 /** Validation of the VDAC channel for assert statements. */
47 #define VDAC_CH_VALID(ch) ((ch) <= 1)
48
49 /** A maximum VDAC clock. */
50 #define VDAC_MAX_CLOCK 1000000
51
52 /** The maximum clock frequency of the internal clock oscillator, 10 MHz + 20%. */
53 #define VDAC_INTERNAL_CLOCK_FREQ 12000000
54
55 /** @endcond */
56
57 /*******************************************************************************
58 *************************** LOCAL FUNCTIONS *******************************
59 ******************************************************************************/
60
61 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
62
63 #if defined(_VDAC_EN_MASK)
VDAC_DisableModule(VDAC_TypeDef * vdac)64 static void VDAC_DisableModule(VDAC_TypeDef* vdac)
65 {
66 while (vdac->STATUS & VDAC_STATUS_SYNCBUSY) {
67 }
68
69 /* Wait for all synchronizations to finish */
70 if (vdac->EN & VDAC_EN_EN) {
71 vdac->CMD = _VDAC_CMD_CH0DIS_MASK;
72 while (vdac->STATUS & (VDAC_STATUS_CH0ENS)) {
73 }
74
75 vdac->CMD = _VDAC_CMD_CH1DIS_MASK;
76 while (vdac->STATUS & (VDAC_STATUS_CH1ENS)) {
77 }
78
79 #if defined(_VDAC_CMD_CH0FIFOFLUSH_MASK)
80 while (vdac->STATUS & VDAC_STATUS_SYNCBUSY) {
81 }
82
83 vdac->CMD = VDAC_CMD_CH0FIFOFLUSH | VDAC_CMD_CH1FIFOFLUSH;
84
85 while (vdac->STATUS & (VDAC_STATUS_SYNCBUSY | VDAC_STATUS_CH0FIFOFLBUSY | VDAC_STATUS_CH1FIFOFLBUSY)) {
86 }
87 #endif
88 vdac->EN_CLR = _VDAC_EN_EN_MASK;
89 while (vdac->EN & _VDAC_EN_DISABLING_MASK) {
90 }
91 }
92 }
93 #endif
94
95 /** @endcond */
96
97 /*******************************************************************************
98 ************************** GLOBAL FUNCTIONS *******************************
99 ******************************************************************************/
100
101 /***************************************************************************//**
102 * @brief
103 * Enable/disable the VDAC channel.
104 *
105 * @param[in] vdac
106 * A pointer to the VDAC peripheral register block.
107 *
108 * @param[in] ch
109 * A channel to enable/disable.
110 *
111 * @param[in] enable
112 * True to enable VDAC channel, false to disable.
113 ******************************************************************************/
VDAC_Enable(VDAC_TypeDef * vdac,unsigned int ch,bool enable)114 void VDAC_Enable(VDAC_TypeDef *vdac, unsigned int ch, bool enable)
115 {
116 EFM_ASSERT(VDAC_REF_VALID(vdac));
117 EFM_ASSERT(VDAC_CH_VALID(ch));
118
119 #if defined(_VDAC_STATUS_SYNCBUSY_MASK)
120 while (vdac->STATUS & VDAC_STATUS_SYNCBUSY) {
121 }
122 #endif
123
124 if (ch == 0) {
125 if (enable) {
126 vdac->CMD = VDAC_CMD_CH0EN;
127 while ((vdac->STATUS & VDAC_STATUS_CH0ENS) == 0) {
128 }
129 } else {
130 vdac->CMD = VDAC_CMD_CH0DIS;
131 while (vdac->STATUS & VDAC_STATUS_CH0ENS) {
132 }
133 #if defined(_VDAC_CMD_CH0FIFOFLUSH_MASK)
134 while (vdac->STATUS & VDAC_STATUS_SYNCBUSY) {
135 }
136 vdac->CMD = VDAC_CMD_CH0FIFOFLUSH;
137 while (vdac->STATUS & VDAC_STATUS_CH0FIFOFLBUSY) {
138 }
139 #endif
140 }
141 } else {
142 if (enable) {
143 vdac->CMD = VDAC_CMD_CH1EN;
144 while ((vdac->STATUS & VDAC_STATUS_CH1ENS) == 0) {
145 }
146 } else {
147 vdac->CMD = VDAC_CMD_CH1DIS;
148 while (vdac->STATUS & VDAC_STATUS_CH1ENS) {
149 }
150
151 #if defined(_VDAC_CMD_CH1FIFOFLUSH_MASK)
152 while (vdac->STATUS & VDAC_STATUS_SYNCBUSY) {
153 }
154 vdac->CMD = VDAC_CMD_CH1FIFOFLUSH;
155 while (vdac->STATUS & VDAC_STATUS_CH1FIFOFLBUSY) {
156 }
157 #endif
158 }
159 }
160 }
161
162 /***************************************************************************//**
163 * @brief
164 * Initialize VDAC.
165 *
166 * @details
167 * Initializes the common parts for both channels. This function will also load
168 * calibration values from the Device Information (DI) page into the VDAC
169 * calibration register.
170 * To complete a VDAC setup, channel control configuration must also be done.
171 * See VDAC_InitChannel().
172 *
173 * @note
174 * This function will disable both channels prior to configuration.
175 *
176 * @param[in] vdac
177 * A pointer to the VDAC peripheral register block.
178 *
179 * @param[in] init
180 * A pointer to the VDAC initialization structure.
181 ******************************************************************************/
VDAC_Init(VDAC_TypeDef * vdac,const VDAC_Init_TypeDef * init)182 void VDAC_Init(VDAC_TypeDef *vdac, const VDAC_Init_TypeDef *init)
183 {
184 EFM_ASSERT(VDAC_REF_VALID(vdac));
185 uint32_t config = 0;
186
187 #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
188 uint32_t cal;
189 uint32_t const volatile *calData;
190
191 /* Make sure both channels are disabled. */
192 vdac->CMD = VDAC_CMD_CH0DIS | VDAC_CMD_CH1DIS;
193 while (vdac->STATUS & (VDAC_STATUS_CH0ENS | VDAC_STATUS_CH1ENS)) {
194 }
195
196 /* Get the OFFSETTRIM calibration value. */
197 cal = ((DEVINFO->VDAC0CH1CAL & _DEVINFO_VDAC0CH1CAL_OFFSETTRIM_MASK)
198 >> _DEVINFO_VDAC0CH1CAL_OFFSETTRIM_SHIFT)
199 << _VDAC_CAL_OFFSETTRIM_SHIFT;
200
201 if (init->mainCalibration) {
202 calData = &DEVINFO->VDAC0MAINCAL;
203 } else {
204 calData = &DEVINFO->VDAC0ALTCAL;
205 }
206
207 /* Get the correct GAINERRTRIM calibration value. */
208 switch (init->reference) {
209 case vdacRef1V25Ln:
210 config = (*calData & _DEVINFO_VDAC0MAINCAL_GAINERRTRIM1V25LN_MASK)
211 >> _DEVINFO_VDAC0MAINCAL_GAINERRTRIM1V25LN_SHIFT;
212 break;
213
214 case vdacRef2V5Ln:
215 config = (*calData & _DEVINFO_VDAC0MAINCAL_GAINERRTRIM2V5LN_MASK)
216 >> _DEVINFO_VDAC0MAINCAL_GAINERRTRIM2V5LN_SHIFT;
217 break;
218
219 case vdacRef1V25:
220 config = (*calData & _DEVINFO_VDAC0MAINCAL_GAINERRTRIM1V25_MASK)
221 >> _DEVINFO_VDAC0MAINCAL_GAINERRTRIM1V25_SHIFT;
222 break;
223
224 case vdacRef2V5:
225 config = (*calData & _DEVINFO_VDAC0MAINCAL_GAINERRTRIM2V5_MASK)
226 >> _DEVINFO_VDAC0MAINCAL_GAINERRTRIM2V5_SHIFT;
227 break;
228
229 case vdacRefAvdd:
230 case vdacRefExtPin:
231 config = (*calData & _DEVINFO_VDAC0MAINCAL_GAINERRTRIMVDDANAEXTPIN_MASK)
232 >> _DEVINFO_VDAC0MAINCAL_GAINERRTRIMVDDANAEXTPIN_SHIFT;
233 break;
234 }
235
236 /* Set the sGAINERRTRIM calibration value. */
237 cal |= config << _VDAC_CAL_GAINERRTRIM_SHIFT;
238
239 /* Get the GAINERRTRIMCH1 calibration value. */
240 switch (init->reference) {
241 case vdacRef1V25Ln:
242 case vdacRef1V25:
243 case vdacRefAvdd:
244 case vdacRefExtPin:
245 config = (DEVINFO->VDAC0CH1CAL & _DEVINFO_VDAC0CH1CAL_GAINERRTRIMCH1A_MASK)
246 >> _DEVINFO_VDAC0CH1CAL_GAINERRTRIMCH1A_SHIFT;
247 break;
248
249 case vdacRef2V5Ln:
250 case vdacRef2V5:
251 config = (DEVINFO->VDAC0CH1CAL & _DEVINFO_VDAC0CH1CAL_GAINERRTRIMCH1B_MASK)
252 >> _DEVINFO_VDAC0CH1CAL_GAINERRTRIMCH1B_SHIFT;
253 break;
254 }
255
256 /* Set the GAINERRTRIM calibration value. */
257 cal |= config << _VDAC_CAL_GAINERRTRIMCH1_SHIFT;
258
259 config = ((uint32_t)init->asyncClockMode << _VDAC_CTRL_DACCLKMODE_SHIFT)
260 | ((uint32_t)init->warmupKeepOn << _VDAC_CTRL_WARMUPMODE_SHIFT)
261 | ((uint32_t)init->refresh << _VDAC_CTRL_REFRESHPERIOD_SHIFT)
262 | (((uint32_t)init->prescaler << _VDAC_CTRL_PRESC_SHIFT)
263 & _VDAC_CTRL_PRESC_MASK)
264 | ((uint32_t)init->reference << _VDAC_CTRL_REFSEL_SHIFT)
265 | ((uint32_t)init->ch0ResetPre << _VDAC_CTRL_CH0PRESCRST_SHIFT)
266 | ((uint32_t)init->outEnablePRS << _VDAC_CTRL_OUTENPRS_SHIFT)
267 | ((uint32_t)init->sineEnable << _VDAC_CTRL_SINEMODE_SHIFT)
268 | ((uint32_t)init->diff << _VDAC_CTRL_DIFF_SHIFT);
269
270 /* Write to VDAC registers. */
271 vdac->CAL = cal;
272 vdac->CTRL = config;
273 #elif defined(_SILICON_LABS_32B_SERIES_2)
274
275 VDAC_DisableModule(vdac);
276
277 config = (
278 #if defined(VDAC_CFG_SINEMODEPRS)
279 ((uint32_t)init->sineModePrsEnable ? VDAC_CFG_SINEMODEPRS : 0U) |
280 #endif
281 #if defined(VDAC_CFG_OUTENPRS)
282 ((uint32_t)init->prsOutEnable ? VDAC_CFG_OUTENPRS : 0U) |
283 #endif
284 (((uint32_t)init->warmupTime << _VDAC_CFG_WARMUPTIME_SHIFT) & _VDAC_CFG_WARMUPTIME_MASK)
285 | ((uint32_t)init->dbgHalt << _VDAC_CFG_DBGHALT_SHIFT)
286 | ((uint32_t)init->onDemandClk << _VDAC_CFG_ONDEMANDCLK_SHIFT)
287 | ((uint32_t)init->dmaWakeUp << _VDAC_CFG_DMAWU_SHIFT)
288 | ((uint32_t)init->biasKeepWarm << _VDAC_CFG_BIASKEEPWARM_SHIFT)
289 | ((uint32_t)init->refresh << _VDAC_CFG_REFRESHPERIOD_SHIFT)
290 | ((uint32_t)init->timerOverflow << _VDAC_CFG_TIMEROVRFLOWPERIOD_SHIFT)
291 | (((uint32_t)init->prescaler << _VDAC_CFG_PRESC_SHIFT) & _VDAC_CFG_PRESC_MASK)
292 | ((uint32_t)init->reference << _VDAC_CFG_REFRSEL_SHIFT)
293 | ((uint32_t)init->ch0ResetPre << _VDAC_CFG_CH0PRESCRST_SHIFT)
294 | ((uint32_t)init->sineReset << _VDAC_CFG_SINERESET_SHIFT)
295 | ((uint32_t)init->sineEnable << _VDAC_CFG_SINEMODE_SHIFT)
296 | ((uint32_t)init->diff << _VDAC_CFG_DIFF_SHIFT));
297
298 vdac->CFG = config;
299 #endif
300 }
301
302 /***************************************************************************//**
303 * @brief
304 * Initialize a VDAC channel.
305 *
306 * @param[in] vdac
307 * A pointer to the VDAC peripheral register block.
308 *
309 * @param[in] init
310 * A pointer to the VDAC channel initialization structure.
311 *
312 * @param[in] ch
313 * A channel number to initialize.
314 ******************************************************************************/
VDAC_InitChannel(VDAC_TypeDef * vdac,const VDAC_InitChannel_TypeDef * init,unsigned int ch)315 void VDAC_InitChannel(VDAC_TypeDef *vdac,
316 const VDAC_InitChannel_TypeDef *init,
317 unsigned int ch)
318 {
319 uint32_t channelConfig, vdacStatus;
320
321 EFM_ASSERT(VDAC_REF_VALID(vdac));
322 EFM_ASSERT(VDAC_CH_VALID(ch));
323
324 vdacStatus = vdac->STATUS;
325
326 #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
327
328 /* Make sure both channels are disabled. */
329 vdac->CMD = VDAC_CMD_CH0DIS | VDAC_CMD_CH1DIS;
330 while (vdac->STATUS & (VDAC_STATUS_CH0ENS | VDAC_STATUS_CH1ENS)) {
331 }
332
333 channelConfig = ((uint32_t)init->prsSel << _VDAC_CH0CTRL_PRSSEL_SHIFT)
334 | ((uint32_t)init->prsAsync << _VDAC_CH0CTRL_PRSASYNC_SHIFT)
335 | ((uint32_t)init->trigMode << _VDAC_CH0CTRL_TRIGMODE_SHIFT)
336 | ((uint32_t)init->sampleOffMode << _VDAC_CH0CTRL_CONVMODE_SHIFT);
337
338 if (ch == 0) {
339 vdac->CH0CTRL = channelConfig;
340 } else {
341 vdac->CH1CTRL = channelConfig;
342 }
343
344 #elif defined(_SILICON_LABS_32B_SERIES_2)
345
346 VDAC_DisableModule(vdac);
347
348 channelConfig = ((uint32_t)init->warmupKeepOn << _VDAC_CH0CFG_KEEPWARM_SHIFT)
349 | ((uint32_t)init->highCapLoadEnable << _VDAC_CH0CFG_HIGHCAPLOADEN_SHIFT)
350 | (((uint32_t)init->fifoLowDataThreshold << _VDAC_CH0CFG_FIFODVL_SHIFT) & _VDAC_CH0CFG_FIFODVL_MASK)
351 | ((uint32_t)init->chRefreshSource << _VDAC_CH0CFG_REFRESHSOURCE_SHIFT)
352 | ((uint32_t)init->trigMode << _VDAC_CH0CFG_TRIGMODE_SHIFT)
353 | ((uint32_t)init->powerMode << _VDAC_CH0CFG_POWERMODE_SHIFT)
354 | ((uint32_t)init->sampleOffMode << _VDAC_CH0CFG_CONVMODE_SHIFT);
355
356 if (ch == 0) {
357 vdac->CH0CFG = channelConfig;
358
359 vdac->OUTTIMERCFG = ((uint32_t)(vdac->OUTTIMERCFG & ~(_VDAC_OUTTIMERCFG_CH0OUTHOLDTIME_MASK)))
360 | (((uint32_t)init->holdOutTime << _VDAC_OUTTIMERCFG_CH0OUTHOLDTIME_SHIFT) & _VDAC_OUTTIMERCFG_CH0OUTHOLDTIME_MASK);
361
362 vdac->EN_SET = _VDAC_EN_EN_MASK;
363
364 vdac->OUTCTRL = ((uint32_t)(vdac->OUTCTRL & ~(_VDAC_OUTCTRL_ABUSPINSELCH0_MASK | _VDAC_OUTCTRL_ABUSPORTSELCH0_MASK | _VDAC_OUTCTRL_SHORTCH0_MASK | _VDAC_OUTCTRL_AUXOUTENCH0_MASK | _VDAC_OUTCTRL_MAINOUTENCH0_MASK)))
365 | (((uint32_t)init->pin << _VDAC_OUTCTRL_ABUSPINSELCH0_SHIFT) & _VDAC_OUTCTRL_ABUSPINSELCH0_MASK)
366 | ((uint32_t)init->port << _VDAC_OUTCTRL_ABUSPORTSELCH0_SHIFT)
367 | ((uint32_t)init->shortOutput << _VDAC_OUTCTRL_SHORTCH0_SHIFT)
368 | ((uint32_t)init->auxOutEnable << _VDAC_OUTCTRL_AUXOUTENCH0_SHIFT)
369 | ((uint32_t)init->mainOutEnable << _VDAC_OUTCTRL_MAINOUTENCH0_SHIFT);
370 } else if (ch == 1) {
371 vdac->CH1CFG = channelConfig;
372
373 vdac->OUTTIMERCFG = (vdac->OUTTIMERCFG & ~(_VDAC_OUTTIMERCFG_CH1OUTHOLDTIME_MASK))
374 | (((uint32_t)init->holdOutTime << _VDAC_OUTTIMERCFG_CH1OUTHOLDTIME_SHIFT) & _VDAC_OUTTIMERCFG_CH1OUTHOLDTIME_MASK);
375
376 vdac->EN_SET = _VDAC_EN_EN_MASK;
377
378 vdac->OUTCTRL = ((uint32_t)(vdac->OUTCTRL & ~(_VDAC_OUTCTRL_ABUSPINSELCH1_MASK | _VDAC_OUTCTRL_ABUSPORTSELCH1_MASK | _VDAC_OUTCTRL_SHORTCH1_MASK | _VDAC_OUTCTRL_AUXOUTENCH1_MASK | _VDAC_OUTCTRL_MAINOUTENCH1_MASK)))
379 | (((uint32_t)init->pin << _VDAC_OUTCTRL_ABUSPINSELCH1_SHIFT) & _VDAC_OUTCTRL_ABUSPINSELCH1_MASK)
380 | ((uint32_t)init->port << _VDAC_OUTCTRL_ABUSPORTSELCH1_SHIFT)
381 | ((uint32_t)init->shortOutput << _VDAC_OUTCTRL_SHORTCH1_SHIFT)
382 | ((uint32_t)init->auxOutEnable << _VDAC_OUTCTRL_AUXOUTENCH1_SHIFT)
383 | ((uint32_t)init->mainOutEnable << _VDAC_OUTCTRL_MAINOUTENCH1_SHIFT);
384 }
385 #endif
386
387 /* Check if the channel must be enabled. */
388 if (init->enable) {
389 if (ch == 0) {
390 vdac->CMD = VDAC_CMD_CH0EN;
391 } else {
392 vdac->CMD = VDAC_CMD_CH1EN;
393 }
394 }
395
396 /* Check if the other channel had to be turned off above
397 * and needs to be turned on again. */
398 if (ch == 0) {
399 if (vdacStatus & VDAC_STATUS_CH1ENS) {
400 vdac->CMD = VDAC_CMD_CH1EN;
401 }
402 } else {
403 if (vdacStatus & VDAC_STATUS_CH0ENS) {
404 vdac->CMD = VDAC_CMD_CH0EN;
405 }
406 }
407 }
408
409 /***************************************************************************//**
410 * @brief
411 * Set the output signal of a VDAC channel to a given value.
412 *
413 * @details
414 * This function sets the output signal of a VDAC channel by writing @p value
415 * to the corresponding CHnDATA register.
416 *
417 * @param[in] vdac
418 * A pointer to the VDAC peripheral register block.
419 *
420 * @param[in] channel
421 * A channel number to set the output of.
422 *
423 * @param[in] value
424 * A value to write to the channel output register CHnDATA.
425 ******************************************************************************/
VDAC_ChannelOutputSet(VDAC_TypeDef * vdac,unsigned int channel,uint32_t value)426 void VDAC_ChannelOutputSet(VDAC_TypeDef *vdac,
427 unsigned int channel,
428 uint32_t value)
429 {
430 switch (channel) {
431 case 0:
432 VDAC_Channel0OutputSet(vdac, value);
433 break;
434 case 1:
435 VDAC_Channel1OutputSet(vdac, value);
436 break;
437 default:
438 EFM_ASSERT(0);
439 break;
440 }
441 }
442
443 #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
444 /***************************************************************************//**
445 * @brief
446 * Calculate the prescaler value used to determine VDAC clock.
447 *
448 * @details
449 * The VDAC clock is given by the input clock divided by the prescaler+1.
450 *
451 * VDAC_CLK = IN_CLK / (prescale + 1)
452 *
453 * The maximum VDAC clock is 1 MHz. The input clock is HFPERCLK/HFPERCCLK
454 * when VDAC synchronous mode is selected, or an internal oscillator of
455 * 10 MHz +/- 20% when asynchronous mode is selected.
456 *
457 * @note
458 * If the requested VDAC frequency is low and the maximum prescaler value can't
459 * adjust the actual VDAC frequency lower than requested, the maximum prescaler
460 * value is returned resulting in a higher VDAC frequency than requested.
461 *
462 * @param[in] vdacFreq VDAC frequency target. The frequency will automatically
463 * be adjusted to be below maximum allowed VDAC clock.
464 *
465 * @param[in] syncMode Set to true if you intend to use VDAC in synchronous
466 * mode.
467 *
468 * @param[in] hfperFreq Frequency in Hz of HFPERCLK/HFPERCCLK oscillator.
469 * Set to 0 to use the currently defined HFPERCLK/HFPERCCLK clock setting.
470 * This parameter is only used when syncMode is set to true.
471 *
472 * @return
473 * A prescaler value to use for VDAC to achieve a clock value less than
474 * or equal to @p vdacFreq.
475 ******************************************************************************/
VDAC_PrescaleCalc(uint32_t vdacFreq,bool syncMode,uint32_t hfperFreq)476 uint32_t VDAC_PrescaleCalc(uint32_t vdacFreq, bool syncMode, uint32_t hfperFreq)
477 {
478 uint32_t ret, refFreq;
479
480 /* Make sure that the selected VDAC clock is below the maximum value. */
481 if (vdacFreq > VDAC_MAX_CLOCK) {
482 vdacFreq = VDAC_MAX_CLOCK;
483 }
484
485 if (!syncMode) {
486 refFreq = VDAC_INTERNAL_CLOCK_FREQ;
487 } else {
488 if (hfperFreq) {
489 refFreq = hfperFreq;
490 } else {
491 refFreq = CMU_ClockFreqGet(cmuClock_VDAC0);
492 }
493 }
494
495 /* Iterate to determine the best prescaler value. Start with the lowest */
496 /* prescaler value to get the first equal or less VDAC */
497 /* frequency value. */
498 for (ret = 0; ret <= (_VDAC_CTRL_PRESC_MASK >> _VDAC_CTRL_PRESC_SHIFT); ret++) {
499 if ((refFreq / (ret + 1)) <= vdacFreq) {
500 break;
501 }
502 }
503
504 /* If ret is higher than the maximum prescaler value, make sure to return
505 the maximum value. */
506 if (ret > (_VDAC_CTRL_PRESC_MASK >> _VDAC_CTRL_PRESC_SHIFT)) {
507 ret = _VDAC_CTRL_PRESC_MASK >> _VDAC_CTRL_PRESC_SHIFT;
508 }
509
510 return ret;
511 }
512 #else
513 /***************************************************************************//**
514 * @brief
515 * Calculate the prescaler value used to determine VDAC clock.
516 *
517 * @details
518 * The VDAC clock is given by the input clock divided by the prescaler+1.
519 *
520 * VDAC_CLK = IN_CLK / (prescale + 1)
521 *
522 * The maximum VDAC clock is 1 MHz.
523 *
524 * @note
525 * If the requested VDAC frequency is low and the maximum prescaler value can't
526 * adjust the actual VDAC frequency lower than requested, the maximum prescaler
527 * value is returned resulting in a higher VDAC frequency than requested.
528 *
529 * @param[in] vdac
530 * Pointer to VDAC peripheral register block.
531 *
532 * @param[in] vdacFreq VDAC frequency target. The frequency will automatically
533 * be adjusted to be below maximum allowed VDAC clock.
534 *
535 * @return
536 * A prescaler value to use for VDAC to achieve a clock value less than
537 * or equal to @p vdacFreq.
538 ******************************************************************************/
VDAC_PrescaleCalc(VDAC_TypeDef * vdac,uint32_t vdacFreq)539 uint32_t VDAC_PrescaleCalc(VDAC_TypeDef *vdac, uint32_t vdacFreq)
540 {
541 uint32_t ret = 0;
542 uint32_t refFreq = 0;
543
544 /* Make sure that the selected VDAC clock is below the maximum value. */
545 if (vdacFreq > VDAC_MAX_CLOCK) {
546 vdacFreq = VDAC_MAX_CLOCK;
547 }
548
549 if (vdac == VDAC0) {
550 refFreq = CMU_ClockFreqGet(cmuClock_VDAC0);
551 }
552 #if defined(VDAC1)
553 else if (vdac == VDAC1) {
554 refFreq = CMU_ClockFreqGet(cmuClock_VDAC1);
555 }
556 #endif
557 else {
558 EFM_ASSERT(0);
559 }
560
561 /* Iterate to determine the best prescaler value. Start with the lowest */
562 /* prescaler value to get the first equal or less VDAC */
563 /* frequency value. */
564 for (ret = 0; ret <= (_VDAC_CFG_PRESC_MASK >> _VDAC_CFG_PRESC_SHIFT); ret++) {
565 if ((refFreq / (ret + 1)) <= vdacFreq) {
566 break;
567 }
568 }
569
570 /* If ret is higher than the maximum prescaler value, make sure to return
571 the maximum value. */
572 if (ret > (_VDAC_CFG_PRESC_MASK >> _VDAC_CFG_PRESC_SHIFT)) {
573 ret = _VDAC_CFG_PRESC_MASK >> _VDAC_CFG_PRESC_SHIFT;
574 }
575
576 return ret;
577 }
578 #endif
579
580 /***************************************************************************//**
581 * @brief
582 * Reset VDAC to same state that it was in after a hardwares reset.
583 *
584 * @param[in] vdac
585 * A pointer to the VDAC peripheral register block.
586 ******************************************************************************/
VDAC_Reset(VDAC_TypeDef * vdac)587 void VDAC_Reset(VDAC_TypeDef *vdac)
588 {
589 #if defined(VDAC_SWRST_SWRST)
590
591 while (vdac->STATUS & VDAC_STATUS_SYNCBUSY) {
592 }
593
594 /* Wait for all synchronizations to finish and disable the vdac channels */
595 if (vdac->EN & VDAC_EN_EN) {
596 vdac->CMD = _VDAC_CMD_CH0DIS_MASK;
597 while (vdac->STATUS & VDAC_STATUS_CH0ENS ) {
598 }
599
600 vdac->CMD = _VDAC_CMD_CH1DIS_MASK;
601 while (vdac->STATUS & VDAC_STATUS_CH1ENS ) {
602 }
603
604 while (vdac->STATUS & VDAC_STATUS_SYNCBUSY) {
605 }
606
607 vdac->CMD = _VDAC_CMD_CH0FIFOFLUSH_MASK | _VDAC_CMD_CH1FIFOFLUSH_MASK;
608 while (vdac->STATUS & (VDAC_STATUS_CH0FIFOFLBUSY | VDAC_STATUS_CH1FIFOFLBUSY)) {
609 }
610
611 while (vdac->STATUS & VDAC_STATUS_SYNCBUSY) {
612 }
613 }
614
615 vdac->SWRST_SET = VDAC_SWRST_SWRST;
616 while (vdac->SWRST & _VDAC_SWRST_RESETTING_MASK) {
617 }
618
619 #else
620 /* Disable channels before resetting other registers. */
621 vdac->CMD = VDAC_CMD_CH0DIS | VDAC_CMD_CH1DIS;
622 while (vdac->STATUS & (VDAC_STATUS_CH0ENS | VDAC_STATUS_CH1ENS)) {
623 }
624 vdac->CH0CTRL = _VDAC_CH0CTRL_RESETVALUE;
625 vdac->CH1CTRL = _VDAC_CH1CTRL_RESETVALUE;
626 vdac->CH0DATA = _VDAC_CH0DATA_RESETVALUE;
627 vdac->CH1DATA = _VDAC_CH1DATA_RESETVALUE;
628 vdac->CTRL = _VDAC_CTRL_RESETVALUE;
629 vdac->IEN = _VDAC_IEN_RESETVALUE;
630 vdac->IFC = _VDAC_IFC_MASK;
631 vdac->CAL = _VDAC_CAL_RESETVALUE;
632 #endif
633 }
634
635 /** @} (end addtogroup vdac) */
636 #endif /* defined(VDAC_COUNT) && (VDAC_COUNT > 0) */
637