1 /**************************************************************************//**
2  * @file
3  * @brief Operational Amplifier (OPAMP) 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_part.h"
34 #if defined( OPAMP_PRESENT ) && ( OPAMP_COUNT == 1 )
35 
36 #include "em_system.h"
37 #include "em_assert.h"
38 #include "em_opamp.h"
39 
40 /***************************************************************************//**
41  * @addtogroup EM_Library
42  * @{
43  ******************************************************************************/
44 
45 
46 /***************************************************************************//**
47  * @addtogroup OPAMP
48  * @brief Operational Amplifier (OPAMP) peripheral API
49  * @details
50  *  This module contains functions to:
51  *   @li OPAMP_Enable()       Configure and enable an opamp.
52  *   @li OPAMP_Disable()      Disable an opamp.
53  *
54  * All OPAMP functions assume that the DAC clock is running. If the DAC is not
55  * used, the clock can be turned off when the opamp's are configured.
56  *
57  * If the available gain values dont suit the application at hand, the resistor
58  * ladders can be disabled and external gain programming resistors used.
59  *
60  * A number of predefined opamp setup macros are available for configuration
61  * of the most common opamp topologies (see figures below).
62  *
63  * @note
64  * <em>The terms POSPAD and NEGPAD in the figures are used to indicate that these
65  * pads should be connected to a suitable signal ground.</em>
66  *
67  * \n<b>Unity gain voltage follower.</b>\n
68  * Use predefined macros @ref OPA_INIT_UNITY_GAIN and
69  * @ref OPA_INIT_UNITY_GAIN_OPA2.
70  * @verbatim
71 
72                        |\
73             ___________|+\
74                        |  \_______
75                     ___|_ /    |
76                    |   | /     |
77                    |   |/      |
78                    |___________|
79    @endverbatim
80  *
81  * \n<b>Non-inverting amplifier.</b>\n
82  * Use predefined macros @ref OPA_INIT_NON_INVERTING and
83  * @ref OPA_INIT_NON_INVERTING_OPA2.
84  * @verbatim
85 
86                        |\
87             ___________|+\
88                        |  \_______
89                     ___|_ /    |
90                    |   | /     |
91                    |   |/      |
92                    |_____R2____|
93                    |
94                    R1
95                    |
96                  NEGPAD @endverbatim
97  *
98  * \n<b>Inverting amplifier.</b>\n
99  * Use predefined macros @ref OPA_INIT_INVERTING and
100  * @ref OPA_INIT_INVERTING_OPA2.
101  * @verbatim
102 
103                     _____R2____
104                    |           |
105                    |   |\      |
106             ____R1_|___|_\     |
107                        |  \____|___
108                     ___|  /
109                    |   |+/
110                    |   |/
111                    |
112                  POSPAD @endverbatim
113  *
114  * \n<b>Cascaded non-inverting amplifiers.</b>\n
115  * Use predefined macros @ref OPA_INIT_CASCADED_NON_INVERTING_OPA0,
116  * @ref OPA_INIT_CASCADED_NON_INVERTING_OPA1 and
117  * @ref OPA_INIT_CASCADED_NON_INVERTING_OPA2.
118  * @verbatim
119 
120                        |\                       |\                       |\
121             ___________|+\ OPA0      ___________|+\ OPA1      ___________|+\ OPA2
122                        |  \_________|           |  \_________|           |  \_______
123                     ___|_ /    |             ___|_ /    |             ___|_ /    |
124                    |   | /     |            |   | /     |            |   | /     |
125                    |   |/      |            |   |/      |            |   |/      |
126                    |_____R2____|            |_____R2____|            |_____R2____|
127                    |                        |                        |
128                    R1                       R1                       R1
129                    |                        |                        |
130                  NEGPAD                   NEGPAD                   NEGPAD @endverbatim
131  *
132  * \n<b>Cascaded inverting amplifiers.</b>\n
133  * Use predefined macros @ref OPA_INIT_CASCADED_INVERTING_OPA0,
134  * @ref OPA_INIT_CASCADED_INVERTING_OPA1 and
135  * @ref OPA_INIT_CASCADED_INVERTING_OPA2.
136  * @verbatim
137 
138                     _____R2____              _____R2____              _____R2____
139                    |           |            |           |            |           |
140                    |   |\      |            |   |\      |            |   |\      |
141             ____R1_|___|_\     |     ____R1_|___|_\     |     ____R1_|___|_\     |
142                        |  \____|____|           |  \____|___|            |  \____|__
143                     ___|  /                  ___|  /                  ___|  /
144                    |   |+/ OPA0             |   |+/ OPA1             |   |+/ OPA2
145                    |   |/                   |   |/                   |   |/
146                    |                        |                        |
147                  POSPAD                   POSPAD                   POSPAD @endverbatim
148  *
149  * \n<b>Differential driver with two opamp's.</b>\n
150  * Use predefined macros @ref OPA_INIT_DIFF_DRIVER_OPA0 and
151  * @ref OPA_INIT_DIFF_DRIVER_OPA1.
152  * @verbatim
153 
154                                      __________________________
155                                     |                          +
156                                     |        _____R2____
157                        |\           |       |           |
158             ___________|+\ OPA0     |       |   |\ OPA1 |
159                        |  \_________|____R1_|___|_\     |      _
160                     ___|_ /         |           |  \____|______
161                    |   | /          |        ___|  /
162                    |   |/           |       |   |+/
163                    |________________|       |   |/
164                                             |
165                                           POSPAD @endverbatim
166  *
167  * \n<b>Differential receiver with three opamp's.</b>\n
168  * Use predefined macros @ref OPA_INIT_DIFF_RECEIVER_OPA0,
169  * @ref OPA_INIT_DIFF_RECEIVER_OPA1 and @ref OPA_INIT_DIFF_RECEIVER_OPA2.
170  * @verbatim
171 
172                        |\
173              __________|+\ OPA1
174             _          |  \_________
175                     ___|_ /    |    |        _____R2____
176                    |   | /     |    |       |           |
177                    |   |/      |    |       |   |\      |
178                    |___________|    |____R1_|___|_\     |
179                                                 |  \____|___
180                        |\            ____R1_ ___|  /
181             +__________|+\ OPA0     |       |   |+/ OPA2
182                        |  \_________|       |   |/
183                     ___|_ /    |            R2
184                    |   | /     |            |
185                    |   |/      |          NEGPAD OPA0
186                    |___________|
187    @endverbatim
188  *
189  * @{
190  ******************************************************************************/
191 
192 /*******************************************************************************
193  **************************   GLOBAL FUNCTIONS   *******************************
194  ******************************************************************************/
195 
196 /***************************************************************************//**
197  * @brief
198  *   Disable an Operational Amplifier.
199  *
200  * @param[in] dac
201  *   Pointer to DAC peripheral register block.
202  *
203  * @param[in] opa
204  *   Selects an OPA, valid vaules are @ref OPA0, @ref OPA1 and @ref OPA2.
205  ******************************************************************************/
OPAMP_Disable(DAC_TypeDef * dac,OPAMP_TypeDef opa)206 void OPAMP_Disable( DAC_TypeDef *dac, OPAMP_TypeDef opa )
207 {
208   EFM_ASSERT( DAC_REF_VALID( dac ) );
209   EFM_ASSERT( DAC_OPA_VALID( opa ) );
210 
211   if ( opa == OPA0 )
212   {
213     dac->CH0CTRL &= ~DAC_CH0CTRL_EN;
214     dac->OPACTRL &= ~DAC_OPACTRL_OPA0EN;
215   }
216   else if ( opa == OPA1 )
217   {
218     dac->CH1CTRL &= ~DAC_CH1CTRL_EN;
219     dac->OPACTRL &= ~DAC_OPACTRL_OPA1EN;
220   }
221   else /* OPA2 */
222   {
223     dac->OPACTRL &= ~DAC_OPACTRL_OPA2EN;
224   }
225 }
226 
227 
228 /***************************************************************************//**
229  * @brief
230  *   Configure and enable an Operational Amplifier.
231  *
232  * @details
233  *
234  * @param[in] dac
235  *   Pointer to DAC peripheral register block.
236  *
237  * @param[in] opa
238  *   Selects an OPA, valid vaules are @ref OPA0, @ref OPA1 and @ref OPA2.
239  *
240  * @param[in] init
241  *   Pointer to a structure containing OPAMP init information.
242  ******************************************************************************/
OPAMP_Enable(DAC_TypeDef * dac,OPAMP_TypeDef opa,const OPAMP_Init_TypeDef * init)243 void OPAMP_Enable( DAC_TypeDef *dac, OPAMP_TypeDef opa, const OPAMP_Init_TypeDef *init )
244 {
245   uint32_t offset;
246 
247   EFM_ASSERT( DAC_REF_VALID( dac ) );
248   EFM_ASSERT( DAC_OPA_VALID( opa ) );
249   EFM_ASSERT( init->bias <= ( _DAC_BIASPROG_BIASPROG_MASK  >>
250                               _DAC_BIASPROG_BIASPROG_SHIFT    ) );
251 
252   if ( opa == OPA0 )
253   {
254     EFM_ASSERT( ( init->outPen & ~_DAC_OPA0MUX_OUTPEN_MASK ) == 0 );
255 
256     dac->BIASPROG = ( dac->BIASPROG
257                       & ~( _DAC_BIASPROG_BIASPROG_MASK             |
258                             DAC_BIASPROG_HALFBIAS                    )  ) |
259                     ( init->bias      << _DAC_BIASPROG_BIASPROG_SHIFT   ) |
260                     ( init->halfBias   ? DAC_BIASPROG_HALFBIAS      : 0 );
261 
262     if ( init->defaultOffset )
263     {
264       offset = SYSTEM_GetCalibrationValue( &dac->CAL );
265       dac->CAL = ( dac->CAL & ~_DAC_CAL_CH0OFFSET_MASK ) |
266                  ( offset   &  _DAC_CAL_CH0OFFSET_MASK );
267     }
268     else
269     {
270       EFM_ASSERT( init->offset <= ( _DAC_CAL_CH0OFFSET_MASK >>
271                                     _DAC_CAL_CH0OFFSET_SHIFT   ) );
272 
273       dac->CAL = ( dac->CAL      & ~_DAC_CAL_CH0OFFSET_MASK  ) |
274                  ( init->offset <<  _DAC_CAL_CH0OFFSET_SHIFT );
275     }
276 
277     dac->OPA0MUX  = (uint32_t)init->resSel                                |
278                     (uint32_t)init->outMode                               |
279                     init->outPen                                          |
280                     (uint32_t)init->resInMux                              |
281                     (uint32_t)init->negSel                                |
282                     (uint32_t)init->posSel                                |
283                     ( init->nextOut     ? DAC_OPA0MUX_NEXTOUT       : 0 ) |
284                     ( init->npEn        ? DAC_OPA0MUX_NPEN          : 0 ) |
285                     ( init->ppEn        ? DAC_OPA0MUX_PPEN          : 0 );
286 
287     dac->CH0CTRL |= DAC_CH0CTRL_EN;
288     dac->OPACTRL  = ( dac->OPACTRL
289                       & ~(  DAC_OPACTRL_OPA0SHORT                  |
290                            _DAC_OPACTRL_OPA0LPFDIS_MASK            |
291                             DAC_OPACTRL_OPA0HCMDIS                   )  ) |
292                     ( init->shortInputs ?  DAC_OPACTRL_OPA0SHORT    : 0 ) |
293                     ( init->lpfPosPadDisable ?
294                                      DAC_OPACTRL_OPA0LPFDIS_PLPFDIS : 0 ) |
295                     ( init->lpfNegPadDisable ?
296                                      DAC_OPACTRL_OPA0LPFDIS_NLPFDIS : 0 ) |
297                     ( init->hcmDisable  ?  DAC_OPACTRL_OPA0HCMDIS   : 0 ) |
298                     ( DAC_OPACTRL_OPA0EN                                );
299   }
300   else if ( opa == OPA1 )
301   {
302     EFM_ASSERT( ( init->outPen & ~_DAC_OPA1MUX_OUTPEN_MASK ) == 0 );
303 
304     dac->BIASPROG = ( dac->BIASPROG
305                       & ~( _DAC_BIASPROG_BIASPROG_MASK             |
306                             DAC_BIASPROG_HALFBIAS                    )  ) |
307                     ( init->bias      << _DAC_BIASPROG_BIASPROG_SHIFT   ) |
308                     ( init->halfBias   ? DAC_BIASPROG_HALFBIAS      : 0 );
309 
310     if ( init->defaultOffset )
311     {
312       offset = SYSTEM_GetCalibrationValue( &dac->CAL );
313       dac->CAL = ( dac->CAL & ~_DAC_CAL_CH1OFFSET_MASK ) |
314                  ( offset   &  _DAC_CAL_CH1OFFSET_MASK );
315     }
316     else
317     {
318       EFM_ASSERT( init->offset <= ( _DAC_CAL_CH1OFFSET_MASK >>
319                                     _DAC_CAL_CH1OFFSET_SHIFT   ) );
320 
321       dac->CAL = ( dac->CAL      & ~_DAC_CAL_CH1OFFSET_MASK  ) |
322                  ( init->offset <<  _DAC_CAL_CH1OFFSET_SHIFT );
323     }
324 
325     dac->OPA1MUX  = (uint32_t)init->resSel                                |
326                     (uint32_t)init->outMode                               |
327                     init->outPen                                          |
328                     (uint32_t)init->resInMux                              |
329                     (uint32_t)init->negSel                                |
330                     (uint32_t)init->posSel                                |
331                     ( init->nextOut     ? DAC_OPA1MUX_NEXTOUT       : 0 ) |
332                     ( init->npEn        ? DAC_OPA1MUX_NPEN          : 0 ) |
333                     ( init->ppEn        ? DAC_OPA1MUX_PPEN          : 0 );
334 
335     dac->CH1CTRL |= DAC_CH1CTRL_EN;
336     dac->OPACTRL  = ( dac->OPACTRL
337                       & ~(  DAC_OPACTRL_OPA1SHORT                  |
338                            _DAC_OPACTRL_OPA1LPFDIS_MASK            |
339                              DAC_OPACTRL_OPA1HCMDIS                  )  ) |
340                     ( init->shortInputs ?  DAC_OPACTRL_OPA1SHORT    : 0 ) |
341                     ( init->lpfPosPadDisable ?
342                                      DAC_OPACTRL_OPA1LPFDIS_PLPFDIS : 0 ) |
343                     ( init->lpfNegPadDisable ?
344                                      DAC_OPACTRL_OPA1LPFDIS_NLPFDIS : 0 ) |
345                     ( init->hcmDisable  ?  DAC_OPACTRL_OPA1HCMDIS   : 0 ) |
346                     ( DAC_OPACTRL_OPA1EN                                );
347   }
348   else /* OPA2 */
349   {
350     EFM_ASSERT( ( init->posSel == DAC_OPA2MUX_POSSEL_DISABLE ) ||
351                 ( init->posSel == DAC_OPA2MUX_POSSEL_POSPAD  ) ||
352                 ( init->posSel == DAC_OPA2MUX_POSSEL_OPA1INP ) ||
353                 ( init->posSel == DAC_OPA2MUX_POSSEL_OPATAP  )    );
354 
355     EFM_ASSERT( ( init->outMode & ~DAC_OPA2MUX_OUTMODE ) == 0 );
356 
357     EFM_ASSERT( ( init->outPen & ~_DAC_OPA2MUX_OUTPEN_MASK ) == 0 );
358 
359     dac->BIASPROG = ( dac->BIASPROG
360                       & ~( _DAC_BIASPROG_OPA2BIASPROG_MASK         |
361                             DAC_BIASPROG_OPA2HALFBIAS                )  ) |
362                     ( init->bias   << _DAC_BIASPROG_OPA2BIASPROG_SHIFT  ) |
363                     ( init->halfBias   ? DAC_BIASPROG_OPA2HALFBIAS  : 0 );
364 
365     if ( init->defaultOffset )
366     {
367       offset = SYSTEM_GetCalibrationValue( &dac->OPAOFFSET );
368       dac->OPAOFFSET = ( dac->OPAOFFSET & ~_DAC_OPAOFFSET_OPA2OFFSET_MASK ) |
369                        ( offset         &  _DAC_OPAOFFSET_OPA2OFFSET_MASK );
370     }
371     else
372     {
373       EFM_ASSERT( init->offset <= ( _DAC_OPAOFFSET_OPA2OFFSET_MASK >>
374                                     _DAC_OPAOFFSET_OPA2OFFSET_SHIFT   ) );
375 
376       dac->CAL = ( dac->CAL      & ~_DAC_OPAOFFSET_OPA2OFFSET_MASK  ) |
377                  ( init->offset <<  _DAC_OPAOFFSET_OPA2OFFSET_SHIFT );
378     }
379 
380     dac->OPA2MUX  = (uint32_t)init->resSel                                |
381                     (uint32_t)init->outMode                               |
382                     init->outPen                                          |
383                     (uint32_t)init->resInMux                              |
384                     (uint32_t)init->negSel                                |
385                     (uint32_t)init->posSel                                |
386                     ( init->nextOut     ? DAC_OPA2MUX_NEXTOUT       : 0 ) |
387                     ( init->npEn        ? DAC_OPA2MUX_NPEN          : 0 ) |
388                     ( init->ppEn        ? DAC_OPA2MUX_PPEN          : 0 );
389 
390     dac->OPACTRL  = ( dac->OPACTRL
391                       & ~(  DAC_OPACTRL_OPA2SHORT                  |
392                            _DAC_OPACTRL_OPA2LPFDIS_MASK            |
393                             DAC_OPACTRL_OPA2HCMDIS                   )  ) |
394                     ( init->shortInputs ?  DAC_OPACTRL_OPA2SHORT    : 0 ) |
395                     ( init->lpfPosPadDisable ?
396                                      DAC_OPACTRL_OPA2LPFDIS_PLPFDIS : 0 ) |
397                     ( init->lpfNegPadDisable ?
398                                      DAC_OPACTRL_OPA2LPFDIS_NLPFDIS : 0 ) |
399                     ( init->hcmDisable  ?  DAC_OPACTRL_OPA2HCMDIS   : 0 ) |
400                     ( DAC_OPACTRL_OPA2EN                                );
401   }
402 }
403 
404 /** @} (end addtogroup OPAMP) */
405 /** @} (end addtogroup EM_Library) */
406 
407 #endif /* defined( OPAMP_PRESENT ) && ( OPAMP_COUNT == 1 ) */
408