1 /***************************************************************************//**
2  * @file
3  * @brief Pulse Counter (PCNT) 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_pcnt.h"
32 #if defined(PCNT_COUNT) && (PCNT_COUNT > 0)
33 
34 #include "em_cmu.h"
35 #include "sl_assert.h"
36 #include "em_bus.h"
37 
38 /***************************************************************************//**
39  * @addtogroup pcnt PCNT - Pulse Counter
40  * @brief Pulse Counter (PCNT) Peripheral API
41  * @details
42  *  This module contains functions to control the PCNT peripheral of Silicon
43  *  Labs 32-bit MCUs and SoCs. The PCNT decodes incoming pulses. The module has
44  *  a quadrature mode which may be used to decode the speed and direction of a
45  *  mechanical shaft.
46  * @{
47  ******************************************************************************/
48 
49 /*******************************************************************************
50  *******************************   DEFINES   ***********************************
51  ******************************************************************************/
52 
53 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
54 
55 /** Validation of the PCNT register block pointer reference for assert statements. */
56 #if (PCNT_COUNT == 1)
57 #define PCNT_REF_VALID(ref)    ((ref) == PCNT0)
58 #elif (PCNT_COUNT == 2)
59 #define PCNT_REF_VALID(ref)    (((ref) == PCNT0) || ((ref) == PCNT1))
60 #elif (PCNT_COUNT == 3)
61 #define PCNT_REF_VALID(ref)    (((ref) == PCNT0) || ((ref) == PCNT1) \
62                                 || ((ref) == PCNT2))
63 #else
64 #error "Undefined number of pulse counters (PCNT)."
65 #endif
66 
67 /** @endcond */
68 
69 /*******************************************************************************
70  **************************   LOCAL VARIABLES   ********************************
71  ******************************************************************************/
72 #if defined(_SILICON_LABS_32B_SERIES_2)
73 static PCNT_CntEvent_TypeDef initCntEvent;
74 static PCNT_CntEvent_TypeDef initAuxCntEvent;
75 #endif
76 
77 /*******************************************************************************
78  **************************   LOCAL FUNCTIONS   ********************************
79  ******************************************************************************/
80 
81 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
82 
83 #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
84 /***************************************************************************//**
85  * @brief
86  *   Map PCNT structure into an instance number.
87  *
88  * @param[in] pcnt
89  *   A pointer to the PCNT peripheral register block.
90  *
91  * @return
92  *   An instance number.
93  ******************************************************************************/
PCNT_Map(PCNT_TypeDef * pcnt)94 __STATIC_INLINE unsigned int PCNT_Map(PCNT_TypeDef *pcnt)
95 {
96   return ((uint32_t)pcnt - PCNT0_BASE) / 0x400;
97 }
98 #endif
99 /** @endcond */
100 
101 /*******************************************************************************
102  **************************   GLOBAL FUNCTIONS   *******************************
103  ******************************************************************************/
104 
105 /***************************************************************************//**
106  * @brief
107  *   Reset PCNT counters and TOP register.
108  *
109  * @note
110  *   Notice that special SYNCBUSY handling is not applicable for the RSTEN
111  *   bit of the control register, so we don't need to wait for it when only
112  *   modifying RSTEN. (It would mean undefined wait time if clocked by an external
113  *   clock.) The SYNCBUSY bit will however be set, leading to a synchronization
114  *   in the LF domain, with, in reality, no changes.
115  *
116  * @param[in] pcnt
117  *   A pointer to the PCNT peripheral register block.
118  ******************************************************************************/
PCNT_CounterReset(PCNT_TypeDef * pcnt)119 void PCNT_CounterReset(PCNT_TypeDef *pcnt)
120 {
121   EFM_ASSERT(PCNT_REF_VALID(pcnt));
122 
123 #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
124   /* Enable reset of the CNT and TOP register. */
125   BUS_RegBitWrite(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1);
126 
127   /* Disable reset of the CNT and TOP register. */
128   BUS_RegBitWrite(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0);
129 #else
130   /* Reset of the CNT and TOP register. */
131   PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD);
132   pcnt->CMD_SET = PCNT_CMD_CNTRST | PCNT_CMD_AUXCNTRST;
133   PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOP);
134   pcnt->TOP = _PCNT_TOP_RESETVALUE;
135 #endif
136 }
137 
138 /***************************************************************************//**
139  * @brief
140  *   Set PCNT operational mode.
141  *
142  * @details
143  *   Notice that this function does not do any configuration. Setting operational
144  *   mode is normally only required after initialization is done, and if not
145  *   done as part of initialization or if requiring to disable/reenable pulse
146  *   counter.
147  *
148  * @note
149  *   This function may stall until synchronization to low-frequency domain is
150  *   completed. For that reason, it should normally not be used when
151  *   an external clock is used for the PCNT module, since stall time may be
152  *   undefined.
153  *
154  * @param[in] pcnt
155  *   A pointer to the PCNT peripheral register block.
156  *
157  * @param[in] mode
158  *   An operational mode to use for PCNT.
159  ******************************************************************************/
PCNT_Enable(PCNT_TypeDef * pcnt,PCNT_Mode_TypeDef mode)160 void PCNT_Enable(PCNT_TypeDef *pcnt, PCNT_Mode_TypeDef mode)
161 {
162   uint32_t tmp;
163 
164   EFM_ASSERT(PCNT_REF_VALID(pcnt));
165 
166   /* Set as specified. */
167 #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
168   tmp  = pcnt->CTRL & ~_PCNT_CTRL_MODE_MASK;
169   tmp |= (uint32_t)mode << _PCNT_CTRL_MODE_SHIFT;
170 
171   /* LF register about to be modified requires sync; busy check. */
172   PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
173   pcnt->CTRL = tmp;
174 #else
175   /* Disable module if disable mode is passed. */
176   if (mode == pcntModeDisable) {
177     PCNT_Sync(pcnt, _PCNT_SYNCBUSY_MASK);
178     pcnt->EN_CLR = PCNT_EN_EN;
179 #if defined(_PCNT_EN_DISABLING_MASK)
180     while (pcnt->EN & _PCNT_EN_DISABLING_MASK) {
181     }
182 #endif
183     return;
184   }
185   /* Check if given mode is same as already configured. */
186   tmp = (pcnt->CFG & _PCNT_CFG_MODE_MASK) >> _PCNT_CFG_MODE_SHIFT;
187   if (tmp != mode) {
188     PCNT_Sync(pcnt, _PCNT_SYNCBUSY_MASK);
189     pcnt->EN_CLR = PCNT_EN_EN;
190 #if defined(_PCNT_EN_DISABLING_MASK)
191     while (pcnt->EN & _PCNT_EN_DISABLING_MASK) {
192     }
193 #endif
194     pcnt->CFG_SET = (uint32_t)mode << _PCNT_CFG_MODE_SHIFT;
195   }
196   /* Enable module */
197   pcnt->EN_SET = PCNT_EN_EN;
198   /* Start Counters*/
199   if (initCntEvent != pcntCntEventNone) {
200     PCNT_StartMainCnt(pcnt);
201   }
202   if (initAuxCntEvent != pcntCntEventNone) {
203     PCNT_StartAuxCnt(pcnt);
204   }
205 
206 #endif
207 }
208 
209 /***************************************************************************//**
210  * @brief
211  *   Returns if the PCNT module is enabled or not.
212  *
213  * @details
214  *   Notice that this function does not do any configuration.
215  *
216  * @param[in] pcnt
217  *   A pointer to the PCNT peripheral register block.
218  *
219  * @return Returns TRUE if the module is enabled.
220  ******************************************************************************/
PCNT_IsEnabled(PCNT_TypeDef * pcnt)221 bool PCNT_IsEnabled(PCNT_TypeDef *pcnt)
222 {
223   EFM_ASSERT(PCNT_REF_VALID(pcnt));
224 
225 #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
226   return ((pcnt->CTRL & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE);
227 #else
228   return (pcnt->EN & _PCNT_EN_EN_MASK);
229 #endif
230 }
231 
232 /***************************************************************************//**
233  * @brief
234  *   Set the counter and top values.
235  *
236  * @details
237  *   The pulse counter is disabled while changing these values and reenabled
238  *   (if originally enabled) when values have been set.
239  *
240  * @note
241  *   This function will stall until synchronization to low-frequency domain is
242  *   completed. For that reason, it should normally not be used when
243  *   an external clock is used for the PCNT module, since stall time may be
244  *   undefined. The counter should normally only be set when
245  *   operating in (or about to enable) #pcntModeOvsSingle mode.
246  *
247  * @param[in] pcnt
248  *   A pointer to the PCNT peripheral register block.
249  *
250  * @param[in] count
251  *   A value to set in the counter register.
252  *
253  * @param[in] top
254  *   A value to set in the top register.
255  ******************************************************************************/
PCNT_CounterTopSet(PCNT_TypeDef * pcnt,uint32_t count,uint32_t top)256 void PCNT_CounterTopSet(PCNT_TypeDef *pcnt, uint32_t count, uint32_t top)
257 {
258 #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
259   uint32_t ctrl;
260 #endif
261 
262   EFM_ASSERT(PCNT_REF_VALID(pcnt));
263 
264 #ifdef PCNT0
265   if (PCNT0 == pcnt) {
266     EFM_ASSERT((1 << PCNT0_CNT_SIZE) > count);
267     EFM_ASSERT((1 << PCNT0_CNT_SIZE) > top);
268   }
269 #endif
270 
271 #ifdef PCNT1
272   if (PCNT1 == pcnt) {
273     EFM_ASSERT((1 << PCNT1_CNT_SIZE) > count);
274     EFM_ASSERT((1 << PCNT1_CNT_SIZE) > top);
275   }
276 #endif
277 
278 #ifdef PCNT2
279   if (PCNT2 == pcnt) {
280     EFM_ASSERT((1 << PCNT2_CNT_SIZE) > count);
281     EFM_ASSERT((1 << PCNT2_CNT_SIZE) > top);
282   }
283 #endif
284 
285 #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
286   /* Keep the current control setting, must be restored. */
287   ctrl = pcnt->CTRL;
288 
289   /* If enabled, disable pulse counter before changing values. */
290   if ((ctrl & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE) {
291     PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
292     pcnt->CTRL = (ctrl & ~_PCNT_CTRL_MODE_MASK) | PCNT_CTRL_MODE_DISABLE;
293   }
294 
295   /* Load into TOPB. */
296   PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB);
297   pcnt->TOPB = count;
298 
299   /* Load TOPB value into TOP. */
300   PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD);
301 
302   /* This bit has no effect on rev. C and onwards parts - for compatibility. */
303   pcnt->CMD = PCNT_CMD_LTOPBIM;
304   PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD);
305 
306   /* Load TOP into CNT. */
307   pcnt->CMD = PCNT_CMD_LCNTIM;
308 
309   /* Restore TOP. ('count' setting has been loaded into pcnt->TOP, better
310    * to use 'top' than pcnt->TOP in compare, since latter may not
311    * be visible yet.) */
312   if (top != count) {
313     /* Wait for the command to sync LCNTIM before setting TOPB. */
314     PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD);
315 
316     /* Load into TOPB. No need to check for TOPB sync complete, which
317      * has been ensured above. */
318     pcnt->TOPB = top;
319 
320     /* Load TOPB value into TOP. */
321     PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD);
322     pcnt->CMD = PCNT_CMD_LTOPBIM;
323   }
324 
325   /* Reenable if it was enabled. */
326   PCNT_Enable(pcnt, (PCNT_Mode_TypeDef)(ctrl & _PCNT_CTRL_MODE_MASK));
327   if ((ctrl & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE) {
328     PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL | PCNT_SYNCBUSY_CMD);
329     pcnt->CTRL = ctrl;
330   }
331 #else
332   /* Load into TOP. */
333   PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOP);
334   pcnt->TOP = count;
335   PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOP);
336 
337   /* Load TOP into CNT. */
338   PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD);
339   pcnt->CMD = PCNT_CMD_LCNTIM;
340 
341   if (top != count) {
342     /* Wait for the command to sync LCNTIM before setting TOPB. */
343     PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD);
344 
345     pcnt->TOP = top;
346   }
347 #endif
348 }
349 
350 #if defined(_PCNT_INPUT_MASK) || defined(_SILICON_LABS_32B_SERIES_2)
351 /***************************************************************************//**
352  * @brief
353  *   Enable/disable the selected PRS input of PCNT.
354  *
355  * @details
356  *   Notice that this function does not do any configuration.
357  *
358  * @param[in] pcnt
359  *   A pointer to the PCNT peripheral register block.
360  *
361  * @param[in] prsInput
362  *   PRS input (S0 or S1) of the selected PCNT module.
363  *
364  * @param[in] enable
365  *   Set to true to enable, false to disable the selected PRS input.
366  ******************************************************************************/
PCNT_PRSInputEnable(PCNT_TypeDef * pcnt,PCNT_PRSInput_TypeDef prsInput,bool enable)367 void PCNT_PRSInputEnable(PCNT_TypeDef *pcnt,
368                          PCNT_PRSInput_TypeDef prsInput,
369                          bool enable)
370 {
371   EFM_ASSERT(PCNT_REF_VALID(pcnt));
372 
373 #if defined(_SILICON_LABS_32B_SERIES_2)
374   bool module_enable = PCNT_IsEnabled(pcnt);
375 
376   /* Disable module before writing to CFG register. */
377   if (module_enable == true) {
378     PCNT_Sync(pcnt, _PCNT_SYNCBUSY_MASK);
379   }
380 
381   pcnt->EN_CLR = PCNT_EN_EN;
382 #if defined(_PCNT_EN_DISABLING_MASK)
383   while (pcnt->EN & _PCNT_EN_DISABLING_MASK) {
384   }
385 #endif
386 #endif
387 
388   /* Enable/disable the selected PRS input on the selected PCNT module. */
389   switch (prsInput) {
390     /* Enable/disable PRS input S0. */
391     case pcntPRSInputS0:
392 #if defined(_PCNT_INPUT_MASK)
393       BUS_RegBitWrite(&(pcnt->INPUT), _PCNT_INPUT_S0PRSEN_SHIFT, enable);
394 #elif defined(_SILICON_LABS_32B_SERIES_2)
395       BUS_RegBitWrite(&(pcnt->CFG), _PCNT_CFG_S0PRSEN_SHIFT, enable);
396 #endif
397       break;
398 
399     /* Enable/disable PRS input S1. */
400     case pcntPRSInputS1:
401 #if defined(_PCNT_INPUT_MASK)
402       BUS_RegBitWrite(&(pcnt->INPUT), _PCNT_INPUT_S1PRSEN_SHIFT, enable);
403 #elif defined(_SILICON_LABS_32B_SERIES_2)
404       BUS_RegBitWrite(&(pcnt->CFG), _PCNT_CFG_S1PRSEN_SHIFT, enable);
405 #endif
406       break;
407 
408     /* An invalid parameter, asserted. */
409     default:
410       EFM_ASSERT(0);
411       break;
412   }
413 
414 #if defined(_SILICON_LABS_32B_SERIES_2)
415   /* Re-Enable if necessary the PCNT module after change. */
416   if (module_enable == true) {
417     pcnt->EN_SET = PCNT_EN_EN;
418   }
419 #endif
420 }
421 #endif
422 
423 #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
424 /***************************************************************************//**
425  * @brief
426  *   PCNT register synchronization freeze control.
427  *
428  * @details
429  *   Some PCNT registers require synchronization into the low-frequency (LF)
430  *   domain. The freeze feature allows for several registers to be
431  *   modified before passing them to the LF domain simultaneously, which
432  *   takes place when the freeze mode is disabled.
433  *
434  * @note
435  *   When enabling freeze mode, this function will wait for all current
436  *   ongoing PCNT synchronization to the LF domain to complete (normally
437  *   synchronization will not be in progress). However, for this reason, when
438  *   using freeze mode, modifications of registers requiring the LF synchronization
439  *   should be done within one freeze enable/disable block to avoid unnecessary
440  *   stalling.
441  *
442  * @param[in] pcnt
443  *   A pointer to the PCNT peripheral register block.
444  *
445  * @param[in] enable
446  *   @li True - enable freeze, modified registers are not propagated to the
447  *       LF domain.
448  *   @li False - disables freeze, modified registers are propagated to LF
449  *       domain.
450  ******************************************************************************/
PCNT_FreezeEnable(PCNT_TypeDef * pcnt,bool enable)451 void PCNT_FreezeEnable(PCNT_TypeDef *pcnt, bool enable)
452 {
453   EFM_ASSERT(PCNT_REF_VALID(pcnt));
454 
455   if (enable) {
456     /* Wait for any ongoing LF synchronization to complete to
457      * protect against the rare case when a user:
458      * - modifies a register requiring LF sync
459      * - then enables freeze before LF sync completed
460      * - then modifies the same register again
461      * since modifying a register while it is in sync progress should be
462      * avoided. */
463     while (pcnt->SYNCBUSY) {
464     }
465 
466     pcnt->FREEZE = PCNT_FREEZE_REGFREEZE;
467   } else {
468     pcnt->FREEZE = 0;
469   }
470 }
471 #endif
472 
473 /***************************************************************************//**
474  * @brief
475  *   Initialize the pulse counter.
476  *
477  * @details
478  *   This function will configure the pulse counter. The clock selection is
479  *   configured as follows, depending on operational mode:
480  *
481  *   @li #pcntModeOvsSingle - Use LFACLK.
482  *   @li #pcntModeExtSingle - Use external PCNTn_S0 pin.
483  *   @li #pcntModeExtQuad - Use external PCNTn_S0 pin.
484  *
485  *   Notice that the LFACLK must be enabled in all modes, since some basic setup
486  *   is done with this clock even if the external pin clock usage mode is chosen.
487  *   The pulse counter clock for the selected instance must also be enabled
488  *   prior to initialization.
489  *
490  *   Notice that pins used by the PCNT module must be properly configured
491  *   by the user explicitly through setting the ROUTE register for
492  *   the PCNT to work as intended.
493  *
494  *   Writing to CNT will not occur in external clock modes (EXTCLKQUAD and
495  *   EXTCLKSINGLE) because the external clock rate is unknown. The user should
496  *   handle it manually depending on the application.
497  *
498  *   TOPB is written for all modes but in external clock mode it will take
499  *   3 external clock cycles to sync to TOP.
500  *
501  *
502  * @note
503  *   Initializing requires synchronization into the low-frequency domain. This
504  *   may cause a delay.
505  *
506  * @param[in] pcnt
507  *   A pointer to the PCNT peripheral register block.
508  *
509  * @param[in] init
510  *   A pointer to the initialization structure.
511  ******************************************************************************/
PCNT_Init(PCNT_TypeDef * pcnt,const PCNT_Init_TypeDef * init)512 void PCNT_Init(PCNT_TypeDef *pcnt, const PCNT_Init_TypeDef *init)
513 {
514   unsigned int inst = 0;
515   uint32_t     tmp;
516 
517   (void)&tmp;
518 
519   EFM_ASSERT(PCNT_REF_VALID(pcnt));
520 
521 #ifdef PCNT0
522   if (PCNT0 == pcnt) {
523     EFM_ASSERT((1 << PCNT0_CNT_SIZE) > init->counter);
524     EFM_ASSERT((1 << PCNT0_CNT_SIZE) > init->top);
525   }
526 #endif
527 
528 #ifdef PCNT1
529   if (PCNT1 == pcnt) {
530     EFM_ASSERT((1 << PCNT1_CNT_SIZE) > init->counter);
531     EFM_ASSERT((1 << PCNT1_CNT_SIZE) > init->top);
532   }
533 #endif
534 
535 #ifdef PCNT2
536   if (PCNT2 == pcnt) {
537     EFM_ASSERT((1 << PCNT2_CNT_SIZE) > init->counter);
538     EFM_ASSERT((1 << PCNT2_CNT_SIZE) > init->top);
539   }
540 #endif
541 
542 #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
543   /* Map the pointer to an instance. */
544   inst = PCNT_Map(pcnt);
545 #endif
546 
547 #if defined(_PCNT_INPUT_MASK)
548   /* Selecting the PRS channels for the PRS input sources of the PCNT. These are
549    * written with a Read-Modify-Write sequence to keep the value of the
550    * input enable bits which can be modified using PCNT_PRSInputEnable(). */
551   tmp = pcnt->INPUT & ~(_PCNT_INPUT_S0PRSSEL_MASK | _PCNT_INPUT_S1PRSSEL_MASK);
552   tmp |= ((uint32_t)init->s0PRS << _PCNT_INPUT_S0PRSSEL_SHIFT)
553          | ((uint32_t)init->s1PRS << _PCNT_INPUT_S1PRSSEL_SHIFT);
554   pcnt->INPUT = tmp;
555 #endif
556 
557 #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
558   /* Build the CTRL setting, except for mode. */
559   tmp = 0;
560   if (init->negEdge) {
561     tmp |= PCNT_CTRL_EDGE_NEG;
562   }
563 
564   if (init->countDown) {
565     tmp |= PCNT_CTRL_CNTDIR_DOWN;
566   }
567 
568 #if defined(PCNT_CTRL_FILT)
569   if (init->filter) {
570     tmp |= PCNT_CTRL_FILT;
571   }
572 #endif
573 
574 #if defined(PCNT_CTRL_HYST)
575   if (init->hyst) {
576     tmp |= PCNT_CTRL_HYST;
577   }
578 #endif
579 
580 #if defined(PCNT_CTRL_S1CDIR)
581   if (init->s1CntDir) {
582     tmp |= PCNT_CTRL_S1CDIR;
583   }
584 #endif
585 
586   /* Configure counter events for regular and auxiliary counters. */
587 #if defined(_PCNT_CTRL_CNTEV_SHIFT)
588   tmp |= ((uint32_t)init->cntEvent) << _PCNT_CTRL_CNTEV_SHIFT;
589 #endif
590 
591 #if defined(_PCNT_CTRL_AUXCNTEV_SHIFT)
592   {
593     /* Modify the auxCntEvent value before writing to the AUXCNTEV field in
594        the CTRL register because the AUXCNTEV field values are different than
595        the CNTEV field values, and cntEvent and auxCntEvent are of the same type
596        PCNT_CntEvent_TypeDef.
597      */
598     uint32_t auxCntEventField = 0; /* Get rid of compiler warning. */
599     switch (init->auxCntEvent) {
600       case pcntCntEventBoth:
601         auxCntEventField = pcntCntEventNone;
602         break;
603 
604 #if defined(_PCNT_CTRL_CNTEV_NONE)
605       case pcntCntEventNone:
606         auxCntEventField = pcntCntEventBoth;
607         break;
608 #endif
609 
610       case pcntCntEventUp:
611       case pcntCntEventDown:
612         auxCntEventField = init->auxCntEvent;
613         break;
614 
615       default:
616         /* An invalid parameter, asserted. */
617         EFM_ASSERT(0);
618         break;
619     }
620     tmp |= auxCntEventField << _PCNT_CTRL_AUXCNTEV_SHIFT;
621   }
622 #endif
623 
624   /* Reset the pulse counter while changing the clock source. The reset bit */
625   /* is asynchronous, no need to check for SYNCBUSY. */
626   BUS_RegBitWrite(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1);
627 
628   /* Select LFACLK to clock in the control setting. */
629   CMU_PCNTClockExternalSet(inst, false);
630 
631   /* Handling depends on whether using an external clock. */
632   switch (init->mode) {
633     case pcntModeExtSingle:
634     case pcntModeExtQuad:
635       tmp |= ((uint32_t)init->mode) << _PCNT_CTRL_MODE_SHIFT;
636 
637       /* In most cases, the SYNCBUSY bit is set due to the reset bit set and waiting
638        * for asynchronous reset bit is strictly not necessary.
639        * In theory, other operations on CTRL register may have been done
640        * outside this function, so wait. */
641       PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
642 
643       /* Enable PCNT Clock Domain Reset. The PCNT must be in reset before changing
644        * the clock source to an external clock. */
645       pcnt->CTRL = PCNT_CTRL_RSTEN;
646 
647       /* Wait until the CTRL write is synchronized into the LF domain. */
648       PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
649 
650       /* Change to the external clock BEFORE disabling reset. */
651       CMU_PCNTClockExternalSet(inst, true);
652 
653       /* Write to TOPB. If using the external clock, TOPB will sync to TOP at the same
654        * time as the mode ensuring that if the user chooses to count
655        * down, the first "countable" pulse will make CNT go to TOP and not 0xFF
656        * (default TOP value). */
657       pcnt->TOPB = init->top;
658 
659       /* This bit has no effect on rev. C and onwards parts - for compatibility. */
660       pcnt->CMD = PCNT_CMD_LTOPBIM;
661 
662       /* Write the CTRL register with the configurations.
663        * This should be written after TOPB in the eventuality of a pulse between
664        * these two writes that would cause the CTRL register to be synced one
665        * clock cycle earlier than the TOPB. */
666       pcnt->CTRL = tmp;
667 
668       /* There are no syncs for TOP, CMD, or CTRL because the clock rate is unknown
669        * and the program could stall.
670        * These will be synced within 3 clock cycles of the external clock.  /
671        * For the same reason CNT cannot be written here. */
672       break;
673 
674     /* pcntModeDisable */
675     /* pcntModeOvsSingle */
676     /* pcntModeOvsQuadx */
677     default:
678       /* No need to set disabled mode if already disabled. */
679       if ((pcnt->CTRL & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE) {
680         /* Set control to disabled mode and leave reset on until ensured disabled.
681          * No need to wait for CTRL SYNCBUSY completion. It was
682          * triggered by the reset bit above, which is asynchronous. */
683         pcnt->CTRL = tmp | PCNT_CTRL_MODE_DISABLE | PCNT_CTRL_RSTEN;
684 
685         /* Wait until the CTRL write is synchronized into the LF domain before proceeding
686          * to disable reset. */
687         PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
688       }
689 
690       /* Disable reset bit. The counter should now be in disabled mode. */
691       BUS_RegBitWrite(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0);
692 
693       /* Set the counter and top values as specified. */
694       PCNT_CounterTopSet(pcnt, init->counter, init->top);
695 
696       /* Enter oversampling mode if selected. */
697       if (init->mode != pcntModeDisable) {
698         PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
699         pcnt->CTRL = tmp | (init->mode << _PCNT_CTRL_MODE_SHIFT);
700       }
701       break;
702   }
703 
704 #else
705   /* If PCNT is enabled wait for all SYNCBUSY signals to complete. */
706   if (pcnt->EN == 1U) {
707     PCNT_Sync(pcnt, _PCNT_SYNCBUSY_MASK);
708   }
709 
710   /* Disable PCNT. */
711   pcnt->EN_CLR = PCNT_EN_EN;
712 #if defined(_PCNT_EN_DISABLING_MASK)
713   while (pcnt->EN & _PCNT_EN_DISABLING_MASK) {
714   }
715 #endif
716   /* Build the CFG setting. */
717   pcnt->CFG &= ~(_PCNT_CFG_DEBUGHALT_MASK | _PCNT_CFG_FILTEN_MASK | _PCNT_CFG_HYST_MASK);
718   pcnt->CFG |= (((uint32_t)init->filter) << _PCNT_CFG_FILTEN_SHIFT)
719                | (((uint32_t)init->hyst) << _PCNT_CFG_HYST_SHIFT)
720                | (((uint32_t)init->debugHalt) << _PCNT_CFG_DEBUGHALT_SHIFT);
721 
722   /* Set Mode setting. */
723   /* Write the CFG register with the configurations. */
724   if (init->mode != pcntModeDisable) {
725     pcnt->CFG = ((pcnt->CFG & (~_PCNT_CFG_MODE_MASK)) | (((uint32_t)init->mode) << _PCNT_CFG_MODE_SHIFT));
726   }
727 
728   pcnt->EN_SET = PCNT_EN_EN;
729   PCNT_Sync(pcnt, _PCNT_SYNCBUSY_MASK);
730 
731   /* Build the CTRL setting */
732   tmp = (((uint32_t)init->negEdge) << _PCNT_CTRL_EDGE_SHIFT)
733         | (((uint32_t)init->countDown) << _PCNT_CTRL_CNTDIR_SHIFT)
734         | (((uint32_t)init->s1CntDir) << _PCNT_CTRL_S1CDIR_SHIFT);
735 
736   /* Configure counter events for regular and auxiliary counters. */
737   if (init->cntEvent != PCNT_CNT_EVENT_NONE) {
738     tmp |= ((uint32_t)init->cntEvent) << _PCNT_CTRL_CNTEV_SHIFT;
739   }
740   if (init->auxCntEvent != PCNT_CNT_EVENT_NONE) {
741     tmp |= ((uint32_t)init->auxCntEvent) << _PCNT_CTRL_AUXCNTEV_SHIFT;
742   }
743 
744   pcnt->CTRL = tmp;
745 
746   /* Set PRS inputs */
747   EFM_ASSERT(init->s0PRS < PRS_ASYNC_CH_NUM);
748   EFM_ASSERT(init->s1PRS < PRS_ASYNC_CH_NUM);
749   PRS->CONSUMER_PCNT0_S0IN = init->s0PRS;
750   PRS->CONSUMER_PCNT0_S1IN = init->s1PRS;
751 
752   if (init->mode == pcntModeExtSingle || init->mode == pcntModeExtQuad) {
753     /* Enable PCNT Clock Domain Reset. The PCNT must be in reset before changing
754        the clock source to an external clock. */
755     pcnt->CMD_SET = PCNT_CMD_CORERST;
756     /* Change to the external clock. */
757     CMU_PCNTClockExternalSet(inst, true);
758   } else {
759     /* Change to the internal clock. */
760     CMU_PCNTClockExternalSet(inst, false);
761   }
762 
763   /* Start counter(s) */
764   if (init->cntEvent != pcntCntEventNone) {
765     PCNT_StartMainCnt(pcnt);
766   }
767   if (init->auxCntEvent != pcntCntEventNone) {
768     PCNT_StartAuxCnt(pcnt);
769   }
770 
771   PCNT_CounterTopSet(pcnt, init->counter, init->top);
772   PCNT_TopBufferSet(pcnt, init->top);
773 
774   /* Save values of primary and auxiliary counter event. */
775   initCntEvent = init->cntEvent;
776   initAuxCntEvent = init->auxCntEvent;
777 
778   if (init->mode == pcntModeDisable) {
779     /* Disable PCNT. */
780     pcnt->EN_CLR = PCNT_EN_EN;
781 #if defined(_PCNT_EN_DISABLING_MASK)
782     while (pcnt->EN & _PCNT_EN_DISABLING_MASK) {
783     }
784 #endif
785   }
786 #endif
787 }
788 
789 /***************************************************************************//**
790  * @brief
791  *   Reset PCNT to the same state that it was in after a hardware reset.
792  *
793  * @details
794  *   Notice the LFACLK must be enabled, since some basic reset is done with
795  *   this clock. The pulse counter clock for the selected instance must also
796  *   be enabled prior to initialization.
797  *
798  * @note
799  *   The ROUTE register is NOT reset by this function to allow for
800  *   centralized setup of this feature.
801  *
802  * @param[in] pcnt
803  *   A pointer to the PCNT peripheral register block.
804  ******************************************************************************/
PCNT_Reset(PCNT_TypeDef * pcnt)805 void PCNT_Reset(PCNT_TypeDef *pcnt)
806 {
807   unsigned int inst = 0;
808   EFM_ASSERT(PCNT_REF_VALID(pcnt));
809 
810 #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
811   /* A map pointer to the instance and clock information. */
812   inst = PCNT_Map(pcnt);
813   pcnt->IEN = _PCNT_IEN_RESETVALUE;
814 
815   /* Notice that special SYNCBUSY handling is not applicable for the RSTEN
816    * bit of the control register, so no need to wait for it when only
817    * modifying RSTEN. The SYNCBUSY bit will be set, leading to a
818    * synchronization in the LF domain, with, in reality, no changes to the LF domain.
819    * Enable reset of the CNT and TOP register. */
820   BUS_RegBitWrite(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1);
821 
822   /* Select LFACLK as default. */
823   CMU_PCNTClockExternalSet(inst, false);
824 
825   PCNT_TopBufferSet(pcnt, _PCNT_TOPB_RESETVALUE);
826 
827   /* Reset CTRL leaving RSTEN set. */
828   pcnt->CTRL = _PCNT_CTRL_RESETVALUE | PCNT_CTRL_RSTEN;
829 
830   /* Disable reset after CTRL register has been synchronized. */
831   PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
832   BUS_RegBitWrite(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0);
833 
834   /* Clear pending interrupts. */
835   pcnt->IFC = _PCNT_IFC_MASK;
836 
837   /* Do not reset route register, setting should be done independently. */
838 #else
839   /* Disable PCNT module. */
840   PCNT_Sync(pcnt, _PCNT_SYNCBUSY_MASK);
841   pcnt->EN_CLR = PCNT_EN_EN;
842 
843   /* Select LFACLK as default. */
844   /* Recommended to switch to internal clock before reset. */
845   CMU_PCNTClockExternalSet(inst, false);
846 
847   while (pcnt->EN & _PCNT_EN_DISABLING_MASK) {
848   }
849 
850   /* Clear registers. */
851   pcnt->SWRST_SET = PCNT_SWRST_SWRST;
852 
853   while (pcnt->SWRST & PCNT_SWRST_RESETTING) {
854   }
855 
856 #endif
857 }
858 
859 #if defined(PCNT_OVSCFG_FILTLEN_DEFAULT) || defined(_SILICON_LABS_32B_SERIES_2)
860 /***************************************************************************//**
861  * @brief
862  *   Set the filter configuration.
863  *
864  * @details
865  *   This function will configure the PCNT input filter when the PCNT mode is
866  *   configured to take an LFA-derived clock as an input clock.
867  *
868  * @param[in] pcnt
869  *   A pointer to the PCNT peripheral register block.
870  *
871  * @param[in] config
872  *   A pointer to the configuration structure to be applied.
873  *
874  * @param[in] enable
875  *   Indicates whether to enable or disable filtering.
876  ******************************************************************************/
PCNT_FilterConfiguration(PCNT_TypeDef * pcnt,const PCNT_Filter_TypeDef * config,bool enable)877 void PCNT_FilterConfiguration(PCNT_TypeDef *pcnt, const PCNT_Filter_TypeDef *config, bool enable)
878 {
879   uint32_t ovscfg = 0;
880 #if defined(_SILICON_LABS_32B_SERIES_2)
881   bool module_enable = false;
882 #endif
883 
884   EFM_ASSERT(PCNT_REF_VALID(pcnt));
885 
886 #if defined(PCNT_OVSCFG_FILTLEN_DEFAULT)
887   /* Construct the new filter setting value. */
888   ovscfg  = ((config->filtLen & _PCNT_OVSCFG_FILTLEN_MASK) << _PCNT_OVSCFG_FILTLEN_SHIFT)
889             | ((config->flutterrm & 0x1) << _PCNT_OVSCFG_FLUTTERRM_SHIFT);
890 
891   /* Set the new configuration. LF register requires sync check before writing. */
892   PCNT_Sync(pcnt, PCNT_SYNCBUSY_OVSCFG);
893   pcnt->OVSCFG = ovscfg;
894 
895   /* Set new state of the filter. LF register requires sync check before writing. */
896   PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
897   if (enable) {
898     pcnt->CTRL |= PCNT_CTRL_FILT;
899   } else {
900     pcnt->CTRL &= ~PCNT_CTRL_FILT;
901   }
902 
903 #elif  defined(_SILICON_LABS_32B_SERIES_2)
904   /* Disable module before changing CFG register. */
905   module_enable = PCNT_IsEnabled(pcnt);
906   if (module_enable == true) {
907     PCNT_Sync(pcnt, _PCNT_SYNCBUSY_MASK);
908   }
909   pcnt->EN_CLR = PCNT_EN_EN;
910 #if defined(_PCNT_EN_DISABLING_MASK)
911   while (pcnt->EN & _PCNT_EN_DISABLING_MASK) {
912   }
913 #endif
914   /* Construct the new filter setting value. */
915   ovscfg  = ((config->filtLen & _PCNT_OVSCTRL_FILTLEN_MASK) << _PCNT_OVSCTRL_FILTLEN_SHIFT)
916             | ((config->flutterrm & 0x1) << _PCNT_OVSCTRL_FLUTTERRM_SHIFT);
917 
918   /* Set the new configuration. LF register requires sync check before writing. */
919   PCNT_Sync(pcnt, PCNT_SYNCBUSY_OVSCTRL);
920   pcnt->OVSCTRL = ovscfg;
921 
922   /* Set new state of the filter. */
923   if (enable) {
924     pcnt->CFG |= PCNT_CFG_FILTEN;
925   } else {
926     pcnt->CFG &= ~PCNT_CFG_FILTEN;
927   }
928 
929   /* Re-Enable module if necessary after change. */
930   if (module_enable == true) {
931     pcnt->EN_SET = PCNT_EN_EN;
932   }
933 #endif
934 }
935 #endif
936 
937 #if defined(PCNT_CTRL_TCCMODE_DEFAULT)
938 /***************************************************************************//**
939  * @brief
940  *   Set Triggered Compare and Clear configuration.
941  *
942  * @details
943  *   This function will configure the PCNT TCC (Triggered Compare and Clear)
944  *   module. This module can, upon a configurable trigger source, compare the
945  *   current counter value with the configured TOP value. Upon match, the counter
946  *   will be reset and the TCC PRS output and TCC interrupt flag will be set.
947  *
948  *   Since there is a comparison with the TOP value, the counter will not stop
949  *   counting nor wrap when hitting the TOP value, but it will keep on counting
950  *   until its maximum value. Then, it will not wrap, but stop counting
951  *   and set the overflow flag.
952  *
953  * @param[in] pcnt
954  *   A pointer to the PCNT peripheral register block.
955  *
956  * @param[in] config
957  *   A pointer to the configuration structure to be applied.
958  ******************************************************************************/
PCNT_TCCConfiguration(PCNT_TypeDef * pcnt,const PCNT_TCC_TypeDef * config)959 void PCNT_TCCConfiguration(PCNT_TypeDef *pcnt, const PCNT_TCC_TypeDef *config)
960 {
961   uint32_t ctrl = 0;
962   uint32_t mask = _PCNT_CTRL_TCCMODE_MASK
963                   | _PCNT_CTRL_TCCPRESC_MASK
964                   | _PCNT_CTRL_TCCCOMP_MASK
965                   | _PCNT_CTRL_PRSGATEEN_MASK
966                   | _PCNT_CTRL_TCCPRSPOL_MASK
967                   | _PCNT_CTRL_TCCPRSSEL_MASK;
968 
969   EFM_ASSERT(PCNT_REF_VALID(pcnt));
970 
971   /* Construct the TCC part of the configuration register. */
972   ctrl |= (config->mode          << _PCNT_CTRL_TCCMODE_SHIFT)   & _PCNT_CTRL_TCCMODE_MASK;
973   ctrl |= (config->prescaler     << _PCNT_CTRL_TCCPRESC_SHIFT)  & _PCNT_CTRL_TCCPRESC_MASK;
974   ctrl |= (config->compare       << _PCNT_CTRL_TCCCOMP_SHIFT)   & _PCNT_CTRL_TCCCOMP_MASK;
975   ctrl |= (config->tccPRS        << _PCNT_CTRL_TCCPRSSEL_SHIFT) & _PCNT_CTRL_TCCPRSSEL_MASK;
976   ctrl |= (config->prsPolarity   << _PCNT_CTRL_TCCPRSPOL_SHIFT) & _PCNT_CTRL_TCCPRSPOL_MASK;
977   ctrl |= (config->prsGateEnable << _PCNT_CTRL_PRSGATEEN_SHIFT) & _PCNT_CTRL_PRSGATEEN_MASK;
978 
979   /* Load new TCC configuration to PCNT. LF register requires a sync check before write. */
980   PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
981   pcnt->CTRL = (pcnt->CTRL & (~mask)) | ctrl;
982 }
983 #endif
984 
985 /***************************************************************************//**
986  * @brief
987  *   Set top buffer value.
988  *
989  * @note
990  *   This function may stall until synchronization to low-frequency domain is
991  *   completed. For that reason, it should normally not be used when
992  *   an external clock is used for the PCNT module since stall time may be
993  *   undefined.
994  *
995  * @param[in] pcnt
996  *   A pointer to the PCNT peripheral register block.
997  *
998  * @param[in] val
999  *   A value to set in the top buffer register.
1000  ******************************************************************************/
PCNT_TopBufferSet(PCNT_TypeDef * pcnt,uint32_t val)1001 void PCNT_TopBufferSet(PCNT_TypeDef *pcnt, uint32_t val)
1002 {
1003   EFM_ASSERT(PCNT_REF_VALID(pcnt));
1004 
1005   /* LF register about to be modified require sync. busy check */
1006   PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB);
1007   pcnt->TOPB = val;
1008 }
1009 
1010 /***************************************************************************//**
1011  * @brief
1012  *   Set the top value.
1013  *
1014  * @note
1015  *   This function will stall until synchronization to low-frequency domain is
1016  *   completed. For that reason, it should normally not be used when
1017  *   an external clock is used for the PCNT module since stall time may be
1018  *   undefined.
1019  *
1020  * @param[in] pcnt
1021  *   A pointer to the PCNT peripheral register block.
1022  *
1023  * @param[in] val
1024  *   A value to set in the top register.
1025  ******************************************************************************/
PCNT_TopSet(PCNT_TypeDef * pcnt,uint32_t val)1026 void PCNT_TopSet(PCNT_TypeDef *pcnt, uint32_t val)
1027 {
1028   EFM_ASSERT(PCNT_REF_VALID(pcnt));
1029 
1030 #ifdef PCNT0
1031   if (PCNT0 == pcnt) {
1032     EFM_ASSERT((1 << PCNT0_CNT_SIZE) > val);
1033   }
1034 #endif
1035 
1036 #ifdef PCNT1
1037   if (PCNT1 == pcnt) {
1038     EFM_ASSERT((1 << PCNT1_CNT_SIZE) > val);
1039   }
1040 #endif
1041 
1042 #ifdef PCNT2
1043   if (PCNT2 == pcnt) {
1044     EFM_ASSERT((1 << PCNT2_CNT_SIZE) > val);
1045   }
1046 #endif
1047 
1048 #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
1049   /* LF register about to be modified requires sync; busy check. */
1050 
1051   /* Load into TOPB. */
1052   PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB);
1053   pcnt->TOPB = val;
1054 
1055   /* Load TOPB value into TOP. */
1056   PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD);
1057   pcnt->CMD = PCNT_CMD_LTOPBIM;
1058 #else
1059   /* LF register about to be modified requires sync; busy check. */
1060   PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOP);
1061   /* Load into TOP. */
1062   pcnt->TOP = val;
1063 #endif
1064 }
1065 
1066 /** @} (end addtogroup pcnt) */
1067 #endif /* defined(PCNT_COUNT) && (PCNT_COUNT > 0) */
1068