1 /***************************************************************************//**
2 * @file
3 * @brief Pulse Counter (PCNT) peripheral API
4 * @author Energy Micro AS
5 * @version 3.0.0
6 *******************************************************************************
7 * @section License
8 * <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
9 *******************************************************************************
10 *
11 * Permission is granted to anyone to use this software for any purpose,
12 * including commercial applications, and to alter it and redistribute it
13 * freely, subject to the following restrictions:
14 *
15 * 1. The origin of this software must not be misrepresented; you must not
16 * claim that you wrote the original software.
17 * 2. Altered source versions must be plainly marked as such, and must not be
18 * misrepresented as being the original software.
19 * 3. This notice may not be removed or altered from any source distribution.
20 *
21 * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
22 * obligation to support this Software. Energy Micro AS is providing the
23 * Software "AS IS", with no express or implied warranties of any kind,
24 * including, but not limited to, any implied warranties of merchantability
25 * or fitness for any particular purpose or warranties against infringement
26 * of any proprietary rights of a third party.
27 *
28 * Energy Micro AS will not be liable for any consequential, incidental, or
29 * special damages, or any other relief, or for any claim by any third party,
30 * arising from your use of this Software.
31 *
32 ******************************************************************************/
33 #include "em_pcnt.h"
34 #include "em_cmu.h"
35 #include "em_assert.h"
36 #include "em_bitband.h"
37
38 /***************************************************************************//**
39 * @addtogroup EM_Library
40 * @{
41 ******************************************************************************/
42
43 /***************************************************************************//**
44 * @addtogroup PCNT
45 * @brief Pulse Counter (PCNT) Peripheral API
46 * @{
47 ******************************************************************************/
48
49 /*******************************************************************************
50 ******************************* DEFINES ***********************************
51 ******************************************************************************/
52
53 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
54
55
56 /** Validation of PCNT register block pointer reference for assert statements. */
57 #if (PCNT_COUNT == 1)
58 #define PCNT_REF_VALID(ref) ((ref) == PCNT0)
59 #elif (PCNT_COUNT == 2)
60 #define PCNT_REF_VALID(ref) (((ref) == PCNT0) || ((ref) == PCNT1))
61 #elif (PCNT_COUNT == 3)
62 #define PCNT_REF_VALID(ref) (((ref) == PCNT0) || ((ref) == PCNT1) || \
63 ((ref) == PCNT2))
64 #else
65 #error Undefined number of pulse counters (PCNT).
66 #endif
67
68 /** @endcond */
69
70
71 /*******************************************************************************
72 ************************** LOCAL FUNCTIONS ********************************
73 ******************************************************************************/
74
75 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
76
77 /***************************************************************************//**
78 * @brief
79 * Map PCNT structure into instance number.
80 *
81 * @param[in] pcnt
82 * Pointer to PCNT peripheral register block
83 *
84 * @return
85 * Instance number.
86 ******************************************************************************/
PCNT_Map(PCNT_TypeDef * pcnt)87 __STATIC_INLINE unsigned int PCNT_Map(PCNT_TypeDef *pcnt)
88 {
89 return(((uint32_t)pcnt - PCNT0_BASE) / 0x400);
90 }
91
92
93 /***************************************************************************//**
94 * @brief
95 * Wait for ongoing sync of register(s) to low frequency domain to complete.
96 *
97 * @param[in] pcnt
98 * Pointer to PCNT peripheral register block
99 *
100 * @param[in] mask
101 * Bitmask corresponding to SYNCBUSY register defined bits, indicating
102 * registers that must complete any ongoing synchronization.
103 ******************************************************************************/
PCNT_Sync(PCNT_TypeDef * pcnt,uint32_t mask)104 __STATIC_INLINE void PCNT_Sync(PCNT_TypeDef *pcnt, uint32_t mask)
105 {
106 /* Avoid deadlock if modifying the same register twice when freeze mode is
107 * activated. */
108 if (pcnt->FREEZE & PCNT_FREEZE_REGFREEZE)
109 {
110 return;
111 }
112
113 /* Wait for any pending previous write operation to have been completed in low
114 * frequency domain. */
115 while (pcnt->SYNCBUSY & mask)
116 ;
117 }
118
119 /** @endcond */
120
121 /*******************************************************************************
122 ************************** GLOBAL FUNCTIONS *******************************
123 ******************************************************************************/
124
125 /***************************************************************************//**
126 * @brief
127 * Reset PCNT counters and TOP register.
128 *
129 * @note
130 * Notice that special SYNCBUSY handling is not applicable for the RSTEN
131 * bit of the control register, so we don't need to wait for it when only
132 * modifying RSTEN. (It would mean undefined wait time if clocked by external
133 * clock.) The SYNCBUSY bit will however be set, leading to a synchronization
134 * in the LF domain, with in reality no changes.
135 *
136 * @param[in] pcnt
137 * Pointer to PCNT peripheral register block.
138 ******************************************************************************/
PCNT_CounterReset(PCNT_TypeDef * pcnt)139 void PCNT_CounterReset(PCNT_TypeDef *pcnt)
140 {
141 EFM_ASSERT(PCNT_REF_VALID(pcnt));
142
143 /* Enable reset of CNT and TOP register */
144 BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1);
145
146 /* Disable reset of CNT and TOP register */
147 BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0);
148 }
149
150
151 /***************************************************************************//**
152 * @brief
153 * Set counter and top values.
154 *
155 * @details
156 * The pulse counter is disabled while changing these values, and reenabled
157 * (if originally enabled) when values have been set.
158 *
159 * @note
160 * This function will stall until synchronization to low frequency domain is
161 * completed. For that reason, it should normally not be used when using
162 * an external clock to clock the PCNT module, since stall time may be
163 * undefined in that case. The counter should normally only be set when
164 * operating in (or about to enable) #pcntModeOvsSingle mode.
165 *
166 * @param[in] pcnt
167 * Pointer to PCNT peripheral register block.
168 *
169 * @param[in] count
170 * Value to set in counter register.
171 *
172 * @param[in] top
173 * Value to set in top register.
174 ******************************************************************************/
PCNT_CounterTopSet(PCNT_TypeDef * pcnt,uint32_t count,uint32_t top)175 void PCNT_CounterTopSet(PCNT_TypeDef *pcnt, uint32_t count, uint32_t top)
176 {
177 uint32_t ctrl;
178
179 EFM_ASSERT(PCNT_REF_VALID(pcnt));
180
181 /* Keep current control setting, must be restored */
182 ctrl = pcnt->CTRL;
183
184 /* If enabled, disable pulse counter before changing values */
185 if ((ctrl & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE)
186 {
187 PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
188 pcnt->CTRL = (ctrl & ~_PCNT_CTRL_MODE_MASK) | PCNT_CTRL_MODE_DISABLE;
189 }
190
191 /* Load into TOPB */
192 PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB);
193 pcnt->TOPB = count;
194
195 /* Load TOPB value into TOP */
196 PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD);
197
198 /* This bit has no effect on rev. C and onwards parts - for compatibility */
199 pcnt->CMD = PCNT_CMD_LTOPBIM;
200 PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD);
201
202 /* Load TOP into CNT */
203 pcnt->CMD = PCNT_CMD_LCNTIM;
204
205 /* Restore TOP? ('count' setting has been loaded into pcnt->TOP, better
206 * to use 'top' than pcnt->TOP in compare, since latter may in theory not
207 * be visible yet.) */
208 if (top != count)
209 {
210 /* Wait for command to sync LCNTIM before setting TOPB */
211 PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD);
212
213 /* Load into TOPB, we don't need to check for TOPB sync complete here,
214 * it has been ensured above. */
215 pcnt->TOPB = top;
216
217 /* Load TOPB value into TOP */
218 PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD);
219 pcnt->CMD = PCNT_CMD_LTOPBIM;
220 }
221
222 /* Reenable if it was enabled */
223 if ((ctrl & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE)
224 {
225 PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL | PCNT_SYNCBUSY_CMD);
226 pcnt->CTRL = ctrl;
227 }
228 }
229
230
231 /***************************************************************************//**
232 * @brief
233 * Set PCNT operational mode.
234 *
235 * @details
236 * Notice that this function does not do any configuration. Setting operational
237 * mode is normally only required after initialization is done, and if not
238 * done as part of initialization. Or if requiring to disable/reenable pulse
239 * counter.
240 *
241 * @note
242 * This function may stall until synchronization to low frequency domain is
243 * completed. For that reason, it should normally not be used when using
244 * an external clock to clock the PCNT module, since stall time may be
245 * undefined in that case.
246 *
247 * @param[in] pcnt
248 * Pointer to PCNT peripheral register block.
249 *
250 * @param[in] mode
251 * Operational mode to use for PCNT.
252 ******************************************************************************/
PCNT_Enable(PCNT_TypeDef * pcnt,PCNT_Mode_TypeDef mode)253 void PCNT_Enable(PCNT_TypeDef *pcnt, PCNT_Mode_TypeDef mode)
254 {
255 uint32_t tmp;
256
257 EFM_ASSERT(PCNT_REF_VALID(pcnt));
258
259 /* Set as specified */
260 tmp = pcnt->CTRL & ~_PCNT_CTRL_MODE_MASK;
261 tmp |= (uint32_t)mode << _PCNT_CTRL_MODE_SHIFT;
262
263 /* LF register about to be modified require sync. busy check */
264 PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
265 pcnt->CTRL = tmp;
266 }
267
268 #if (defined (_EFM32_TINY_FAMILY) || defined (_EFM32_GIANT_FAMILY))
269 /***************************************************************************//**
270 * @brief
271 * Enable/disable the selected PRS input of PCNT.
272 *
273 * @details
274 * Notice that this function does not do any configuration.
275 *
276 * @param[in] pcnt
277 * Pointer to PCNT peripheral register block.
278 *
279 * @param[in] prsInput
280 * PRS input (S0 or S1) of the selected PCNT module.
281 *
282 * @param[in] enable
283 * Set to true to enable, false to disable the selected PRS input.
284 ******************************************************************************/
PCNT_PRSInputEnable(PCNT_TypeDef * pcnt,PCNT_PRSInput_TypeDef prsInput,bool enable)285 void PCNT_PRSInputEnable(PCNT_TypeDef *pcnt,
286 PCNT_PRSInput_TypeDef prsInput,
287 bool enable)
288 {
289 EFM_ASSERT(PCNT_REF_VALID(pcnt));
290
291 /* Enable/disable the selected PRS input on the selected PCNT module. */
292 switch (prsInput)
293 {
294 /* Enable/disable PRS input S0. */
295 case pcntPRSInputS0:
296 {
297 BITBAND_Peripheral(&(pcnt->INPUT), _PCNT_INPUT_S0PRSEN_SHIFT, (uint32_t)enable);
298 }
299 break;
300
301 /* Enable/disable PRS input S1. */
302 case pcntPRSInputS1:
303 {
304 BITBAND_Peripheral(&(pcnt->INPUT), _PCNT_INPUT_S1PRSEN_SHIFT, (uint32_t)enable);
305 }
306 break;
307
308 /* Invalid parameter, asserted. */
309 default:
310 {
311 EFM_ASSERT(0);
312 }
313 break;
314 }
315 }
316 #endif
317
318
319 /***************************************************************************//**
320 * @brief
321 * PCNT register synchronization freeze control.
322 *
323 * @details
324 * Some PCNT registers require synchronization into the low frequency (LF)
325 * domain. The freeze feature allows for several such registers to be
326 * modified before passing them to the LF domain simultaneously (which
327 * takes place when the freeze mode is disabled).
328 *
329 * @note
330 * When enabling freeze mode, this function will wait for all current
331 * ongoing PCNT synchronization to LF domain to complete (Normally
332 * synchronization will not be in progress.) However for this reason, when
333 * using freeze mode, modifications of registers requiring LF synchronization
334 * should be done within one freeze enable/disable block to avoid unecessary
335 * stalling.
336 *
337 * @param[in] pcnt
338 * Pointer to PCNT peripheral register block.
339 *
340 * @param[in] enable
341 * @li true - enable freeze, modified registers are not propagated to the
342 * LF domain
343 * @li false - disables freeze, modified registers are propagated to LF
344 * domain
345 ******************************************************************************/
PCNT_FreezeEnable(PCNT_TypeDef * pcnt,bool enable)346 void PCNT_FreezeEnable(PCNT_TypeDef *pcnt, bool enable)
347 {
348 EFM_ASSERT(PCNT_REF_VALID(pcnt));
349
350 if (enable)
351 {
352 /* Wait for any ongoing LF synchronization to complete. This is just to
353 * protect against the rare case when a user:
354 * - modifies a register requiring LF sync
355 * - then enables freeze before LF sync completed
356 * - then modifies the same register again
357 * since modifying a register while it is in sync progress should be
358 * avoided. */
359 while (pcnt->SYNCBUSY)
360 ;
361
362 pcnt->FREEZE = PCNT_FREEZE_REGFREEZE;
363 }
364 else
365 {
366 pcnt->FREEZE = 0;
367 }
368 }
369
370
371 /***************************************************************************//**
372 * @brief
373 * Init pulse counter.
374 *
375 * @details
376 * This function will configure the pulse counter. The clock selection is
377 * configured as follows, depending on operational mode:
378 *
379 * @li #pcntModeOvsSingle - Use LFACLK.
380 * @li #pcntModeExtSingle - Use external PCNTn_S0 pin.
381 * @li #pcntModeExtQuad - Use external PCNTn_S0 pin.
382 *
383 * Notice that the LFACLK must be enabled in all modes, since some basic setup
384 * is done with this clock even if external pin clock usage mode is chosen.
385 * The pulse counter clock for the selected instance must also be enabled
386 * prior to init.
387 *
388 * Notice that pins used by the PCNT module must be properly configured
389 * by the user explicitly through setting the ROUTE register, in order for
390 * the PCNT to work as intended.
391 *
392 * Writing to CNT will not occur in external clock modes (EXTCLKQUAD and
393 * EXTCLKSINGLE) because the external clock rate is unknown. The user should
394 * handle it manually depending on the application
395 *
396 * TOPB is written for all modes but in external clock mode it will take
397 * 3 external clock cycles to sync to TOP
398 *
399 *
400 * @note
401 * Initializing requires synchronization into the low frequency domain. This
402 * may cause some delay.
403 *
404 * @param[in] pcnt
405 * Pointer to PCNT peripheral register block.
406 *
407 * @param[in] init
408 * Pointer to initialization structure used to initialize.
409 ******************************************************************************/
PCNT_Init(PCNT_TypeDef * pcnt,const PCNT_Init_TypeDef * init)410 void PCNT_Init(PCNT_TypeDef *pcnt, const PCNT_Init_TypeDef *init)
411 {
412 unsigned int inst;
413 uint32_t tmp;
414
415 EFM_ASSERT(PCNT_REF_VALID(pcnt));
416
417 /* Map pointer to instance */
418 inst = PCNT_Map(pcnt);
419
420 #if (defined (_EFM32_TINY_FAMILY) || defined (_EFM32_GIANT_FAMILY))
421 /* Selecting the PRS channels for the PRS input sources of the PCNT. These are
422 * written with a Read-Modify-Write sequence in order to keep the value of the
423 * input enable bits which can be modified using PCNT_PRSInputEnable(). */
424 tmp = pcnt->INPUT & ~(_PCNT_INPUT_S0PRSSEL_MASK | _PCNT_INPUT_S1PRSSEL_MASK);
425 tmp |= ((uint32_t)init->s0PRS << _PCNT_INPUT_S0PRSSEL_SHIFT) |
426 ((uint32_t)init->s1PRS << _PCNT_INPUT_S1PRSSEL_SHIFT);
427 pcnt->INPUT = tmp;
428 #endif
429
430 /* Build CTRL setting, except for mode */
431 tmp = 0;
432 if (init->negEdge)
433 {
434 tmp |= PCNT_CTRL_EDGE_NEG;
435 }
436
437 if (init->countDown)
438 {
439 tmp |= PCNT_CTRL_CNTDIR_DOWN;
440 }
441
442 if (init->filter)
443 {
444 tmp |= PCNT_CTRL_FILT;
445 }
446
447 #if (defined (_EFM32_TINY_FAMILY) || defined (_EFM32_GIANT_FAMILY))
448 if (init->hyst)
449 {
450 tmp |= PCNT_CTRL_HYST;
451 }
452
453 if (init->s1CntDir)
454 {
455 tmp |= PCNT_CTRL_S1CDIR;
456 }
457
458 /* Configure counter events for regular and auxiliary counter. */
459 tmp |= init->cntEvent << _PCNT_CTRL_CNTEV_SHIFT;
460 tmp |= init->auxCntEvent << _PCNT_CTRL_AUXCNTEV_SHIFT;
461 #endif
462
463 /* Reset pulse counter while changing clock source. The reset bit */
464 /* is asynchronous, we don't have to check for SYNCBUSY. */
465 BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1);
466
467 /* Select LFACLK to clock in control setting */
468 CMU_PCNTClockExternalSet(inst, false);
469
470 /* Handling depends on whether using external clock or not. */
471 switch (init->mode)
472 {
473 case pcntModeExtSingle:
474 case pcntModeExtQuad:
475 tmp |= init->mode << _PCNT_CTRL_MODE_SHIFT;
476
477 /* In most cases, the SYNCBUSY bit is set due to reset bit set, and waiting
478 * for asynchronous reset bit is strictly not necessary.
479 * But in theory, other operations on CTRL register may have been done
480 * outside this function, so wait. */
481 PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
482
483 /* Enable PCNT Clock Domain Reset. The PCNT must be in reset before changing
484 * the clock source to an external clock */
485 pcnt->CTRL = PCNT_CTRL_RSTEN;
486
487 /* Wait until CTRL write synchronized into LF domain. */
488 PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
489
490 /* Change to external clock BEFORE disabling reset */
491 CMU_PCNTClockExternalSet(inst, true);
492
493 /* Write to TOPB. If using external clock TOPB will sync to TOP at the same
494 * time as the mode. This will insure that if the user chooses to count
495 * down, the first "countable" pulse will make CNT go to TOP and not 0xFF
496 * (default TOP value). */
497 pcnt->TOPB = init->top;
498
499 /* This bit has no effect on rev. C and onwards parts - for compatibility */
500 pcnt->CMD = PCNT_CMD_LTOPBIM;
501
502 /* Write the CTRL register with the configurations.
503 * This should be written after TOPB in the eventuality of a pulse between
504 * these two writes that would cause the CTRL register to be synced one
505 * clock cycle earlier than the TOPB. */
506 pcnt->CTRL = tmp;
507
508 /* There are no syncs for TOP, CMD or CTRL because the clock rate is unknown
509 * and the program could stall
510 * These will be synced within 3 clock cycles of the external clock /
511 * For the same reason CNT cannot be written here. */
512 break;
513
514 /* pcntModeDisable */
515 /* pcntModeOvsSingle */
516 default:
517 /* No need to set disabled mode if already disabled. */
518 if ((pcnt->CTRL & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE)
519 {
520 /* Set control to disabled mode, leave reset on until ensured disabled.
521 * We don't need to wait for CTRL SYNCBUSY completion here, it was
522 * triggered by reset bit above, which is asynchronous. */
523 pcnt->CTRL = tmp | PCNT_CTRL_MODE_DISABLE | PCNT_CTRL_RSTEN;
524
525 /* Wait until CTRL write synchronized into LF domain before proceeding
526 * to disable reset. */
527 PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
528 }
529
530 /* Disable reset bit, counter should now be in disabled mode. */
531 BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0);
532
533 /* Set counter and top values as specified. */
534 PCNT_CounterTopSet(pcnt, init->counter, init->top);
535
536 /* Enter oversampling mode if selected. */
537 if (init->mode == pcntModeOvsSingle)
538 {
539 PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
540 pcnt->CTRL = tmp | (init->mode << _PCNT_CTRL_MODE_SHIFT);
541 }
542 break;
543 }
544 }
545
546
547 /***************************************************************************//**
548 * @brief
549 * Reset PCNT to same state as after a HW reset.
550 *
551 * @details
552 * Notice the LFACLK must be enabled, since some basic reset is done with
553 * this clock. The pulse counter clock for the selected instance must also
554 * be enabled prior to init.
555 *
556 * @note
557 * The ROUTE register is NOT reset by this function, in order to allow for
558 * centralized setup of this feature.
559 *
560 * @param[in] pcnt
561 * Pointer to PCNT peripheral register block.
562 ******************************************************************************/
PCNT_Reset(PCNT_TypeDef * pcnt)563 void PCNT_Reset(PCNT_TypeDef *pcnt)
564 {
565 unsigned int inst;
566
567 EFM_ASSERT(PCNT_REF_VALID(pcnt));
568
569 /* Map pointer to instance and clock info */
570 inst = PCNT_Map(pcnt);
571
572 pcnt->IEN = _PCNT_IEN_RESETVALUE;
573
574 /* Notice that special SYNCBUSY handling is not applicable for the RSTEN
575 * bit of the control register, so we don't need to wait for it when only
576 * modifying RSTEN. The SYNCBUSY bit will be set, leading to a
577 * synchronization in the LF domain, with in reality no changes to LF domain.
578 * Enable reset of CNT and TOP register. */
579 BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1);
580
581 /* Select LFACLK as default */
582 CMU_PCNTClockExternalSet(inst, false);
583
584 PCNT_TopBufferSet(pcnt, _PCNT_TOPB_RESETVALUE);
585
586 /* Reset CTRL leaving RSTEN set */
587 pcnt->CTRL = _PCNT_CTRL_RESETVALUE | PCNT_CTRL_RSTEN;
588
589 /* Disable reset after CTRL reg has been synchronized */
590 PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
591 BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0);
592
593 /* Clear pending interrupts */
594 pcnt->IFC = _PCNT_IFC_MASK;
595
596 /* Do not reset route register, setting should be done independently */
597 }
598
599
600 /***************************************************************************//**
601 * @brief
602 * Set top buffer value.
603 *
604 * @note
605 * This function may stall until synchronization to low frequency domain is
606 * completed. For that reason, it should normally not be used when using
607 * an external clock to clock the PCNT module, since stall time may be
608 * undefined in that case.
609 *
610 * @param[in] pcnt
611 * Pointer to PCNT peripheral register block.
612 *
613 * @param[in] val
614 * Value to set in top buffer register.
615 ******************************************************************************/
PCNT_TopBufferSet(PCNT_TypeDef * pcnt,uint32_t val)616 void PCNT_TopBufferSet(PCNT_TypeDef *pcnt, uint32_t val)
617 {
618 EFM_ASSERT(PCNT_REF_VALID(pcnt));
619
620 /* LF register about to be modified require sync. busy check */
621 PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB);
622 pcnt->TOPB = val;
623 }
624
625
626 /***************************************************************************//**
627 * @brief
628 * Set top value.
629 *
630 * @note
631 * This function will stall until synchronization to low frequency domain is
632 * completed. For that reason, it should normally not be used when using
633 * an external clock to clock the PCNT module, since stall time may be
634 * undefined in that case.
635 *
636 * @param[in] pcnt
637 * Pointer to PCNT peripheral register block.
638 *
639 * @param[in] val
640 * Value to set in top register.
641 ******************************************************************************/
PCNT_TopSet(PCNT_TypeDef * pcnt,uint32_t val)642 void PCNT_TopSet(PCNT_TypeDef *pcnt, uint32_t val)
643 {
644 EFM_ASSERT(PCNT_REF_VALID(pcnt));
645
646 /* LF register about to be modified require sync. busy check */
647
648 /* Load into TOPB */
649 PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB);
650 pcnt->TOPB = val;
651
652 /* Load TOPB value into TOP */
653 PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD);
654 pcnt->CMD = PCNT_CMD_LTOPBIM;
655 }
656
657
658 /** @} (end addtogroup PCNT) */
659 /** @} (end addtogroup EM_Library) */
660