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