2626*******************************************************************************/
2727
2828#include "cmsis_compiler.h"
29- #include "cy_wdt .h"
29+ #include "cy_mcwdt .h"
3030#include "cy_syslib.h"
3131#include "cy_sysint.h"
3232#include "cyhal_lptimer.h"
@@ -52,16 +52,11 @@ static MCWDT_STRUCT_Type * const CYHAL_LPTIMER_BASE_ADDRESSES[] = {
5252#endif
5353};
5454
55- #if !defined (CY_CFG_SYSCLK_CLKLF_FREQ_HZ )
56- #define CY_CFG_SYSCLK_CLKLF_FREQ_HZ 32768UL /* Default to 32K ILO */
57- #endif /* CY_CFG_SYSCLK_CLKLF_FREQ_HZ */
58-
59- #define CY_MCWDT_COUNTER0_MAX_TICKS (0xffffUL)
60- #define CY_MCWDT_COUNTER1_MAX_TICKS (0xffffUL)
61- #define CY_MCWDT_COUNTER2_MAX_TICKS (0xffffffffUL)
6255#define CY_MCWDT_MAX_DELAY_TICKS (0xfff0ffffUL) /* ~36hours, Not set to 0xffffffff to avoid C0 and C1 both overflowing */
6356#define CY_MCWDT_LPTIMER_CTRL (CY_MCWDT_CTR0 | CY_MCWDT_CTR1 | CY_MCWDT_CTR2)
6457
58+ #define CY_MCWDT_MIN_DELAY 3 /* minimum amount of lfclk cycles of that LPTIMER can delay for. */
59+
6560#define CY_DEFAULT_MCWDT_PRIORITY 3
6661
6762static const uint16_t CY_MCWDT_RESET_TIME_US = 62 ;
@@ -95,8 +90,8 @@ cy_rslt_t cyhal_lptimer_init(cyhal_lptimer_t *obj)
9590 obj -> base = CYHAL_LPTIMER_BASE_ADDRESSES [obj -> resource .block_num ];
9691
9792 const cy_stc_mcwdt_config_t cfg = {
98- .c0Match = CY_MCWDT_COUNTER0_MAX_TICKS ,
99- .c1Match = CY_MCWDT_COUNTER1_MAX_TICKS ,
93+ .c0Match = 0xFFFF ,
94+ .c1Match = 0xFFFF ,
10095 .c0Mode = CY_MCWDT_MODE_INT ,
10196 .c1Mode = CY_MCWDT_MODE_INT ,
10297 .c2Mode = CY_MCWDT_MODE_NONE ,
@@ -107,25 +102,28 @@ cy_rslt_t cyhal_lptimer_init(cyhal_lptimer_t *obj)
107102 .c1c2Cascade = false
108103 };
109104 rslt = (cy_rslt_t ) Cy_MCWDT_Init (obj -> base , & cfg );
105+ }
110106
107+ if (CY_RSLT_SUCCESS == rslt )
108+ {
109+ obj -> callback_data .callback = NULL ;
110+ obj -> callback_data .callback_arg = NULL ;
111+ cyhal_lptimer_config_structs [obj -> resource .block_num ] = obj ;
112+ }
113+
114+ if (CY_RSLT_SUCCESS == rslt )
115+ {
116+ IRQn_Type irqn = (IRQn_Type ) (srss_interrupt_mcwdt_0_IRQn + obj -> resource .block_num );
117+ cy_stc_sysint_t irqCfg = { irqn , CY_DEFAULT_MCWDT_PRIORITY };
118+ rslt = (cy_rslt_t ) Cy_SysInt_Init (& irqCfg , & cyhal_lptimer_irq_handler );
111119 if (CY_RSLT_SUCCESS == rslt )
112120 {
113- obj -> callback_data .callback = NULL ;
114- obj -> callback_data .callback_arg = NULL ;
115- cyhal_lptimer_config_structs [obj -> resource .block_num ] = obj ;
116-
117- IRQn_Type irqn = (IRQn_Type ) (srss_interrupt_mcwdt_0_IRQn + obj -> resource .block_num );
118- cy_stc_sysint_t irqCfg = { irqn , CY_DEFAULT_MCWDT_PRIORITY };
119- rslt = (cy_rslt_t ) Cy_SysInt_Init (& irqCfg , & cyhal_lptimer_irq_handler );
120-
121- if (CY_RSLT_SUCCESS == rslt )
122- {
123- NVIC_EnableIRQ (irqn );
124- Cy_MCWDT_Enable (obj -> base , CY_MCWDT_LPTIMER_CTRL , CY_MCWDT_RESET_TIME_US );
125- }
121+ NVIC_EnableIRQ (irqn );
122+ Cy_MCWDT_Enable (obj -> base , CY_MCWDT_LPTIMER_CTRL , CY_MCWDT_RESET_TIME_US );
126123 }
127124 }
128125
126+
129127 if (CY_RSLT_SUCCESS != rslt )
130128 {
131129 cyhal_lptimer_free (obj );
@@ -154,96 +152,74 @@ void cyhal_lptimer_free(cyhal_lptimer_t *obj)
154152
155153cy_rslt_t cyhal_lptimer_reload (cyhal_lptimer_t * obj )
156154{
157- Cy_MCWDT_ResetCounters (obj -> base , ( CY_MCWDT_CTR0 | CY_MCWDT_CTR1 ) , CY_MCWDT_RESET_TIME_US );
155+ Cy_MCWDT_ResetCounters (obj -> base , CY_MCWDT_CTR2 , CY_MCWDT_RESET_TIME_US );
158156 return CY_RSLT_SUCCESS ;
159157}
160158
161- cy_rslt_t cyhal_lptimer_set_time (cyhal_lptimer_t * obj , uint32_t ticks )
159+ cy_rslt_t cyhal_lptimer_set_match (cyhal_lptimer_t * obj , uint32_t ticks )
162160{
163- return cyhal_lptimer_set_match (obj , ticks );
161+ return cyhal_lptimer_set_delay (obj , ticks - cyhal_lptimer_read ( obj ) );
164162}
165163
166- cy_rslt_t cyhal_lptimer_set_match (cyhal_lptimer_t * obj , uint32_t ticks )
164+ cy_rslt_t cyhal_lptimer_set_delay (cyhal_lptimer_t * obj , uint32_t delay )
167165{
168- uint16_t c0_match_ticks ;
169- uint16_t c1_match_ticks ;
170- uint32_t mcwdt_interrupt_mask ;
171- uint16_t c0_current_ticks = Cy_MCWDT_GetCount (obj -> base , CY_MCWDT_COUNTER0 );
172- uint16_t c1_current_ticks = Cy_MCWDT_GetCount (obj -> base , CY_MCWDT_COUNTER1 );
173-
174- Cy_MCWDT_ClearInterrupt (obj -> base , (CY_MCWDT_CTR0 | CY_MCWDT_CTR1 ));
175-
176- /* Use MCWDT C0,C1 and C2 to implement a 32bit free running counter
177- C2 alone can not be used as it does not support interrupt on match feature
178- C2 is used to keep track of time, while C0 and C1 are used to set interrupts
179- To set an interrupt:
180- 1. delay = diff between timestamp(time in future) vs current value of C2
181- 2. if delay > 2seconds (Max time that can be counted by C0)
182- Yes
183- - use both C0 and C1
184- - Increment C0 by delay % (CY_MCWDT_COUNTER0_MAX_TICKS + 1)
185- - Increment C1 by delay / (CY_MCWDT_COUNTER1_MAX_TICKS + 1)
186- - Special case : In case delay is multiple of (CY_MCWDT_COUNTER0_MAX_TICKS + 1), then
187- delay % (CY_MCWDT_COUNTER0_MAX_TICKS + 1) will be 0, in this case
188- - Increment C0 by c0_current_ticks -1
189- - Increment C1 by (delay / (CY_MCWDT_COUNTER1_MAX_TICKS + 1)) -1
190- No
191- - Use only C0
192- */
193- if (ticks > CY_MCWDT_COUNTER0_MAX_TICKS )
166+ /**
167+ * 16 bit C0/C1 are cascaded to generated a 32 bit counter.
168+ * Counter0 continues counting after reaching its match value
169+ * Interrupt is generated on Counter1 match.
170+ *
171+ * Supposed T=C0=C1=0, and we need to trigger an interrupt at T=0x28000.
172+ * We set C0_match to 0x8000 and C1 match to 1.
173+ * At T = 0x8000, C0_value matches C0_match so C1 get incremented. C1/C0=0x18000.
174+ * At T = 0x18000, C0_value matches C0_match again so C1 get incremented from 1 to 2.
175+ * When C1 get incremented from 1 to 2 theinterrupt is generated.
176+ * At T = 0x18000, C1/C0 = 0x28000.
177+ */
178+
179+ if (delay <= CY_MCWDT_MIN_DELAY )
194180 {
195- uint16_t c0_increment ;
196- uint16_t c1_increment ;
197-
198- if (ticks > CY_MCWDT_MAX_DELAY_TICKS )
199- {
200- ticks = CY_MCWDT_MAX_DELAY_TICKS ;
201- }
202-
203- c0_increment = ticks % (CY_MCWDT_COUNTER0_MAX_TICKS + 1 );
204- c0_match_ticks = (c0_current_ticks + c0_increment ) % (CY_MCWDT_COUNTER0_MAX_TICKS + 1 );
205- c1_increment = (ticks ) / (CY_MCWDT_COUNTER0_MAX_TICKS + 1 );
206- c1_match_ticks = (c1_current_ticks + c1_increment ) % (CY_MCWDT_COUNTER1_MAX_TICKS + 1 );
207-
208- /* Special case - ticks is multiple of (CY_MCWDT_COUNTER0_MAX_TICKS + 1) */
209- if (c0_increment == 0 )
210- {
211- c0_match_ticks = c0_current_ticks - 1 ;
212- c1_match_ticks = c1_match_ticks - 1 ;
213- }
214-
215- mcwdt_interrupt_mask = CY_MCWDT_CTR1 ;
181+ delay = CY_MCWDT_MIN_DELAY ;
216182 }
217- else
183+ if ( delay > CY_MCWDT_MAX_DELAY_TICKS )
218184 {
219- c0_match_ticks = c0_current_ticks + ( uint16_t ) ticks ;
220- c1_match_ticks = CY_MCWDT_COUNTER1_MAX_TICKS ;
185+ delay = CY_MCWDT_MAX_DELAY_TICKS ;
186+ }
221187
222- /* MCWDT has internal delay of about 1.5 LF clock ticks, so this is the minimum
223- * that we can schedule.
224- */
225- if (ticks < 3 )
226- {
227- /* Cheating a bit here. */
228- c0_match_ticks = c0_current_ticks + 3 ;
229- }
188+ uint16_t c0_increment = (uint16_t )delay ;
189+ uint16_t c1_increment = (uint16_t )(delay >> 16 );
230190
231- mcwdt_interrupt_mask = CY_MCWDT_CTR0 ;
232- }
191+ Cy_MCWDT_ClearInterrupt (obj -> base , CY_MCWDT_CTR1 );
192+
193+ uint16_t c0_old_match = Cy_MCWDT_GetMatch (obj -> base , CY_MCWDT_COUNTER0 );
194+
195+ uint32_t critical_section = cyhal_system_critical_section_enter ();
233196
234- if (c1_match_ticks == 0 )
197+ /* Cascading from C0 match into C1 is queued and can take 1 full LF clk cycle.
198+ * There are 3 cases:
199+ * Case 1: if c0 = match0 then the cascade into C1 will happen 1 cycle from now. The value c1_current_ticks is 1 lower than expected.
200+ * Case 2: if c0 = match0 -1 then cascade may or not happen before new match value would occur. Match occurs on rising clock edge.
201+ * Synching match value occurs on falling edge. Wait until c0 = match0 to ensure cascade occurs.
202+ * Case 3: everything works as expected.
203+ */
204+ uint16_t c0_current_ticks ;
205+ while ((c0_current_ticks = (Cy_MCWDT_GetCount (obj -> base , CY_MCWDT_COUNTER0 ))) == c0_old_match ) {}
206+
207+ uint16_t c1_current_ticks = Cy_MCWDT_GetCount (obj -> base , CY_MCWDT_COUNTER1 );
208+ if (c0_current_ticks == c0_old_match + 1 )
235209 {
236- c1_match_ticks = 1 ;
210+ c1_current_ticks ++ ;
237211 }
238-
239- if (c0_match_ticks == 0 )
212+ if (Cy_MCWDT_GetCount (obj -> base , CY_MCWDT_COUNTER0 ) != c0_current_ticks )
240213 {
241- c0_match_ticks = 1 ;
214+ // Just in the very unlikely case that an increment occurred while previous instruction was running.
215+ c1_current_ticks = Cy_MCWDT_GetCount (obj -> base , CY_MCWDT_COUNTER1 );
242216 }
217+ Cy_MCWDT_SetMatch (obj -> base , CY_MCWDT_COUNTER0 , c0_current_ticks + c0_increment , CY_MCWDT_SETMATCH_NOWAIT_TIME_US );
218+ Cy_MCWDT_SetMatch (obj -> base , CY_MCWDT_COUNTER1 , c1_current_ticks + c1_increment , CY_MCWDT_SETMATCH_NOWAIT_TIME_US );
219+
220+ cyhal_system_critical_section_exit (critical_section );
243221
244- Cy_MCWDT_SetMatch (obj -> base , CY_MCWDT_COUNTER0 , c0_match_ticks , CY_MCWDT_SETMATCH_NOWAIT_TIME_US );
245- Cy_MCWDT_SetMatch (obj -> base , CY_MCWDT_COUNTER1 , c1_match_ticks , CY_MCWDT_SETMATCH_NOWAIT_TIME_US );
246- Cy_MCWDT_SetInterruptMask (obj -> base , mcwdt_interrupt_mask );
222+ Cy_MCWDT_SetInterruptMask (obj -> base , CY_MCWDT_CTR1 );
247223
248224 return CY_RSLT_SUCCESS ;
249225}
@@ -265,7 +241,9 @@ void cyhal_lptimer_register_callback(cyhal_lptimer_t *obj, cyhal_lptimer_event_c
265241
266242void cyhal_lptimer_enable_event (cyhal_lptimer_t * obj , cyhal_lptimer_event_t event , uint8_t intrPriority , bool enable )
267243{
268- Cy_MCWDT_SetInterruptMask (obj -> base , enable ? CY_MCWDT_CTR0 : 0 );
244+ CY_ASSERT (event == CYHAL_LPTIMER_COMPARE_MATCH );
245+ Cy_MCWDT_ClearInterrupt (obj -> base , CY_MCWDT_CTR1 );
246+ Cy_MCWDT_SetInterruptMask (obj -> base , enable ? CY_MCWDT_CTR1 : 0 );
269247
270248 IRQn_Type irqn = (IRQn_Type )(srss_interrupt_mcwdt_0_IRQn + obj -> resource .block_num );
271249 NVIC_SetPriority (irqn , intrPriority );
0 commit comments