26
26
*******************************************************************************/
27
27
28
28
#include "cmsis_compiler.h"
29
- #include "cy_wdt .h"
29
+ #include "cy_mcwdt .h"
30
30
#include "cy_syslib.h"
31
31
#include "cy_sysint.h"
32
32
#include "cyhal_lptimer.h"
@@ -52,16 +52,11 @@ static MCWDT_STRUCT_Type * const CYHAL_LPTIMER_BASE_ADDRESSES[] = {
52
52
#endif
53
53
};
54
54
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)
62
55
#define CY_MCWDT_MAX_DELAY_TICKS (0xfff0ffffUL) /* ~36hours, Not set to 0xffffffff to avoid C0 and C1 both overflowing */
63
56
#define CY_MCWDT_LPTIMER_CTRL (CY_MCWDT_CTR0 | CY_MCWDT_CTR1 | CY_MCWDT_CTR2)
64
57
58
+ #define CY_MCWDT_MIN_DELAY 3 /* minimum amount of lfclk cycles of that LPTIMER can delay for. */
59
+
65
60
#define CY_DEFAULT_MCWDT_PRIORITY 3
66
61
67
62
static const uint16_t CY_MCWDT_RESET_TIME_US = 62 ;
@@ -95,8 +90,8 @@ cy_rslt_t cyhal_lptimer_init(cyhal_lptimer_t *obj)
95
90
obj -> base = CYHAL_LPTIMER_BASE_ADDRESSES [obj -> resource .block_num ];
96
91
97
92
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 ,
100
95
.c0Mode = CY_MCWDT_MODE_INT ,
101
96
.c1Mode = CY_MCWDT_MODE_INT ,
102
97
.c2Mode = CY_MCWDT_MODE_NONE ,
@@ -107,25 +102,28 @@ cy_rslt_t cyhal_lptimer_init(cyhal_lptimer_t *obj)
107
102
.c1c2Cascade = false
108
103
};
109
104
rslt = (cy_rslt_t ) Cy_MCWDT_Init (obj -> base , & cfg );
105
+ }
110
106
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 );
111
119
if (CY_RSLT_SUCCESS == rslt )
112
120
{
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 );
126
123
}
127
124
}
128
125
126
+
129
127
if (CY_RSLT_SUCCESS != rslt )
130
128
{
131
129
cyhal_lptimer_free (obj );
@@ -154,96 +152,74 @@ void cyhal_lptimer_free(cyhal_lptimer_t *obj)
154
152
155
153
cy_rslt_t cyhal_lptimer_reload (cyhal_lptimer_t * obj )
156
154
{
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 );
158
156
return CY_RSLT_SUCCESS ;
159
157
}
160
158
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 )
162
160
{
163
- return cyhal_lptimer_set_match (obj , ticks );
161
+ return cyhal_lptimer_set_delay (obj , ticks - cyhal_lptimer_read ( obj ) );
164
162
}
165
163
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 )
167
165
{
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 )
194
180
{
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 ;
216
182
}
217
- else
183
+ if ( delay > CY_MCWDT_MAX_DELAY_TICKS )
218
184
{
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
+ }
221
187
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 );
230
190
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 ();
233
196
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 )
235
209
{
236
- c1_match_ticks = 1 ;
210
+ c1_current_ticks ++ ;
237
211
}
238
-
239
- if (c0_match_ticks == 0 )
212
+ if (Cy_MCWDT_GetCount (obj -> base , CY_MCWDT_COUNTER0 ) != c0_current_ticks )
240
213
{
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 );
242
216
}
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 );
243
221
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 );
247
223
248
224
return CY_RSLT_SUCCESS ;
249
225
}
@@ -265,7 +241,9 @@ void cyhal_lptimer_register_callback(cyhal_lptimer_t *obj, cyhal_lptimer_event_c
265
241
266
242
void cyhal_lptimer_enable_event (cyhal_lptimer_t * obj , cyhal_lptimer_event_t event , uint8_t intrPriority , bool enable )
267
243
{
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 );
269
247
270
248
IRQn_Type irqn = (IRQn_Type )(srss_interrupt_mcwdt_0_IRQn + obj -> resource .block_num );
271
249
NVIC_SetPriority (irqn , intrPriority );
0 commit comments