1 /***************************************************************************//**
2  * @file
3  * @brief Digital to Analog Coversion (DAC) Peripheral API
4  * @author Energy Micro AS
5  * @version 3.0.0
6  *******************************************************************************
7  * @section License
8  * <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
9  *******************************************************************************
10  *
11  * Permission is granted to anyone to use this software for any purpose,
12  * including commercial applications, and to alter it and redistribute it
13  * freely, subject to the following restrictions:
14  *
15  * 1. The origin of this software must not be misrepresented; you must not
16  *    claim that you wrote the original software.
17  * 2. Altered source versions must be plainly marked as such, and must not be
18  *    misrepresented as being the original software.
19  * 3. This notice may not be removed or altered from any source distribution.
20  *
21  * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
22  * obligation to support this Software. Energy Micro AS is providing the
23  * Software "AS IS", with no express or implied warranties of any kind,
24  * including, but not limited to, any implied warranties of merchantability
25  * or fitness for any particular purpose or warranties against infringement
26  * of any proprietary rights of a third party.
27  *
28  * Energy Micro AS will not be liable for any consequential, incidental, or
29  * special damages, or any other relief, or for any claim by any third party,
30  * arising from your use of this Software.
31  *
32  ******************************************************************************/
33 #include "em_dac.h"
34 #include "em_cmu.h"
35 #include "em_assert.h"
36 #include "em_bitband.h"
37 
38 /***************************************************************************//**
39  * @addtogroup EM_Library
40  * @{
41  ******************************************************************************/
42 
43 /***************************************************************************//**
44  * @addtogroup DAC
45  * @brief Digital to Analog Coversion (DAC) Peripheral API
46  * @{
47  ******************************************************************************/
48 
49 /*******************************************************************************
50  *******************************   DEFINES   ***********************************
51  ******************************************************************************/
52 
53 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
54 
55 /** Validation of DAC channel for assert statements. */
56 #define DAC_CH_VALID(ch)    ((ch) <= 1)
57 
58 /** Max DAC clock */
59 #define DAC_MAX_CLOCK    1000000
60 
61 /** @endcond */
62 
63 /*******************************************************************************
64  **************************   GLOBAL FUNCTIONS   *******************************
65  ******************************************************************************/
66 
67 /***************************************************************************//**
68  * @brief
69  *   Enable/disable DAC channel.
70  *
71  * @param[in] dac
72  *   Pointer to DAC peripheral register block.
73  *
74  * @param[in] ch
75  *   Channel to enable/disable.
76  *
77  * @param[in] enable
78  *   true to enable DAC channel, false to disable.
79  ******************************************************************************/
DAC_Enable(DAC_TypeDef * dac,unsigned int ch,bool enable)80 void DAC_Enable(DAC_TypeDef *dac, unsigned int ch, bool enable)
81 {
82   volatile uint32_t *reg;
83 
84   EFM_ASSERT(DAC_REF_VALID(dac));
85   EFM_ASSERT(DAC_CH_VALID(ch));
86 
87   if (!ch)
88   {
89     reg = &(dac->CH0CTRL);
90   }
91   else
92   {
93     reg = &(dac->CH1CTRL);
94   }
95 
96   BITBAND_Peripheral(reg, _DAC_CH0CTRL_EN_SHIFT, (unsigned int)enable);
97 }
98 
99 
100 /***************************************************************************//**
101  * @brief
102  *   Initialize DAC.
103  *
104  * @details
105  *   Initializes common parts for both channels. In addition, channel control
106  *   configuration must be done, please refer to DAC_InitChannel().
107  *
108  * @note
109  *   This function will disable both channels prior to configuration.
110  *
111  * @param[in] dac
112  *   Pointer to DAC peripheral register block.
113  *
114  * @param[in] init
115  *   Pointer to DAC initialization structure.
116  ******************************************************************************/
DAC_Init(DAC_TypeDef * dac,const DAC_Init_TypeDef * init)117 void DAC_Init(DAC_TypeDef *dac, const DAC_Init_TypeDef *init)
118 {
119   uint32_t tmp;
120 
121   EFM_ASSERT(DAC_REF_VALID(dac));
122 
123   /* Make sure both channels are disabled. */
124   BITBAND_Peripheral(&(dac->CH0CTRL), _DAC_CH0CTRL_EN_SHIFT, 0);
125   BITBAND_Peripheral(&(dac->CH1CTRL), _DAC_CH0CTRL_EN_SHIFT, 0);
126 
127   /* Load proper calibration data depending on selected reference */
128   switch (init->reference)
129   {
130   case dacRef2V5:
131     dac->CAL = DEVINFO->DAC0CAL1;
132     break;
133 
134   case dacRefVDD:
135     dac->CAL = DEVINFO->DAC0CAL2;
136     break;
137 
138   default: /* 1.25V */
139     dac->CAL = DEVINFO->DAC0CAL0;
140     break;
141   }
142 
143   tmp = ((uint32_t)(init->refresh) << _DAC_CTRL_REFRSEL_SHIFT) |
144         (((uint32_t)(init->prescale) << _DAC_CTRL_PRESC_SHIFT) & _DAC_CTRL_PRESC_MASK) |
145         ((uint32_t)(init->reference) << _DAC_CTRL_REFSEL_SHIFT) |
146         ((uint32_t)(init->outMode) << _DAC_CTRL_OUTMODE_SHIFT) |
147         ((uint32_t)(init->convMode) << _DAC_CTRL_CONVMODE_SHIFT);
148 
149   if (init->ch0ResetPre)
150   {
151     tmp |= DAC_CTRL_CH0PRESCRST;
152   }
153 
154   if (init->outEnablePRS)
155   {
156     tmp |= DAC_CTRL_OUTENPRS;
157   }
158 
159   if (init->sineEnable)
160   {
161     tmp |= DAC_CTRL_SINEMODE;
162   }
163 
164   if (init->diff)
165   {
166     tmp |= DAC_CTRL_DIFF;
167   }
168 
169   dac->CTRL = tmp;
170 }
171 
172 
173 /***************************************************************************//**
174  * @brief
175  *   Initialize DAC channel.
176  *
177  * @param[in] dac
178  *   Pointer to DAC peripheral register block.
179  *
180  * @param[in] init
181  *   Pointer to DAC initialization structure.
182  *
183  * @param[in] ch
184  *   Channel number to initialize.
185  ******************************************************************************/
DAC_InitChannel(DAC_TypeDef * dac,const DAC_InitChannel_TypeDef * init,unsigned int ch)186 void DAC_InitChannel(DAC_TypeDef *dac,
187                      const DAC_InitChannel_TypeDef *init,
188                      unsigned int ch)
189 {
190   uint32_t tmp;
191 
192   EFM_ASSERT(DAC_REF_VALID(dac));
193   EFM_ASSERT(DAC_CH_VALID(ch));
194 
195   tmp = (uint32_t)(init->prsSel) << _DAC_CH0CTRL_PRSSEL_SHIFT;
196 
197   if (init->enable)
198   {
199     tmp |= DAC_CH0CTRL_EN;
200   }
201 
202   if (init->prsEnable)
203   {
204     tmp |= DAC_CH0CTRL_PRSEN;
205   }
206 
207   if (init->refreshEnable)
208   {
209     tmp |= DAC_CH0CTRL_REFREN;
210   }
211 
212   if (ch)
213   {
214     dac->CH1CTRL = tmp;
215   }
216   else
217   {
218     dac->CH0CTRL = tmp;
219   }
220 }
221 
222 
223 /***************************************************************************//**
224  * @brief
225  *   Calculate prescaler value used to determine DAC clock.
226  *
227  * @details
228  *   The DAC clock is given by: HFPERCLK / (prescale ^ 2).
229  *
230  * @param[in] dacFreq DAC frequency wanted. The frequency will automatically
231  *   be adjusted to be below max allowed DAC clock.
232  *
233  * @param[in] hfperFreq Frequency in Hz of reference HFPER clock. Set to 0 to
234  *   use currently defined HFPER clock setting.
235  *
236  * @return
237  *   Prescaler value to use for DAC in order to achieve a clock value
238  *   <= @p dacFreq.
239  ******************************************************************************/
DAC_PrescaleCalc(uint32_t dacFreq,uint32_t hfperFreq)240 uint8_t DAC_PrescaleCalc(uint32_t dacFreq, uint32_t hfperFreq)
241 {
242   uint32_t ret;
243 
244   /* Make sure selected DAC clock is below max value */
245   if (dacFreq > DAC_MAX_CLOCK)
246   {
247     dacFreq = DAC_MAX_CLOCK;
248   }
249 
250   /* Use current HFPER frequency? */
251   if (!hfperFreq)
252   {
253     hfperFreq = CMU_ClockFreqGet(cmuClock_HFPER);
254   }
255 
256   /* Iterate in order to determine best prescale value. Only a few possible */
257   /* values. We start with lowest prescaler value in order to get first */
258   /* equal or below wanted DAC frequency value. */
259   for (ret = 0; ret <= (_DAC_CTRL_PRESC_MASK >> _DAC_CTRL_PRESC_SHIFT); ret++)
260   {
261     if ((hfperFreq >> ret) <= dacFreq)
262       break;
263   }
264 
265   return((uint8_t)ret);
266 }
267 
268 
269 /***************************************************************************//**
270  * @brief
271  *   Reset DAC to same state as after a HW reset.
272  *
273  * @param[in] dac
274  *   Pointer to ADC peripheral register block.
275  ******************************************************************************/
DAC_Reset(DAC_TypeDef * dac)276 void DAC_Reset(DAC_TypeDef *dac)
277 {
278   /* Disable channels, before resetting other registers. */
279   dac->CH0CTRL  = _DAC_CH0CTRL_RESETVALUE;
280   dac->CH1CTRL  = _DAC_CH1CTRL_RESETVALUE;
281   dac->CTRL     = _DAC_CTRL_RESETVALUE;
282   dac->IEN      = _DAC_IEN_RESETVALUE;
283   dac->IFC      = _DAC_IFC_MASK;
284   dac->CAL      = DEVINFO->DAC0CAL0;
285   dac->BIASPROG = _DAC_BIASPROG_RESETVALUE;
286   /* Do not reset route register, setting should be done independently */
287 }
288 
289 
290 /** @} (end addtogroup DAC) */
291 /** @} (end addtogroup EM_Library) */
292