From 823d50d6fdd91b96fa344c723be776fdc9ae1bc7 Mon Sep 17 00:00:00 2001 From: Shuopeng Deng Date: Wed, 27 Nov 2019 12:20:33 -0800 Subject: [PATCH] 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. --- .../TARGET_PSOC6/cy_lp_ticker_api.c | 7 +- .../psoc6csp/hal/include/cyhal_lptimer.h | 35 ++-- .../psoc6csp/hal/src/cyhal_lptimer.c | 170 ++++++++---------- 3 files changed, 100 insertions(+), 112 deletions(-) diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/cy_lp_ticker_api.c b/targets/TARGET_Cypress/TARGET_PSOC6/cy_lp_ticker_api.c index ac20ec6d165..eba20492214 100644 --- a/targets/TARGET_Cypress/TARGET_PSOC6/cy_lp_ticker_api.c +++ b/targets/TARGET_Cypress/TARGET_PSOC6/cy_lp_ticker_api.c @@ -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"); } } diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/include/cyhal_lptimer.h b/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/include/cyhal_lptimer.h index 5f77945d292..ff621632ea0 100644 --- a/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/include/cyhal_lptimer.h +++ b/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/include/cyhal_lptimer.h @@ -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,32 +89,37 @@ void cyhal_lptimer_free(cyhal_lptimer_t *obj); */ cy_rslt_t cyhal_lptimer_reload(cyhal_lptimer_t *obj); -/** Set timeframe between interrupts +/** Deprecated. Call cyhal_lptimer_set_match instead. */ +#define cyhal_lptimer_set_time cyhal_lptimer_set_match + +/** Update the match/compare value * - * 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(). + * Update the match value of an already configured LPTIMER set up + * to generate an interrupt on match. Note that this function does not + * reinitialize the counter or the associated peripheral initialization + * sequence. * * @param[in] obj The LPTIMER object - * @param[in] time The time in ticks to be set + * @param[in] value The tick value to match * - * @return The status of the set_time request + * @return The status of the set_match request */ -cy_rslt_t cyhal_lptimer_set_time(cyhal_lptimer_t *obj, uint32_t time); +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. Note that this function does not - * reinitialize the counter or the associated peripheral initialization + * 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] value The match value in ticks + * @param[in] delay The ticks to wait * * @return The status of the set_match request */ -cy_rslt_t cyhal_lptimer_set_match(cyhal_lptimer_t *obj, uint32_t value); +cy_rslt_t cyhal_lptimer_set_delay(cyhal_lptimer_t *obj, uint32_t delay); /** Read the current tick * diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/src/cyhal_lptimer.c b/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/src/cyhal_lptimer.c index 8c3ceb70d2a..2c57d0fc8c3 100644 --- a/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/src/cyhal_lptimer.c +++ b/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/src/cyhal_lptimer.c @@ -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) +cy_rslt_t cyhal_lptimer_set_match(cyhal_lptimer_t *obj, uint32_t ticks) { - return cyhal_lptimer_set_match(obj, ticks); + return cyhal_lptimer_set_delay(obj, ticks - cyhal_lptimer_read(obj)); } -cy_rslt_t cyhal_lptimer_set_match(cyhal_lptimer_t *obj, uint32_t ticks) +cy_rslt_t cyhal_lptimer_set_delay(cyhal_lptimer_t *obj, uint32_t delay) { - 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); - 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) + /** + * 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) { - 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; + delay = CY_MCWDT_MIN_DELAY; } - else + if (delay > CY_MCWDT_MAX_DELAY_TICKS) { - c0_match_ticks = c0_current_ticks + (uint16_t)ticks; - c1_match_ticks = CY_MCWDT_COUNTER1_MAX_TICKS; + delay = CY_MCWDT_MAX_DELAY_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; - } + uint16_t c0_increment = (uint16_t)delay; + uint16_t c1_increment = (uint16_t)(delay >> 16); - mcwdt_interrupt_mask = CY_MCWDT_CTR0; - } + 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(); - if(c1_match_ticks == 0) + /* 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); + if (c0_current_ticks == c0_old_match + 1) { - c1_match_ticks = 1; + c1_current_ticks++; } - - if(c0_match_ticks == 0) + if (Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER0) != c0_current_ticks) { - c0_match_ticks = 1; + // 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); + + cyhal_system_critical_section_exit(critical_section); - 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);