Skip to content

Commit 3ad3110

Browse files
authored
Merge pull request #12019 from shuopeng-deng/pr/cypress-lptimer-hal-rework
Cypress: rework lptimer hal
2 parents e2ee381 + 823d50d commit 3ad3110

File tree

3 files changed

+100
-112
lines changed

3 files changed

+100
-112
lines changed

targets/TARGET_Cypress/TARGET_PSOC6/cy_lp_ticker_api.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,8 @@ uint32_t lp_ticker_read(void)
6262

6363
void lp_ticker_set_interrupt(timestamp_t timestamp)
6464
{
65-
uint32_t delay;
66-
delay = (uint32_t)timestamp - cyhal_lptimer_read(&cy_lptimer0);
67-
68-
if (CY_RSLT_SUCCESS != cyhal_lptimer_set_match(&cy_lptimer0, delay)) {
69-
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_lptimer_set_time");
65+
if (CY_RSLT_SUCCESS != cyhal_lptimer_set_match(&cy_lptimer0, (uint32_t)timestamp)) {
66+
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_lptimer_set_match");
7067
}
7168
}
7269

targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/include/cyhal_lptimer.h

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
* \ingroup group_hal
3131
* \{
3232
* High level interface for interacting with the Cypress LPTIMER.
33+
*
34+
* This can be used to measure timing between events, or to perform
35+
* some action the ability after a set interval. It continues to operate
36+
* in some low power modes; see the device datasheet for details.
3337
*/
3438

3539
#pragma once
@@ -43,6 +47,10 @@
4347
extern "C" {
4448
#endif
4549

50+
51+
/** Failed to configure power management callback */
52+
#define CYHAL_LPTIMER_RSLT_ERR_PM_CALLBACK (CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_WDT, 0))
53+
4654
/** LPTIMER interrupt triggers */
4755
typedef enum {
4856
CYHAL_LPTIMER_COMPARE_MATCH,
@@ -81,32 +89,37 @@ void cyhal_lptimer_free(cyhal_lptimer_t *obj);
8189
*/
8290
cy_rslt_t cyhal_lptimer_reload(cyhal_lptimer_t *obj);
8391

84-
/** Set timeframe between interrupts
92+
/** Deprecated. Call cyhal_lptimer_set_match instead. */
93+
#define cyhal_lptimer_set_time cyhal_lptimer_set_match
94+
95+
/** Update the match/compare value
8596
*
86-
* Configures the LPTIMER in free-running mode. Generates an interrupt on match.
87-
* This function is for initial configuration. For quick updates to the match
88-
* value, use cyhal_lptimer_set_time().
97+
* Update the match value of an already configured LPTIMER set up
98+
* to generate an interrupt on match. Note that this function does not
99+
* reinitialize the counter or the associated peripheral initialization
100+
* sequence.
89101
*
90102
* @param[in] obj The LPTIMER object
91-
* @param[in] time The time in ticks to be set
103+
* @param[in] value The tick value to match
92104
*
93-
* @return The status of the set_time request
105+
* @return The status of the set_match request
94106
*/
95-
cy_rslt_t cyhal_lptimer_set_time(cyhal_lptimer_t *obj, uint32_t time);
107+
cy_rslt_t cyhal_lptimer_set_match(cyhal_lptimer_t *obj, uint32_t value);
96108

97109
/** Update the match/compare value
98110
*
99111
* Update the match value of an already configured LPTIMER set up
100-
* to generate an interrupt on match. Note that this function does not
101-
* reinitialize the counter or the associated peripheral initialization
112+
* to generate an interrupt on match delay from the current counter value.
113+
* Note that this function does not reinitialize the counter or the
114+
* associated peripheral initialization
102115
* sequence.
103116
*
104117
* @param[in] obj The LPTIMER object
105-
* @param[in] value The match value in ticks
118+
* @param[in] delay The ticks to wait
106119
*
107120
* @return The status of the set_match request
108121
*/
109-
cy_rslt_t cyhal_lptimer_set_match(cyhal_lptimer_t *obj, uint32_t value);
122+
cy_rslt_t cyhal_lptimer_set_delay(cyhal_lptimer_t *obj, uint32_t delay);
110123

111124
/** Read the current tick
112125
*

targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/src/cyhal_lptimer.c

Lines changed: 74 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
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

6762
static 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

155153
cy_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

266242
void 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

Comments
 (0)