Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of mbed-dev by
Diff: targets/TARGET_ONSEMI/TARGET_NCS36510/rtc.c
- Revision:
- 167:e84263d55307
- Parent:
- 150:02e0a0aed4ec
- Child:
- 174:b96e65c34a4d
diff -r c97ed07ec1a8 -r e84263d55307 targets/TARGET_ONSEMI/TARGET_NCS36510/rtc.c --- a/targets/TARGET_ONSEMI/TARGET_NCS36510/rtc.c Thu Jun 08 15:02:37 2017 +0100 +++ b/targets/TARGET_ONSEMI/TARGET_NCS36510/rtc.c Wed Jun 21 17:46:44 2017 +0100 @@ -7,11 +7,11 @@ * $Rev: 3525 $ * $Date: 2015-07-20 15:24:25 +0530 (Mon, 20 Jul 2015) $ ****************************************************************************** - * Copyright 2016 Semiconductor Components Industries LLC (d/b/a ON Semiconductor). + * Copyright 2016 Semiconductor Components Industries LLC (d/b/a �ON Semiconductor�). * All rights reserved. This software and/or documentation is licensed by ON Semiconductor * under limited terms and conditions. The terms and conditions pertaining to the software * and/or documentation are available at http://www.onsemi.com/site/pdf/ONSEMI_T&C.pdf - * (ON Semiconductor Standard Terms and Conditions of Sale, Section 8 Software) and + * (�ON Semiconductor Standard Terms and Conditions of Sale, Section 8 Software�) and * if applicable the software license agreement. Do not use this software and/or * documentation unless you have carefully read and you agree to the limited terms and * conditions. By using this software and/or documentation, you agree to the limited @@ -45,8 +45,25 @@ #include "mbed_assert.h" #include "lp_ticker_api.h" -static uint16_t SubSecond; -static uint64_t LastRtcTimeus; +static volatile uint64_t last_time_read; + +/** + * Convert sub seconds ticks to micro seconds. + * The clock running at 32kHz, a tick is 1/32768 of a second. + */ +static inline uint32_t ticks_to_us(uint16_t ticks) { + return (((uint64_t)ticks * RTC_SEC_TO_US) / RTC_CLOCK_HZ); +} + +/** + * Convert us into sub seconds ticks. + * @note result might be troncated to be in the range [0 - RTC_SUB_SEC_MASK]. + */ +static inline uint16_t us_to_ticks(uint32_t us) { + return (((uint64_t) us * RTC_CLOCK_HZ) / RTC_SEC_TO_US) & RTC_SUB_SEC_MASK; +} + +#define RTC_TICK_THRESHOLD 5 /* See rtc.h for details */ void fRtcInit(void) @@ -55,115 +72,131 @@ CLOCKREG->CCR.BITS.RTCEN = True; /* Enable RTC clock 32K */ /* Reset RTC control register */ - RTCREG->CONTROL.WORD = False; + RTCREG->CONTROL.WORD = 0; /* Initialize all counters */ - RTCREG->SECOND_COUNTER = False; - RTCREG->SUB_SECOND_COUNTER = False; - RTCREG->SECOND_ALARM = False; - RTCREG->SUB_SECOND_ALARM = False; - LastRtcTimeus = 0; + RTCREG->SECOND_COUNTER = 0; + RTCREG->SUB_SECOND_COUNTER = 0; + RTCREG->SECOND_ALARM = 0; + RTCREG->SUB_SECOND_ALARM = 0; + last_time_read = 0; /* Reset RTC Status register */ - RTCREG->STATUS.WORD = False; + RTCREG->STATUS.WORD = 0; /* Clear interrupt status */ - RTCREG->INT_CLEAR.WORD = False; + RTCREG->INT_CLEAR.WORD = ( + (1 << RTC_INT_CLR_SUB_SEC_BIT_POS) | + (1 << RTC_INT_CLR_SEC_BIT_POS) + ); + /* Wait previous write to complete */ + while(RTCREG->STATUS.BITS.BSY_CTRL_REG_WRT == True); /* Start sec & sub_sec counter */ - while(RTCREG->STATUS.BITS.BSY_CTRL_REG_WRT == True);/* Wait previous write to complete */ - RTCREG->CONTROL.WORD |= ((True << RTC_CONTROL_SUBSEC_CNT_START_BIT_POS) | - (True << RTC_CONTROL_SEC_CNT_START_BIT_POS)); + RTCREG->CONTROL.WORD |= ( + (True << RTC_CONTROL_SUBSEC_CNT_START_BIT_POS) | + (True << RTC_CONTROL_SEC_CNT_START_BIT_POS) + ); /* enable interruption associated with the rtc at NVIC level */ - NVIC_SetVector(Rtc_IRQn,(uint32_t)fRtcHandler); /* TODO define lp_ticker_isr */ + NVIC_SetVector(Rtc_IRQn,(uint32_t) fRtcHandler); /* TODO define lp_ticker_isr */ NVIC_ClearPendingIRQ(Rtc_IRQn); NVIC_EnableIRQ(Rtc_IRQn); - while(RTCREG->STATUS.BITS.BSY_CTRL_REG_WRT == True); /* Wait for RTC to finish writing register - RTC operates on 32K clock as compared to 32M core*/ - - return; + /* Wait for RTC to finish writing register */ + while(RTCREG->STATUS.BITS.BSY_CTRL_REG_WRT == True); } /* See rtc.h for details */ void fRtcFree(void) { - /* Reset RTC control register */ - RTCREG->CONTROL.WORD = False; + /* Disable interrupts and counter */ + RTCREG->CONTROL.WORD = 0; /* disable interruption associated with the rtc */ NVIC_DisableIRQ(Rtc_IRQn); - while(RTCREG->STATUS.BITS.BSY_CTRL_REG_WRT == True); /* Wait for RTC to finish writing register - RTC operates on 32K clock as compared to 32M core*/ + /* Wait for RTC to finish writing register */ + while(RTCREG->STATUS.BITS.BSY_CTRL_REG_WRT == True); } /* See rtc.h for details */ void fRtcSetInterrupt(uint32_t timestamp) { - SubSecond = False; - uint32_t Second = False, EnableInterrupt = False; - uint8_t DividerAdjust = 1; - - if(timestamp) { - if(timestamp >= RTC_SEC_TO_US) { - /* TimeStamp is big enough to set second alarm */ - Second = ((timestamp / RTC_SEC_TO_US) & RTC_SEC_MASK); /* Convert micro second to second */ - RTCREG->SECOND_ALARM = Second; /* Write to alarm register */ + uint64_t current_time = fRtcRead(); - /* Enable second interrupt */ - EnableInterrupt = True << RTC_CONTROL_SEC_CNT_INT_BIT_POS; - } - timestamp = timestamp - Second * RTC_SEC_TO_US; /* Take out micro second for sub second alarm */ - if(timestamp > False) { - /* We have some thing for sub second */ + /* compute delta between current time and timestamp. + * Note: the current time used to compute the delta is relative (truncated + * to 32 bits). + */ + int32_t delta = timestamp - (uint32_t) current_time; + if (delta <= 0) { + // event considered in the past, set the interrupt as pending. + NVIC_SetPendingIRQ(Rtc_IRQn); + return; + } - /* Convert micro second to sub_seconds(each count = 30.5 us) */ - if(timestamp > 131000) { - DividerAdjust = 100; - } + uint64_t full_timestamp = (current_time & ~UINT32_MAX) | timestamp; + if ( (uint32_t)current_time > timestamp) { + full_timestamp += ((uint64_t) UINT32_MAX) + 1; + } - volatile uint64_t Temp = (timestamp / DividerAdjust * RTC_CLOCK_HZ); - Temp = (uint64_t)(Temp / RTC_SEC_TO_US * DividerAdjust); - SubSecond = Temp & RTC_SUB_SEC_MASK; + uint32_t target_seconds = full_timestamp / RTC_SEC_TO_US; + uint16_t target_ticks = us_to_ticks(full_timestamp); - if(SubSecond <= 5) { - SubSecond = 0; - } - - if(SubSecond > False) { - /* Second interrupt not enabled */ - - /* Set SUB SEC_ALARM */ - RTCREG->SUB_SECOND_ALARM = SubSecond; /* Write to sub second alarm */ + /* + * If the interrupt is in more than one second from now then use the + * second alarm, otherwise use the subsecond alarm. + * In case of the second alarm is used, there is no need to preserve the + * remaining subsecond because the irq handler should manage spurious + * interrupts (like when the timestamp is in the past). In such case, irq + * handler will schedule a new interrupt with the remaining us. + */ + NVIC_DisableIRQ(Rtc_IRQn); + if (target_seconds != RTCREG->SECOND_COUNTER) { + RTCREG->SECOND_ALARM = target_seconds; - /* Enable sub second interrupt */ - EnableInterrupt |= (True << RTC_CONTROL_SUBSEC_CNT_INT_BIT_POS); - } - } - - RTCREG->CONTROL.WORD |= EnableInterrupt; - /* Enable RTC interrupt */ - NVIC_EnableIRQ(Rtc_IRQn); - - /* Wait for RTC to finish writing register - RTC operates on 32K clock as compared to 32M core*/ - while((RTCREG->STATUS.WORD & ((True << RTC_STATUS_SUB_SEC_ALARM_WRT_BIT_POS) | - (True << RTC_STATUS_SEC_ALARM_WRT_BIT_POS) | - (True << RTC_STATUS_CONTROL_WRT_BIT_POS))) == True); - } - return; + uint32_t rtc_control = RTCREG->CONTROL.WORD; + rtc_control |= (1 << RTC_CONTROL_SEC_CNT_INT_BIT_POS); // enable seconds interrupt + rtc_control &= ~(1 << RTC_CONTROL_SUBSEC_CNT_INT_BIT_POS); // disable sub sec interrupt + RTCREG->CONTROL.WORD = rtc_control; + } else { + uint16_t current_ticks = RTCREG->SUB_SECOND_COUNTER; + if (current_ticks == target_ticks || + ((target_ticks > current_ticks) && ((target_ticks - current_ticks) < RTC_TICK_THRESHOLD)) || + ((target_ticks < current_ticks) && ((RTC_SUB_SEC_MASK - (current_ticks - target_ticks)) < RTC_TICK_THRESHOLD))) { + // target ticks too close; schedule the interrupt immediately + NVIC_SetPendingIRQ(Rtc_IRQn); + } else { + RTCREG->SUB_SECOND_ALARM = target_ticks; + + uint32_t rtc_control = RTCREG->CONTROL.WORD; + rtc_control &= ~(1 << RTC_CONTROL_SEC_CNT_INT_BIT_POS); // disable seconds interrupt + rtc_control |= (1 << RTC_CONTROL_SUBSEC_CNT_INT_BIT_POS); // enable sub sec interrupt + RTCREG->CONTROL.WORD = rtc_control; + } + } + NVIC_EnableIRQ(Rtc_IRQn); + + /* Wait for RTC to finish writing register - RTC operates on 32K clock as compared to 32M core*/ + while(RTCREG->STATUS.WORD & + ( + (True << RTC_STATUS_SUB_SEC_ALARM_WRT_BIT_POS) | + (True << RTC_STATUS_SEC_ALARM_WRT_BIT_POS) | + (True << RTC_STATUS_CONTROL_WRT_BIT_POS) + ) + ); } /* See rtc.h for details */ void fRtcDisableInterrupt(void) { - /* Disable RTC interrupt */ NVIC_DisableIRQ(Rtc_IRQn); } /* See rtc.h for details */ void fRtcEnableInterrupt(void) { - /* Enable RTC interrupt */ NVIC_EnableIRQ(Rtc_IRQn); } @@ -176,39 +209,36 @@ (True << RTC_INT_CLR_SEC_BIT_POS)); while((RTCREG->STATUS.WORD & ((True << RTC_STATUS_SUB_SEC_INT_CLR_WRT_BIT_POS) | - (True << RTC_STATUS_SEC_INT_CLR_WRT_BIT_POS))) == True); /* Wait for RTC to finish writing register - RTC operates on 32K clock as compared to 32M core*/ + (True << RTC_STATUS_SEC_INT_CLR_WRT_BIT_POS)))); /* Wait for RTC to finish writing register - RTC operates on 32K clock as compared to 32M core*/ } /* See rtc.h for details */ uint64_t fRtcRead(void) { - uint32_t Second; - uint16_t SubSecond; - /* Hardware Bug fix: The rollover of the sub-second counter initiates the increment of the second counter. * That means there is one cycle where the sub-second has rolled back to zero and the second counter has not incremented * and a read during that cycle will be incorrect. That will occur for one RTC cycle and that is about 31us of exposure. * If you read a zero in the sub-second counter then increment the second counter by 1. * Alternatively, subtract 1 from the Sub-seconds counter to align the Second and Sub-Second rollover. */ - - /* Read the Second and Sub-second counters, then read the Second counter again. - * If it changed, then the Second rolled over while reading Sub-seconds, so go back and read them both again. - */ + uint32_t seconds = RTCREG->SECOND_COUNTER; + uint16_t ticks = (RTCREG->SUB_SECOND_COUNTER - 1) & SUB_SEC_MASK; - do { - Second = RTCREG->SECOND_COUNTER; /* Get SEC_COUNTER reg value */ - SubSecond = (RTCREG->SUB_SECOND_COUNTER - 1) & SUB_SEC_MASK; /* Get SUB_SEC_COUNTER reg value */ - } while (Second != RTCREG->SECOND_COUNTER); /* Repeat if the second has changed */ + /* + * If seconds has changed while reading ticks, read them both again. + */ + while (seconds != RTCREG->SECOND_COUNTER) { + seconds = RTCREG->SECOND_COUNTER; + ticks = (RTCREG->SUB_SECOND_COUNTER - 1) & SUB_SEC_MASK; + } - //note: casting to float removed to avoid reduction in resolution - uint64_t RtcTimeus = ((uint64_t)SubSecond * RTC_SEC_TO_US / RTC_CLOCK_HZ) + ((uint64_t)Second * RTC_SEC_TO_US); + uint64_t current_time = ((uint64_t) seconds * RTC_SEC_TO_US) + ticks_to_us(ticks); /*check that the time did not go backwards */ - MBED_ASSERT(RtcTimeus >= LastRtcTimeus); - LastRtcTimeus = RtcTimeus; + MBED_ASSERT(current_time >= last_time_read); + last_time_read = current_time; - return RtcTimeus; + return current_time; } /* See rtc.h for details */ @@ -244,43 +274,31 @@ /* See rtc.h for details */ void fRtcHandler(void) { - /* SUB_SECOND/SECOND interrupt occured */ - volatile uint32_t TempStatus = RTCREG->STATUS.WORD; - /* Disable RTC interrupt */ NVIC_DisableIRQ(Rtc_IRQn); /* Clear sec & sub_sec interrupts */ - RTCREG->INT_CLEAR.WORD = ((True << RTC_INT_CLR_SUB_SEC_BIT_POS) | - (True << RTC_INT_CLR_SEC_BIT_POS)); + RTCREG->INT_CLEAR.WORD = ( + (True << RTC_INT_CLR_SUB_SEC_BIT_POS) | + (True << RTC_INT_CLR_SEC_BIT_POS) + ); - /* TODO ANDing SUB_SEC & SEC interrupt - work around for RTC issue - will be resolved in REV G */ - if(TempStatus & RTC_SEC_INT_STATUS_MASK) { - /* Second interrupt occured */ - if(SubSecond > False) { - /* Set SUB SEC_ALARM */ - RTCREG->SUB_SECOND_ALARM = SubSecond + RTCREG->SUB_SECOND_COUNTER; - /* Enable sub second interrupt */ - RTCREG->CONTROL.WORD |= (True << RTC_CONTROL_SUBSEC_CNT_INT_BIT_POS); - } else { - /* We reach here after second interrupt is occured */ - RTCREG->CONTROL.WORD &= ~(True << RTC_CONTROL_SUBSEC_CNT_INT_BIT_POS) | - (True << RTC_CONTROL_SEC_CNT_INT_BIT_POS); - } - } else { - /* We reach here after sub_second or (Sub second + second) interrupt occured */ - /* Disable Second and sub_second interrupt */ - RTCREG->CONTROL.WORD &= ~(True << RTC_CONTROL_SUBSEC_CNT_INT_BIT_POS) | - (True << RTC_CONTROL_SEC_CNT_INT_BIT_POS); - } + /* Disable sub seconds and seconds interrupts */ + RTCREG->CONTROL.WORD &= ~( + (True << RTC_CONTROL_SUBSEC_CNT_INT_BIT_POS) | + (True << RTC_CONTROL_SEC_CNT_INT_BIT_POS) + ); NVIC_EnableIRQ(Rtc_IRQn); - /* Wait for RTC to finish writing register - RTC operates on 32K clock as compared to 32M core*/ - while((RTCREG->STATUS.WORD & ((True << RTC_STATUS_SUB_SEC_ALARM_WRT_BIT_POS) | - (True << RTC_STATUS_CONTROL_WRT_BIT_POS) | - (True << RTC_STATUS_SUB_SEC_INT_CLR_WRT_BIT_POS) | - (True << RTC_STATUS_SEC_INT_CLR_WRT_BIT_POS))) == True); + /* Wait for RTC to finish writing registers */ + while(RTCREG->STATUS.WORD & + ( + (True << RTC_STATUS_CONTROL_WRT_BIT_POS) | + (True << RTC_STATUS_SUB_SEC_INT_CLR_WRT_BIT_POS) | + (True << RTC_STATUS_SEC_INT_CLR_WRT_BIT_POS) + ) + ); lp_ticker_irq_handler(); }