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