1 /**************************************************************************//**
2  * @file     ttmr.c
3  * @brief    Tick Timer Controller (TTMR) 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 /** @addtogroup Standard_Driver Standard Driver
11   @{
12 */
13 
14 /** @addtogroup TTMR_Driver TTMR Driver
15   @{
16 */
17 
18 /** @addtogroup TTMR_EXPORTED_FUNCTIONS TTMR Exported Functions
19   @{
20 */
21 
22 /**
23   * @brief      Open TTMR with Operate Mode and Frequency
24   *
25   * @param[in]  ttmr        The pointer of the specified TTMR module. It could be TTMR0, TTMR1.
26   * @param[in]  u32Mode     Operation mode. Possible options are
27   *                         - \ref TTMR_ONESHOT_MODE
28   *                         - \ref TTMR_PERIODIC_MODE
29   *                         - \ref TTMR_CONTINUOUS_MODE
30   * @param[in]  u32Freq     Target working frequency
31   *
32   * @return     Real ttmr working frequency
33   *
34   * @details    This API is used to configure ttmr to operate in specified mode and frequency.
35   *             If ttmr cannot work in target frequency, a closest frequency will be chose and returned.
36   * @note       After calling this API, Timer is \b NOT running yet. But could start ttmr running be calling
37   *             \ref TTMR_Start macro or program registers directly.
38   */
TTMR_Open(TTMR_T * ttmr,uint32_t u32Mode,uint32_t u32Freq)39 uint32_t TTMR_Open(TTMR_T *ttmr, uint32_t u32Mode, uint32_t u32Freq)
40 {
41     uint32_t u32Clk = TTMR_GetModuleClock(ttmr);
42     uint32_t u32Cmpr = 0UL, u32Prescale = 0UL;
43 
44     if (u32Freq == 0)
45         return 0;
46 
47     /* Fastest possible ttmr working freq is (u32Clk / 2). While cmpr = 2, prescaler = 0. */
48     if(u32Freq > (u32Clk / 2UL))
49     {
50         u32Cmpr = 2UL;
51     }
52     else
53     {
54         u32Cmpr = u32Clk / u32Freq;
55         u32Prescale = (u32Cmpr >> 24);  /* for 24 bits CMPDAT */
56         if (u32Prescale > 0UL)
57             u32Cmpr = u32Cmpr / (u32Prescale + 1UL);
58     }
59 
60     ttmr->CTL = (u32Mode | u32Prescale);
61     ttmr->CMP = u32Cmpr;
62 
63     return(u32Clk / (u32Cmpr * (u32Prescale + 1UL)));
64 }
65 
66 
67 /**
68   * @brief      Stop TTMR Counting
69   *
70   * @param[in]  ttmr        The pointer of the specified TTMR module. It could be TTMR0, TTMR1.
71   *
72   * @return     None
73   *
74   * @details    This API stops ttmr counting and disable all ttmr interrupt function.
75   */
TTMR_Close(TTMR_T * ttmr)76 void TTMR_Close(TTMR_T *ttmr)
77 {
78     ttmr->CTL = 0UL;
79 }
80 
81 
82 /**
83   * @brief      Create a specify Delay Time
84   *
85   * @param[in]  ttmr        The pointer of the specified TTMR module. It could be TTMR0, TTMR1.
86   * @param[in]  u32Usec     Delay period in micro seconds. Valid values are between 100~1000000 (100 micro second ~ 1 second).
87   *
88   * @retval     0                Delay success, target delay time reached
89   * @retval     TTMR_TIMEOUT_ERR Delay function execute failed due to timer stop working
90   *
91   * @details    This API is used to create a delay loop for u32usec micro seconds by using ttmr one-shot mode.
92   * @note       This API overwrites the register setting of the ttmr used to count the delay time.
93   * @note       This API use polling mode. So there is no need to enable interrupt for the ttmr module used to generate delay.
94   */
TTMR_Delay(TTMR_T * ttmr,uint32_t u32Usec)95 int32_t TTMR_Delay(TTMR_T *ttmr, uint32_t u32Usec)
96 {
97     uint32_t u32Clk = TTMR_GetModuleClock(ttmr);
98     uint32_t u32Prescale = 0UL, u32Delay;
99     uint32_t u32Cmpr, u32Cntr, u32NsecPerTick, i = 0UL;
100 
101     /* Clear current ttmr configuration */
102     ttmr->CTL = 0UL;
103 
104     if(u32Clk <= 1000000UL)   /* min delay is 1000 us if ttmr clock source is <= 1 MHz */
105     {
106         if(u32Usec < 1000UL)
107         {
108             u32Usec = 1000UL;
109         }
110         if(u32Usec > 1000000UL)
111         {
112             u32Usec = 1000000UL;
113         }
114     }
115     else
116     {
117         if(u32Usec < 100UL)
118         {
119             u32Usec = 100UL;
120         }
121         if(u32Usec > 1000000UL)
122         {
123             u32Usec = 1000000UL;
124         }
125     }
126 
127     if(u32Clk <= 1000000UL)
128     {
129         u32Prescale = 0UL;
130         u32NsecPerTick = 1000000000UL / u32Clk;
131         u32Cmpr = (u32Usec * 1000UL) / u32NsecPerTick;
132     }
133     else
134     {
135         u32Cmpr = u32Usec * (u32Clk / 1000000UL);
136         u32Prescale = (u32Cmpr >> 24);  /* for 24 bits CMPDAT */
137         if (u32Prescale > 0UL)
138             u32Cmpr = u32Cmpr / (u32Prescale + 1UL);
139     }
140 
141     ttmr->CMP = u32Cmpr;
142     ttmr->CTL = TTMR_CTL_CNTEN_Msk | TTMR_ONESHOT_MODE | u32Prescale;
143 
144     /* When system clock is faster than TTMR clock, it is possible TTMR active bit cannot set
145        in time while we check it. And the while loop below return immediately, so put a tiny
146        delay larger than 1 ECLK here allowing timer start counting and raise active flag. */
147     for(u32Delay = (SystemCoreClock / u32Clk) + 1UL; u32Delay > 0UL; u32Delay--)
148     {
149         __NOP();
150     }
151 
152     /* Add a bail out counter here in case timer clock source is disabled accidentally.
153        Prescale counter reset every ECLK * (prescale value + 1).
154        The u32Delay here is to make sure timer counter value changed when prescale counter reset */
155     u32Delay = (SystemCoreClock / TTMR_GetModuleClock(ttmr)) * (u32Prescale + 1);
156     u32Cntr = ttmr->CNT;
157     i = 0;
158     while(ttmr->CTL & TTMR_CTL_ACTSTS_Msk)
159     {
160         /* Bailed out if timer stop counting e.g. Some interrupt handler close timer clock source. */
161         if(u32Cntr == ttmr->CNT)
162         {
163             if(i++ > u32Delay)
164             {
165                 return TTMR_TIMEOUT_ERR;
166             }
167         }
168         else
169         {
170             i = 0;
171             u32Cntr = ttmr->CNT;
172         }
173     }
174     return 0;
175 }
176 
177 
178 /**
179   * @brief      Get TTMR Clock Frequency
180   *
181   * @param[in]  ttmr   The pointer of the specified TTMR module. It could be TTMR0, TTMR1.
182   *
183   * @return     TTMR clock frequency
184   *
185   * @details    This API is used to get the TTMR clock frequency.
186   * @note       This API cannot return correct clock rate if TTMR source is from external clock input.
187   */
TTMR_GetModuleClock(TTMR_T * ttmr)188 uint32_t TTMR_GetModuleClock(TTMR_T *ttmr)
189 {
190     uint32_t u32Src, u32Clk;
191     const uint32_t au32Clk[] = {__HIRC, __MIRC, __LXT, __LIRC};
192 
193     if(ttmr == TTMR0)
194     {
195         u32Src = (LPSCC->CLKSEL0 & LPSCC_CLKSEL0_TTMR0SEL_Msk) >> LPSCC_CLKSEL0_TTMR0SEL_Pos;
196     }
197     else if(ttmr == TTMR1)
198     {
199         u32Src = (LPSCC->CLKSEL0 & LPSCC_CLKSEL0_TTMR1SEL_Msk) >> LPSCC_CLKSEL0_TTMR1SEL_Pos;
200     }
201 
202     u32Clk = au32Clk[u32Src];
203 
204     return u32Clk;
205 }
206 
207 
208 /**
209   * @brief This function is used to set modules trigger by TTMR interrupt
210   * @param[in] ttmr The base address of TTMR module
211   * @param[in] u32Mask The mask of modules (Low power IPs and LPPDMA) trigger by TTMR. Is the combination of
212   *             - \ref TTMR_TRGEN
213   *             - \ref TTMR_TRG_TO_LPPDMA
214   * @return None
215   */
TTMR_SetTriggerTarget(TTMR_T * ttmr,uint32_t u32Mask)216 void TTMR_SetTriggerTarget(TTMR_T *ttmr, uint32_t u32Mask)
217 {
218     ttmr->TRGCTL = (ttmr->TRGCTL & ~(TTMR_TRGCTL_TRGEN_Msk | TTMR_TRGCTL_TRGLPPDMA_Msk)) | u32Mask;
219 }
220 
221 
222 /**
223   * @brief      Reset TTMR Counter
224   *
225   * @param[in]  ttmr The base address of TTMR module
226   *
227   * @retval     0                TTMR reset success
228   * @retval     TTMR_TIMEOUT_ERR TTMR reset failed
229   *
230   * @details    This function is used to reset current counter value and internal prescale counter value.
231   */
TTMR_ResetCounter(TTMR_T * ttmr)232 int32_t TTMR_ResetCounter(TTMR_T *ttmr)
233 {
234     uint32_t u32Delay;
235 
236     ttmr->CNT |= TTMR_CNT_RSTACT_Msk;
237     /* Takes 2~3 ECLKs to reset timer counter */
238     u32Delay = (SystemCoreClock / TTMR_GetModuleClock(ttmr)) * 3;
239     while(((ttmr->CNT & TTMR_CNT_RSTACT_Msk) == TTMR_CNT_RSTACT_Msk) && (--u32Delay))
240     {
241         __NOP();
242     }
243     return ((u32Delay > 0) ? 0 : TTMR_TIMEOUT_ERR);
244 }
245 
246 /*@}*/ /* end of group TTMR_EXPORTED_FUNCTIONS */
247 
248 /*@}*/ /* end of group TTMR_Driver */
249 
250 /*@}*/ /* end of group Standard_Driver */
251