mirror of https://github.com/ARMmbed/mbed-os.git
rework cypress lptimer hal
Changed set_match api to use an absolute ticks rather than delayed tick to match api name. Added api set_delay to delay by a specific amount of ticks. Removed unused set_time api. Simplified the logic for computing interrupts match value for cascading counters. Fixed an issue when incorrect base time would be read when trying to set match values.pull/12019/head
parent
a1cddbae5f
commit
823d50d6fd
|
@ -62,11 +62,8 @@ uint32_t lp_ticker_read(void)
|
|||
|
||||
void lp_ticker_set_interrupt(timestamp_t timestamp)
|
||||
{
|
||||
uint32_t delay;
|
||||
delay = (uint32_t)timestamp - cyhal_lptimer_read(&cy_lptimer0);
|
||||
|
||||
if (CY_RSLT_SUCCESS != cyhal_lptimer_set_match(&cy_lptimer0, delay)) {
|
||||
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_lptimer_set_time");
|
||||
if (CY_RSLT_SUCCESS != cyhal_lptimer_set_match(&cy_lptimer0, (uint32_t)timestamp)) {
|
||||
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_lptimer_set_match");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,10 @@
|
|||
* \ingroup group_hal
|
||||
* \{
|
||||
* High level interface for interacting with the Cypress LPTIMER.
|
||||
*
|
||||
* This can be used to measure timing between events, or to perform
|
||||
* some action the ability after a set interval. It continues to operate
|
||||
* in some low power modes; see the device datasheet for details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
@ -43,6 +47,10 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/** Failed to configure power management callback */
|
||||
#define CYHAL_LPTIMER_RSLT_ERR_PM_CALLBACK (CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_WDT, 0))
|
||||
|
||||
/** LPTIMER interrupt triggers */
|
||||
typedef enum {
|
||||
CYHAL_LPTIMER_COMPARE_MATCH,
|
||||
|
@ -81,18 +89,8 @@ void cyhal_lptimer_free(cyhal_lptimer_t *obj);
|
|||
*/
|
||||
cy_rslt_t cyhal_lptimer_reload(cyhal_lptimer_t *obj);
|
||||
|
||||
/** Set timeframe between interrupts
|
||||
*
|
||||
* Configures the LPTIMER in free-running mode. Generates an interrupt on match.
|
||||
* This function is for initial configuration. For quick updates to the match
|
||||
* value, use cyhal_lptimer_set_time().
|
||||
*
|
||||
* @param[in] obj The LPTIMER object
|
||||
* @param[in] time The time in ticks to be set
|
||||
*
|
||||
* @return The status of the set_time request
|
||||
*/
|
||||
cy_rslt_t cyhal_lptimer_set_time(cyhal_lptimer_t *obj, uint32_t time);
|
||||
/** Deprecated. Call cyhal_lptimer_set_match instead. */
|
||||
#define cyhal_lptimer_set_time cyhal_lptimer_set_match
|
||||
|
||||
/** Update the match/compare value
|
||||
*
|
||||
|
@ -102,12 +100,27 @@ cy_rslt_t cyhal_lptimer_set_time(cyhal_lptimer_t *obj, uint32_t time);
|
|||
* sequence.
|
||||
*
|
||||
* @param[in] obj The LPTIMER object
|
||||
* @param[in] value The match value in ticks
|
||||
* @param[in] value The tick value to match
|
||||
*
|
||||
* @return The status of the set_match request
|
||||
*/
|
||||
cy_rslt_t cyhal_lptimer_set_match(cyhal_lptimer_t *obj, uint32_t value);
|
||||
|
||||
/** Update the match/compare value
|
||||
*
|
||||
* Update the match value of an already configured LPTIMER set up
|
||||
* to generate an interrupt on match delay from the current counter value.
|
||||
* Note that this function does not reinitialize the counter or the
|
||||
* associated peripheral initialization
|
||||
* sequence.
|
||||
*
|
||||
* @param[in] obj The LPTIMER object
|
||||
* @param[in] delay The ticks to wait
|
||||
*
|
||||
* @return The status of the set_match request
|
||||
*/
|
||||
cy_rslt_t cyhal_lptimer_set_delay(cyhal_lptimer_t *obj, uint32_t delay);
|
||||
|
||||
/** Read the current tick
|
||||
*
|
||||
* If no rollover has occurred, the seconds passed since cyhal_lptimer_init() or cyhal_lptimer_set_time()
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
*******************************************************************************/
|
||||
|
||||
#include "cmsis_compiler.h"
|
||||
#include "cy_wdt.h"
|
||||
#include "cy_mcwdt.h"
|
||||
#include "cy_syslib.h"
|
||||
#include "cy_sysint.h"
|
||||
#include "cyhal_lptimer.h"
|
||||
|
@ -52,16 +52,11 @@ static MCWDT_STRUCT_Type * const CYHAL_LPTIMER_BASE_ADDRESSES[] = {
|
|||
#endif
|
||||
};
|
||||
|
||||
#if !defined (CY_CFG_SYSCLK_CLKLF_FREQ_HZ)
|
||||
#define CY_CFG_SYSCLK_CLKLF_FREQ_HZ 32768UL /* Default to 32K ILO */
|
||||
#endif /* CY_CFG_SYSCLK_CLKLF_FREQ_HZ */
|
||||
|
||||
#define CY_MCWDT_COUNTER0_MAX_TICKS (0xffffUL)
|
||||
#define CY_MCWDT_COUNTER1_MAX_TICKS (0xffffUL)
|
||||
#define CY_MCWDT_COUNTER2_MAX_TICKS (0xffffffffUL)
|
||||
#define CY_MCWDT_MAX_DELAY_TICKS (0xfff0ffffUL) /* ~36hours, Not set to 0xffffffff to avoid C0 and C1 both overflowing */
|
||||
#define CY_MCWDT_LPTIMER_CTRL (CY_MCWDT_CTR0 | CY_MCWDT_CTR1 | CY_MCWDT_CTR2)
|
||||
|
||||
#define CY_MCWDT_MIN_DELAY 3 /* minimum amount of lfclk cycles of that LPTIMER can delay for. */
|
||||
|
||||
#define CY_DEFAULT_MCWDT_PRIORITY 3
|
||||
|
||||
static const uint16_t CY_MCWDT_RESET_TIME_US = 62;
|
||||
|
@ -95,8 +90,8 @@ cy_rslt_t cyhal_lptimer_init(cyhal_lptimer_t *obj)
|
|||
obj->base = CYHAL_LPTIMER_BASE_ADDRESSES[obj->resource.block_num];
|
||||
|
||||
const cy_stc_mcwdt_config_t cfg = {
|
||||
.c0Match = CY_MCWDT_COUNTER0_MAX_TICKS,
|
||||
.c1Match = CY_MCWDT_COUNTER1_MAX_TICKS,
|
||||
.c0Match = 0xFFFF,
|
||||
.c1Match = 0xFFFF,
|
||||
.c0Mode = CY_MCWDT_MODE_INT,
|
||||
.c1Mode = CY_MCWDT_MODE_INT,
|
||||
.c2Mode = CY_MCWDT_MODE_NONE,
|
||||
|
@ -107,25 +102,28 @@ cy_rslt_t cyhal_lptimer_init(cyhal_lptimer_t *obj)
|
|||
.c1c2Cascade = false
|
||||
};
|
||||
rslt = (cy_rslt_t) Cy_MCWDT_Init(obj->base, &cfg);
|
||||
}
|
||||
|
||||
if (CY_RSLT_SUCCESS == rslt)
|
||||
{
|
||||
obj->callback_data.callback = NULL;
|
||||
obj->callback_data.callback_arg = NULL;
|
||||
cyhal_lptimer_config_structs[obj->resource.block_num] = obj;
|
||||
}
|
||||
|
||||
if (CY_RSLT_SUCCESS == rslt)
|
||||
{
|
||||
IRQn_Type irqn = (IRQn_Type) (srss_interrupt_mcwdt_0_IRQn + obj->resource.block_num);
|
||||
cy_stc_sysint_t irqCfg = { irqn, CY_DEFAULT_MCWDT_PRIORITY };
|
||||
rslt = (cy_rslt_t) Cy_SysInt_Init(&irqCfg, &cyhal_lptimer_irq_handler);
|
||||
if (CY_RSLT_SUCCESS == rslt)
|
||||
{
|
||||
obj->callback_data.callback = NULL;
|
||||
obj->callback_data.callback_arg = NULL;
|
||||
cyhal_lptimer_config_structs[obj->resource.block_num] = obj;
|
||||
|
||||
IRQn_Type irqn = (IRQn_Type) (srss_interrupt_mcwdt_0_IRQn + obj->resource.block_num);
|
||||
cy_stc_sysint_t irqCfg = { irqn, CY_DEFAULT_MCWDT_PRIORITY };
|
||||
rslt = (cy_rslt_t) Cy_SysInt_Init(&irqCfg, &cyhal_lptimer_irq_handler);
|
||||
|
||||
if (CY_RSLT_SUCCESS == rslt)
|
||||
{
|
||||
NVIC_EnableIRQ(irqn);
|
||||
Cy_MCWDT_Enable(obj->base, CY_MCWDT_LPTIMER_CTRL, CY_MCWDT_RESET_TIME_US);
|
||||
}
|
||||
NVIC_EnableIRQ(irqn);
|
||||
Cy_MCWDT_Enable(obj->base, CY_MCWDT_LPTIMER_CTRL, CY_MCWDT_RESET_TIME_US);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (CY_RSLT_SUCCESS != rslt)
|
||||
{
|
||||
cyhal_lptimer_free(obj);
|
||||
|
@ -154,96 +152,74 @@ void cyhal_lptimer_free(cyhal_lptimer_t *obj)
|
|||
|
||||
cy_rslt_t cyhal_lptimer_reload(cyhal_lptimer_t *obj)
|
||||
{
|
||||
Cy_MCWDT_ResetCounters(obj->base, (CY_MCWDT_CTR0 | CY_MCWDT_CTR1), CY_MCWDT_RESET_TIME_US);
|
||||
Cy_MCWDT_ResetCounters(obj->base, CY_MCWDT_CTR2, CY_MCWDT_RESET_TIME_US);
|
||||
return CY_RSLT_SUCCESS;
|
||||
}
|
||||
|
||||
cy_rslt_t cyhal_lptimer_set_time(cyhal_lptimer_t *obj, uint32_t ticks)
|
||||
{
|
||||
return cyhal_lptimer_set_match(obj, ticks);
|
||||
}
|
||||
|
||||
cy_rslt_t cyhal_lptimer_set_match(cyhal_lptimer_t *obj, uint32_t ticks)
|
||||
{
|
||||
uint16_t c0_match_ticks;
|
||||
uint16_t c1_match_ticks;
|
||||
uint32_t mcwdt_interrupt_mask;
|
||||
uint16_t c0_current_ticks = Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER0);
|
||||
return cyhal_lptimer_set_delay(obj, ticks - cyhal_lptimer_read(obj));
|
||||
}
|
||||
|
||||
cy_rslt_t cyhal_lptimer_set_delay(cyhal_lptimer_t *obj, uint32_t delay)
|
||||
{
|
||||
/**
|
||||
* 16 bit C0/C1 are cascaded to generated a 32 bit counter.
|
||||
* Counter0 continues counting after reaching its match value
|
||||
* Interrupt is generated on Counter1 match.
|
||||
*
|
||||
* Supposed T=C0=C1=0, and we need to trigger an interrupt at T=0x28000.
|
||||
* We set C0_match to 0x8000 and C1 match to 1.
|
||||
* At T = 0x8000, C0_value matches C0_match so C1 get incremented. C1/C0=0x18000.
|
||||
* At T = 0x18000, C0_value matches C0_match again so C1 get incremented from 1 to 2.
|
||||
* When C1 get incremented from 1 to 2 theinterrupt is generated.
|
||||
* At T = 0x18000, C1/C0 = 0x28000.
|
||||
*/
|
||||
|
||||
if (delay <= CY_MCWDT_MIN_DELAY)
|
||||
{
|
||||
delay = CY_MCWDT_MIN_DELAY;
|
||||
}
|
||||
if (delay > CY_MCWDT_MAX_DELAY_TICKS)
|
||||
{
|
||||
delay = CY_MCWDT_MAX_DELAY_TICKS;
|
||||
}
|
||||
|
||||
uint16_t c0_increment = (uint16_t)delay;
|
||||
uint16_t c1_increment = (uint16_t)(delay >> 16);
|
||||
|
||||
Cy_MCWDT_ClearInterrupt(obj->base, CY_MCWDT_CTR1);
|
||||
|
||||
uint16_t c0_old_match = Cy_MCWDT_GetMatch(obj->base, CY_MCWDT_COUNTER0);
|
||||
|
||||
uint32_t critical_section = cyhal_system_critical_section_enter();
|
||||
|
||||
/* Cascading from C0 match into C1 is queued and can take 1 full LF clk cycle.
|
||||
* There are 3 cases:
|
||||
* 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.
|
||||
* Case 2: if c0 = match0 -1 then cascade may or not happen before new match value would occur. Match occurs on rising clock edge.
|
||||
* Synching match value occurs on falling edge. Wait until c0 = match0 to ensure cascade occurs.
|
||||
* Case 3: everything works as expected.
|
||||
*/
|
||||
uint16_t c0_current_ticks;
|
||||
while ((c0_current_ticks = (Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER0))) == c0_old_match) {}
|
||||
|
||||
uint16_t c1_current_ticks = Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER1);
|
||||
|
||||
Cy_MCWDT_ClearInterrupt(obj->base, (CY_MCWDT_CTR0 | CY_MCWDT_CTR1));
|
||||
|
||||
/* Use MCWDT C0,C1 and C2 to implement a 32bit free running counter
|
||||
C2 alone can not be used as it does not support interrupt on match feature
|
||||
C2 is used to keep track of time, while C0 and C1 are used to set interrupts
|
||||
To set an interrupt:
|
||||
1. delay = diff between timestamp(time in future) vs current value of C2
|
||||
2. if delay > 2seconds (Max time that can be counted by C0)
|
||||
Yes
|
||||
- use both C0 and C1
|
||||
- Increment C0 by delay % (CY_MCWDT_COUNTER0_MAX_TICKS + 1)
|
||||
- Increment C1 by delay / (CY_MCWDT_COUNTER1_MAX_TICKS + 1)
|
||||
- Special case : In case delay is multiple of (CY_MCWDT_COUNTER0_MAX_TICKS + 1), then
|
||||
delay % (CY_MCWDT_COUNTER0_MAX_TICKS + 1) will be 0, in this case
|
||||
- Increment C0 by c0_current_ticks -1
|
||||
- Increment C1 by (delay / (CY_MCWDT_COUNTER1_MAX_TICKS + 1)) -1
|
||||
No
|
||||
- Use only C0
|
||||
*/
|
||||
if (ticks > CY_MCWDT_COUNTER0_MAX_TICKS)
|
||||
if (c0_current_ticks == c0_old_match + 1)
|
||||
{
|
||||
uint16_t c0_increment;
|
||||
uint16_t c1_increment;
|
||||
|
||||
if (ticks > CY_MCWDT_MAX_DELAY_TICKS)
|
||||
{
|
||||
ticks = CY_MCWDT_MAX_DELAY_TICKS;
|
||||
}
|
||||
|
||||
c0_increment = ticks % (CY_MCWDT_COUNTER0_MAX_TICKS + 1);
|
||||
c0_match_ticks = (c0_current_ticks + c0_increment) % (CY_MCWDT_COUNTER0_MAX_TICKS + 1);
|
||||
c1_increment = (ticks) / (CY_MCWDT_COUNTER0_MAX_TICKS + 1);
|
||||
c1_match_ticks = (c1_current_ticks + c1_increment) % (CY_MCWDT_COUNTER1_MAX_TICKS + 1);
|
||||
|
||||
/* Special case - ticks is multiple of (CY_MCWDT_COUNTER0_MAX_TICKS + 1) */
|
||||
if (c0_increment == 0)
|
||||
{
|
||||
c0_match_ticks = c0_current_ticks - 1;
|
||||
c1_match_ticks = c1_match_ticks -1;
|
||||
}
|
||||
|
||||
mcwdt_interrupt_mask = CY_MCWDT_CTR1;
|
||||
c1_current_ticks++;
|
||||
}
|
||||
else
|
||||
if (Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER0) != c0_current_ticks)
|
||||
{
|
||||
c0_match_ticks = c0_current_ticks + (uint16_t)ticks;
|
||||
c1_match_ticks = CY_MCWDT_COUNTER1_MAX_TICKS;
|
||||
|
||||
/* MCWDT has internal delay of about 1.5 LF clock ticks, so this is the minimum
|
||||
* that we can schedule.
|
||||
*/
|
||||
if (ticks < 3)
|
||||
{
|
||||
/* Cheating a bit here. */
|
||||
c0_match_ticks = c0_current_ticks + 3;
|
||||
}
|
||||
|
||||
mcwdt_interrupt_mask = CY_MCWDT_CTR0;
|
||||
// Just in the very unlikely case that an increment occurred while previous instruction was running.
|
||||
c1_current_ticks = Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER1);
|
||||
}
|
||||
Cy_MCWDT_SetMatch(obj->base, CY_MCWDT_COUNTER0, c0_current_ticks + c0_increment, CY_MCWDT_SETMATCH_NOWAIT_TIME_US);
|
||||
Cy_MCWDT_SetMatch(obj->base, CY_MCWDT_COUNTER1, c1_current_ticks + c1_increment, CY_MCWDT_SETMATCH_NOWAIT_TIME_US);
|
||||
|
||||
if(c1_match_ticks == 0)
|
||||
{
|
||||
c1_match_ticks = 1;
|
||||
}
|
||||
cyhal_system_critical_section_exit(critical_section);
|
||||
|
||||
if(c0_match_ticks == 0)
|
||||
{
|
||||
c0_match_ticks = 1;
|
||||
}
|
||||
|
||||
Cy_MCWDT_SetMatch(obj->base, CY_MCWDT_COUNTER0, c0_match_ticks, CY_MCWDT_SETMATCH_NOWAIT_TIME_US);
|
||||
Cy_MCWDT_SetMatch(obj->base, CY_MCWDT_COUNTER1, c1_match_ticks, CY_MCWDT_SETMATCH_NOWAIT_TIME_US);
|
||||
Cy_MCWDT_SetInterruptMask(obj->base, mcwdt_interrupt_mask);
|
||||
Cy_MCWDT_SetInterruptMask(obj->base, CY_MCWDT_CTR1);
|
||||
|
||||
return CY_RSLT_SUCCESS;
|
||||
}
|
||||
|
@ -265,7 +241,9 @@ void cyhal_lptimer_register_callback(cyhal_lptimer_t *obj, cyhal_lptimer_event_c
|
|||
|
||||
void cyhal_lptimer_enable_event(cyhal_lptimer_t *obj, cyhal_lptimer_event_t event, uint8_t intrPriority, bool enable)
|
||||
{
|
||||
Cy_MCWDT_SetInterruptMask(obj->base, enable ? CY_MCWDT_CTR0 : 0);
|
||||
CY_ASSERT(event == CYHAL_LPTIMER_COMPARE_MATCH);
|
||||
Cy_MCWDT_ClearInterrupt(obj->base, CY_MCWDT_CTR1);
|
||||
Cy_MCWDT_SetInterruptMask(obj->base, enable ? CY_MCWDT_CTR1 : 0);
|
||||
|
||||
IRQn_Type irqn = (IRQn_Type)(srss_interrupt_mcwdt_0_IRQn + obj->resource.block_num);
|
||||
NVIC_SetPriority(irqn, intrPriority);
|
||||
|
|
Loading…
Reference in New Issue