1 /***************************************************************************//**
2  * @file
3  * @brief Operational Amplifier (OPAMP) 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_opamp.h"
32 #if ((defined(_SILICON_LABS_32B_SERIES_0) && defined(OPAMP_PRESENT) && (OPAMP_COUNT == 1)) \
33   || (defined(_SILICON_LABS_32B_SERIES_1) && defined(VDAC_PRESENT)  && (VDAC_COUNT > 0)))
34 
35 #include "em_system.h"
36 #include "em_assert.h"
37 
38 /* *INDENT-OFF* */
39 /***************************************************************************//**
40  * @addtogroup opamp OPAMP - Operational Amplifier
41  * @brief Operational Amplifier (OPAMP) peripheral API
42  * @details
43  *  This module contains functions to:
44  *   @li OPAMP_Enable()       Configure and enable OPAMP.
45  *   @li OPAMP_Disable()      Disable OPAMP.
46  *
47  * @if DOXYDOC_P1_DEVICE
48  * All OPAMP functions assume that the DAC clock is running. If DAC is not
49  * used, the clock can be turned off when OPAMPs are configured.
50  * @elseif DOXYDOC_P2_DEVICE
51  * All OPAMP functions assume that the VDAC clock is running. If VDAC is not
52  * used, the clock can be turned off when the OPAMPs are configured.
53  * @endif
54  *
55  * If the available gain values don't suit the application at hand, the resistor
56  * ladders can be disabled and external gain programming resistors used.
57  *
58  * A number of predefined OPAMP setup macros are available for configuration
59  * of the most common OPAMP topologies (see figures below).
60  *
61  * @note
62  * <em>The terms POSPAD and NEGPAD in the figures are used to indicate that these
63  * pads should be connected to a suitable signal ground.</em>
64  *
65  * \n<b>Unity gain voltage follower.</b>\n
66  * @if DOXYDOC_P1_DEVICE
67  * Use predefined macros @ref OPA_INIT_UNITY_GAIN and
68  * @ref OPA_INIT_UNITY_GAIN_OPA2.
69  * @elseif DOXYDOC_P2_DEVICE
70  * Use predefined macro @ref OPA_INIT_UNITY_GAIN.
71  * @endif
72  * @verbatim
73 
74                        |\
75             ___________|+\
76                        |  \_______
77                     ___|_ /    |
78                    |   | /     |
79                    |   |/      |
80                    |___________|
81    @endverbatim
82  *
83  * \n<b>Non-inverting amplifier.</b>\n
84  * @if DOXYDOC_P1_DEVICE
85  * Use predefined macros @ref OPA_INIT_NON_INVERTING and
86  * @ref OPA_INIT_NON_INVERTING_OPA2.
87  * @elseif DOXYDOC_P2_DEVICE
88  * Use predefined macro @ref OPA_INIT_NON_INVERTING.
89  * @endif
90  * @verbatim
91 
92                        |\
93             ___________|+\
94                        |  \_______
95                     ___|_ /    |
96                    |   | /     |
97                    |   |/      |
98                    |_____R2____|
99                    |
100                    R1
101                    |
102                  NEGPAD @endverbatim
103  *
104  * \n<b>Inverting amplifier.</b>\n
105  * @if DOXYDOC_P1_DEVICE
106  * Use predefined macros @ref OPA_INIT_INVERTING and
107  * @ref OPA_INIT_INVERTING_OPA2.
108  * @elseif DOXYDOC_P2_DEVICE
109  * Use predefined macro @ref OPA_INIT_INVERTING.
110  * @endif
111  * @verbatim
112 
113                     _____R2____
114                    |           |
115                    |   |\      |
116             ____R1_|___|_\     |
117                        |  \____|___
118                     ___|  /
119                    |   |+/
120                    |   |/
121                    |
122                  POSPAD @endverbatim
123  *
124  * \n<b>Cascaded non-inverting amplifiers.</b>\n
125  * Use predefined macros @ref OPA_INIT_CASCADED_NON_INVERTING_OPA0,
126  * @ref OPA_INIT_CASCADED_NON_INVERTING_OPA1 and
127  * @ref OPA_INIT_CASCADED_NON_INVERTING_OPA2.
128  * @verbatim
129 
130                        |\                       |\                       |\
131             ___________|+\ OPA0      ___________|+\ OPA1      ___________|+\ OPA2
132                        |  \_________|           |  \_________|           |  \_______
133                     ___|_ /    |             ___|_ /    |             ___|_ /    |
134                    |   | /     |            |   | /     |            |   | /     |
135                    |   |/      |            |   |/      |            |   |/      |
136                    |_____R2____|            |_____R2____|            |_____R2____|
137                    |                        |                        |
138                    R1                       R1                       R1
139                    |                        |                        |
140                  NEGPAD                   NEGPAD                   NEGPAD @endverbatim
141  *
142  * \n<b>Cascaded inverting amplifiers.</b>\n
143  * Use predefined macros @ref OPA_INIT_CASCADED_INVERTING_OPA0,
144  * @ref OPA_INIT_CASCADED_INVERTING_OPA1 and
145  * @ref OPA_INIT_CASCADED_INVERTING_OPA2.
146  * @verbatim
147 
148                     _____R2____              _____R2____              _____R2____
149                    |           |            |           |            |           |
150                    |   |\      |            |   |\      |            |   |\      |
151             ____R1_|___|_\     |     ____R1_|___|_\     |     ____R1_|___|_\     |
152                        |  \____|____|           |  \____|___|            |  \____|__
153                     ___|  /                  ___|  /                  ___|  /
154                    |   |+/ OPA0             |   |+/ OPA1             |   |+/ OPA2
155                    |   |/                   |   |/                   |   |/
156                    |                        |                        |
157                  POSPAD                   POSPAD                   POSPAD @endverbatim
158  *
159  * \n<b>Differential driver with two opamp's.</b>\n
160  * Use predefined macros @ref OPA_INIT_DIFF_DRIVER_OPA0 and
161  * @ref OPA_INIT_DIFF_DRIVER_OPA1.
162  * @verbatim
163 
164                                      __________________________
165                                     |                          +
166                                     |        _____R2____
167                        |\           |       |           |
168             ___________|+\ OPA0     |       |   |\ OPA1 |
169                        |  \_________|____R1_|___|_\     |      _
170                     ___|_ /         |           |  \____|______
171                    |   | /          |        ___|  /
172                    |   |/           |       |   |+/
173                    |________________|       |   |/
174                                             |
175                                           POSPAD @endverbatim
176  *
177  * \n<b>Differential receiver with three opamp's.</b>\n
178  * Use predefined macros @ref OPA_INIT_DIFF_RECEIVER_OPA0,
179  * @ref OPA_INIT_DIFF_RECEIVER_OPA1 and @ref OPA_INIT_DIFF_RECEIVER_OPA2.
180  * @verbatim
181 
182                        |\
183              __________|+\ OPA1
184             _          |  \_________
185                     ___|_ /    |    |        _____R2____
186                    |   | /     |    |       |           |
187                    |   |/      |    |       |   |\      |
188                    |___________|    |____R1_|___|_\     |
189                                                 |  \____|___
190                        |\            ____R1_ ___|  /
191             +__________|+\ OPA0     |       |   |+/ OPA2
192                        |  \_________|       |   |/
193                     ___|_ /    |            R2
194                    |   | /     |            |
195                    |   |/      |          NEGPAD OPA0
196                    |___________|
197    @endverbatim
198  *
199  * @if DOXYDOC_P2_DEVICE
200  * \n<b>Instrumentation amplifier.</b>\n
201  * Use predefined macros @ref OPA_INIT_INSTR_AMP_OPA0 and
202  * @ref OPA_INIT_INSTR_AMP_OPA1.
203  * @verbatim
204 
205                        |\
206              __________|+\ OPA1
207                        |  \______________
208                     ___|_ /     |
209                    |   | /      |
210                    |   |/       R2
211                    |____________|
212                                 |
213                                 R1
214                                 |
215                                 R1
216                     ____________|
217                    |            |
218                    |            R2
219                    |   |\       |
220                    |___|+\ OPA0 |
221                        |  \_____|________
222              __________|_ /
223                        | /
224                        |/
225 
226    @endverbatim
227  * @endif
228  *
229  * @{
230  ******************************************************************************/
231 /* *INDENT-ON* */
232 
233 /*******************************************************************************
234  **************************   GLOBAL FUNCTIONS   *******************************
235  ******************************************************************************/
236 
237 /***************************************************************************//**
238  * @brief
239  *   Disable an Operational Amplifier.
240  *
241  * @if DOXYDOC_P1_DEVICE
242  * @param[in] dac
243  *   A pointer to the DAC peripheral register block.
244  * @elseif DOXYDOC_P2_DEVICE
245  * @param[in] dac
246  *   A pointer to the VDAC peripheral register block.
247  * @endif
248  *
249  * @param[in] opa
250  *   Selects an OPA, valid values are OPA0, OPA1, and OPA2.
251  ******************************************************************************/
OPAMP_Disable(DAC_TypeDef * dac,OPAMP_TypeDef opa)252 void OPAMP_Disable(
253 #if defined(_SILICON_LABS_32B_SERIES_0)
254   DAC_TypeDef *dac,
255 #elif defined(_SILICON_LABS_32B_SERIES_1)
256   VDAC_TypeDef *dac,
257 #endif
258   OPAMP_TypeDef opa)
259 {
260 #if defined(_SILICON_LABS_32B_SERIES_0)
261   EFM_ASSERT(DAC_REF_VALID(dac));
262   EFM_ASSERT(DAC_OPA_VALID(opa));
263 
264   if (opa == OPA0) {
265     dac->CH0CTRL &= ~DAC_CH0CTRL_EN;
266     dac->OPACTRL &= ~DAC_OPACTRL_OPA0EN;
267   } else if (opa == OPA1) {
268     dac->CH1CTRL &= ~DAC_CH1CTRL_EN;
269     dac->OPACTRL &= ~DAC_OPACTRL_OPA1EN;
270   } else { /* OPA2 */
271     dac->OPACTRL &= ~DAC_OPACTRL_OPA2EN;
272   }
273 
274 #elif defined(_SILICON_LABS_32B_SERIES_1)
275   EFM_ASSERT(VDAC_REF_VALID(dac));
276   EFM_ASSERT(VDAC_OPA_VALID(opa));
277 
278   if (opa == OPA0) {
279 #if defined(VDAC_STATUS_OPA0ENS)
280     dac->CMD |= VDAC_CMD_OPA0DIS;
281     while (dac->STATUS & VDAC_STATUS_OPA0ENS) {
282     }
283 #endif
284 #if defined(VDAC_STATUS_OPA1ENS)
285   } else if (opa == OPA1) {
286     dac->CMD |= VDAC_CMD_OPA1DIS;
287     while (dac->STATUS & VDAC_STATUS_OPA1ENS) {
288     }
289 #endif
290 #if defined(VDAC_STATUS_OPA2ENS)
291   } else if (opa == OPA2) {
292     dac->CMD |= VDAC_CMD_OPA2DIS;
293     while (dac->STATUS & VDAC_STATUS_OPA2ENS) {
294     }
295 #endif
296   } else { /* OPA3 */
297 #if defined(VDAC_STATUS_OPA3ENS)
298     dac->CMD |= VDAC_CMD_OPA3DIS;
299     while (dac->STATUS & VDAC_STATUS_OPA3ENS) {
300     }
301 #endif
302   }
303 #endif
304 }
305 
306 /***************************************************************************//**
307  * @brief
308  *   Configure and enable an Operational Amplifier.
309  *
310  * @if DOXYDOC_P1_DEVICE
311  * @note
312  *   The value of the alternate output enable bit mask in the OPAMP_Init_TypeDef
313  *   structure should consist of one or more of the
314  *   DAC_OPA[opa#]MUX_OUTPEN_OUT[output#] flags
315  *   (defined in \<part_name\>_dac.h) OR'ed together. @n @n
316  *   For OPA0:
317  *   @li DAC_OPA0MUX_OUTPEN_OUT0
318  *   @li DAC_OPA0MUX_OUTPEN_OUT1
319  *   @li DAC_OPA0MUX_OUTPEN_OUT2
320  *   @li DAC_OPA0MUX_OUTPEN_OUT3
321  *   @li DAC_OPA0MUX_OUTPEN_OUT4
322  *
323  *   For OPA1:
324  *   @li DAC_OPA1MUX_OUTPEN_OUT0
325  *   @li DAC_OPA1MUX_OUTPEN_OUT1
326  *   @li DAC_OPA1MUX_OUTPEN_OUT2
327  *   @li DAC_OPA1MUX_OUTPEN_OUT3
328  *   @li DAC_OPA1MUX_OUTPEN_OUT4
329  *
330  *   For OPA2:
331  *   @li DAC_OPA2MUX_OUTPEN_OUT0
332  *   @li DAC_OPA2MUX_OUTPEN_OUT1
333  *
334  *   E.g: @n
335  *   init.outPen = DAC_OPA0MUX_OUTPEN_OUT0 | DAC_OPA0MUX_OUTPEN_OUT4;
336  *
337  * @param[in] dac
338  *   A pointer to the DAC peripheral register block.
339  * @elseif DOXYDOC_P2_DEVICE
340  * @note
341  *   The value of the alternate output enable bit mask in the OPAMP_Init_TypeDef
342  *   structure should consist of one or more of the
343  *   VDAC_OPA_OUT_ALTOUTPADEN_OUT[output#] flags
344  *   (defined in \<part_name\>_vdac.h) OR'ed together. @n @n
345  *   @li VDAC_OPA_OUT_ALTOUTPADEN_OUT0
346  *   @li VDAC_OPA_OUT_ALTOUTPADEN_OUT1
347  *   @li VDAC_OPA_OUT_ALTOUTPADEN_OUT2
348  *   @li VDAC_OPA_OUT_ALTOUTPADEN_OUT3
349  *   @li VDAC_OPA_OUT_ALTOUTPADEN_OUT4
350  *
351  *   For example: @n
352  *   init.outPen = VDAC_OPA_OUT_ALTOUTPADEN_OUT0 | VDAC_OPA_OUT_ALTOUTPADEN_OUT4;
353  * @param[in] dac
354  *   A pointer to the VDAC peripheral register block.
355  * @endif
356  *
357  * @param[in] opa
358  *   Selects an OPA, valid values are OPA0, OPA1, and OPA2.
359  *
360  * @param[in] init
361  *   A pointer to a structure containing OPAMP initialization information.
362  ******************************************************************************/
OPAMP_Enable(DAC_TypeDef * dac,OPAMP_TypeDef opa,const OPAMP_Init_TypeDef * init)363 void OPAMP_Enable(
364 #if defined(_SILICON_LABS_32B_SERIES_0)
365   DAC_TypeDef *dac,
366 #elif defined(_SILICON_LABS_32B_SERIES_1)
367   VDAC_TypeDef *dac,
368 #endif
369   OPAMP_TypeDef opa,
370   const OPAMP_Init_TypeDef *init)
371 {
372 #if defined(_SILICON_LABS_32B_SERIES_0)
373   uint32_t gain;
374 
375   EFM_ASSERT(DAC_REF_VALID(dac));
376   EFM_ASSERT(DAC_OPA_VALID(opa));
377   EFM_ASSERT(init->bias <= (_DAC_BIASPROG_BIASPROG_MASK
378                             >> _DAC_BIASPROG_BIASPROG_SHIFT));
379 
380   if (opa == OPA0) {
381     EFM_ASSERT((init->outPen & ~_DAC_OPA0MUX_OUTPEN_MASK) == 0);
382 
383     dac->BIASPROG = (dac->BIASPROG
384                      & ~(_DAC_BIASPROG_BIASPROG_MASK
385                          | DAC_BIASPROG_HALFBIAS))
386                     | (init->bias     << _DAC_BIASPROG_BIASPROG_SHIFT)
387                     | (init->halfBias ?   DAC_BIASPROG_HALFBIAS : 0);
388 
389     if (init->defaultOffset) {
390       gain = dac->CAL & _DAC_CAL_GAIN_MASK;
391       SYSTEM_GetCalibrationValue(&dac->CAL);
392       dac->CAL = (dac->CAL & ~_DAC_CAL_GAIN_MASK) | gain;
393     } else {
394       EFM_ASSERT(init->offset <= (_DAC_CAL_CH0OFFSET_MASK
395                                   >> _DAC_CAL_CH0OFFSET_SHIFT));
396 
397       dac->CAL = (dac->CAL & ~_DAC_CAL_CH0OFFSET_MASK)
398                  | (init->offset << _DAC_CAL_CH0OFFSET_SHIFT);
399     }
400 
401     dac->OPA0MUX  = (uint32_t)init->resSel
402                     | (uint32_t)init->outMode
403                     | init->outPen
404                     | (uint32_t)init->resInMux
405                     | (uint32_t)init->negSel
406                     | (uint32_t)init->posSel
407                     | (init->nextOut ? DAC_OPA0MUX_NEXTOUT : 0)
408                     | (init->npEn    ? DAC_OPA0MUX_NPEN    : 0)
409                     | (init->ppEn    ? DAC_OPA0MUX_PPEN    : 0);
410 
411     dac->CH0CTRL |= DAC_CH0CTRL_EN;
412     dac->OPACTRL  = (dac->OPACTRL
413                      & ~(DAC_OPACTRL_OPA0SHORT
414                          | _DAC_OPACTRL_OPA0LPFDIS_MASK
415                          |  DAC_OPACTRL_OPA0HCMDIS))
416                     | (init->shortInputs ?  DAC_OPACTRL_OPA0SHORT : 0)
417                     | (init->lpfPosPadDisable
418                        ? DAC_OPACTRL_OPA0LPFDIS_PLPFDIS : 0)
419                     | (init->lpfNegPadDisable
420                        ? DAC_OPACTRL_OPA0LPFDIS_NLPFDIS : 0)
421                     | (init->hcmDisable ? DAC_OPACTRL_OPA0HCMDIS : 0)
422                     | DAC_OPACTRL_OPA0EN;
423   } else if ( opa == OPA1 ) {
424     EFM_ASSERT((init->outPen & ~_DAC_OPA1MUX_OUTPEN_MASK) == 0);
425 
426     dac->BIASPROG = (dac->BIASPROG
427                      & ~(_DAC_BIASPROG_BIASPROG_MASK
428                          | DAC_BIASPROG_HALFBIAS))
429                     | (init->bias   << _DAC_BIASPROG_BIASPROG_SHIFT)
430                     | (init->halfBias ? DAC_BIASPROG_HALFBIAS : 0);
431 
432     if (init->defaultOffset) {
433       gain = dac->CAL & _DAC_CAL_GAIN_MASK;
434       SYSTEM_GetCalibrationValue(&dac->CAL);
435       dac->CAL = (dac->CAL & ~_DAC_CAL_GAIN_MASK) | gain;
436     } else {
437       EFM_ASSERT(init->offset <= (_DAC_CAL_CH1OFFSET_MASK
438                                   >> _DAC_CAL_CH1OFFSET_SHIFT));
439 
440       dac->CAL = (dac->CAL & ~_DAC_CAL_CH1OFFSET_MASK)
441                  | (init->offset << _DAC_CAL_CH1OFFSET_SHIFT);
442     }
443 
444     dac->OPA1MUX  = (uint32_t)init->resSel
445                     | (uint32_t)init->outMode
446                     | init->outPen
447                     | (uint32_t)init->resInMux
448                     | (uint32_t)init->negSel
449                     | (uint32_t)init->posSel
450                     | (init->nextOut ? DAC_OPA1MUX_NEXTOUT : 0)
451                     | (init->npEn    ? DAC_OPA1MUX_NPEN    : 0)
452                     | (init->ppEn    ? DAC_OPA1MUX_PPEN    : 0);
453 
454     dac->CH1CTRL |= DAC_CH1CTRL_EN;
455     dac->OPACTRL  = (dac->OPACTRL
456                      & ~(DAC_OPACTRL_OPA1SHORT
457                          | _DAC_OPACTRL_OPA1LPFDIS_MASK
458                          | DAC_OPACTRL_OPA1HCMDIS))
459                     | (init->shortInputs ? DAC_OPACTRL_OPA1SHORT : 0)
460                     | (init->lpfPosPadDisable
461                        ? DAC_OPACTRL_OPA1LPFDIS_PLPFDIS : 0)
462                     | (init->lpfNegPadDisable
463                        ? DAC_OPACTRL_OPA1LPFDIS_NLPFDIS : 0)
464                     | (init->hcmDisable ? DAC_OPACTRL_OPA1HCMDIS : 0)
465                     | DAC_OPACTRL_OPA1EN;
466   } else { /* OPA2 */
467     EFM_ASSERT((init->posSel == DAC_OPA2MUX_POSSEL_DISABLE)
468                || (init->posSel == DAC_OPA2MUX_POSSEL_POSPAD)
469                || (init->posSel == DAC_OPA2MUX_POSSEL_OPA1INP)
470                || (init->posSel == DAC_OPA2MUX_POSSEL_OPATAP));
471 
472     EFM_ASSERT((init->outMode & ~DAC_OPA2MUX_OUTMODE) == 0);
473 
474     EFM_ASSERT((init->outPen & ~_DAC_OPA2MUX_OUTPEN_MASK) == 0);
475 
476     dac->BIASPROG = (dac->BIASPROG
477                      & ~(_DAC_BIASPROG_OPA2BIASPROG_MASK
478                          | DAC_BIASPROG_OPA2HALFBIAS))
479                     | (init->bias << _DAC_BIASPROG_OPA2BIASPROG_SHIFT)
480                     | (init->halfBias ? DAC_BIASPROG_OPA2HALFBIAS : 0);
481 
482     if (init->defaultOffset) {
483       SYSTEM_GetCalibrationValue(&dac->OPAOFFSET);
484     } else {
485       EFM_ASSERT(init->offset <= (_DAC_OPAOFFSET_OPA2OFFSET_MASK
486                                   >> _DAC_OPAOFFSET_OPA2OFFSET_SHIFT));
487       dac->OPAOFFSET = (dac->OPAOFFSET & ~_DAC_OPAOFFSET_OPA2OFFSET_MASK)
488                        | (init->offset << _DAC_OPAOFFSET_OPA2OFFSET_SHIFT);
489     }
490 
491     dac->OPA2MUX  = (uint32_t)init->resSel
492                     | (uint32_t)init->outMode
493                     | init->outPen
494                     | (uint32_t)init->resInMux
495                     | (uint32_t)init->negSel
496                     | (uint32_t)init->posSel
497                     | (init->nextOut ? DAC_OPA2MUX_NEXTOUT : 0)
498                     | (init->npEn    ? DAC_OPA2MUX_NPEN    : 0)
499                     | (init->ppEn    ? DAC_OPA2MUX_PPEN    : 0);
500 
501     dac->OPACTRL  = (dac->OPACTRL
502                      & ~(DAC_OPACTRL_OPA2SHORT
503                          | _DAC_OPACTRL_OPA2LPFDIS_MASK
504                          | DAC_OPACTRL_OPA2HCMDIS))
505                     | (init->shortInputs ?  DAC_OPACTRL_OPA2SHORT : 0)
506                     | (init->lpfPosPadDisable
507                        ? DAC_OPACTRL_OPA2LPFDIS_PLPFDIS : 0)
508                     | (init->lpfNegPadDisable
509                        ? DAC_OPACTRL_OPA2LPFDIS_NLPFDIS : 0)
510                     | (init->hcmDisable ? DAC_OPACTRL_OPA2HCMDIS : 0)
511                     | DAC_OPACTRL_OPA2EN;
512   }
513 
514 #elif defined(_SILICON_LABS_32B_SERIES_1)
515   uint32_t calData = 0;
516   uint32_t warmupTime;
517 
518   EFM_ASSERT(VDAC_REF_VALID(dac));
519   EFM_ASSERT(VDAC_OPA_VALID(opa));
520   EFM_ASSERT(init->settleTime <= (_VDAC_OPA_TIMER_SETTLETIME_MASK
521                                   >> _VDAC_OPA_TIMER_SETTLETIME_SHIFT));
522   EFM_ASSERT(init->startupDly <= (_VDAC_OPA_TIMER_STARTUPDLY_MASK
523                                   >> _VDAC_OPA_TIMER_STARTUPDLY_SHIFT));
524   EFM_ASSERT((init->outPen & ~_VDAC_OPA_OUT_ALTOUTPADEN_MASK) == 0);
525   EFM_ASSERT(!((init->gain3xEn == true)
526                && ((init->negSel == opaNegSelResTap)
527                    || (init->posSel == opaPosSelResTap))));
528   EFM_ASSERT((init->drvStr == opaDrvStrLowerAccLowStr)
529              || (init->drvStr == opaDrvStrLowAccLowStr)
530              || (init->drvStr == opaDrvStrHighAccHighStr)
531              || (init->drvStr == opaDrvStrHigherAccHighStr));
532 
533   /* Disable OPAMP before writing to registers. */
534   OPAMP_Disable(dac, opa);
535 
536   /* Get the calibration value based on OPAMP, Drive Strength, and INCBW. */
537   switch (opa) {
538 #if defined(VDAC_STATUS_OPA0ENS)
539     case OPA0:
540       switch (init->drvStr) {
541         case opaDrvStrLowerAccLowStr:
542           calData = (init->ugBwScale ? DEVINFO->OPA0CAL0 : DEVINFO->OPA0CAL4);
543           break;
544         case opaDrvStrLowAccLowStr:
545           calData = (init->ugBwScale ? DEVINFO->OPA0CAL1 : DEVINFO->OPA0CAL5);
546           break;
547         case opaDrvStrHighAccHighStr:
548           calData = (init->ugBwScale ? DEVINFO->OPA0CAL2 : DEVINFO->OPA0CAL6);
549           break;
550         case opaDrvStrHigherAccHighStr:
551           calData = (init->ugBwScale ? DEVINFO->OPA0CAL3 : DEVINFO->OPA0CAL7);
552           break;
553       }
554       break;
555 #endif
556 
557 #if defined(VDAC_STATUS_OPA1ENS)
558     case OPA1:
559       switch (init->drvStr) {
560         case opaDrvStrLowerAccLowStr:
561           calData = (init->ugBwScale ? DEVINFO->OPA1CAL0 : DEVINFO->OPA1CAL4);
562           break;
563         case opaDrvStrLowAccLowStr:
564           calData = (init->ugBwScale ? DEVINFO->OPA1CAL1 : DEVINFO->OPA1CAL5);
565           break;
566         case opaDrvStrHighAccHighStr:
567           calData = (init->ugBwScale ? DEVINFO->OPA1CAL2 : DEVINFO->OPA1CAL6);
568           break;
569         case opaDrvStrHigherAccHighStr:
570           calData = (init->ugBwScale ? DEVINFO->OPA1CAL3 : DEVINFO->OPA1CAL7);
571           break;
572       }
573       break;
574 #endif
575 
576 #if defined(VDAC_STATUS_OPA2ENS)
577     case OPA2:
578       switch (init->drvStr) {
579         case opaDrvStrLowerAccLowStr:
580           calData = (init->ugBwScale ? DEVINFO->OPA2CAL0 : DEVINFO->OPA2CAL4);
581           break;
582         case opaDrvStrLowAccLowStr:
583           calData = (init->ugBwScale ? DEVINFO->OPA2CAL1 : DEVINFO->OPA2CAL5);
584           break;
585         case opaDrvStrHighAccHighStr:
586           calData = (init->ugBwScale ? DEVINFO->OPA2CAL2 : DEVINFO->OPA2CAL6);
587           break;
588         case opaDrvStrHigherAccHighStr:
589           calData = (init->ugBwScale ? DEVINFO->OPA2CAL3 : DEVINFO->OPA2CAL7);
590           break;
591       }
592       break;
593 #endif
594 
595 #if defined(VDAC_STATUS_OPA3ENS)
596     case OPA3:
597       switch (init->drvStr) {
598         case opaDrvStrLowerAccLowStr:
599           calData = (init->ugBwScale ? DEVINFO->OPA3CAL0 : DEVINFO->OPA3CAL4);
600           break;
601         case opaDrvStrLowAccLowStr:
602           calData = (init->ugBwScale ? DEVINFO->OPA3CAL1 : DEVINFO->OPA3CAL5);
603           break;
604         case opaDrvStrHighAccHighStr:
605           calData = (init->ugBwScale ? DEVINFO->OPA3CAL2 : DEVINFO->OPA3CAL6);
606           break;
607         case opaDrvStrHigherAccHighStr:
608           calData = (init->ugBwScale ? DEVINFO->OPA3CAL3 : DEVINFO->OPA3CAL7);
609           break;
610       }
611       break;
612 #endif
613   }
614   if (!init->defaultOffsetN) {
615     EFM_ASSERT(init->offsetN <= (_VDAC_OPA_CAL_OFFSETN_MASK
616                                  >> _VDAC_OPA_CAL_OFFSETN_SHIFT));
617     calData = (calData & ~_VDAC_OPA_CAL_OFFSETN_MASK)
618               | (init->offsetN << _VDAC_OPA_CAL_OFFSETN_SHIFT);
619   }
620   if (!init->defaultOffsetP) {
621     EFM_ASSERT(init->offsetP <= (_VDAC_OPA_CAL_OFFSETP_MASK
622                                  >> _VDAC_OPA_CAL_OFFSETP_SHIFT));
623     calData = (calData & ~_VDAC_OPA_CAL_OFFSETP_MASK)
624               | (init->offsetP << _VDAC_OPA_CAL_OFFSETP_SHIFT);
625   }
626 
627   dac->OPA[opa].CAL = (calData &  _VDAC_OPA_CAL_MASK);
628 
629   dac->OPA[opa].MUX = (uint32_t)init->resSel
630                       | (init->gain3xEn  ? VDAC_OPA_MUX_GAIN3X : 0)
631                       | (uint32_t)init->resInMux
632                       | (uint32_t)init->negSel
633                       | (uint32_t)init->posSel;
634 
635   dac->OPA[opa].OUT = (uint32_t)init->outMode
636                       | (uint32_t)init->outPen;
637 
638   switch (init->drvStr) {
639     case opaDrvStrHigherAccHighStr:
640       warmupTime = 6;
641       break;
642 
643     case opaDrvStrHighAccHighStr:
644       warmupTime = 8;
645       break;
646 
647     case opaDrvStrLowAccLowStr:
648       warmupTime = 85;
649       break;
650 
651     case opaDrvStrLowerAccLowStr:
652     default:
653       warmupTime = 100;
654       break;
655   }
656 
657   dac->OPA[opa].TIMER = (uint32_t)(init->settleTime
658                                    << _VDAC_OPA_TIMER_SETTLETIME_SHIFT)
659                         | (uint32_t)(warmupTime
660                                      << _VDAC_OPA_TIMER_WARMUPTIME_SHIFT)
661                         | (uint32_t)(init->startupDly
662                                      << _VDAC_OPA_TIMER_STARTUPDLY_SHIFT);
663 
664   dac->OPA[opa].CTRL  = (init->aportYMasterDisable
665                          ? VDAC_OPA_CTRL_APORTYMASTERDIS : 0)
666                         | (init->aportXMasterDisable
667                            ? VDAC_OPA_CTRL_APORTXMASTERDIS : 0)
668                         | (uint32_t)init->prsOutSel
669                         | (uint32_t)init->prsSel
670                         | (uint32_t)init->prsMode
671                         | (init->prsEn ? VDAC_OPA_CTRL_PRSEN : 0)
672                         | (init->halfDrvStr
673                            ? VDAC_OPA_CTRL_OUTSCALE_HALF
674                            : VDAC_OPA_CTRL_OUTSCALE_FULL)
675                         | (init->hcmDisable ? VDAC_OPA_CTRL_HCMDIS : 0)
676                         | (init->ugBwScale ? VDAC_OPA_CTRL_INCBW : 0)
677                         | (uint32_t)init->drvStr;
678 
679   if (opa == OPA0) {
680 #if defined(VDAC_STATUS_OPA0ENS)
681     dac->CMD |= VDAC_CMD_OPA0EN;
682 #endif
683 #if defined(VDAC_STATUS_OPA1ENS)
684   } else if (opa == OPA1) {
685     dac->CMD |= VDAC_CMD_OPA1EN;
686 #endif
687 #if defined(VDAC_STATUS_OPA2ENS)
688   } else if (opa == OPA2) {
689     dac->CMD |= VDAC_CMD_OPA2EN;
690 #endif
691 #if defined(VDAC_STATUS_OPA3ENS)
692   } else { /* OPA3 */
693     dac->CMD |= VDAC_CMD_OPA3EN;
694 #endif
695   }
696 
697 #endif
698 }
699 
700 /** @} (end addtogroup opamp) */
701 
702 #endif /* (defined(OPAMP_PRESENT) && (OPAMP_COUNT == 1)
703        || defined(VDAC_PRESENT) && (VDAC_COUNT > 0) */
704