1 /**************************************************************************//**
2  * @file     lptmr.c
3  * @brief    LPTMR Controller (Low Power Timer) driver source file
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  * @copyright (C) 2023 Nuvoton Technology Corp. All rights reserved.
7 *****************************************************************************/
8 #include "NuMicro.h"
9 
10 
11 /** @addtogroup Standard_Driver Standard Driver
12   @{
13 */
14 
15 /** @addtogroup LPTMR_Driver LPTMR Driver
16   @{
17 */
18 
19 /** @addtogroup LPTMR_EXPORTED_FUNCTIONS LPTMR Exported Functions
20   @{
21 */
22 
23 /**
24   * @brief      Open LPTMR with Operate Mode and Frequency
25   *
26   * @param[in]  lptmr       The pointer of the specified LPTMR module. It could be LPTMR0, LPTMR1.
27   * @param[in]  u32Mode     Operation mode. Possible options are
28   *                         - \ref LPTMR_ONESHOT_MODE
29   *                         - \ref LPTMR_PERIODIC_MODE
30   *                         - \ref LPTMR_TOGGLE_MODE
31   *                         - \ref LPTMR_CONTINUOUS_MODE
32   * @param[in]  u32Freq     Target working frequency
33   *
34   * @return     Real lptmr working frequency
35   *
36   * @details    This API is used to configure lptmr to operate in specified mode and frequency.
37   *             If lptmr cannot work in target frequency, a closest frequency will be chose and returned.
38   * @note       After calling this API, LPTMR is \b NOT running yet. But could start lptmr running be calling
39   *             \ref LPTMR_Start macro or program registers directly.
40   */
LPTMR_Open(LPTMR_T * lptmr,uint32_t u32Mode,uint32_t u32Freq)41 uint32_t LPTMR_Open(LPTMR_T *lptmr, uint32_t u32Mode, uint32_t u32Freq)
42 {
43     uint32_t u32Clk = LPTMR_GetModuleClock(lptmr);
44     uint32_t u32Cmpr = 0UL, u32Prescale = 0UL;
45 
46     if (u32Freq == 0)
47         return 0;
48 
49     /* Fastest possible lptmr working freq is (u32Clk / 2). While cmpr = 2, prescaler = 0. */
50     if(u32Freq > (u32Clk / 2UL))
51     {
52         u32Cmpr = 2UL;
53     }
54     else
55     {
56         u32Cmpr = u32Clk / u32Freq;
57         u32Prescale = (u32Cmpr >> 24);  /* for 24 bits CMPDAT */
58         if (u32Prescale > 0UL)
59             u32Cmpr = u32Cmpr / (u32Prescale + 1UL);
60     }
61 
62     lptmr->CTL = (u32Mode | u32Prescale);
63     lptmr->CMP = u32Cmpr;
64 
65     return(u32Clk / (u32Cmpr * (u32Prescale + 1UL)));
66 }
67 
68 
69 /**
70   * @brief      Stop LPTMR Counting
71   *
72   * @param[in]  lptmr   The pointer of the specified LPTMR module. It could be LPTMR0, LPTMR1.
73   *
74   * @return     None
75   *
76   * @details    This API stops lptmr counting and disable all lptmr interrupt function.
77   */
LPTMR_Close(LPTMR_T * lptmr)78 void LPTMR_Close(LPTMR_T *lptmr)
79 {
80     lptmr->CTL = 0UL;
81     lptmr->EXTCTL = 0UL;
82 }
83 
84 
85 /**
86   * @brief      Create a specify Delay Time
87   *
88   * @param[in]  lptmr       The pointer of the specified LPTMR module. It could be LPTMR0, LPTMR1.
89   * @param[in]  u32Usec     Delay period in micro seconds. Valid values are between 100~1000000 (100 micro second ~ 1 second).
90   *
91   * @retval     0                 Delay success, target delay time reached
92   * @retval     LPTMR_TIMEOUT_ERR Delay function execute failed due to timer stop working
93   *
94   * @details    This API is used to create a delay loop for u32usec micro seconds by using lptmr one-shot mode.
95   * @note       This API overwrites the register setting of the lptmr used to count the delay time.
96   * @note       This API use polling mode. So there is no need to enable interrupt for the lptmr module used to generate delay.
97   */
LPTMR_Delay(LPTMR_T * lptmr,uint32_t u32Usec)98 int32_t LPTMR_Delay(LPTMR_T *lptmr, uint32_t u32Usec)
99 {
100     uint32_t u32Clk = LPTMR_GetModuleClock(lptmr);
101     uint32_t u32Prescale = 0UL, u32Delay;
102     uint32_t u32Cmpr, u32Cntr, u32NsecPerTick, i = 0UL;
103 
104     /* Clear current lptmr configuration */
105     lptmr->CTL = 0UL;
106     lptmr->EXTCTL = 0UL;
107 
108     if(u32Clk <= 1000000UL)   /* min delay is 1000 us if lptmr clock source is <= 1 MHz */
109     {
110         if(u32Usec < 1000UL)
111         {
112             u32Usec = 1000UL;
113         }
114         if(u32Usec > 1000000UL)
115         {
116             u32Usec = 1000000UL;
117         }
118     }
119     else
120     {
121         if(u32Usec < 100UL)
122         {
123             u32Usec = 100UL;
124         }
125         if(u32Usec > 1000000UL)
126         {
127             u32Usec = 1000000UL;
128         }
129     }
130 
131     if(u32Clk <= 1000000UL)
132     {
133         u32Prescale = 0UL;
134         u32NsecPerTick = 1000000000UL / u32Clk;
135         u32Cmpr = (u32Usec * 1000UL) / u32NsecPerTick;
136     }
137     else
138     {
139         u32Cmpr = u32Usec * (u32Clk / 1000000UL);
140         u32Prescale = (u32Cmpr >> 24);  /* for 24 bits CMPDAT */
141         if (u32Prescale > 0UL)
142             u32Cmpr = u32Cmpr / (u32Prescale + 1UL);
143     }
144 
145     lptmr->CMP = u32Cmpr;
146     lptmr->CTL = LPTMR_CTL_CNTEN_Msk | LPTMR_ONESHOT_MODE | u32Prescale;
147 
148     /* When system clock is faster than LPTMR clock, it is possible LPTMR active bit cannot set
149        in time while we check it. And the while loop below return immediately, so put a tiny
150        delay larger than 1 ECLK here allowing LPTMR start counting and raise active flag. */
151     for(u32Delay = (SystemCoreClock / u32Clk) + 1UL; u32Delay > 0UL; u32Delay--)
152     {
153         __NOP();
154     }
155 
156     /* Add a bail out counter here in case timer clock source is disabled accidentally.
157        Prescale counter reset every ECLK * (prescale value + 1).
158        The u32Delay here is to make sure timer counter value changed when prescale counter reset */
159     u32Delay = (SystemCoreClock / LPTMR_GetModuleClock(lptmr)) * (u32Prescale + 1);
160     u32Cntr = lptmr->CNT;
161     i = 0;
162     while(lptmr->CTL & LPTMR_CTL_ACTSTS_Msk)
163     {
164         /* Bailed out if LPTMR stop counting e.g. Some interrupt handler close LPTMR clock source. */
165         if(u32Cntr == lptmr->CNT)
166         {
167             if(i++ > u32Delay)
168             {
169                 return LPTMR_TIMEOUT_ERR;
170             }
171         }
172         else
173         {
174             i = 0;
175             u32Cntr = lptmr->CNT;
176         }
177     }
178     return 0;
179 }
180 
181 
182 /**
183   * @brief      Enable LPTMR Capture Function
184   *
185   * @param[in]  lptmr       The pointer of the specified LPTMR module. It could be LPTMR0, LPTMR1.
186   * @param[in]  u32CapMode  LPTMR capture mode. Could be
187   *                         - \ref LPTMR_CAPTURE_FREE_COUNTING_MODE
188   *                         - \ref LPTMR_CAPTURE_COUNTER_RESET_MODE
189   * @param[in]  u32Edge     LPTMR capture trigger edge. Possible values are
190   *                         - \ref LPTMR_CAPTURE_EVENT_FALLING
191   *                         - \ref LPTMR_CAPTURE_EVENT_RISING
192   *                         - \ref LPTMR_CAPTURE_EVENT_FALLING_RISING
193   *                         - \ref LPTMR_CAPTURE_EVENT_RISING_FALLING
194   *                         - \ref LPTMR_CAPTURE_EVENT_GET_LOW_PERIOD
195   *                         - \ref LPTMR_CAPTURE_EVENT_GET_HIGH_PERIOD
196   *
197   * @return     None
198   *
199   * @details    This API is used to enable lptmr capture function with specify capture trigger edge \n
200   *             to get current counter value or reset counter value to 0.
201   * @note       LPTMR frequency should be configured separately by using \ref LPTMR_Open API, or program registers directly.
202   */
LPTMR_EnableCapture(LPTMR_T * lptmr,uint32_t u32CapMode,uint32_t u32Edge)203 void LPTMR_EnableCapture(LPTMR_T *lptmr, uint32_t u32CapMode, uint32_t u32Edge)
204 {
205     lptmr->EXTCTL = (lptmr->EXTCTL & ~(LPTMR_EXTCTL_CAPFUNCS_Msk | LPTMR_EXTCTL_CAPEDGE_Msk)) |
206                     u32CapMode | u32Edge | LPTMR_EXTCTL_CAPEN_Msk;
207 }
208 
209 
210 /**
211   * @brief      Disable LPTMR Capture Function
212   *
213   * @param[in]  lptmr   The pointer of the specified LPTMR module. It could be LPTMR0, LPTMR1.
214   *
215   * @return     None
216   *
217   * @details    This API is used to disable the lptmr capture function.
218   */
LPTMR_DisableCapture(LPTMR_T * lptmr)219 void LPTMR_DisableCapture(LPTMR_T *lptmr)
220 {
221     lptmr->EXTCTL &= ~LPTMR_EXTCTL_CAPEN_Msk;
222 }
223 
224 
225 /**
226   * @brief      Enable LPTMR Counter Function
227   *
228   * @param[in]  lptmr       The pointer of the specified LPTMR module. It could be LPTMR0, LPTMR1.
229   * @param[in]  u32Edge     Detection edge of counter pin. Could be ether
230   *                         - \ref LPTMR_COUNTER_EVENT_FALLING
231   *                         - \ref LPTMR_COUNTER_EVENT_RISING
232   *
233   * @return     None
234   *
235   * @details    This function is used to enable the lptmr counter function with specify detection edge.
236   * @note       LPTMR compare value should be configured separately by using \ref LPTMR_SET_CMP_VALUE macro or program registers directly.
237   * @note       While using event counter function, \ref LPTMR_TOGGLE_MODE cannot set as lptmr operation mode.
238   */
LPTMR_EnableEventCounter(LPTMR_T * lptmr,uint32_t u32Edge)239 void LPTMR_EnableEventCounter(LPTMR_T *lptmr, uint32_t u32Edge)
240 {
241     lptmr->EXTCTL = (lptmr->EXTCTL & ~LPTMR_EXTCTL_CNTPHASE_Msk) | u32Edge;
242     lptmr->CTL |= LPTMR_CTL_EXTCNTEN_Msk;
243 }
244 
245 
246 /**
247   * @brief      Disable LPTMR Counter Function
248   *
249   * @param[in]  lptmr   The pointer of the specified LPTMR module. It could be LPTMR0, LPTMR1.
250   *
251   * @return     None
252   *
253   * @details    This API is used to disable the lptmr event counter function.
254   */
LPTMR_DisableEventCounter(LPTMR_T * lptmr)255 void LPTMR_DisableEventCounter(LPTMR_T *lptmr)
256 {
257     lptmr->CTL &= ~LPTMR_CTL_EXTCNTEN_Msk;
258 }
259 
260 
261 /**
262   * @brief      Get LPTMR Clock Frequency
263   *
264   * @param[in]  lptmr   The pointer of the specified LPTMR module. It could be LPTMR0, LPTMR1.
265   *
266   * @return     LPTMR clock frequency
267   *
268   * @details    This API is used to get the lptmr clock frequency.
269   * @note       This API cannot return correct clock rate if lptmr source is from external clock input.
270   */
LPTMR_GetModuleClock(LPTMR_T * lptmr)271 uint32_t LPTMR_GetModuleClock(LPTMR_T *lptmr)
272 {
273     uint32_t u32Src, u32Clk;
274     const uint32_t au32Clk[] = {__HIRC, __MIRC, __LXT, __LIRC, 0UL, 0UL, 0UL, 0UL};
275 
276     if(lptmr == LPTMR0)
277     {
278         u32Src = (LPSCC->CLKSEL0 & LPSCC_CLKSEL0_LPTMR0SEL_Msk) >> LPSCC_CLKSEL0_LPTMR0SEL_Pos;
279     }
280     else if(lptmr == LPTMR1)
281     {
282         u32Src = (LPSCC->CLKSEL0 & LPSCC_CLKSEL0_LPTMR1SEL_Msk) >> LPSCC_CLKSEL0_LPTMR1SEL_Pos;
283     }
284 
285     u32Clk = au32Clk[u32Src];
286 
287     return u32Clk;
288 }
289 
290 
291 /**
292   * @brief This function is used to select the interrupt source used to trigger other modules.
293   * @param[in] lptmr The base address of LPTMR module
294   * @param[in] u32Src Selects the interrupt source to trigger other modules. Could be:
295   *              - \ref LPTMR_TRGSRC_TIMEOUT_EVENT
296   *              - \ref LPTMR_TRGSRC_CAPTURE_EVENT
297   * @return None
298   */
LPTMR_SetTriggerSource(LPTMR_T * lptmr,uint32_t u32Src)299 void LPTMR_SetTriggerSource(LPTMR_T *lptmr, uint32_t u32Src)
300 {
301     lptmr->TRGCTL = (lptmr->TRGCTL & ~LPTMR_TRGCTL_TRGSSEL_Msk) | u32Src;
302 }
303 
304 
305 /**
306   * @brief This function is used to set modules trigger by lptmr interrupt
307   * @param[in] lptmr The base address of LPTMR module
308   * @param[in] u32Mask The mask of modules (Low power IPs and LPPDMA) trigger by lptmr. Is the combination of
309   *             - \ref LPTMR_TRGEN
310   *             - \ref LPTMR_TRG_TO_LPPDMA
311   * @return None
312   */
LPTMR_SetTriggerTarget(LPTMR_T * lptmr,uint32_t u32Mask)313 void LPTMR_SetTriggerTarget(LPTMR_T *lptmr, uint32_t u32Mask)
314 {
315     lptmr->TRGCTL = (lptmr->TRGCTL & ~(LPTMR_TRGCTL_TRGEN_Msk | LPTMR_TRGCTL_TRGLPPDMA_Msk)) | u32Mask;
316 }
317 
318 
319 /**
320   * @brief      Select LPTMR Capture Source
321   *
322   * @param[in]  lptmr       The pointer of the specified LPTMR module.
323   * @param[in]  u32Src      LPTMR capture source. Possible values are
324   *                         - \ref LPTMR_CAPTURE_FROM_EXTERNAL
325   *                         - \ref LPTMR_CAPTURE_FROM_ACMP0
326   *                         - \ref LPTMR_CAPTURE_FROM_ACMP1
327   *                         - \ref LPTMR_CAPTURE_FROM_ACMP2
328   *
329   * @return     None
330   *
331   * @details    This API is used to select LPTMR capture source from Tx_EXT or internal signal.
332   */
LPTMR_CaptureSelect(LPTMR_T * lptmr,uint32_t u32Src)333 void LPTMR_CaptureSelect(LPTMR_T *lptmr, uint32_t u32Src)
334 {
335     if (u32Src == LPTMR_CAPTURE_FROM_EXTERNAL)
336     {
337         lptmr->CTL = (lptmr->CTL & ~(LPTMR_CTL_CAPSRC_Msk)) |
338                      (LPTMR_CAPSRC_TMX_EXT);
339     }
340     else
341     {
342         lptmr->CTL = (lptmr->CTL & ~(LPTMR_CTL_CAPSRC_Msk)) |
343                      (LPTMR_CAPSRC_INTERNAL);
344         lptmr->EXTCTL = (lptmr->EXTCTL & ~(LPTMR_EXTCTL_INTERCAPSEL_Msk)) |
345                         (u32Src);
346     }
347 }
348 
349 
350 /**
351   * @brief      Reset LPTMR Counter
352   *
353   * @param[in]  lptmr The base address of Timer module
354   *
355   * @return     Reset success or not
356   * @retval     0 Timer reset success
357   * @retval     LPTMR_TIMEOUT_ERR Timer reset failed
358   *
359   * @details    This function is used to reset current counter value and internal prescale counter value.
360   */
LPTMR_ResetCounter(LPTMR_T * lptmr)361 int32_t LPTMR_ResetCounter(LPTMR_T *lptmr)
362 {
363     uint32_t u32Delay;
364 
365     lptmr->CNT |= LPTMR_CNT_RSTACT_Msk;
366     /* Takes 2~3 ECLKs to reset timer counter */
367     u32Delay = (SystemCoreClock / LPTMR_GetModuleClock(lptmr)) * 3;
368     while(((lptmr->CNT & LPTMR_CNT_RSTACT_Msk) == LPTMR_CNT_RSTACT_Msk) && (--u32Delay))
369     {
370         __NOP();
371     }
372     return ((u32Delay > 0) ? 0 : LPTMR_TIMEOUT_ERR);
373 }
374 
375 /**
376   * @brief      Enable Capture Input Noise Filter Function
377   *
378   * @param[in]  lptmr           The pointer of the specified LPTMR module. It could be LPTMR0, LPTMR1.
379   *
380   * @param[in]  u32FilterCount  Noise filter counter. Valid values are between 0~7.
381   *
382   * @param[in]  u32ClkSrcSel    Noise filter counter clock source, could be one of following source
383   *                                 - \ref LPTMR_CAPTURE_NOISE_FILTER_PCLK_DIV_1
384   *                                 - \ref LPTMR_CAPTURE_NOISE_FILTER_PCLK_DIV_2
385   *                                 - \ref LPTMR_CAPTURE_NOISE_FILTER_PCLK_DIV_4
386   *                                 - \ref LPTMR_CAPTURE_NOISE_FILTER_PCLK_DIV_8
387   *                                 - \ref LPTMR_CAPTURE_NOISE_FILTER_PCLK_DIV_16
388   *                                 - \ref LPTMR_CAPTURE_NOISE_FILTER_PCLK_DIV_32
389   *                                 - \ref LPTMR_CAPTURE_NOISE_FILTER_PCLK_DIV_64
390   *                                 - \ref LPTMR_CAPTURE_NOISE_FILTER_PCLK_DIV_128
391   *
392   * @return     None
393   *
394   * @details    This function is used to enable capture input noise filter function.
395   */
LPTMR_EnableCaptureInputNoiseFilter(LPTMR_T * lptmr,uint32_t u32FilterCount,uint32_t u32ClkSrcSel)396 void LPTMR_EnableCaptureInputNoiseFilter(LPTMR_T *lptmr, uint32_t u32FilterCount, uint32_t u32ClkSrcSel)
397 {
398     lptmr->CAPNF = ( ((lptmr)->CAPNF & ~(LPTMR_CAPNF_CAPNFEN_Msk | LPTMR_CAPNF_CAPNFCNT_Msk | LPTMR_CAPNF_CAPNFSEL_Msk))
399                      | (LPTMR_CAPNF_CAPNFEN_Msk | (u32FilterCount << LPTMR_CAPNF_CAPNFCNT_Pos) | (u32ClkSrcSel << LPTMR_CAPNF_CAPNFSEL_Pos)) );
400 }
401 
402 /**
403   * @brief      Disable Capture Input Noise Filter Function
404   *
405   * @param[in]  lptmr       The pointer of the specified LPTMR module. It could be LPTMR0, LPTMR1.
406   *
407   * @return     None
408   *
409   * @details    This function is used to disable capture input noise filter function.
410   */
LPTMR_DisableCaptureInputNoiseFilter(LPTMR_T * lptmr)411 void LPTMR_DisableCaptureInputNoiseFilter(LPTMR_T *lptmr)
412 {
413     lptmr->CAPNF &= ~LPTMR_CAPNF_CAPNFEN_Msk;
414 }
415 
416 /*@}*/ /* end of group LPTMR_EXPORTED_FUNCTIONS */
417 
418 /*@}*/ /* end of group LPTMR_Driver */
419 
420 /*@}*/ /* end of group Standard_Driver */
421