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 #ifndef __EM_PCNT_H
34 #define __EM_PCNT_H
35 
36 #include <stdbool.h>
37 #include "em_part.h"
38 
39 #ifdef __cplusplus
40 extern "C" {
41 #endif
42 
43 /***************************************************************************//**
44  * @addtogroup EM_Library
45  * @{
46  ******************************************************************************/
47 
48 /***************************************************************************//**
49  * @addtogroup PCNT
50  * @{
51  ******************************************************************************/
52 
53 /*******************************************************************************
54  ********************************   ENUMS   ************************************
55  ******************************************************************************/
56 
57 /** Mode selection. */
58 typedef enum
59 {
60   /** Disable pulse counter. */
61   pcntModeDisable   = _PCNT_CTRL_MODE_DISABLE,
62 
63   /** Single input LFACLK oversampling mode (available in EM0-EM2). */
64   pcntModeOvsSingle = _PCNT_CTRL_MODE_OVSSINGLE,
65 
66   /** Externally clocked single input counter mode (available in EM0-EM3). */
67   pcntModeExtSingle = _PCNT_CTRL_MODE_EXTCLKSINGLE,
68 
69   /** Externally clocked quadrature decoder mode (available in EM0-EM3). */
70   pcntModeExtQuad   = _PCNT_CTRL_MODE_EXTCLKQUAD
71 } PCNT_Mode_TypeDef;
72 
73 
74 #if (defined (_EFM32_TINY_FAMILY) || defined (_EFM32_GIANT_FAMILY))
75 /** Counter event selection.
76  *  Note: unshifted values are being used for enumeration because multiple
77  *  configuration structure members use this type definition. */
78 typedef enum
79 {
80   /** Counts up on up-count and down on down-count events. */
81   pcntCntEventBoth = _PCNT_CTRL_CNTEV_BOTH,
82 
83   /** Only counts up on up-count events. */
84   pcntCntEventUp   = _PCNT_CTRL_CNTEV_UP,
85 
86   /** Only counts down on down-count events. */
87   pcntCntEventDown = _PCNT_CTRL_CNTEV_DOWN,
88 
89   /** Never counts. */
90   pcntCntEventNone = _PCNT_CTRL_CNTEV_NONE
91 } PCNT_CntEvent_TypeDef;
92 
93 
94 /** PRS sources for @p s0PRS and @p s1PRS. */
95 typedef enum
96 {
97   pcntPRSCh0 = 0,     /**< PRS channel 0. */
98   pcntPRSCh1 = 1,     /**< PRS channel 1. */
99   pcntPRSCh2 = 2,     /**< PRS channel 2. */
100   pcntPRSCh3 = 3,     /**< PRS channel 3. */
101   pcntPRSCh4 = 4,     /**< PRS channel 4. */
102   pcntPRSCh5 = 5,     /**< PRS channel 5. */
103   pcntPRSCh6 = 6,     /**< PRS channel 6. */
104   pcntPRSCh7 = 7      /**< PRS channel 7. */
105 } PCNT_PRSSel_TypeDef;
106 
107 
108 /** PRS inputs of PCNT. */
109 typedef enum
110 {
111   pcntPRSInputS0 = 0, /** PRS input 0. */
112   pcntPRSInputS1 = 1  /** PRS input 1. */
113 } PCNT_PRSInput_TypeDef;
114 #endif
115 
116 
117 /*******************************************************************************
118  *******************************   STRUCTS   ***********************************
119  ******************************************************************************/
120 
121 /** Init structure. */
122 typedef struct
123 {
124   /** Mode to operate in. */
125   PCNT_Mode_TypeDef     mode;
126 
127   /** Initial counter value (refer to reference manual for max value allowed).
128    * Only used for #pcntModeOvsSingle (and possibly #pcntModeDisable) modes.
129    * If using #pcntModeExtSingle or #pcntModeExtQuad modes, the counter
130    * value is reset to HW reset value. */
131   uint32_t              counter;
132 
133   /** Initial top value (refer to reference manual for max value allowed).
134    * Only used for #pcntModeOvsSingle (and possibly #pcntModeDisable) modes.
135    * If using #pcntModeExtSingle or #pcntModeExtQuad modes, the top
136    * value is reset to HW reset value. */
137   uint32_t              top;
138 
139   /** Polarity of incoming edge.
140    * @li #pcntModeExtSingle mode - if false, positive edges are counted,
141    *   otherwise negative edges.
142    * @li #pcntModeExtQuad mode - if true, counting direction is inverted. */
143   bool                  negEdge;
144 
145   /** Counting direction, only applicable for #pcntModeOvsSingle and
146    * #pcntModeExtSingle modes. */
147   bool                  countDown;
148 
149   /** Enable filter, only available in #pcntModeOvsSingle mode. */
150   bool                  filter;
151 
152 #if (defined (_EFM32_TINY_FAMILY) || defined (_EFM32_GIANT_FAMILY))
153   /** Set to true to enable hysteresis. When its enabled, the PCNT will always
154    *  overflow and underflow to TOP/2. */
155   bool                  hyst;
156 
157   /** Set to true to enable S1 to determine the direction of counting in
158    *  OVSSINGLE or EXTCLKSINGLE modes.
159    *  When S1 is high, the count direction is given by CNTDIR, and when S1 is
160    *  low, the count direction is the opposite. */
161   bool                  s1CntDir;
162 
163   /** Selects whether the regular counter responds to up-count events,
164    *  down-count events, both or none. */
165   PCNT_CntEvent_TypeDef cntEvent;
166 
167   /** Selects whether the auxiliary counter responds to up-count events,
168    *  down-count events, both or none. */
169   PCNT_CntEvent_TypeDef auxCntEvent;
170 
171   /** Select PRS channel as input to S0IN in PCNTx_INPUT register. */
172   PCNT_PRSSel_TypeDef   s0PRS;
173 
174   /** Select PRS channel as input to S1IN in PCNTx_INPUT register. */
175   PCNT_PRSSel_TypeDef   s1PRS;
176 #endif
177 } PCNT_Init_TypeDef;
178 
179 /** Default config for PCNT init structure. */
180 #if defined (_EFM32_GECKO_FAMILY)
181 #define PCNT_INIT_DEFAULT                                                           \
182   { pcntModeDisable,                          /* Disabled by default. */            \
183     _PCNT_CNT_RESETVALUE,                     /* Default counter HW reset value. */ \
184     _PCNT_TOP_RESETVALUE,                     /* Default counter HW reset value. */ \
185     false,                                    /* Use positive edge. */              \
186     false,                                    /* Up-counting. */                    \
187     false                                     /* Filter disabled. */                \
188   }
189 #elif (defined (_EFM32_TINY_FAMILY) || defined (_EFM32_GIANT_FAMILY))
190 #define PCNT_INIT_DEFAULT                                                                        \
191   { pcntModeDisable,                          /* Disabled by default. */                         \
192     _PCNT_CNT_RESETVALUE,                     /* Default counter HW reset value. */              \
193     _PCNT_TOP_RESETVALUE,                     /* Default counter HW reset value. */              \
194     false,                                    /* Use positive edge. */                           \
195     false,                                    /* Up-counting. */                                 \
196     false,                                    /* Filter disabled. */                             \
197     false,                                    /* Hysteresis disabled. */                         \
198     true,                                     /* Counter direction is given by CNTDIR. */        \
199     pcntCntEventUp,                           /* Regular counter counts up on upcount events. */ \
200     pcntCntEventNone,                         /* Auxiliary counter doesn't respond to events. */ \
201     pcntPRSCh0,                               /* PRS channel 0 selected as S0IN. */              \
202     pcntPRSCh0                                /* PRS channel 0 selected as S1IN. */              \
203   }
204 #endif
205 
206 
207 /*******************************************************************************
208  *****************************   PROTOTYPES   **********************************
209  ******************************************************************************/
210 
211 /***************************************************************************//**
212  * @brief
213  *   Get pulse counter value.
214  *
215  * @param[in] pcnt
216  *   Pointer to PCNT peripheral register block.
217  *
218  * @return
219  *   Current pulse counter value.
220  ******************************************************************************/
PCNT_CounterGet(PCNT_TypeDef * pcnt)221 __STATIC_INLINE uint32_t PCNT_CounterGet(PCNT_TypeDef *pcnt)
222 {
223   return pcnt->CNT;
224 }
225 
226 
227 #if (defined (_EFM32_TINY_FAMILY) || defined (_EFM32_GIANT_FAMILY))
228 /***************************************************************************//**
229  * @brief
230  *   Get auxiliary counter value.
231  *
232  * @param[in] pcnt
233  *   Pointer to PCNT peripheral register block.
234  *
235  * @return
236  *   Current auxiliary counter value.
237  ******************************************************************************/
PCNT_AuxCounterGet(PCNT_TypeDef * pcnt)238 __STATIC_INLINE uint32_t PCNT_AuxCounterGet(PCNT_TypeDef *pcnt)
239 {
240   return pcnt->AUXCNT;
241 }
242 #endif
243 
244 
245 void PCNT_CounterReset(PCNT_TypeDef *pcnt);
246 void PCNT_CounterTopSet(PCNT_TypeDef *pcnt, uint32_t count, uint32_t top);
247 
248 
249 /***************************************************************************//**
250  * @brief
251  *   Set counter value.
252  *
253  * @details
254  *   The pulse counter is disabled while changing counter value, and reenabled
255  *   (if originally enabled) when counter value has been set.
256  *
257  * @note
258  *   This function will stall until synchronization to low frequency domain is
259  *   completed. For that reason, it should normally not be used when using
260  *   an external clock to clock the PCNT module, since stall time may be
261  *   undefined in that case. The counter should normally only be set when
262  *   operating in (or about to enable) #pcntModeOvsSingle mode.
263  *
264  * @param[in] pcnt
265  *   Pointer to PCNT peripheral register block.
266  *
267  * @param[in] count
268  *   Value to set in counter register.
269  ******************************************************************************/
PCNT_CounterSet(PCNT_TypeDef * pcnt,uint32_t count)270 __STATIC_INLINE void PCNT_CounterSet(PCNT_TypeDef *pcnt, uint32_t count)
271 {
272   PCNT_CounterTopSet(pcnt, count, pcnt->TOP);
273 }
274 
275 
276 void PCNT_Enable(PCNT_TypeDef *pcnt, PCNT_Mode_TypeDef mode);
277 void PCNT_FreezeEnable(PCNT_TypeDef *pcnt, bool enable);
278 void PCNT_Init(PCNT_TypeDef *pcnt, const PCNT_Init_TypeDef *init);
279 
280 #if (defined (_EFM32_TINY_FAMILY) || defined (_EFM32_GIANT_FAMILY))
281 void PCNT_PRSInputEnable(PCNT_TypeDef *pcnt,
282                          PCNT_PRSInput_TypeDef prsInput,
283                          bool enable);
284 #endif
285 
286 
287 /***************************************************************************//**
288  * @brief
289  *   Clear one or more pending PCNT interrupts.
290  *
291  * @param[in] pcnt
292  *   Pointer to PCNT peripheral register block.
293  *
294  * @param[in] flags
295  *   Pending PCNT interrupt source to clear. Use a bitwise logic OR combination
296  *   of valid interrupt flags for the PCNT module (PCNT_IF_nnn).
297  ******************************************************************************/
PCNT_IntClear(PCNT_TypeDef * pcnt,uint32_t flags)298 __STATIC_INLINE void PCNT_IntClear(PCNT_TypeDef *pcnt, uint32_t flags)
299 {
300   pcnt->IFC = flags;
301 }
302 
303 
304 /***************************************************************************//**
305  * @brief
306  *   Disable one or more PCNT interrupts.
307  *
308  * @param[in] pcnt
309  *   Pointer to PCNT peripheral register block.
310  *
311  * @param[in] flags
312  *   PCNT interrupt sources to disable. Use a bitwise logic OR combination of
313  *   valid interrupt flags for the PCNT module (PCNT_IF_nnn).
314  ******************************************************************************/
PCNT_IntDisable(PCNT_TypeDef * pcnt,uint32_t flags)315 __STATIC_INLINE void PCNT_IntDisable(PCNT_TypeDef *pcnt, uint32_t flags)
316 {
317   pcnt->IEN &= ~(flags);
318 }
319 
320 
321 /***************************************************************************//**
322  * @brief
323  *   Enable one or more PCNT interrupts.
324  *
325  * @note
326  *   Depending on the use, a pending interrupt may already be set prior to
327  *   enabling the interrupt. Consider using PCNT_IntClear() prior to enabling
328  *   if such a pending interrupt should be ignored.
329  *
330  * @param[in] pcnt
331  *   Pointer to PCNT peripheral register block.
332  *
333  * @param[in] flags
334  *   PCNT interrupt sources to enable. Use a bitwise logic OR combination of
335  *   valid interrupt flags for the PCNT module (PCNT_IF_nnn).
336  ******************************************************************************/
PCNT_IntEnable(PCNT_TypeDef * pcnt,uint32_t flags)337 __STATIC_INLINE void PCNT_IntEnable(PCNT_TypeDef *pcnt, uint32_t flags)
338 {
339   pcnt->IEN |= flags;
340 }
341 
342 
343 /***************************************************************************//**
344  * @brief
345  *   Get pending PCNT interrupt flags.
346  *
347  * @note
348  *   The event bits are not cleared by the use of this function.
349  *
350  * @param[in] pcnt
351  *   Pointer to PCNT peripheral register block.
352  *
353  * @return
354  *   PCNT interrupt sources pending. A bitwise logic OR combination of valid
355  *   interrupt flags for the PCNT module (PCNT_IF_nnn).
356  ******************************************************************************/
PCNT_IntGet(PCNT_TypeDef * pcnt)357 __STATIC_INLINE uint32_t PCNT_IntGet(PCNT_TypeDef *pcnt)
358 {
359   return pcnt->IF;
360 }
361 
362 
363 /***************************************************************************//**
364  * @brief
365  *   Get enabled and pending PCNT interrupt flags.
366  *
367  * @details
368  *   Useful for handling more interrupt sources in the same interrupt handler.
369  *
370  * @note
371  *   The event bits are not cleared by the use of this function.
372  *
373  * @param[in] pcnt
374  *   Pointer to PCNT peripheral register block.
375  *
376  * @return
377  *   Pending and enabled PCNT interrupt sources.
378  *   The return value is the bitwise AND combination of
379  *   - the OR combination of enabled interrupt sources in PCNT_IEN_nnn
380  *   register (PCNT_IEN_nnn) and
381  *   - the OR combination of valid interrupt flags of the PCNT module
382  *   (PCNT_IF_nnn).
383  ******************************************************************************/
PCNT_IntGetEnabled(PCNT_TypeDef * pcnt)384 __STATIC_INLINE uint32_t PCNT_IntGetEnabled(PCNT_TypeDef *pcnt)
385 {
386   uint32_t tmp = 0U;
387 
388 
389   /* Store pcnt->IEN in temporary variable in order to define explicit order
390    * of volatile accesses. */
391   tmp = pcnt->IEN;
392 
393   /* Bitwise AND of pending and enabled interrupts */
394   return pcnt->IF & tmp;
395 }
396 
397 
398 /***************************************************************************//**
399  * @brief
400  *   Set one or more pending PCNT interrupts from SW.
401  *
402  * @param[in] pcnt
403  *   Pointer to PCNT peripheral register block.
404  *
405  * @param[in] flags
406  *   PCNT interrupt sources to set to pending. Use a bitwise logic OR combination
407  *   of valid interrupt flags for the PCNT module (PCNT_IF_nnn).
408  ******************************************************************************/
PCNT_IntSet(PCNT_TypeDef * pcnt,uint32_t flags)409 __STATIC_INLINE void PCNT_IntSet(PCNT_TypeDef *pcnt, uint32_t flags)
410 {
411   pcnt->IFS = flags;
412 }
413 
414 void PCNT_Reset(PCNT_TypeDef *pcnt);
415 
416 
417 /***************************************************************************//**
418  * @brief
419  *   Get pulse counter top buffer value.
420  *
421  * @param[in] pcnt
422  *   Pointer to PCNT peripheral register block.
423  *
424  * @return
425  *   Current pulse counter top buffer value.
426  ******************************************************************************/
PCNT_TopBufferGet(PCNT_TypeDef * pcnt)427 __STATIC_INLINE uint32_t PCNT_TopBufferGet(PCNT_TypeDef *pcnt)
428 {
429   return pcnt->TOPB;
430 }
431 
432 void PCNT_TopBufferSet(PCNT_TypeDef *pcnt, uint32_t val);
433 
434 /***************************************************************************//**
435  * @brief
436  *   Get pulse counter top value.
437  *
438  * @param[in] pcnt
439  *   Pointer to PCNT peripheral register block.
440  *
441  * @return
442  *   Current pulse counter top value.
443  ******************************************************************************/
PCNT_TopGet(PCNT_TypeDef * pcnt)444 __STATIC_INLINE uint32_t PCNT_TopGet(PCNT_TypeDef *pcnt)
445 {
446   return pcnt->TOP;
447 }
448 
449 void PCNT_TopSet(PCNT_TypeDef *pcnt, uint32_t val);
450 
451 
452 /** @} (end addtogroup PCNT) */
453 /** @} (end addtogroup EM_Library) */
454 
455 #ifdef __cplusplus
456 }
457 #endif
458 
459 #endif /* __EM_PCNT_H */
460