diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/source/cyhal_rtc.c b/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/source/cyhal_rtc.c index bc8b9f4431..28172f5efb 100644 --- a/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/source/cyhal_rtc.c +++ b/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/source/cyhal_rtc.c @@ -30,6 +30,25 @@ #include "cyhal_rtc.h" #include "cyhal_system.h" +/** +* \addtogroup group_hal_psoc6_rtc RTC (Real Time Clock) +* \ingroup group_hal_psoc6 +* \{ +* +* Internally the PSoC6 RTC only stores the year as a two digit BCD value +* (0-99); no century information is stored. On RTC initialization the HAL must, +* as a result, assume a default century. If cyhal_rtc_write has been called +* with a different century than the default, its value must be stored and that +* value must persist through deep sleep, hibernate, software resets, etc. PSoC6 +* hardware provides a number of BREG registers which exist in the BACKUP domain +* and will persist over these power modes and resets. The HAL uses the highest +* indexed BACKUP->BREG register to store the century for the RTC. +* +* Therefore do not use the highest indexed BACKUP->BREG register as it is +* reserved for internal HAL usage. +* \} group_hal_psoc6_wdt +*/ + #ifdef CY_IP_MXS40SRSS_RTC_INSTANCES #if defined(__cplusplus) @@ -40,9 +59,15 @@ extern "C" { #define CYHAL_RTC_STATE_ENABLED 1 #define CYHAL_RTC_STATE_TIME_SET 2 #define CYHAL_RTC_DEFAULT_PRIORITY 5 -#define CYHAL_RTC_CENTURY 2000 +#define CYHAL_RTC_INIT_CENTURY 2000 #define CYHAL_TM_YEAR_BASE 1900 +#define CYHAL_RTC_BREG (BACKUP->BREG[SRSS_BACKUP_NUM_BREG-1]) +#define CYHAL_RTC_BREG_CENTURY_Pos 0UL +#define CYHAL_RTC_BREG_CENTURY_Msk 0x0000FFFFUL +#define CYHAL_RTC_BREG_STATE_Pos 16UL +#define CYHAL_RTC_BREG_STATE_Msk 0xFFFF0000UL + /** Wrapper around the PDL Cy_RTC_DeepSleepCallback to adapt the function signature */ static cy_en_syspm_status_t cyhal_rtc_syspm_callback(cy_stc_syspm_callback_params_t *params, cy_en_syspm_callback_mode_t mode) { @@ -59,7 +84,32 @@ static cy_stc_syspm_callback_t cyhal_rtc_pm_cb = { static cyhal_rtc_event_callback_t cyhal_rtc_user_handler; static void *cyhal_rtc_handler_arg; -static uint8_t cyhal_rtc_initialized = CYHAL_RTC_STATE_UNINITIALIZED; + +/* Returns century portion of BREG register used to store century info */ +static inline uint16_t get_rtc_century() +{ + return _FLD2VAL(CYHAL_RTC_BREG_CENTURY, CYHAL_RTC_BREG); +} + +/* Sets century portion of BREG register used to store century info */ +static inline void set_rtc_century(uint16_t century) +{ + CYHAL_RTC_BREG &= CYHAL_RTC_BREG_STATE_Msk; + CYHAL_RTC_BREG |= _VAL2FLD(CYHAL_RTC_BREG_CENTURY, century); +} + +/* Returns state portion of BREG register used to store century info */ +static inline uint16_t get_rtc_state() +{ + return _FLD2VAL(CYHAL_RTC_BREG_STATE, CYHAL_RTC_BREG); +} + +/* Sets state portion of BREG register used to store century info */ +static inline void set_rtc_state(uint16_t init) +{ + CYHAL_RTC_BREG &= CYHAL_RTC_BREG_CENTURY_Msk; + CYHAL_RTC_BREG |= _VAL2FLD(CYHAL_RTC_BREG_STATE, init); +} /** Wrapper around the PDL RTC interrupt handler to adapt the function signature */ static void cyhal_rtc_internal_handler(void) @@ -67,7 +117,6 @@ static void cyhal_rtc_internal_handler(void) Cy_RTC_Interrupt(dst, NULL != dst); } -/* Override weak function from PDL */ void Cy_RTC_Alarm1Interrupt(void) { if (NULL != cyhal_rtc_user_handler) @@ -76,24 +125,50 @@ void Cy_RTC_Alarm1Interrupt(void) } } +void Cy_RTC_CenturyInterrupt(void) +{ + set_rtc_century(get_rtc_century() + 100); +} + cy_rslt_t cyhal_rtc_init(cyhal_rtc_t *obj) { CY_ASSERT(NULL != obj); cy_rslt_t rslt = CY_RSLT_SUCCESS; - if (cyhal_rtc_initialized == CYHAL_RTC_STATE_UNINITIALIZED) + if (get_rtc_state() == CYHAL_RTC_STATE_UNINITIALIZED) { - static const cy_stc_sysint_t irqCfg = {.intrSrc = srss_interrupt_backup_IRQn, .intrPriority = CYHAL_RTC_DEFAULT_PRIORITY}; - Cy_SysInt_Init(&irqCfg, &cyhal_rtc_internal_handler); + if (Cy_RTC_IsExternalResetOccurred()) + { + // Reset to default time + static const cy_stc_rtc_config_t defaultTime = { + .dayOfWeek = CY_RTC_SATURDAY, + .date = 1, + .month = 1, + .year = 0, + }; + Cy_RTC_SetDateAndTime(&defaultTime); + set_rtc_century(CYHAL_RTC_INIT_CENTURY); + } if (Cy_SysPm_RegisterCallback(&cyhal_rtc_pm_cb)) { - cyhal_rtc_initialized = CYHAL_RTC_STATE_ENABLED; + set_rtc_state(CYHAL_RTC_STATE_ENABLED); } else { rslt = CY_RSLT_RTC_NOT_INITIALIZED; } } + else if(get_rtc_state() == CYHAL_RTC_STATE_ENABLED || get_rtc_state() == CYHAL_RTC_STATE_TIME_SET) + { + if(Cy_RTC_GetInterruptStatus() & CY_RTC_INTR_CENTURY) + Cy_RTC_CenturyInterrupt(); + } + + Cy_RTC_ClearInterrupt(CY_RTC_INTR_CENTURY); + Cy_RTC_SetInterruptMask(CY_RTC_INTR_CENTURY); + static const cy_stc_sysint_t irqCfg = {.intrSrc = srss_interrupt_backup_IRQn, .intrPriority = CYHAL_RTC_DEFAULT_PRIORITY}; + Cy_SysInt_Init(&irqCfg, &cyhal_rtc_internal_handler); + if (rslt == CY_RSLT_SUCCESS) { dst = NULL; @@ -108,25 +183,28 @@ void cyhal_rtc_free(cyhal_rtc_t *obj) CY_ASSERT(NULL != obj); NVIC_DisableIRQ(srss_interrupt_backup_IRQn); + Cy_RTC_SetInterruptMask(CY_RTC_INTR_CENTURY); dst = NULL; } bool cyhal_rtc_is_enabled(cyhal_rtc_t *obj) { CY_ASSERT(NULL != obj); - return (cyhal_rtc_initialized == CYHAL_RTC_STATE_TIME_SET); + return (get_rtc_state() == CYHAL_RTC_STATE_TIME_SET); } cy_rslt_t cyhal_rtc_read(cyhal_rtc_t *obj, struct tm *time) { - // The number of days that precede each month of the year, not including Feb 29 CY_ASSERT(NULL != obj); + // The number of days that precede each month of the year, not including Feb 29 static const uint16_t CUMULATIVE_DAYS[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + cy_stc_rtc_config_t dateTime; uint32_t savedIntrStatus = cyhal_system_critical_section_enter(); Cy_RTC_GetDateAndTime(&dateTime); - int year = dateTime.year + CYHAL_RTC_CENTURY; + int year = dateTime.year + get_rtc_century(); cyhal_system_critical_section_exit(savedIntrStatus); + time->tm_sec = dateTime.sec; time->tm_min = dateTime.min; time->tm_hour = dateTime.hour; @@ -137,16 +215,13 @@ cy_rslt_t cyhal_rtc_read(cyhal_rtc_t *obj, struct tm *time) time->tm_yday = CUMULATIVE_DAYS[time->tm_mon] + dateTime.date - 1u + ((dateTime.month >= 3 && Cy_RTC_IsLeapYear(year)) ? 1u : 0u); time->tm_isdst = -1; + return CY_RSLT_SUCCESS; } cy_rslt_t cyhal_rtc_write(cyhal_rtc_t *obj, const struct tm *time) { CY_ASSERT(NULL != obj); - int year = CYHAL_TM_YEAR_BASE + time->tm_year; - if (year < 2000 || year > 2099) { - return CY_RSLT_RTC_BAD_ARGUMENT; - } uint32_t year2digit = time->tm_year % 100; cy_stc_rtc_config_t newtime = { .sec = time->tm_sec, @@ -158,18 +233,25 @@ cy_rslt_t cyhal_rtc_write(cyhal_rtc_t *obj, const struct tm *time) .month = time->tm_mon + 1, .year = year2digit }; + cy_rslt_t rslt; uint32_t retry = 0; static const uint32_t MAX_RETRY = 10, RETRY_DELAY_MS = 1; do { if (retry != 0) Cy_SysLib_Delay(RETRY_DELAY_MS); + uint32_t savedIntrStatus = cyhal_system_critical_section_enter(); rslt = (cy_rslt_t)Cy_RTC_SetDateAndTime(&newtime); + if (rslt == CY_RSLT_SUCCESS) + set_rtc_century(time->tm_year - year2digit + CYHAL_TM_YEAR_BASE); + cyhal_system_critical_section_exit(savedIntrStatus); ++retry; } while (rslt == CY_RTC_INVALID_STATE && retry < MAX_RETRY); + while (CY_RTC_BUSY == Cy_RTC_GetSyncStatus()) { } + if (rslt == CY_RSLT_SUCCESS) - cyhal_rtc_initialized = CYHAL_RTC_STATE_TIME_SET; + set_rtc_state(CYHAL_RTC_STATE_TIME_SET); return rslt; } @@ -240,13 +322,13 @@ void cyhal_rtc_register_callback(cyhal_rtc_t *obj, cyhal_rtc_event_callback_t ca cyhal_system_critical_section_exit(savedIntrStatus); } -void cyhal_rtc_enable_event(cyhal_rtc_t *obj, cyhal_rtc_event_t event, uint8_t intrPriority, bool enable) +void cyhal_rtc_enable_event(cyhal_rtc_t *obj, cyhal_rtc_event_t event, uint8_t intr_priority, bool enable) { CY_ASSERT(NULL != obj); CY_ASSERT(CYHAL_RTC_ALARM == event); Cy_RTC_ClearInterrupt(CY_RTC_INTR_ALARM1 | CY_RTC_INTR_ALARM2); - Cy_RTC_SetInterruptMask(enable ? CY_RTC_INTR_ALARM1 : 0); - NVIC_SetPriority(srss_interrupt_backup_IRQn, intrPriority); + Cy_RTC_SetInterruptMask((enable ? CY_RTC_INTR_ALARM1 : 0) | CY_RTC_INTR_CENTURY); + NVIC_SetPriority(srss_interrupt_backup_IRQn, intr_priority); } #if defined(__cplusplus)