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