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