1 /***************************************************************************//**
2 * @file
3 * @brief Analog Comparator (ACMP) 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_acmp.h"
32 #if defined(ACMP_COUNT) && (ACMP_COUNT > 0)
33
34 #include <stdbool.h>
35 #include "em_bus.h"
36 #include "sl_assert.h"
37 #include "em_gpio.h"
38
39 /***************************************************************************//**
40 * @addtogroup acmp ACMP - Analog Comparator
41 * @brief Analog comparator (ACMP) Peripheral API
42 *
43 * @details
44 * The Analog Comparator is used to compare voltage of two analog inputs
45 * with a digital output indicating which input voltage is higher. Inputs can
46 * either be one of the selectable internal references or from external pins.
47 * Response time and current consumption can be configured by
48 * altering the current supply to the comparator.
49 *
50 * ACMP is available down to EM3 and is able to wake up the system when
51 * input signals pass a certain threshold. Use @ref ACMP_IntEnable() to enable
52 * an edge interrupt to use this functionality.
53 *
54 * This example shows how to use the em_acmp.h API for comparing an input
55 * pin to an internal 2.5 V reference voltage.
56 *
57 * @if DOXYDOC_P1_DEVICE
58 * @include em_acmp_compare_s0.c
59 * @endif
60 *
61 * @if DOXYDOC_P2_DEVICE
62 * @include em_acmp_compare_s1.c
63 * @endif
64 *
65 * @if DOXYDOC_S2_DEVICE
66 * @include em_acmp_compare_s2.c
67 * @endif
68 *
69 * @note
70 * ACMP can also be used to compare two separate input pins.
71 *
72 * @details
73 * ACMP also contains specialized hardware for capacitive sensing. This
74 * module contains the @ref ACMP_CapsenseInit() function to initialize
75 * ACMP for capacitive sensing and the @ref ACMP_CapsenseChannelSet() function
76 * to select the current capsense channel.
77 *
78 * For applications that require capacitive sensing it is recommended to use a
79 * library, such as cslib, which is provided by Silicon Labs.
80 *
81 * @{
82 ******************************************************************************/
83
84 /*******************************************************************************
85 ******************************* DEFINES ***********************************
86 ******************************************************************************/
87
88 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
89
90 /** Validation of ACMP register block pointer reference
91 * for assert statements. */
92 #if (ACMP_COUNT == 1)
93 #define ACMP_REF_VALID(ref) ((ref) == ACMP0)
94 #elif (ACMP_COUNT == 2)
95 #define ACMP_REF_VALID(ref) (((ref) == ACMP0) || ((ref) == ACMP1))
96 #elif (ACMP_COUNT == 3)
97 #define ACMP_REF_VALID(ref) (((ref) == ACMP0) || ((ref) == ACMP1) || ((ref) == ACMP2))
98 #elif (ACMP_COUNT == 4)
99 #define ACMP_REF_VALID(ref) (((ref) == ACMP0) \
100 || ((ref) == ACMP1) \
101 || ((ref) == ACMP2) \
102 || ((ref) == ACMP3))
103 #else
104 #error Undefined number of analog comparators (ACMP).
105 #endif
106
107 /** The maximum value that can be inserted in the route location register
108 * for the specific device. */
109 #if defined(_ACMP_ROUTE_LOCATION_LOC3)
110 #define _ACMP_ROUTE_LOCATION_MAX _ACMP_ROUTE_LOCATION_LOC3
111 #elif defined(_ACMP_ROUTE_LOCATION_LOC2)
112 #define _ACMP_ROUTE_LOCATION_MAX _ACMP_ROUTE_LOCATION_LOC2
113 #elif defined(_ACMP_ROUTE_LOCATION_LOC1)
114 #define _ACMP_ROUTE_LOCATION_MAX _ACMP_ROUTE_LOCATION_LOC1
115 #elif defined(_ACMP_ROUTELOC0_OUTLOC_LOC31)
116 #define _ACMP_ROUTE_LOCATION_MAX _ACMP_ROUTELOC0_OUTLOC_LOC31
117 #elif defined(_ACMP_ROUTELOC0_OUTLOC_MASK)
118 #define _ACMP_ROUTE_LOCATION_MAX _ACMP_ROUTELOC0_OUTLOC_MASK
119 #endif
120
121 /** Map ACMP reference to index of device. */
122 #if (ACMP_COUNT == 1)
123 #define ACMP_DEVICE_ID(acmp) ( \
124 (acmp) == ACMP0 ? 0 \
125 : 0)
126 #elif (ACMP_COUNT == 2)
127 #define ACMP_DEVICE_ID(acmp) ( \
128 (acmp) == ACMP0 ? 0 \
129 : (acmp) == ACMP1 ? 1 \
130 : 0)
131 #endif
132
133 /** @endcond */
134
135 /*******************************************************************************
136 ************************** GLOBAL FUNCTIONS *******************************
137 ******************************************************************************/
138
139 /***************************************************************************//**
140 * @brief
141 * Set up ACMP for use in capacitive sense applications.
142 *
143 * @details
144 * This function sets up ACMP for use in capacitive sense applications.
145 * To use the capacitive sense functionality in the ACMP, use
146 * the PRS output of the ACMP module to count the number of oscillations
147 * in the capacitive sense circuit (possibly using a TIMER).
148 *
149 * @note
150 * A basic example of capacitive sensing can be found in the STK BSP
151 * (capsense demo).
152 *
153 * @cond DOXYDOC_S2_DEVICE
154 * @note
155 * A call to ACMP_CapsenseInit will enable and disable the ACMP peripheral,
156 * which can cause side effects if it was previously set up.
157 * @endcond
158 *
159 * @param[in] acmp
160 * A pointer to the ACMP peripheral register block.
161 *
162 * @param[in] init
163 * A pointer to the initialization structure used to configure ACMP for capacitive
164 * sensing operation.
165 ******************************************************************************/
ACMP_CapsenseInit(ACMP_TypeDef * acmp,const ACMP_CapsenseInit_TypeDef * init)166 void ACMP_CapsenseInit(ACMP_TypeDef *acmp, const ACMP_CapsenseInit_TypeDef *init)
167 {
168 EFM_ASSERT(ACMP_REF_VALID(acmp));
169
170 #if defined(_SILICON_LABS_32B_SERIES_2)
171 EFM_ASSERT(init->vrefDiv < 64);
172 EFM_ASSERT(init->biasProg
173 <= (_ACMP_CFG_BIAS_MASK >> _ACMP_CFG_BIAS_SHIFT));
174
175 ACMP_Disable(acmp);
176 acmp->CFG = (init->biasProg << _ACMP_CFG_BIAS_SHIFT)
177 | (init->hysteresisLevel << _ACMP_CFG_HYST_SHIFT);
178 acmp->CTRL = _ACMP_CTRL_RESETVALUE;
179 ACMP_Enable(acmp);
180 acmp->INPUTCTRL = (init->resistor << _ACMP_INPUTCTRL_CSRESSEL_SHIFT)
181 | (init->vrefDiv << _ACMP_INPUTCTRL_VREFDIV_SHIFT)
182 | (ACMP_INPUTCTRL_NEGSEL_CAPSENSE);
183 if (!init->enable) {
184 ACMP_Disable(acmp);
185 }
186
187 #elif defined(_SILICON_LABS_32B_SERIES_1)
188 EFM_ASSERT(init->vddLevelLow < 64);
189 EFM_ASSERT(init->vddLevelHigh < 64);
190 EFM_ASSERT(init->biasProg
191 <= (_ACMP_CTRL_BIASPROG_MASK >> _ACMP_CTRL_BIASPROG_SHIFT));
192
193 /* Set the control register. No need to set interrupt modes. */
194 acmp->CTRL = (init->fullBias << _ACMP_CTRL_FULLBIAS_SHIFT)
195 | (init->biasProg << _ACMP_CTRL_BIASPROG_SHIFT)
196 | ACMP_CTRL_ACCURACY_HIGH;
197 acmp->HYSTERESIS0 = (init->vddLevelHigh << _ACMP_HYSTERESIS0_DIVVA_SHIFT)
198 | (init->hysteresisLevel_0 << _ACMP_HYSTERESIS0_HYST_SHIFT);
199 acmp->HYSTERESIS1 = (init->vddLevelLow << _ACMP_HYSTERESIS1_DIVVA_SHIFT)
200 | (init->hysteresisLevel_1 << _ACMP_HYSTERESIS1_HYST_SHIFT);
201 /* Select capacitive sensing mode by selecting a resistor and enabling it. */
202 acmp->INPUTSEL = (init->resistor << _ACMP_INPUTSEL_CSRESSEL_SHIFT)
203 | ACMP_INPUTSEL_CSRESEN
204 | ACMP_INPUTSEL_VASEL_VDD
205 | ACMP_INPUTSEL_NEGSEL_VADIV;
206 BUS_RegBitWrite(&acmp->CTRL, _ACMP_CTRL_EN_SHIFT, init->enable);
207
208 #elif defined(_SILICON_LABS_32B_SERIES_0)
209 EFM_ASSERT(init->vddLevel < 64);
210 EFM_ASSERT(init->biasProg
211 <= (_ACMP_CTRL_BIASPROG_MASK >> _ACMP_CTRL_BIASPROG_SHIFT));
212
213 /* Set the control register. No need to set interrupt modes. */
214 acmp->CTRL = (init->fullBias << _ACMP_CTRL_FULLBIAS_SHIFT)
215 | (init->halfBias << _ACMP_CTRL_HALFBIAS_SHIFT)
216 | (init->biasProg << _ACMP_CTRL_BIASPROG_SHIFT)
217 | (init->warmTime << _ACMP_CTRL_WARMTIME_SHIFT)
218 | (init->hysteresisLevel << _ACMP_CTRL_HYSTSEL_SHIFT);
219 /* Select capacitive sensing mode by selecting a resistor and enabling it. */
220 acmp->INPUTSEL = (init->resistor << _ACMP_INPUTSEL_CSRESSEL_SHIFT)
221 | ACMP_INPUTSEL_CSRESEN
222 | (init->lowPowerReferenceEnabled << _ACMP_INPUTSEL_LPREF_SHIFT)
223 | (init->vddLevel << _ACMP_INPUTSEL_VDDLEVEL_SHIFT)
224 | ACMP_INPUTSEL_NEGSEL_CAPSENSE;
225 BUS_RegBitWrite(&acmp->CTRL, _ACMP_CTRL_EN_SHIFT, init->enable);
226 #endif
227 }
228
229 /***************************************************************************//**
230 * @brief
231 * Set the ACMP channel used for capacitive sensing.
232 *
233 * @note
234 * A basic example of capacitive sensing can be found in the STK BSP
235 * (capsense demo).
236 *
237 * @cond DOXYDOC_S2_DEVICE
238 * @note
239 * Can only be called when the peripheral is enabled.
240 * @endcond
241 *
242 * @param[in] acmp
243 * A pointer to the ACMP peripheral register block.
244 *
245 * @param[in] channel
246 * The ACMP channel to use for capacitive sensing (Possel).
247 ******************************************************************************/
ACMP_CapsenseChannelSet(ACMP_TypeDef * acmp,ACMP_Channel_TypeDef channel)248 void ACMP_CapsenseChannelSet(ACMP_TypeDef *acmp, ACMP_Channel_TypeDef channel)
249 {
250 /* Make sure the module exists on the selected chip. */
251 EFM_ASSERT(ACMP_REF_VALID(acmp));
252
253 #if defined(_ACMP_INPUTSEL_POSSEL_CH7)
254 /* Make sure that only external channels are used. */
255 EFM_ASSERT(channel <= _ACMP_INPUTSEL_POSSEL_CH7);
256 #elif defined(_ACMP_INPUTCTRL_POSSEL_PD15)
257 EFM_ASSERT(channel != _ACMP_INPUTCTRL_NEGSEL_CAPSENSE);
258 EFM_ASSERT(_ACMP_INPUTCTRL_POSSEL_PA0 <= channel);
259 EFM_ASSERT(channel <= _ACMP_INPUTCTRL_POSSEL_PD15);
260 #endif
261
262 #if defined(_ACMP_INPUTCTRL_MASK)
263 /* Make sure that the ACMP is enabled before changing INPUTCTRL. */
264 EFM_ASSERT(acmp->EN & ACMP_EN_EN);
265
266 while (acmp->SYNCBUSY != 0U) {
267 /* Wait for synchronization to finish */
268 }
269 /* Set channel as positive channel in ACMP */
270 BUS_RegMaskedWrite(&acmp->INPUTCTRL, _ACMP_INPUTCTRL_POSSEL_MASK,
271 channel << _ACMP_INPUTCTRL_POSSEL_SHIFT);
272 #else
273 /* Set channel as a positive channel in ACMP. */
274 BUS_RegMaskedWrite(&acmp->INPUTSEL, _ACMP_INPUTSEL_POSSEL_MASK,
275 channel << _ACMP_INPUTSEL_POSSEL_SHIFT);
276 #endif
277 }
278
279 /***************************************************************************//**
280 * @brief
281 * Disable ACMP.
282 *
283 * @param[in] acmp
284 * A pointer to the ACMP peripheral register block.
285 ******************************************************************************/
ACMP_Disable(ACMP_TypeDef * acmp)286 void ACMP_Disable(ACMP_TypeDef *acmp)
287 {
288 /* Make sure the module exists on the selected chip. */
289 EFM_ASSERT(ACMP_REF_VALID(acmp));
290
291 #if defined(_ACMP_EN_MASK)
292 while ((acmp->EN != 0U) && (acmp->SYNCBUSY != 0U)) {
293 /* Wait for synchronization to finish */
294 }
295 acmp->EN_CLR = ACMP_EN_EN;
296
297 #if defined(_ACMP_EN_DISABLING_MASK)
298 while (acmp->EN & _ACMP_EN_DISABLING_MASK) {
299 // Wait for disabling to finish
300 }
301 #endif
302
303 #else
304 acmp->CTRL &= ~ACMP_CTRL_EN;
305 #endif
306 }
307
308 /***************************************************************************//**
309 * @brief
310 * Enable ACMP.
311 *
312 * @param[in] acmp
313 * A pointer to the ACMP peripheral register block.
314 ******************************************************************************/
ACMP_Enable(ACMP_TypeDef * acmp)315 void ACMP_Enable(ACMP_TypeDef *acmp)
316 {
317 /* Make sure the module exists on the selected chip. */
318 EFM_ASSERT(ACMP_REF_VALID(acmp));
319
320 #if defined(_ACMP_EN_MASK)
321 acmp->EN_SET = ACMP_EN_EN;
322 #else
323 acmp->CTRL |= ACMP_CTRL_EN;
324 #endif
325 }
326
327 #if defined(_ACMP_EXTIFCTRL_MASK)
328 /***************************************************************************//**
329 * @brief
330 * Select and enable external input.
331 *
332 * @details
333 * This is used when an external module needs to take control of the ACMP
334 * POSSEL field to configure the APORT input for the ACMP. Modules,
335 * such as LESENSE, use this to change the ACMP input during a scan sequence.
336 *
337 * @param[in] acmp
338 * A pointer to the ACMP peripheral register block.
339 *
340 * @param[in] aport
341 * This parameter decides which APORT(s) the ACMP will use when it's
342 * controlled by an external module.
343 ******************************************************************************/
ACMP_ExternalInputSelect(ACMP_TypeDef * acmp,ACMP_ExternalInput_Typedef aport)344 void ACMP_ExternalInputSelect(ACMP_TypeDef *acmp, ACMP_ExternalInput_Typedef aport)
345 {
346 acmp->EXTIFCTRL = (aport << _ACMP_EXTIFCTRL_APORTSEL_SHIFT)
347 | ACMP_EXTIFCTRL_EN;
348 while (!(acmp->STATUS & ACMP_STATUS_EXTIFACT)) {
349 }
350 }
351 #endif
352
353 /***************************************************************************//**
354 * @brief
355 * Reset ACMP to the same state that it was in after a hardware reset.
356 *
357 * @note
358 * The GPIO ACMP ROUTE register is NOT reset by this function to allow for
359 * centralized setup of this feature.
360 *
361 * @note
362 * The peripheral may be enabled and disabled during reset.
363 *
364 * @param[in] acmp
365 * A pointer to the ACMP peripheral register block.
366 ******************************************************************************/
ACMP_Reset(ACMP_TypeDef * acmp)367 void ACMP_Reset(ACMP_TypeDef *acmp)
368 {
369 /* Make sure the module exists on the selected chip */
370 EFM_ASSERT(ACMP_REF_VALID(acmp));
371
372 #if defined(_SILICON_LABS_32B_SERIES_2)
373 #if defined(ACMP_SWRST_SWRST)
374 acmp->SWRST_SET = ACMP_SWRST_SWRST;
375 while (acmp->SWRST & _ACMP_SWRST_RESETTING_MASK) {
376 }
377 #else
378 acmp->IEN = _ACMP_IEN_RESETVALUE;
379 ACMP_Enable(acmp);
380 acmp->INPUTCTRL = _ACMP_INPUTCTRL_RESETVALUE;
381 ACMP_Disable(acmp);
382 acmp->CFG = PM5507_ACMP_CFG_RESETVALUE;
383 acmp->CTRL = _ACMP_CTRL_RESETVALUE;
384 acmp->IF_CLR = _ACMP_IF_MASK;
385 #endif
386 #else // Series 0 and Series 1 devices
387 acmp->IEN = _ACMP_IEN_RESETVALUE;
388 acmp->CTRL = _ACMP_CTRL_RESETVALUE;
389 acmp->INPUTSEL = _ACMP_INPUTSEL_RESETVALUE;
390 #if defined(_ACMP_HYSTERESIS0_HYST_MASK)
391 acmp->HYSTERESIS0 = _ACMP_HYSTERESIS0_RESETVALUE;
392 acmp->HYSTERESIS1 = _ACMP_HYSTERESIS1_RESETVALUE;
393 #endif
394 acmp->IFC = _ACMP_IF_MASK;
395 #endif
396 }
397
398 #if defined(_GPIO_ACMP_ROUTEEN_MASK)
399 /***************************************************************************//**
400 * @brief
401 * Sets up GPIO output from the ACMP.
402 *
403 * @note
404 * GPIO must be enabled in the CMU before this function call, i.e.
405 * @verbatim CMU_ClockEnable(cmuClock_GPIO, true); @endverbatim
406 *
407 * @param[in] acmp
408 * Pointer to the ACMP peripheral register block.
409 *
410 * @param port
411 * The GPIO port to use.
412 *
413 * @param pin
414 * The GPIO pin to use.
415 *
416 * @param enable
417 * Enable or disable pin output.
418 *
419 * @param invert
420 * Invert output.
421 ******************************************************************************/
ACMP_GPIOSetup(ACMP_TypeDef * acmp,GPIO_Port_TypeDef port,unsigned int pin,bool enable,bool invert)422 void ACMP_GPIOSetup(ACMP_TypeDef *acmp, GPIO_Port_TypeDef port,
423 unsigned int pin, bool enable, bool invert)
424 {
425 int acmpIndex = ACMP_DEVICE_ID(acmp);
426
427 /* Make sure the module exists on the selected chip */
428 EFM_ASSERT(ACMP_REF_VALID(acmp));
429
430 /* Make sure that the port/pin combination is valid. */
431 EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
432
433 /* Set GPIO inversion */
434 acmp->CTRL = (acmp->CTRL & _ACMP_CTRL_NOTRDYVAL_MASK)
435 | (invert << _ACMP_CTRL_GPIOINV_SHIFT);
436
437 GPIO->ACMPROUTE[acmpIndex].ACMPOUTROUTE = (port << _GPIO_ACMP_ACMPOUTROUTE_PORT_SHIFT)
438 | (pin << _GPIO_ACMP_ACMPOUTROUTE_PIN_SHIFT);
439 GPIO->ACMPROUTE[acmpIndex].ROUTEEN = enable ? GPIO_ACMP_ROUTEEN_ACMPOUTPEN : 0;
440 }
441 #else
442 /***************************************************************************//**
443 * @brief
444 * Set up GPIO output from ACMP.
445 *
446 * @note
447 * GPIO must be enabled in the CMU before this function call, i.e.,
448 * @verbatim CMU_ClockEnable(cmuClock_GPIO, true); @endverbatim
449 *
450 * @param[in] acmp
451 * A pointer to the ACMP peripheral register block.
452 *
453 * @param location
454 * The pin location to use. See the data sheet for location to pin mappings.
455 *
456 * @param enable
457 * Enable or disable pin output.
458 *
459 * @param invert
460 * Invert output.
461 ******************************************************************************/
ACMP_GPIOSetup(ACMP_TypeDef * acmp,uint32_t location,bool enable,bool invert)462 void ACMP_GPIOSetup(ACMP_TypeDef *acmp, uint32_t location, bool enable, bool invert)
463 {
464 /* Make sure the module exists on the selected chip */
465 EFM_ASSERT(ACMP_REF_VALID(acmp));
466
467 /* Sanity checking of location */
468 EFM_ASSERT(location <= _ACMP_ROUTE_LOCATION_MAX);
469
470 /* Set GPIO inversion */
471 BUS_RegMaskedWrite(&acmp->CTRL, _ACMP_CTRL_GPIOINV_MASK,
472 invert << _ACMP_CTRL_GPIOINV_SHIFT);
473
474 #if defined(_ACMP_ROUTE_MASK)
475 acmp->ROUTE = (location << _ACMP_ROUTE_LOCATION_SHIFT)
476 | (enable << _ACMP_ROUTE_ACMPPEN_SHIFT);
477 #endif
478 #if defined(_ACMP_ROUTELOC0_MASK)
479 acmp->ROUTELOC0 = location << _ACMP_ROUTELOC0_OUTLOC_SHIFT;
480 acmp->ROUTEPEN = enable ? ACMP_ROUTEPEN_OUTPEN : 0;
481 #endif
482 }
483 #endif /* defined(_GPIO_ACMP_ROUTEEN_MASK) */
484
485 /***************************************************************************//**
486 * @brief
487 * Set which channels should be used in ACMP comparisons.
488 *
489 * @cond DOXYDOC_S2_DEVICE
490 * @note
491 * Can only be called when the peripheral is enabled.
492 *
493 * @note
494 * If GPIO is used for both posSel and negSel, they cannot both use even
495 * or odd pins.
496 * @endcond
497 *
498 * @param[in] acmp
499 * A pointer to the ACMP peripheral register block.
500 *
501 * @param negSel
502 * A channel to use on the negative input to the ACMP.
503 *
504 * @param posSel
505 * A channel to use on the positive input to the ACMP.
506 ******************************************************************************/
ACMP_ChannelSet(ACMP_TypeDef * acmp,ACMP_Channel_TypeDef negSel,ACMP_Channel_TypeDef posSel)507 void ACMP_ChannelSet(ACMP_TypeDef *acmp, ACMP_Channel_TypeDef negSel,
508 ACMP_Channel_TypeDef posSel)
509 {
510 /* Make sure the module exists on the selected chip. */
511 EFM_ASSERT(ACMP_REF_VALID(acmp));
512
513 /* Make sure that posSel and negSel channel selectors are valid. */
514 #if defined(_ACMP_INPUTSEL_NEGSEL_DAC0CH1)
515 EFM_ASSERT(negSel <= _ACMP_INPUTSEL_NEGSEL_DAC0CH1);
516 #elif defined(_ACMP_INPUTSEL_NEGSEL_CAPSENSE)
517 EFM_ASSERT(negSel <= _ACMP_INPUTSEL_NEGSEL_CAPSENSE);
518 #endif
519
520 #if defined(_ACMP_INPUTSEL_POSSEL_CH7)
521 EFM_ASSERT(posSel <= _ACMP_INPUTSEL_POSSEL_CH7);
522 #endif
523
524 /* Make sure that posSel and negSel channel selectors are valid. */
525 #if defined(_ACMP_INPUTCTRL_POSSEL_PD15)
526 EFM_ASSERT(negSel <= _ACMP_INPUTCTRL_POSSEL_PD15);
527 EFM_ASSERT(posSel <= _ACMP_INPUTCTRL_POSSEL_PD15);
528 EFM_ASSERT(posSel != _ACMP_INPUTCTRL_NEGSEL_CAPSENSE);
529
530 /* Make sure that posSel and negSel channel selectors don't both
531 * use odd or even pins. */
532 #if (_SILICON_LABS_32B_SERIES_2_CONFIG > 2)
533 EFM_ASSERT(!((((posSel >= _ACMP_INPUTCTRL_POSSEL_EXTPA)
534 && (posSel <= _ACMP_INPUTCTRL_POSSEL_EXTPD))
535 || (posSel >= _ACMP_INPUTCTRL_POSSEL_PA0))
536 && (negSel >= _ACMP_INPUTCTRL_NEGSEL_PA0)
537 && (posSel % 2 == negSel % 2)));
538 #else
539 EFM_ASSERT(!((posSel >= _ACMP_INPUTCTRL_POSSEL_PA0)
540 && (negSel >= _ACMP_INPUTCTRL_NEGSEL_PA0)
541 && (posSel % 2 == negSel % 2)));
542
543 #endif
544 #endif
545
546 #if defined(_ACMP_INPUTCTRL_MASK)
547 /* Make sure that the ACMP is enabled before changing INPUTCTRL. */
548 EFM_ASSERT(acmp->EN & ACMP_EN_EN);
549 while (acmp->SYNCBUSY != 0U) {
550 /* Wait for synchronization to finish */
551 }
552 acmp->INPUTCTRL = (acmp->INPUTCTRL & ~(_ACMP_INPUTCTRL_POSSEL_MASK
553 | _ACMP_INPUTCTRL_NEGSEL_MASK))
554 | (negSel << _ACMP_INPUTCTRL_NEGSEL_SHIFT)
555 | (posSel << _ACMP_INPUTCTRL_POSSEL_SHIFT);
556 #else
557 acmp->INPUTSEL = (acmp->INPUTSEL & ~(_ACMP_INPUTSEL_POSSEL_MASK
558 | _ACMP_INPUTSEL_NEGSEL_MASK))
559 | (negSel << _ACMP_INPUTSEL_NEGSEL_SHIFT)
560 | (posSel << _ACMP_INPUTSEL_POSSEL_SHIFT);
561 #endif
562 }
563
564 /***************************************************************************//**
565 * @brief
566 * Initialize ACMP.
567 *
568 * @cond DOXYDOC_S2_DEVICE
569 * @note
570 * A call to ACMP_Init can cause side effects since it can enable/disable
571 * the peripheral.
572 * @endcond
573 *
574 * @param[in] acmp
575 * A pointer to the ACMP peripheral register block.
576 *
577 * @param[in] init
578 * A pointer to the initialization structure used to configure ACMP.
579 ******************************************************************************/
ACMP_Init(ACMP_TypeDef * acmp,const ACMP_Init_TypeDef * init)580 void ACMP_Init(ACMP_TypeDef *acmp, const ACMP_Init_TypeDef *init)
581 {
582 /* Make sure the module exists on the selected chip. */
583 EFM_ASSERT(ACMP_REF_VALID(acmp));
584
585 #if defined(_SILICON_LABS_32B_SERIES_2)
586 EFM_ASSERT(init->biasProg
587 <= (_ACMP_CFG_BIAS_MASK >> _ACMP_CFG_BIAS_SHIFT));
588
589 // PM-5507: enforce that biasProg is a functional value
590 #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
591 EFM_ASSERT(init->biasProg >= 4);
592 #elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_3)
593 // Allow customer to use BIASPROG in [2; 3]
594 EFM_ASSERT(init->biasProg >= 2);
595 #else
596 // Allow customer to use BIASPROG in [0; 3]
597 // but the implementation of the wait operation would be their responsibility
598 #endif
599
600 /* Make sure the ACMP is disabled since ACMP power source might be changed.*/
601 ACMP_Disable(acmp);
602
603 acmp->CFG = (init->biasProg << _ACMP_CFG_BIAS_SHIFT)
604 | (init->inputRange << _ACMP_CFG_INPUTRANGE_SHIFT)
605 | (init->accuracy << _ACMP_CFG_ACCURACY_SHIFT)
606 | (init->hysteresisLevel << _ACMP_CFG_HYST_SHIFT);
607 acmp->CTRL = init->inactiveValue << _ACMP_CTRL_NOTRDYVAL_SHIFT;
608 ACMP_Enable(acmp);
609 BUS_RegMaskedWrite(&acmp->INPUTCTRL, _ACMP_INPUTCTRL_VREFDIV_MASK,
610 init->vrefDiv << _ACMP_INPUTCTRL_VREFDIV_SHIFT);
611
612 #elif defined(_SILICON_LABS_32B_SERIES_1)
613 EFM_ASSERT(init->biasProg
614 <= (_ACMP_CTRL_BIASPROG_MASK >> _ACMP_CTRL_BIASPROG_SHIFT));
615 /* Make sure the ACMP is disabled since ACMP power source might be changed.*/
616 ACMP_Disable(acmp);
617
618 acmp->CTRL = (init->fullBias << _ACMP_CTRL_FULLBIAS_SHIFT)
619 | (init->biasProg << _ACMP_CTRL_BIASPROG_SHIFT)
620 | (init->interruptOnFallingEdge << _ACMP_CTRL_IFALL_SHIFT)
621 | (init->interruptOnRisingEdge << _ACMP_CTRL_IRISE_SHIFT)
622 | (init->inputRange << _ACMP_CTRL_INPUTRANGE_SHIFT)
623 | (init->accuracy << _ACMP_CTRL_ACCURACY_SHIFT)
624 | (init->powerSource << _ACMP_CTRL_PWRSEL_SHIFT)
625 | (init->inactiveValue << _ACMP_CTRL_INACTVAL_SHIFT);
626 acmp->INPUTSEL = init->vlpInput << _ACMP_INPUTSEL_VLPSEL_SHIFT;
627 acmp->HYSTERESIS0 = init->hysteresisLevel_0;
628 acmp->HYSTERESIS1 = init->hysteresisLevel_1;
629
630 #elif defined(_SILICON_LABS_32B_SERIES_0)
631 EFM_ASSERT(init->biasProg
632 <= (_ACMP_CTRL_BIASPROG_MASK >> _ACMP_CTRL_BIASPROG_SHIFT));
633 /* Make sure the ACMP is disabled since ACMP power source might be changed.*/
634 ACMP_Disable(acmp);
635
636 acmp->CTRL = (init->fullBias << _ACMP_CTRL_FULLBIAS_SHIFT)
637 | (init->halfBias << _ACMP_CTRL_HALFBIAS_SHIFT)
638 | (init->biasProg << _ACMP_CTRL_BIASPROG_SHIFT)
639 | (init->interruptOnFallingEdge << _ACMP_CTRL_IFALL_SHIFT)
640 | (init->interruptOnRisingEdge << _ACMP_CTRL_IRISE_SHIFT)
641 | (init->warmTime << _ACMP_CTRL_WARMTIME_SHIFT)
642 | (init->hysteresisLevel << _ACMP_CTRL_HYSTSEL_SHIFT)
643 | (init->inactiveValue << _ACMP_CTRL_INACTVAL_SHIFT);
644 acmp->INPUTSEL = (init->lowPowerReferenceEnabled << _ACMP_INPUTSEL_LPREF_SHIFT)
645 | (init->vddLevel << _ACMP_INPUTSEL_VDDLEVEL_SHIFT);
646
647 #endif
648
649 if (init->enable) {
650 ACMP_Enable(acmp);
651 } else {
652 ACMP_Disable(acmp);
653 }
654 }
655
656 #if defined(_ACMP_INPUTSEL_VASEL_MASK)
657 /***************************************************************************//**
658 * @brief
659 * Set up the VA source.
660 *
661 * @param[in] acmp
662 * A pointer to the ACMP peripheral register block.
663 *
664 * @param[in] vaconfig
665 * A pointer to the structure used to configure the VA source. This structure
666 * contains the input source and the 2 divider values.
667 ******************************************************************************/
ACMP_VASetup(ACMP_TypeDef * acmp,const ACMP_VAConfig_TypeDef * vaconfig)668 void ACMP_VASetup(ACMP_TypeDef *acmp, const ACMP_VAConfig_TypeDef *vaconfig)
669 {
670 EFM_ASSERT(vaconfig->div0 < 64);
671 EFM_ASSERT(vaconfig->div1 < 64);
672
673 BUS_RegMaskedWrite(&acmp->INPUTSEL, _ACMP_INPUTSEL_VASEL_MASK,
674 vaconfig->input << _ACMP_INPUTSEL_VASEL_SHIFT);
675 BUS_RegMaskedWrite(&acmp->HYSTERESIS0, _ACMP_HYSTERESIS0_DIVVA_MASK,
676 vaconfig->div0 << _ACMP_HYSTERESIS0_DIVVA_SHIFT);
677 BUS_RegMaskedWrite(&acmp->HYSTERESIS1, _ACMP_HYSTERESIS1_DIVVA_MASK,
678 vaconfig->div1 << _ACMP_HYSTERESIS1_DIVVA_SHIFT);
679 }
680 #endif
681
682 #if defined(_ACMP_INPUTSEL_VBSEL_MASK)
683 /***************************************************************************//**
684 * @brief
685 * Set up the VB Source.
686 *
687 * @param[in] acmp
688 * A pointer to the ACMP peripheral register block.
689 *
690 * @param[in] vbconfig
691 * A pointer to the structure used to configure the VB source. This structure
692 * contains the input source and the 2 divider values.
693 ******************************************************************************/
ACMP_VBSetup(ACMP_TypeDef * acmp,const ACMP_VBConfig_TypeDef * vbconfig)694 void ACMP_VBSetup(ACMP_TypeDef *acmp, const ACMP_VBConfig_TypeDef *vbconfig)
695 {
696 EFM_ASSERT(vbconfig->div0 < 64);
697 EFM_ASSERT(vbconfig->div1 < 64);
698
699 BUS_RegMaskedWrite(&acmp->INPUTSEL, _ACMP_INPUTSEL_VBSEL_MASK,
700 vbconfig->input << _ACMP_INPUTSEL_VBSEL_SHIFT);
701 BUS_RegMaskedWrite(&acmp->HYSTERESIS0, _ACMP_HYSTERESIS0_DIVVB_MASK,
702 vbconfig->div0 << _ACMP_HYSTERESIS0_DIVVB_SHIFT);
703 BUS_RegMaskedWrite(&acmp->HYSTERESIS1, _ACMP_HYSTERESIS1_DIVVB_MASK,
704 vbconfig->div1 << _ACMP_HYSTERESIS1_DIVVB_SHIFT);
705 }
706 #endif
707
708 /** @} (end addtogroup acmp) */
709 #endif /* defined(ACMP_COUNT) && (ACMP_COUNT > 0) */
710