1 /***************************************************************************//**
2  * @file
3  * @brief Analog Comparator (ACMP) 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 <stdbool.h>
34 #include "em_acmp.h"
35 #include "em_bitband.h"
36 #include "em_assert.h"
37 
38 /***************************************************************************//**
39  * @addtogroup EM_Library
40  * @{
41  ******************************************************************************/
42 
43 /***************************************************************************//**
44  * @addtogroup ACMP
45  * @brief Analog comparator (ACMP) Peripheral API
46  * @{
47  ******************************************************************************/
48 
49 /*******************************************************************************
50  *******************************   DEFINES   ***********************************
51  ******************************************************************************/
52 
53 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
54 
55 
56 /** Validation of ACMP register block pointer reference
57  *  for assert statements. */
58 #if (ACMP_COUNT == 1)
59 #define ACMP_REF_VALID(ref)    ((ref) == ACMP0)
60 #elif (ACMP_COUNT == 2)
61 #define ACMP_REF_VALID(ref)    (((ref) == ACMP0) || ((ref) == ACMP1))
62 #else
63 #error Undefined number of analog comparators (ACMP).
64 #endif
65 
66 /** @endcond */
67 
68 /*******************************************************************************
69  **************************   GLOBAL FUNCTIONS   *******************************
70  ******************************************************************************/
71 
72 /***************************************************************************//**
73  * @brief
74  *   Sets up the ACMP for use in capacative sense applications.
75  *
76  * @details
77  *   This function sets up the ACMP for use in capacacitve sense applications.
78  *   To use the capacative sense functionality in the ACMP you need to use
79  *   the PRS output of the ACMP module to count the number of oscillations
80  *   in the capacative sense circuit (possibly using a TIMER).
81  *
82  * @note
83  *   A basic example of capacative sensing can be found in the STK BSP
84  *   (capsense demo).
85  *
86  * @param[in] acmp
87  *   Pointer to ACMP peripheral register block.
88  *
89  * @param[in] init
90  *   Pointer to initialization structure used to configure ACMP for capacative
91  *   sensing operation.
92  ******************************************************************************/
ACMP_CapsenseInit(ACMP_TypeDef * acmp,const ACMP_CapsenseInit_TypeDef * init)93 void ACMP_CapsenseInit(ACMP_TypeDef *acmp, const ACMP_CapsenseInit_TypeDef *init)
94 {
95   /* Make sure the module exists on the selected chip */
96   EFM_ASSERT(ACMP_REF_VALID(acmp));
97 
98   /* Make sure that vddLevel is within bounds */
99   EFM_ASSERT(init->vddLevel < 64);
100 
101   /* Make sure biasprog is within bounds */
102   EFM_ASSERT(init->biasProg < 16);
103 
104   /* Set control register. No need to set interrupt modes */
105   acmp->CTRL = (init->fullBias << _ACMP_CTRL_FULLBIAS_SHIFT)
106                | (init->halfBias << _ACMP_CTRL_HALFBIAS_SHIFT)
107                | (init->biasProg << _ACMP_CTRL_BIASPROG_SHIFT)
108                | (init->warmTime << _ACMP_CTRL_WARMTIME_SHIFT)
109                | (init->hysteresisLevel << _ACMP_CTRL_HYSTSEL_SHIFT);
110 
111   /* Select capacative sensing mode by selecting a resistor and enabling it */
112   acmp->INPUTSEL = (init->resistor << _ACMP_INPUTSEL_CSRESSEL_SHIFT)
113                    | ACMP_INPUTSEL_CSRESEN
114                    | (init->lowPowerReferenceEnabled << _ACMP_INPUTSEL_LPREF_SHIFT)
115                    | (init->vddLevel << _ACMP_INPUTSEL_VDDLEVEL_SHIFT)
116                    | ACMP_INPUTSEL_NEGSEL_CAPSENSE;
117 
118   /* Enable ACMP if requested.
119    * Note: BITBAND_Peripheral() function is used for setting/clearing single
120    * bit peripheral register bitfields. */
121   BITBAND_Peripheral(&(acmp->CTRL),
122                      (uint32_t)_ACMP_CTRL_EN_SHIFT,
123                      (uint32_t)init->enable);
124 }
125 
126 /***************************************************************************//**
127  * @brief
128  *   Sets the ACMP channel used for capacative sensing.
129  *
130  * @note
131  *   A basic example of capacative sensing can be found in the STK BSP
132  *   (capsense demo).
133  *
134  * @param[in] acmp
135  *   Pointer to ACMP peripheral register block.
136  *
137  * @param[in] channel
138  *   The ACMP channel to use for capacative sensing (Possel).
139  ******************************************************************************/
ACMP_CapsenseChannelSet(ACMP_TypeDef * acmp,ACMP_Channel_TypeDef channel)140 void ACMP_CapsenseChannelSet(ACMP_TypeDef *acmp, ACMP_Channel_TypeDef channel)
141 {
142   /* Make sure that only external channels are used */
143   EFM_ASSERT(channel < _ACMP_INPUTSEL_NEGSEL_1V25);
144 
145   /* Set channel as positive channel in ACMP */
146   SET_BIT_FIELD(acmp->INPUTSEL, _ACMP_INPUTSEL_POSSEL_MASK, channel,
147                 _ACMP_INPUTSEL_POSSEL_SHIFT);
148 }
149 
150 /***************************************************************************//**
151  * @brief
152  *   Disables the ACMP.
153  *
154  * @param[in] acmp
155  *   Pointer to ACMP peripheral register block.
156  ******************************************************************************/
ACMP_Disable(ACMP_TypeDef * acmp)157 void ACMP_Disable(ACMP_TypeDef *acmp)
158 {
159   acmp->CTRL &= ~ACMP_CTRL_EN;
160 }
161 
162 /***************************************************************************//**
163  * @brief
164  *   Enables the ACMP.
165  *
166  * @param[in] acmp
167  *   Pointer to ACMP peripheral register block.
168  ******************************************************************************/
ACMP_Enable(ACMP_TypeDef * acmp)169 void ACMP_Enable(ACMP_TypeDef *acmp)
170 {
171   acmp->CTRL |= ACMP_CTRL_EN;
172 }
173 
174 /***************************************************************************//**
175  * @brief
176  *   Reset ACMP to same state as after a HW reset.
177  *
178  * @note
179  *   The ROUTE register is NOT reset by this function, in order to allow for
180  *   centralized setup of this feature.
181  *
182  * @param[in] acmp
183  *   Pointer to the ACMP peripheral register block.
184  ******************************************************************************/
ACMP_Reset(ACMP_TypeDef * acmp)185 void ACMP_Reset(ACMP_TypeDef *acmp)
186 {
187   /* Make sure the module exists on the selected chip */
188   EFM_ASSERT(ACMP_REF_VALID(acmp));
189 
190   acmp->CTRL     = _ACMP_CTRL_RESETVALUE;
191   acmp->INPUTSEL = _ACMP_INPUTSEL_RESETVALUE;
192   acmp->IEN      = _ACMP_IEN_RESETVALUE;
193   acmp->IFC      = _ACMP_IF_MASK;
194 }
195 
196 /***************************************************************************//**
197  * @brief
198  *   Sets up GPIO output from the ACMP.
199  *
200  * @note
201  *    GPIO must be enabled in the CMU before this function call, i.e.
202  * @verbatim CMU_ClockEnable(cmuClock_GPIO, true); @endverbatim
203  *
204  * @param[in] acmp
205  *   Pointer to the ACMP peripheral register block.
206  *
207  * @param location
208  *   The pin location to use. See the datasheet for location to pin mappings.
209  *
210  * @param enable
211  *   Enable or disable pin output.
212  *
213  * @param invert
214  *   Invert output.
215  ******************************************************************************/
ACMP_GPIOSetup(ACMP_TypeDef * acmp,uint32_t location,bool enable,bool invert)216 void ACMP_GPIOSetup(ACMP_TypeDef *acmp, uint32_t location, bool enable, bool invert)
217 {
218   /* Sanity checking of location */
219   EFM_ASSERT(location < 4);
220 
221   /* Set GPIO inversion */
222   SET_BIT_FIELD(acmp->CTRL, _ACMP_CTRL_GPIOINV_MASK, invert,
223                 _ACMP_CTRL_GPIOINV_SHIFT);
224 
225   acmp->ROUTE = (location << _ACMP_ROUTE_LOCATION_SHIFT)
226                 | (enable << _ACMP_ROUTE_ACMPPEN_SHIFT);
227 }
228 
229 /***************************************************************************//**
230  * @brief
231  *   Sets which channels should be used in ACMP comparisons.
232  *
233  * @param[in] acmp
234  *   Pointer to the ACMP peripheral register block.
235  *
236  * @param negSel
237  *   Channel to use on the negative input to the ACMP.
238  *
239  * @param posSel
240  *   Channel to use on the positive input to the ACMP.
241  ******************************************************************************/
ACMP_ChannelSet(ACMP_TypeDef * acmp,ACMP_Channel_TypeDef negSel,ACMP_Channel_TypeDef posSel)242 void ACMP_ChannelSet(ACMP_TypeDef *acmp, ACMP_Channel_TypeDef negSel,
243                      ACMP_Channel_TypeDef posSel)
244 {
245   /* Make sure that only external channels are used as ACMP positive input */
246   EFM_ASSERT(posSel < _ACMP_INPUTSEL_NEGSEL_1V25);
247   /* Sanity checking of ACMP negative input */
248   EFM_ASSERT(negSel <= _ACMP_INPUTSEL_NEGSEL_VDD);
249 
250   acmp->INPUTSEL = (acmp->INPUTSEL & ~(_ACMP_INPUTSEL_POSSEL_MASK |
251                                        _ACMP_INPUTSEL_NEGSEL_MASK))
252                    | (negSel << _ACMP_INPUTSEL_NEGSEL_SHIFT)
253                    | (posSel << _ACMP_INPUTSEL_POSSEL_SHIFT);
254 }
255 
256 /***************************************************************************//**
257  * @brief
258  *
259  *
260  * @param[in] acmp
261  *   Pointer to the ACMP peripheral register block.
262  *
263  * @param[in] init
264  *   Pointer to initialization structure used to configure ACMP for capacative
265  *   sensing operation.
266  ******************************************************************************/
ACMP_Init(ACMP_TypeDef * acmp,const ACMP_Init_TypeDef * init)267 void ACMP_Init(ACMP_TypeDef *acmp, const ACMP_Init_TypeDef *init)
268 {
269   /* Make sure the module exists on the selected chip */
270   EFM_ASSERT(ACMP_REF_VALID(acmp));
271 
272   /* Make sure biasprog is within bounds */
273   EFM_ASSERT(init->biasProg < 16);
274 
275   /* Set control register. No need to set interrupt modes */
276   acmp->CTRL = (init->fullBias << _ACMP_CTRL_FULLBIAS_SHIFT)
277                | (init->halfBias << _ACMP_CTRL_HALFBIAS_SHIFT)
278                | (init->biasProg << _ACMP_CTRL_BIASPROG_SHIFT)
279                | (init->interruptOnFallingEdge << _ACMP_CTRL_IFALL_SHIFT)
280                | (init->interruptOnRisingEdge << _ACMP_CTRL_IRISE_SHIFT)
281                | (init->warmTime << _ACMP_CTRL_WARMTIME_SHIFT)
282                | (init->hysteresisLevel << _ACMP_CTRL_HYSTSEL_SHIFT)
283                | (init->inactiveValue << _ACMP_CTRL_INACTVAL_SHIFT);
284 
285   acmp->INPUTSEL = (init->lowPowerReferenceEnabled << _ACMP_INPUTSEL_LPREF_SHIFT)
286                    | (init->vddLevel << _ACMP_INPUTSEL_VDDLEVEL_SHIFT);
287 
288   /* Enable ACMP if requested.
289    * Note: BITBAND_Peripheral() function is used for setting/clearing single
290    * bit peripheral register bitfields. */
291   BITBAND_Peripheral(&(acmp->CTRL),
292                      (uint32_t)_ACMP_CTRL_EN_SHIFT,
293                      (uint32_t)init->enable);
294 }
295 
296 
297 /** @} (end addtogroup ACMP) */
298 /** @} (end addtogroup EM_Library) */
299