1 /***************************************************************************//**
2  * @file
3  * @brief Digital to Analog Converter (DAC) 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_dac.h"
32 #if defined(DAC_COUNT) && (DAC_COUNT > 0)
33 #include "em_cmu.h"
34 #include "sl_assert.h"
35 #include "em_bus.h"
36 
37 /***************************************************************************//**
38  * @addtogroup dac DAC - Digital to Analog Converter
39  * @brief Digital to Analog Converter (DAC) Peripheral API
40  * @details
41  *  This module contains functions to control the DAC peripheral of Silicon
42  *  Labs 32-bit MCUs and SoCs. The DAC converts digital values to analog signals
43  *  at up to 500 ksps with 12-bit accuracy. The DAC is designed for low-energy
44  *  consumption and can also provide very good performance.
45  * @{
46  ******************************************************************************/
47 
48 /*******************************************************************************
49  *******************************   DEFINES   ***********************************
50  ******************************************************************************/
51 
52 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
53 
54 /** Validation of DAC channel for assert statements. */
55 #define DAC_CH_VALID(ch)    ((ch) <= 1)
56 
57 /** Max DAC clock */
58 #define DAC_MAX_CLOCK    1000000
59 
60 /** @endcond */
61 
62 /*******************************************************************************
63  **************************   GLOBAL FUNCTIONS   *******************************
64  ******************************************************************************/
65 
66 /***************************************************************************//**
67  * @brief
68  *   Enable/disable the DAC channel.
69  *
70  * @param[in] dac
71  *   A pointer to the DAC peripheral register block.
72  *
73  * @param[in] ch
74  *   A channel to enable/disable.
75  *
76  * @param[in] enable
77  *   true to enable DAC channel, false to disable.
78  ******************************************************************************/
DAC_Enable(DAC_TypeDef * dac,unsigned int ch,bool enable)79 void DAC_Enable(DAC_TypeDef *dac, unsigned int ch, bool enable)
80 {
81   volatile uint32_t *reg;
82 
83   EFM_ASSERT(DAC_REF_VALID(dac));
84   EFM_ASSERT(DAC_CH_VALID(ch));
85 
86   if (!ch) {
87     reg = &(dac->CH0CTRL);
88   } else {
89     reg = &(dac->CH1CTRL);
90   }
91 
92   BUS_RegBitWrite(reg, _DAC_CH0CTRL_EN_SHIFT, enable);
93 }
94 
95 /***************************************************************************//**
96  * @brief
97  *   Initialize DAC.
98  *
99  * @details
100  *   Initializes common parts for both channels. In addition, channel control
101  *   configuration must be done. See DAC_InitChannel().
102  *
103  * @note
104  *   This function will disable both channels prior to configuration.
105  *
106  * @param[in] dac
107  *   A pointer to the DAC peripheral register block.
108  *
109  * @param[in] init
110  *   A pointer to the DAC initialization structure.
111  ******************************************************************************/
DAC_Init(DAC_TypeDef * dac,const DAC_Init_TypeDef * init)112 void DAC_Init(DAC_TypeDef *dac, const DAC_Init_TypeDef *init)
113 {
114   uint32_t tmp;
115 
116   EFM_ASSERT(DAC_REF_VALID(dac));
117 
118   /* Make sure both channels are disabled. */
119   BUS_RegBitWrite(&(dac->CH0CTRL), _DAC_CH0CTRL_EN_SHIFT, 0);
120   BUS_RegBitWrite(&(dac->CH1CTRL), _DAC_CH0CTRL_EN_SHIFT, 0);
121 
122   /* Load proper calibration data depending on the selected reference. */
123   switch (init->reference) {
124     case dacRef2V5:
125       dac->CAL = DEVINFO->DAC0CAL1;
126       break;
127 
128     case dacRefVDD:
129       dac->CAL = DEVINFO->DAC0CAL2;
130       break;
131 
132     default: /* 1.25V */
133       dac->CAL = DEVINFO->DAC0CAL0;
134       break;
135   }
136 
137   tmp = ((uint32_t)(init->refresh)     << _DAC_CTRL_REFRSEL_SHIFT)
138         | (((uint32_t)(init->prescale) << _DAC_CTRL_PRESC_SHIFT)
139            & _DAC_CTRL_PRESC_MASK)
140         | ((uint32_t)(init->reference) << _DAC_CTRL_REFSEL_SHIFT)
141         | ((uint32_t)(init->outMode)   << _DAC_CTRL_OUTMODE_SHIFT)
142         | ((uint32_t)(init->convMode)  << _DAC_CTRL_CONVMODE_SHIFT);
143 
144   if (init->ch0ResetPre) {
145     tmp |= DAC_CTRL_CH0PRESCRST;
146   }
147 
148   if (init->outEnablePRS) {
149     tmp |= DAC_CTRL_OUTENPRS;
150   }
151 
152   if (init->sineEnable) {
153     tmp |= DAC_CTRL_SINEMODE;
154   }
155 
156   if (init->diff) {
157     tmp |= DAC_CTRL_DIFF;
158   }
159 
160   dac->CTRL = tmp;
161 }
162 
163 /***************************************************************************//**
164  * @brief
165  *   Initialize DAC channel.
166  *
167  * @param[in] dac
168  *   A pointer to the DAC peripheral register block.
169  *
170  * @param[in] init
171  *   A pointer to the DAC initialization structure.
172  *
173  * @param[in] ch
174  *   A channel number to initialize.
175  ******************************************************************************/
DAC_InitChannel(DAC_TypeDef * dac,const DAC_InitChannel_TypeDef * init,unsigned int ch)176 void DAC_InitChannel(DAC_TypeDef *dac,
177                      const DAC_InitChannel_TypeDef *init,
178                      unsigned int ch)
179 {
180   uint32_t tmp;
181 
182   EFM_ASSERT(DAC_REF_VALID(dac));
183   EFM_ASSERT(DAC_CH_VALID(ch));
184 
185   tmp = (uint32_t)(init->prsSel) << _DAC_CH0CTRL_PRSSEL_SHIFT;
186 
187   if (init->enable) {
188     tmp |= DAC_CH0CTRL_EN;
189   }
190 
191   if (init->prsEnable) {
192     tmp |= DAC_CH0CTRL_PRSEN;
193   }
194 
195   if (init->refreshEnable) {
196     tmp |= DAC_CH0CTRL_REFREN;
197   }
198 
199   if (ch) {
200     dac->CH1CTRL = tmp;
201   } else {
202     dac->CH0CTRL = tmp;
203   }
204 }
205 
206 /***************************************************************************//**
207  * @brief
208  *   Set the output signal of a DAC channel to a given value.
209  *
210  * @details
211  *   This function sets the output signal of a DAC channel by writing @p value
212  *   to the corresponding CHnDATA register.
213  *
214  * @param[in] dac
215  *   A pointer to the DAC peripheral register block.
216  *
217  * @param[in] channel
218  *   A channel number to set the output.
219  *
220  * @param[in] value
221  *   A value to write to the channel output register CHnDATA.
222  ******************************************************************************/
DAC_ChannelOutputSet(DAC_TypeDef * dac,unsigned int channel,uint32_t value)223 void DAC_ChannelOutputSet(DAC_TypeDef *dac,
224                           unsigned int channel,
225                           uint32_t     value)
226 {
227   switch (channel) {
228     case 0:
229       DAC_Channel0OutputSet(dac, value);
230       break;
231     case 1:
232       DAC_Channel1OutputSet(dac, value);
233       break;
234     default:
235       EFM_ASSERT(0);
236       break;
237   }
238 }
239 
240 /***************************************************************************//**
241  * @brief
242  *   Calculate prescaler value used to determine the DAC clock.
243  *
244  * @details
245  *   The DAC clock is given by: HFPERCLK / (prescale ^ 2). If the requested
246  *   DAC frequency is low and the maximum prescaler value can't adjust the
247  *   actual DAC frequency lower than the requested DAC frequency, the
248  *   maximum prescaler value is returned resulting in a higher DAC frequency
249  *   than requested.
250  *
251  * @param[in] dacFreq DAC frequency wanted. The frequency will automatically
252  *   be adjusted to be below the maximum allowed DAC clock.
253  *
254  * @param[in] hfperFreq Frequency in Hz of the reference HFPER clock. Set to 0 to
255  *   use the currently defined HFPER clock setting.
256  *
257  * @return
258  *   Prescaler value to use for DAC to achieve a clock value
259  *   <= @p dacFreq.
260  ******************************************************************************/
DAC_PrescaleCalc(uint32_t dacFreq,uint32_t hfperFreq)261 uint8_t DAC_PrescaleCalc(uint32_t dacFreq, uint32_t hfperFreq)
262 {
263   uint32_t ret;
264 
265   /* Make sure the selected DAC clock is below maximum value. */
266   if (dacFreq > DAC_MAX_CLOCK) {
267     dacFreq = DAC_MAX_CLOCK;
268   }
269 
270   /* Use the current HFPER frequency. */
271   if (!hfperFreq) {
272     hfperFreq = CMU_ClockFreqGet(cmuClock_HFPER);
273   }
274 
275   /* Iterate to determine the best prescale value. Only a few possible */
276   /* values. Lowest prescaler value is started with to get the first */
277   /* equal or below wanted DAC frequency value. */
278   for (ret = 0; ret <= (_DAC_CTRL_PRESC_MASK >> _DAC_CTRL_PRESC_SHIFT); ret++) {
279     if ((hfperFreq >> ret) <= dacFreq) {
280       break;
281     }
282   }
283 
284   /* If return is higher than the maximum prescaler value, make sure to return
285      the max value. */
286   if (ret > (_DAC_CTRL_PRESC_MASK >> _DAC_CTRL_PRESC_SHIFT)) {
287     ret = _DAC_CTRL_PRESC_MASK >> _DAC_CTRL_PRESC_SHIFT;
288   }
289 
290   return (uint8_t)ret;
291 }
292 
293 /***************************************************************************//**
294  * @brief
295  *   Reset DAC to the same state that it was in after a hardware reset.
296  *
297  * @param[in] dac
298  *   A pointer to the ADC peripheral register block.
299  ******************************************************************************/
DAC_Reset(DAC_TypeDef * dac)300 void DAC_Reset(DAC_TypeDef *dac)
301 {
302   /* Disable channels, before resetting other registers. */
303   dac->CH0CTRL  = _DAC_CH0CTRL_RESETVALUE;
304   dac->CH1CTRL  = _DAC_CH1CTRL_RESETVALUE;
305   dac->CTRL     = _DAC_CTRL_RESETVALUE;
306   dac->IEN      = _DAC_IEN_RESETVALUE;
307   dac->IFC      = _DAC_IFC_MASK;
308   dac->CAL      = DEVINFO->DAC0CAL0;
309   dac->BIASPROG = _DAC_BIASPROG_RESETVALUE;
310   /* Do not reset route register, setting should be done independently */
311 }
312 
313 /** @} (end addtogroup dac) */
314 #endif /* defined(DAC_COUNT) && (DAC_COUNT > 0) */
315