diff options
author | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2011-10-01 22:09:00 +0000 |
---|---|---|
committer | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2011-10-01 22:09:00 +0000 |
commit | 078ff53de7f505ba714be768a94c1e52e7261b3e (patch) | |
tree | 9bf8b7599be1785b828c91655da7510937c92303 | |
parent | a65f3d1d8c639d5f3c88cee9c41f95a812abfe13 (diff) | |
download | px4-nuttx-078ff53de7f505ba714be768a94c1e52e7261b3e.tar.gz px4-nuttx-078ff53de7f505ba714be768a94c1e52e7261b3e.tar.bz2 px4-nuttx-078ff53de7f505ba714be768a94c1e52e7261b3e.zip |
Add support for lo- and hi-res RTC hardware
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@4005 42af7a65-404d-4744-a932-0658087f49c3
-rw-r--r-- | nuttx/ChangeLog | 5 | ||||
-rw-r--r-- | nuttx/Documentation/NuttxPortingGuide.html | 69 | ||||
-rw-r--r-- | nuttx/arch/arm/src/stm32/stm32_i2c.c | 3 | ||||
-rw-r--r-- | nuttx/arch/arm/src/stm32/stm32_rtc.c | 614 | ||||
-rwxr-xr-x | nuttx/configs/vsn/nsh/defconfig | 1 | ||||
-rw-r--r-- | nuttx/configs/vsn/src/sif.c | 5 | ||||
-rw-r--r-- | nuttx/include/nuttx/clock.h | 2 | ||||
-rw-r--r-- | nuttx/include/nuttx/rtc.h | 131 | ||||
-rw-r--r-- | nuttx/include/time.h | 2 | ||||
-rw-r--r-- | nuttx/sched/clock_gettime.c | 57 | ||||
-rw-r--r-- | nuttx/sched/clock_initialize.c | 116 | ||||
-rw-r--r-- | nuttx/sched/clock_settime.c | 30 | ||||
-rw-r--r-- | nuttx/sched/clock_systimer.c | 14 |
13 files changed, 746 insertions, 303 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index d1557285c..d774bb48c 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -2121,3 +2121,8 @@ * configs/sam3u/touchscreen - This is the configuration that I plan to use to verify the SAM3U-EK touchscreen driver. However, as of this writing, there is no touchscreen driver for the board. + * CONFIG_RTC_HIRES - Add an option to support either a high-resolution RTC + that completely replaces the system timer tick but may overflow and lose + time when the MCU is off and also for a low-resolution (1 sec/tick) RTC + that can run until 2106 with no overflow. But in this latter case, higher + resolution time must come from the system timer. diff --git a/nuttx/Documentation/NuttxPortingGuide.html b/nuttx/Documentation/NuttxPortingGuide.html index 20f6778ca..d05eea239 100644 --- a/nuttx/Documentation/NuttxPortingGuide.html +++ b/nuttx/Documentation/NuttxPortingGuide.html @@ -12,7 +12,7 @@ <h1><big><font color="#3c34ec"> <i>NuttX RTOS Porting Guide</i> </font></big></h1> - <p>Last Updated: September 13, 2011</p> + <p>Last Updated: October 1, 2011</p> </td> </tr> </table> @@ -1989,18 +1989,30 @@ CONFIG_SYSTEM_UTC=y <h4>4.1.20.2 Hardware</h4> <p> - To enable hardware module use option: -<p> -<ul><pre> -CONFIG_RTC=y -</pre></ul> + To enable hardware module use the following configuration options: +<p> +<ul><dl> + <dt><code>CONFIG_RTC</code> + <dd>Enables general support for a hardware RTC. + Specific architectures may require other specific settings. + <dt><code>CONFIG_RTC_HIRES</code> + <dd>The typical RTC keeps time to resolution of 1 second, usually supporting a 32-bit <code>time_t</code> value. + In this case, the RTC is used to "seed" the normal NuttX timer and the NuttX timer provides for higher resoution time. + If <code>CONFIG_RTC_HIRES</code> is enabled in the NuttX configuration, then the RTC provides higher resolution time and completely replaces the system timer for purpose of date and time. + <dt><code>CONFIG_RTC_FREQUENCY</code> + <dd>If <code>CONFIG_RTC_HIRES</code> is defined, then the frequency of the high resolution RTC must be provided. + If <code>CONFIG_RTC_HIRES</code> is not defined, <code>CONFIG_RTC_FREQUENCY</code> is assumed to be one. + <dt><code>CONFIG_RTC_ALARM</code> + <dd>Enable if the RTC hardware supports setting of an alarm. + A callback function will be executed when the alarm goes off +</dl></ul> <p> which requires the following three base functions to read time: </p> <ul> <li><code>up_rtcinitialize()</code></li> - <li><code>up_rtc_gettime()</code>. UTC time in seconds.</li> - <li><code>up_rtc_getclock()</code>. Replacement for <code>g_system_tick</code></li> + <li><code>up_rtc_time()</code>. UTC time in seconds.</li> + <li><code>up_rtc_gettime()</code>. Replacement for <code>g_system_tick</code></li> </ul> <p> This module depends on <code>CONFIG_SYSTEM_UTC=y</code>. @@ -2024,15 +2036,17 @@ CONFIG_RTC=y Running at rate of system base timer, used for time-slicing, and so forth. </p> <p> - If hardware RTC is present (<code>CONFIG_RTC</code>) and enabled, then after successful - initiliazation variables are overriden by calls to <code>up_rtc_getclock()</code> which is + If hardware RTC is present (<code>CONFIG_RTC</code>) and and high-resolution timeing + is enabled (<code>CONFIG_RTC_HIRES</code>), then after successful + initiliazation variables are overriden by calls to <code>up_rtc_gettime()</code> which is running continously even in power-down modes. </p> <p> - In the case of <code>CONFIG_RTC</code> is set the <code>g_tickcount</code> and <code>g_system_utc</code> keep - counting at rate of a system timer, which however, is disabled in power-down - mode. By comparing this time and RTC (actual time) one may determine the - actual system active time. To retrieve that variable use: + In the case of <code>CONFIG_RTC_HIRES</code> is set the <code>g_tickcount</code> and + <code>g_system_utc</code> keep counting at rate of a system timer, which however, is + disabled in power-down mode. + By comparing this time and RTC (actual time) one may determine the actual system active time. + To retrieve that variable use: </p> <ul><pre> <li><code>clock_gettime(CLOCK_ACTIVETIME, tp)</code> @@ -4239,10 +4253,35 @@ build </ul> <h2>Device Drivers</h2> +<h3>RTC</h3> +<ul> + <li> + <code>CONFIG_RTC</code>: + Enables general support for a hardware RTC. + Specific architectures may require other specific settings. + </li> + <li> + <code>CONFIG_RTC_HIRES</code>: + The typical RTC keeps time to resolution of 1 second, usually supporting a 32-bit <code>time_t</code> value. + In this case, the RTC is used to "seed" the normal NuttX timer and the NuttX timer provides for higher resoution time. + If <code>CONFIG_RTC_HIRES</code> is enabled in the NuttX configuration, then the RTC provides higher resolution time and completely replaces the system timer for purpose of date and time. + </li> + <li> + <code>CONFIG_RTC_FREQUENCY</code>: + If <code>CONFIG_RTC_HIRES</code> is defined, then the frequency of the high resolution RTC must be provided. + If <code>CONFIG_RTC_HIRES</code> is not defined, <code>CONFIG_RTC_FREQUENCY</code> is assumed to be one. + </li> + <li> + <code>CONFIG_RTC_ALARM</code>: + Enable if the RTC hardware supports setting of an alarm. + A callback function will be executed when the alarm goes off + </li> +</ul> + <h3>SPI driver</h3> <ul> <li> - <code>CONFIG_SPI_OWNBUS</code> - Set if there is only one active device + <code>CONFIG_SPI_OWNBUS</code>: Set if there is only one active device on the SPI bus. No locking or SPI configuration will be performed. It is not necessary for clients to lock, re-configure, etc.. </li> diff --git a/nuttx/arch/arm/src/stm32/stm32_i2c.c b/nuttx/arch/arm/src/stm32/stm32_i2c.c index a46cc4af9..74d403648 100644 --- a/nuttx/arch/arm/src/stm32/stm32_i2c.c +++ b/nuttx/arch/arm/src/stm32/stm32_i2c.c @@ -6,6 +6,7 @@ * * With extensions, modifications by: * + * Copyright (C) 2011 Gregory Nutt. All rights reserved. * Author: Gregroy Nutt <gnutt@nuttx.org> * * Redistribution and use in source and binary forms, with or without @@ -932,7 +933,7 @@ static inline void stm32_i2c_enablefsmc(uint32_t ahbenr) } } #else -# define stm32_i2c_disablefsmc() (0) +# define stm32_i2c_disablefsmc(priv) (0) # define stm32_i2c_enablefsmc(ahbenr) #endif diff --git a/nuttx/arch/arm/src/stm32/stm32_rtc.c b/nuttx/arch/arm/src/stm32/stm32_rtc.c index 78105228f..84d50ccda 100644 --- a/nuttx/arch/arm/src/stm32/stm32_rtc.c +++ b/nuttx/arch/arm/src/stm32/stm32_rtc.c @@ -33,14 +33,7 @@ * ************************************************************************************/ -/** \file - * \author Uros Platise - * \brief STM32 Real-Time Clock - * - * \addtogroup STM32_RTC - * \{ - * - * The STM32 RTC Driver offers standard precision of 1 Hz or High Resolution +/* The STM32 RTC Driver offers standard precision of 1 Hz or High Resolution * operating at rate up to 16384 Hz. It provides UTC time and alarm interface * with external output pin (for wake-up). * @@ -53,9 +46,13 @@ * - time is a combination of clock and upper bits stored in backuped domain * with unit of 1 [s] * - * \todo Error Handling in case LSE fails during start-up or during operation. + * TODO: Error Handling in case LSE fails during start-up or during operation. */ +/************************************************************************************ + * Included Files + ************************************************************************************/ + #include <nuttx/config.h> #include <nuttx/arch.h> #include <nuttx/irq.h> @@ -74,21 +71,78 @@ #include "stm32_rtc.h" #include "stm32_waste.h" +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ +/* Configuration ********************************************************************/ + +#ifdef CONFIG_RTC_HIRES +# ifndef CONFIG_RTC_FREQUENCY +# error "CONFIG_RTC_FREQUENCY is required for CONFIG_RTC_HIRES" +# elif CONFIG_RTC_FREQUENCY != 16384 +# error "Only hi-res CONFIG_RTC_FREQUENCY of 16384Hz is supported" +# endif +# ifndef CONFIG_STM32_BKP +# error "CONFIG_STM32_BKP is required for CONFIG_RTC_HIRES" +# endif +#else +# ifndef CONFIG_RTC_FREQUENCY +# define CONFIG_RTC_FREQUENCY 1 +# endif +# if CONFIG_RTC_FREQUENCY != 1 +# error "Only lo-res CONFIG_RTC_FREQUENCY of 1Hz is supported" +# endif +#endif + +/* RTC/BKP Definitions *************************************************************/ +/* STM32_RTC_PRESCALAR_VALUE + * RTC pre-scalar value. The RTC is driven by a 32,768Hz input clock. This input + * value is divided by this value (plus one) to generate the RTC frequency. + * RTC_TIMEMSB_REG + * The BKP module register used to hold the RTC overflow value. Overflows are + * only handled in hi-res mode. + * RTC_CLOCKS_SHIFT + * The shift used to convert the hi-res timer LSB to one second. Not used with + * the lo-res timer. + */ -#if defined(CONFIG_STM32_BKP) +#ifdef CONFIG_RTC_HIRES +# define STM32_RTC_PRESCALAR_VALUE STM32_RTC_PRESCALER_MIN +# define RTC_TIMEMSB_REG STM32_BKP_DR1 +# define RTC_CLOCKS_SHIFT 14 +#else +# define STM32_RTC_PRESCALAR_VALUE STM32_RTC_PRESCALER_SECOND +#endif /************************************************************************************ - * Configuration of the RTC Backup Register (16-bit) + * Private Types ************************************************************************************/ -#define RTC_TIMEMSB_REG STM32_BKP_DR1 +struct rtc_regvals_s +{ + uint16_t cntl; + uint16_t cnth; +#ifdef CONFIG_RTC_HIRES + uint16_t ovf; +#endif +}; /************************************************************************************ * Private Data ************************************************************************************/ -/** Variable determines the state of the LSE oscilator. - * Possible errors: +/* Callback to use when the alarm expires */ + +#ifdef CONFIG_RTC_ALARM +static alarmcb_t g_alarmcb; +#endif + +/************************************************************************************ + * Public Data + ************************************************************************************/ + +/* Variable determines the state of the LSE oscilator. + * Possible errors: * - on start-up * - during operation, reported by LSE interrupt */ @@ -99,113 +153,265 @@ volatile bool g_rtc_enabled = false; * Private Functions ************************************************************************************/ +/************************************************************************************ + * Name: stm32_rtc_beginwr + * + * Description: + * Enter configuration mode + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ************************************************************************************/ + static inline void stm32_rtc_beginwr(void) { - /* Previous write is done? */ - while( (getreg16(STM32_RTC_CRL) & RTC_CRL_RTOFF)==0 ) up_waste(); + /* Previous write is done? */ + + while ((getreg16(STM32_RTC_CRL) & RTC_CRL_RTOFF) == 0) + { + up_waste(); + } + + /* Enter Config mode, Set Value and Exit */ - /* Enter Config mode, Set Value and Exit */ - modifyreg16(STM32_RTC_CRL, 0, RTC_CRL_CNF); + modifyreg16(STM32_RTC_CRL, 0, RTC_CRL_CNF); } +/************************************************************************************ + * Name: stm32_rtc_endwr + * + * Description: + * Exit configuration mode + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ************************************************************************************/ + static inline void stm32_rtc_endwr(void) { - modifyreg16(STM32_RTC_CRL, RTC_CRL_CNF, 0); + modifyreg16(STM32_RTC_CRL, RTC_CRL_CNF, 0); } -/** Wait for registerred to synchronise with RTC module, call after power-up only */ +/************************************************************************************ + * Name: stm32_rtc_wait4rsf + * + * Description: + * Wait for registers to synchronise with RTC module, call after power-up only + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ************************************************************************************/ + static inline void stm32_rtc_wait4rsf(void) { - modifyreg16(STM32_RTC_CRL, RTC_CRL_RSF, 0); - while( !(getreg16(STM32_RTC_CRL) & RTC_CRL_RSF) ) up_waste(); + modifyreg16(STM32_RTC_CRL, RTC_CRL_RSF, 0); + while (!(getreg16(STM32_RTC_CRL) & RTC_CRL_RSF)) + { + up_waste(); + } } /************************************************************************************ - * Interrupt Service Routines + * Name: up_rtc_breakout + * + * Description: + * Set the RTC to the provided time. + * + * Input Parameters: + * tp - the time to use + * + * Returned Value: + * None + * ************************************************************************************/ -static int stm32_rtc_overflow_isr(int irq, void *context) +#ifdef CONFIG_RTC_HIRES +static void up_rtc_breakout(FAR const struct timespec *tp, + FAR struct rtc_regvals_s *regvals) { - uint16_t source = getreg16( STM32_RTC_CRL ); - - if (source & RTC_CRL_OWF) { - putreg16( getreg16(RTC_TIMEMSB_REG) + 1, RTC_TIMEMSB_REG ); + uint64_t frac; + uint32_t cnt; + uint16_t ovf; + + /* Break up the time in seconds + milleconds into the correct values for our use */ + + frac = ((uint64_t)tp->tv_nsec * CONFIG_RTC_FREQUENCY) / 1000000000; + cnt = (tp->tv_sec << RTC_CLOCKS_SHIFT) | ((uint32_t)frac & (CONFIG_RTC_FREQUENCY-1)); + ovf = (tp->tv_sec >> (32 - RTC_CLOCKS_SHIFT)); + + /* Then return the broken out time */ + + regvals->cnth = cnt >> 16; + regvals->cntl = cnt & 0xffff; + regvals->ovf = ovf; +} +#else +static inline void up_rtc_breakout(FAR const struct timespec *tp, + FAR struct rtc_regvals_s *regvals) +{ + /* The low-res timer is easy... tv_sec holds exactly the value needed by the + * CNTH/CNTL registers. + */ + + regvals->cnth = (uint16_t)((uint32_t)tp->tv_sec >> 16); + regvals->cntl = (uint16_t)((uint32_t)tp->tv_sec & 0xffff); +} +#endif + +/************************************************************************************ + * Name: stm32_rtc_interrupt + * + * Description: + * RTC interrupt service routine + * + * Input Parameters: + * irq - The IRQ number that generated the interrupt + * context - Architecture specific register save information. + * + * Returned Value: + * Zero (OK) on success; A negated errno value on failure. + * + ************************************************************************************/ + +#if defined(CONFIG_RTC_HIRES) || defined(CONFIG_RTC_ALARM) +static int stm32_rtc_interrupt(int irq, void *context) +{ + uint16_t source = getreg16(STM32_RTC_CRL); + +#ifdef CONFIG_RTC_HIRES + if ((source & RTC_CRL_OWF) != 0) + { + putreg16(getreg16(RTC_TIMEMSB_REG) + 1, RTC_TIMEMSB_REG); } - - if (source & RTC_CRL_ALRF) { - /* Alarm */ +#endif + +#ifdef CONFIG_RTC_ALARM + if ((source & RTC_CRL_ALRF) != 0 && g_alarmcb != NULL) + { + /* Alarm callback */ + + g_alarmcb(); + g_alarmcb = NULL; } +#endif + + /* Clear pending flags, leave RSF high */ - /* Clear pending flags, leave RSF high */ - - putreg16( RTC_CRL_RSF, STM32_RTC_CRL ); - return 0; + putreg16(RTC_CRL_RSF, STM32_RTC_CRL); + return 0; } +#endif /************************************************************************************ - * Public Function - Initialization + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: up_rtcinitialize + * + * Description: + * Initialize the hardware RTC per the select configuration. This function is + * called once during the OS initialization sequence + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * ************************************************************************************/ -/** Power-up RTC - * - * \param prescaler A 20-bit value determines the time base, and is defined as: - * f = 32768 / (prescaler + 1) - * - * \return State of the RTC unit - * - * \retval OK If RTC has been successfully configured. - * \retval ERROR On error, if LSE does not start. - **/ int up_rtcinitialize(void) { - /* For this initial version we use predefined value */ - - uint32_t prescaler = STM32_RTC_PRESCALER_MIN; - - /* Set access to the peripheral, enable power and LSE */ - - stm32_pwr_enablebkp(); - stm32_rcc_enablelse(); + /* For this initial version we use predefined value */ - // \todo Get state from this function, if everything is - // okay and whether it is already enabled (if it was disabled - // reset upper time register) - g_rtc_enabled = true; - - // \todo Possible stall? should we set the timeout period? and return with -1 - stm32_rtc_wait4rsf(); - - /* Configure prescaler, note that these are write-only registers */ + uint32_t prescaler = STM32_RTC_PRESCALER_MIN; - stm32_rtc_beginwr(); - putreg16(prescaler >> 16, STM32_RTC_PRLH); - putreg16(prescaler & 0xFFFF, STM32_RTC_PRLL); - stm32_rtc_endwr(); - - /* Configure Overflow Interrupt */ - - irq_attach(STM32_IRQ_RTC, stm32_rtc_overflow_isr); - up_enable_irq(STM32_IRQ_RTC); + /* Set access to the peripheral, enable power and LSE */ - /* Previous write is done? This is required prior writing into CRH */ +#ifdef CONFIG_RTC_HIRES + stm32_pwr_enablebkp(); +#endif + stm32_rcc_enablelse(); - while( (getreg16(STM32_RTC_CRL) & RTC_CRL_RTOFF)==0 ) up_waste(); + /* TODO: Get state from this function, if everything is + * okay and whether it is already enabled (if it was disabled + * reset upper time register) + */ + + g_rtc_enabled = true; + + /* TODO: Possible stall? should we set the timeout period? and return with -1 */ + + stm32_rtc_wait4rsf(); + + /* Configure prescaler, note that these are write-only registers */ + + stm32_rtc_beginwr(); + putreg16(prescaler >> 16, STM32_RTC_PRLH); + putreg16(prescaler & 0xFFFF, STM32_RTC_PRLL); + stm32_rtc_endwr(); + + /* Configure RTC interrupt to catch overflow and alarm interrupts. */ + +#if defined(CONFIG_RTC_HIRES) || defined(CONFIG_RTC_ALARM) + irq_attach(STM32_IRQ_RTC, stm32_rtc_interrupt); + up_enable_irq(STM32_IRQ_RTC); +#endif + + /* Previous write is done? This is required prior writing into CRH */ - modifyreg16(STM32_RTC_CRH, 0, RTC_CRH_OWIE); + while ((getreg16(STM32_RTC_CRL) & RTC_CRL_RTOFF) == 0) + { + up_waste(); + } + modifyreg16(STM32_RTC_CRH, 0, RTC_CRH_OWIE); - /* Alarm Int via EXTI Line */ + /* Alarm Int via EXTI Line */ - // STM32_IRQ_RTCALR /* 41: RTC alarm through EXTI line interrupt */ + /* STM32_IRQ_RTCALR 41: RTC alarm through EXTI line interrupt */ - return OK; + return OK; } -/** Get time (counter) value - * - * \return time, where the unit depends on the prescaler value - **/ - -clock_t up_rtc_getclock(void) +/************************************************************************************ + * Name: up_rtc_time + * + * Description: + * Get the current time in seconds. + * + * Input Parameters: + * None + * + * Returned Value: + * The current time in seconds + * + ************************************************************************************/ + +#ifdef CONFIG_RTC_HIRES +time_t up_rtc_time(void) +{ + struct timespec ts; + + /* In the hi-res case, this function is just a wrapper for up_rtc_gettime */ + + (void)up_rtc_gettime(&ts); + return ts.tv_sec; +} +#else +time_t up_rtc_time(void) { irqstate_t flags; uint16_t cnth; @@ -243,98 +449,176 @@ clock_t up_rtc_getclock(void) while (cntl < tmp); irqrestore(flags); - /* Then return the full 32-bit counter value */ + /* Okay.. the samples should be as close together in time as possible and + * we can be assured that no clock rollover occurred between the samples. + * + * Return the time in seconds. + */ - return ((uint32_t)cnth << 16) | (uint32_t)cntl; + return (time_t)cnth << 16 | (time_t)cntl; } +#endif -/** Set time (counter) value - * - * \param time The unit depends on the prescaler value - **/ +/************************************************************************************ + * Name: up_rtc_gettime + * + * Description: + * Get the current time from the high resolution RTC clock. + * + * Input Parameters: + * tp - The location to return the high resolution time value. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ -void up_rtc_setclock(clock_t newclock) +#ifdef CONFIG_RTC_HIRES +int up_rtc_gettime(FAR struct timespec *tp) { - stm32_rtc_beginwr(); - putreg16(newclock >> 16, STM32_RTC_CNTH); - putreg16(newclock & 0xFFFF, STM32_RTC_CNTL); - stm32_rtc_endwr(); -} + irqstate_t flags; + uint32_t ls; + uint32_t ms; + uint16_t ovf; + uint16_t cnth; + uint16_t cntl; + uint16_t tmp; -time_t up_rtc_gettime(void) -{ - /* Fetch time from LSB (hardware counter) and MSB (backup domain) - * Take care on overflow of the LSB: - * - it may overflow just after reading the up_rtc_getclock, transition - * from 0xFF...FF -> 0x000000 - * - ISR would be generated to increment the RTC_TIMEMSB_REG - * - Wrong result would when: DR+1 and LSB is old, resulting in ~DR+2 - * instead of just DR+1 - */ - - irqstate_t irqs = irqsave(); - - uint32_t time_lsb = up_rtc_getclock(); - uint32_t time_msb = getreg16(RTC_TIMEMSB_REG); - - irqrestore( irqs ); - - /* Use the upper bits of the LSB and lower bits of the MSB - * structured as: - * time = time[31:18] from MSB[13:0] | time[17:0] from time_lsb[31:14] - */ - - time_lsb >>= RTC_CLOCKS_SHIFT; - - time_msb <<= (32-RTC_CLOCKS_SHIFT); - time_msb &= ~((1<<(32-RTC_CLOCKS_SHIFT))-1); - - return time_msb | time_lsb; -} + /* The RTC counter is read from two 16-bit registers to form one 32-bit + * value. Because these are non-atomic operations, many things can happen + * between the two reads: This thread could get suspended or interrrupted + * or the lower 16-bit counter could rollover between reads. Disabling + * interrupts will prevent suspensions and interruptions: + */ -void up_rtc_settime(time_t newtime) -{ - /* Do reverse compared to gettime above */ - - uint32_t time_lsb = newtime << RTC_CLOCKS_SHIFT | - (up_rtc_getclock() & ((1<<RTC_CLOCKS_SHIFT)-1)); - - uint32_t time_msb = newtime >> (32-RTC_CLOCKS_SHIFT); - - irqstate_t irqs = irqsave(); - - up_rtc_setclock(time_lsb); - putreg16( time_msb, RTC_TIMEMSB_REG ); - - irqrestore( irqs ); + flags = irqsave(); + + /* And the following loop will handle any clock rollover events that may + * happen between samples. Most of the time (like 99.9%), the following + * loop will execute only once. In the rare rollover case, it should + * execute no more than 2 times. + */ + + do + { + tmp = getreg16(STM32_RTC_CNTL); + cnth = getreg16(STM32_RTC_CNTH); + ovf = getreg16(RTC_TIMEMSB_REG); + cntl = getreg16(STM32_RTC_CNTL); + } + + /* The second sample of CNTL could be less than the first sample of CNTL + * only if rollover occurred. In that case, CNTH may or may not be out + * of sync. The best thing to do is try again until we know that no + * rollover occurred. + */ + + while (cntl < tmp); + irqrestore(flags); + + /* Okay.. the samples should be as close together in time as possible and + * we can be assured that no clock rollover occurred between the samples. + * + * Create a 32-bit value from the LS and MS 16-bit RTC counter values and + * from the MS and overflow 16-bit counter values. + */ + + ls = (uint32_t)cnth << 16 | (uint32_t)cntl; + ms = (uint32_t)ovf << 16 | (uint32_t)cnth; + + /* Then we can save the time in seconds and fractional seconds. */ + + tp->tv_sec = (ms << (32-RTC_CLOCKS_SHIFT-16)) | (ls >> (RTC_CLOCKS_SHIFT+16)); + tp->tv_nsec = (ls & (CONFIG_RTC_FREQUENCY-1)) * (1000000000/CONFIG_RTC_FREQUENCY); + return OK; } +#endif -/** Set ALARM at which time ALARM callback is going to be generated - * - * The function sets the alarm and return present time at the time - * of setting the alarm. - * - * Note that If actual time has already passed callback will not be - * generated and it is up to the higher level code to compare the - * returned (actual) time and desired time of alarm. - * - * \param attime The unit depends on the prescaler value - * \return presenttime, where the unit depends on the prescaler value - **/ -clock_t up_rtc_setalarm(clock_t atclock) +/************************************************************************************ + * Name: up_rtc_settime + * + * Description: + * Set the RTC to the provided time. + * + * Input Parameters: + * tp - the time to use + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +int up_rtc_settime(FAR const struct timespec *tp) { - stm32_rtc_beginwr(); - putreg16(atclock >> 16, STM32_RTC_ALRH); - putreg16(atclock & 0xFFFF, STM32_RTC_ALRL); - stm32_rtc_endwr(); - - return up_rtc_getclock(); + struct rtc_regvals_s regvals; + irqstate_t flags; + + /* Break out the time values */ + + up_rtc_breakout(tp, ®vals); + + /* Then write the broken out values to the RTC counter and BKP overflow register + * (hi-res mode only) + */ + + flags = irqsave(); + stm32_rtc_beginwr(); + putreg16(regvals.cnth, STM32_RTC_CNTH); + putreg16(regvals.cntl, STM32_RTC_CNTL); + stm32_rtc_endwr(); + +#ifdef CONFIG_RTC_HIRES + putreg16(regvals.ovf, RTC_TIMEMSB_REG); +#endif + irqrestore(flags); + return OK; } -/** Set alarm output pin */ -void stm32_rtc_settalarmpin(bool activate) +/************************************************************************************ + * Name: up_rtc_setalarm + * + * Description: + * Set up a alarm. + * + * Input Parameters: + * tp - the time to set the alarm + * callback - the function to call when the alarm expires. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +int up_rtc_setalarm(FAR const struct timespec *tp, alarmcb_t callback); { -} + struct rtc_regvals_s regvals; + irqstate_t flags; + int ret = -EBUSY; + + /* Is there already something waiting on the ALARM? */ + + if (g_alarmcb == NULL) + { + /* No.. Save the callback function pointer */ -#endif // defined(CONFIG_STM32_BKP) -/** \} */ + g_alarmcb = callback; + + /* Break out the time values */ + + up_rtc_breakout(tp, ®vals); + + /* The set the alarm */ + + flags = irqsave(); + stm32_rtc_beginwr(); + putreg16(regvals.cnth, STM32_RTC_ALRH); + putreg16(regvals.cntl, STM32_RTC_ALRL); + stm32_rtc_endwr(); + irqrestore(flags); + + ret = OK; + } + return ret; +} +#endif diff --git a/nuttx/configs/vsn/nsh/defconfig b/nuttx/configs/vsn/nsh/defconfig index ca053ec35..ddb8114ec 100755 --- a/nuttx/configs/vsn/nsh/defconfig +++ b/nuttx/configs/vsn/nsh/defconfig @@ -268,6 +268,7 @@ CONFIG_WIRELESS=y # CONFIG_RTC=y CONFIG_RTC_HIRES=y +CONFIG_RTC_FREQUENCY=16384 CONFIG_SYSTEM_UTC=y # diff --git a/nuttx/configs/vsn/src/sif.c b/nuttx/configs/vsn/src/sif.c index 535a09829..e185d8d0d 100644 --- a/nuttx/configs/vsn/src/sif.c +++ b/nuttx/configs/vsn/src/sif.c @@ -714,9 +714,8 @@ int sif_main(int argc, char *argv[]) struct timespec t_active; clock_gettime(CLOCK_ACTIVETIME, &t_active); - fprintf(stderr, "rtc time = %u / %u, active = %u / %u, time / systick = %u / %u\n", - up_rtc_gettime(), up_rtc_getclock(), - t_active.tv_sec, t_active.tv_nsec, + fprintf(stderr, "rtc time = %u, active = %u / %u, time / systick = %u / %u\n", + up_rtc_time(), t_active.tv_sec, t_active.tv_nsec, time(NULL), clock_systimer() ); return -1; } diff --git a/nuttx/include/nuttx/clock.h b/nuttx/include/nuttx/clock.h index 71f0c7e8a..4990c65b4 100644 --- a/nuttx/include/nuttx/clock.h +++ b/nuttx/include/nuttx/clock.h @@ -172,7 +172,7 @@ extern "C" { * ****************************************************************************/ -#if defined(CONFIG_RTC) || !__HAVE_KERNEL_GLOBALS +#if defined(CONFIG_SYSTEM_UTC) || !__HAVE_KERNEL_GLOBALS EXTERN uint32_t clock_systimer(void); #endif diff --git a/nuttx/include/nuttx/rtc.h b/nuttx/include/nuttx/rtc.h index 5d9ae6341..df633108e 100644 --- a/nuttx/include/nuttx/rtc.h +++ b/nuttx/include/nuttx/rtc.h @@ -4,6 +4,11 @@ * Copyright(C) 2011 Uros Platise. All rights reserved. * Author: Uros Platise <uros.platise@isotel.eu> * + * With extensions, modifications by: + * + * Copyright (C) 2011 Gregory Nutt. All rights reserved. + * Author: Gregroy Nutt <gnutt@nuttx.org> + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -44,19 +49,51 @@ #include <stdint.h> #include <stdbool.h> - -#include <nuttx/clock.h> +#include <time.h> /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ +/* Configuration ************************************************************/ +/* CONFIG_RTC - Enables general support for a hardware RTC. Specific + * architectures may require other specific settings. + * + * CONFIG_RTC_HIRES - The typical RTC keeps time to resolution of 1 second, + * usually supporting a 32-bit time_t value. In this case, the RTC is + * used to "seed" the normal NuttX timer and the NuttX timer provides + * for higher resoution time. + * + * If CONFIG_RTC_HIRES is enabled in the NuttX configuration, then the + * RTC provides higher resolution time and completely replaces the system + * timer for purpose of date and time. + * + * CONFIG_RTC_FREQUENCY - If CONFIG_RTC_HIRES is defined, then the frequency + * of the high resolution RTC must be provided. If CONFIG_RTC_HIRES is + * not defined, CONFIG_RTC_FREQUENCY is assumed to be one. + * + * CONFIG_RTC_ALARM - Enable if the RTC hardware supports setting of an + * alarm. A callback function will be executed when the alarm goes off + */ -#define RTC_CLOCKS_PER_SEC 16384 -#define RTC_CLOCKS_SHIFT 14 +#ifdef CONFIG_RTC_HIRES +# ifndef CONFIG_RTC_FREQUENCY +# error "CONFIG_RTC_FREQUENCY is required for CONFIG_RTC_HIRES" +# endif +#else +# ifndef CONFIG_RTC_FREQUENCY +# define CONFIG_RTC_FREQUENCY 1 +# endif +# if CONFIG_RTC_FREQUENCY != 1 +# error "The low resolution RTC must have frequency 1Hz" +# endif +#endif /**************************************************************************** * Public Types ****************************************************************************/ +/* The form of an alarm callback */ + +typedef void (alarmcb_t)(void); /**************************************************************************** * Public Variables @@ -83,32 +120,92 @@ extern "C" { #define EXTERN extern #endif -/**************************************************************************** +/************************************************************************************ * Name: up_rtcinitialize * * Description: - * Initialize the periodic timer interface. This function is called once - * from the clock_initialize() function. + * Initialize the hardware RTC per the select configuration. This function is + * called once during the OS initialization sequence + * + * Input Parameters: + * None * * Returned Value: - * Returns OK if RTC has successfully started, otherwise ERROR. + * Zero (OK) on success; a negated errno on failure * - ****************************************************************************/ + ************************************************************************************/ EXTERN int up_rtcinitialize(void); -EXTERN int up_rtcinitialize(void); -EXTERN clock_t up_rtc_getclock(void); -EXTERN void up_rtc_setclock(clock_t clock); +/************************************************************************************ + * Name: up_rtc_time + * + * Description: + * Get the current time in seconds. This is similar to the standard time() + * function. + * + * Input Parameters: + * None + * + * Returned Value: + * The current time in seconds + * + ************************************************************************************/ + +EXTERN time_t up_rtc_time(void); -EXTERN time_t up_rtc_gettime(void); -EXTERN void up_rtc_settime(time_t time); +/************************************************************************************ + * Name: up_rtc_gettime + * + * Description: + * Get the current time from the high resolution RTC clock. + * + * Input Parameters: + * tp - The location to return the high resolution time value. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +#ifdef CONFIG_RTC_HIRES +EXTERN int up_rtc_gettime(FAR struct timespec *tp); +#endif + +/************************************************************************************ + * Name: up_rtc_settime + * + * Description: + * Set the RTC to the provided time. + * + * Input Parameters: + * tp - the time to use + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ -EXTERN clock_t up_rtc_setalarm(clock_t atclock); +EXTERN int up_rtc_settime(FAR const struct timespec *tp); -/* This callback is provided by the clock module and called by the RTC ISR */ +/************************************************************************************ + * Name: up_rtc_setalarm + * + * Description: + * Set up a alarm. + * + * Input Parameters: + * tp - the time to set the alarm + * callback - the function to call when the alarm expires. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ -EXTERN void clock_rtcalarmcb(clock_t clock); +#ifdef CONFIG_RTC_ALARM +EXTERN int up_rtc_setalarm(FAR const struct timespec *tp, alarmcb_t callback); +#endif #undef EXTERN #if defined(__cplusplus) diff --git a/nuttx/include/time.h b/nuttx/include/time.h index c52e74bf2..fae948402 100644 --- a/nuttx/include/time.h +++ b/nuttx/include/time.h @@ -77,7 +77,7 @@ * power down modes. Unit is 1 second. */ -#ifdef CONFIG_RTC +#ifdef CONFIG_SYSTEM_UTC # define CLOCK_ACTIVETIME 1 #endif diff --git a/nuttx/sched/clock_gettime.c b/nuttx/sched/clock_gettime.c index 1dc48aeb7..fc3a1593a 100644 --- a/nuttx/sched/clock_gettime.c +++ b/nuttx/sched/clock_gettime.c @@ -113,7 +113,12 @@ int clock_gettime(clockid_t clock_id, struct timespec *tp) if (clock_id == CLOCK_REALTIME && tp) { + /* If CONFIG_SYSTEM_UTC is not defined, then we have to get the time + * from g_system_timer. + */ + #ifndef CONFIG_SYSTEM_UTC + /* Get the elapsed time since power up (in milliseconds) biased * as appropriate. */ @@ -151,47 +156,18 @@ int clock_gettime(clockid_t clock_id, struct timespec *tp) tp->tv_sec = (time_t)secs; tp->tv_nsec = (long)nsecs; -#else /* if CONFIG_SYSTEM_UTC=y */ +#else /* CONFIG_SYSTEM_UTC */ + + /* CONFIG_SYSTEM_UTC is defined. But we might be able to get the time + * from the hardware if a high resolution RTC is available. + */ -#ifdef CONFIG_RTC +#ifdef CONFIG_RTC_HIRES if (g_rtc_enabled) { - /* up_rtc_gettime() returns the time in seconds and up_rtc_getclock() - * will return the time int RTC clock ticks. Under the hood, these - * are probably based on the same running time. However, since we - * sample this time twice, we have to add the following strange logic - * to assure that the fractional second value does not rollover to - * a full second between sampling times. - */ + /* Get the hi-resolution time from the RTC */ - clock_t rtc_frac; /* Current fractional seconds in RTC ticks */ - clock_t rtc_last; /* Previous fractional seconds in RTC ticks */ - time_t rtc_sec; /* Current seconds */ - - /* Interrupts are disabled here only to prevent interrupts and context - * switches from interfering with the consecutive time samples. I - * expect to go through this loop 1 time 99.9% of the time and then - * only twice on the remaining cornercases. - */ - - flags = irqsave(); - rtc_frac = up_rtc_getclock() & (RTC_CLOCKS_PER_SEC-1); - do - { - rtc_last = rtc_frac; - rtc_sec = up_rtc_gettime(); - rtc_frac = up_rtc_getclock() & (RTC_CLOCKS_PER_SEC-1); - } - while (rtc_frac < rtc_last); - irqrestore(flags); - - /* Okay.. the samples should be as close together in time as possible - * and we can be assured that no fractional second rollover occurred - * between the samples. - */ - - tp->tv_sec = rtc_sec; - tp->tv_nsec = rtc_frac * (1000000000/RTC_CLOCKS_PER_SEC); + ret = up_rtc_gettime(tp); } else #endif @@ -209,17 +185,16 @@ int clock_gettime(clockid_t clock_id, struct timespec *tp) tp->tv_sec = system_utc; tp->tv_nsec = tickcount * (1000000000/TICK_PER_SEC); } -#endif +#endif /* CONFIG_SYSTEM_UTC */ - sdbg("Returning tp=(%d,%d)\n", - (int)tp->tv_sec, (int)tp->tv_nsec); + sdbg("Returning tp=(%d,%d)\n", (int)tp->tv_sec, (int)tp->tv_nsec); } /* CLOCK_ACTIVETIME is non-standard. Returns active UTC time, which is * disabled during power down modes. Unit is 1 second. */ -#ifdef CONFIG_RTC +#ifdef CONFIG_SYSTEM_UTC else if (clock_id == CLOCK_ACTIVETIME && g_rtc_enabled && tp) { /* Disable interrupts while g_system_utc and g_tickcount are sampled diff --git a/nuttx/sched/clock_initialize.c b/nuttx/sched/clock_initialize.c index 8919b6005..ed3a25828 100644 --- a/nuttx/sched/clock_initialize.c +++ b/nuttx/sched/clock_initialize.c @@ -54,6 +54,12 @@ * Definitions ****************************************************************************/ +#ifdef CONFIG_RTC +# ifndef CONFIG_SYSTEM_UTC +# error "In order to support hardware RTC system must have set the CONFIG_SYSTEM_UTC=y" +# endif +#endif + /* Standard time definitions (in units of seconds) */ #define SEC_PER_MIN ((time_t)60) @@ -85,11 +91,11 @@ ****************************************************************************/ #if CONFIG_SYSTEM_UTC -volatile time_t g_system_utc = 0; +volatile time_t g_system_utc; #else -volatile clock_t g_system_timer = 0; -struct timespec g_basetime = {0,0}; -uint32_t g_tickbias = 0; +volatile clock_t g_system_timer; +struct timespec g_basetime; +uint32_t g_tickbias; #endif /************************************************************************** @@ -102,11 +108,11 @@ uint32_t g_tickbias = 0; #if CONFIG_SYSTEM_UTC #if TICK_PER_SEC > 32767 -volatile uint32_t g_tickcount = 0; +volatile uint32_t g_tickcount; #elif TICK_PER_SEC > 255 -volatile uint16_t g_tickcount = 0; +volatile uint16_t g_tickcount; #else -volatile uint8_t g_tickcount = 0; +volatile uint8_t g_tickcount; #endif #endif /* CONFIG_SYSTEM_UTC */ @@ -114,7 +120,7 @@ volatile uint8_t g_tickcount = 0; * Private Functions **************************************************************************/ /**************************************************************************** - * Function: clock_timer + * Function: incr_utc * * Description: * This function must be called once every time the real @@ -139,49 +145,40 @@ static inline void incr_utc(void) #endif /**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Function: clock_initialize + * Function: clock_inittime * * Description: - * Perform one-time initialization of the timing facilities. + * Get the initial time value from the best source available. * ****************************************************************************/ -void clock_initialize(void) +#ifdef CONFIG_RTC +#ifdef CONFIG_RTC_HIRES + +static inline void clock_inittime(FAR struct timespec *tp) { -#ifndef CONFIG_SYSTEM_UTC - time_t jdn = 0; -#endif - /* Initialize the real time close (this should be un-nesssary except on a - * restart). - */ + /* Get the complete time from the hi-res RTC. */ + + (void)up_rtc_gettime(tp); +} -#ifdef CONFIG_SYSTEM_UTC - g_system_utc = 0; #else - g_system_timer = 0; -#endif - /* Do we have hardware RTC support? */ +static inline void clock_inittime(FAR struct timespec *tp) +{ + /* Get the seconds (only) from the lo-res RTC */ -#ifdef CONFIG_RTC + tp->tv_sec = up_rtc_time(); + tp->tv_nsec = 0; +} -#ifndef CONFIG_SYSTEM_UTC -# error In order to support hardware RTC system must have set the CONFIG_SYSTEM_UTC=y -#endif +#endif /* CONFIG_RTC_HIRES */ +#else /* CONFIG_RTC */ - up_rtcinitialize(); -#endif - -#ifndef CONFIG_SYSTEM_UTC +static inline void clock_inittime(FAR struct timespec *tp) +{ + time_t jdn = 0; /* Get the EPOCH-relative julian date from the calendar year, * month, and date @@ -192,12 +189,47 @@ void clock_initialize(void) /* Set the base time as seconds into this julian day. */ - g_basetime.tv_sec = jdn * SEC_PER_DAY; - g_basetime.tv_nsec = 0; + tp->tv_sec = jdn * SEC_PER_DAY; + tp->tv_nsec = 0; +} + +#endif /* CONFIG_RTC */ + + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: clock_initialize + * + * Description: + * Perform one-time initialization of the timing facilities. + * + ****************************************************************************/ + +void clock_initialize(void) +{ +#ifdef CONFIG_SYSTEM_UTC + struct timespec ts; +#endif + + /* Initialize the RTC hardware */ + +#ifdef CONFIG_RTC + up_rtcinitialize(); +#endif - /* These is no time bias from this time. */ + /* Initialize the time value */ - g_tickbias = 0; +#ifdef CONFIG_SYSTEM_UTC + clock_inittime(&ts); + g_system_utc = ts.tv_sec; + g_tickcount = ((ts.tv_nsec > 10) * CLOCKS_PER_SEC) / (1000000000 >> 10); +#else + clock_inittime(&g_basetime); + g_system_timer = 0; + g_tickbias = 0; #endif } diff --git a/nuttx/sched/clock_settime.c b/nuttx/sched/clock_settime.c index 77b6ebecc..299e8c65b 100644 --- a/nuttx/sched/clock_settime.c +++ b/nuttx/sched/clock_settime.c @@ -43,6 +43,8 @@ #include <time.h> #include <errno.h> #include <debug.h> + +#include <arch/irq.h> #include "clock_internal.h" /************************************************************************ @@ -85,7 +87,7 @@ * ************************************************************************/ -int clock_settime(clockid_t clock_id, const struct timespec *tp) +int clock_settime(clockid_t clock_id, FAR const struct timespec *tp) { int ret = OK; @@ -116,12 +118,13 @@ int clock_settime(clockid_t clock_id, const struct timespec *tp) #ifdef CONFIG_RTC if (g_rtc_enabled) { - up_rtc_settime(tp->tv_sec); + up_rtc_settime(tp); } else #endif - g_system_utc = tp->tv_sec; - + { + g_system_utc = tp->tv_sec; + } #endif sdbg("basetime=(%d,%d) tickbias=%d\n", @@ -133,10 +136,25 @@ int clock_settime(clockid_t clock_id, const struct timespec *tp) * disabled during power down modes. Unit is 1 second. */ -#ifdef CONFIG_RTC - else if (clock_id == CLOCK_ACTIVETIME && g_rtc_enabled && tp) +#ifdef CONFIG_SYSTEM_UTC + else if (clock_id == CLOCK_ACTIVETIME && tp) { + irqstate_t flags; + uint32_t tickcount; + + /* Calculate the number of ticks correspond to the nanosecond count... + * exercising care to avoid overflows. This could still overflow + * if CLOCKS_PER_SEC is very large (something like 4096). + */ + + tickcount = ((tp->tv_nsec >> 10) * CLOCKS_PER_SEC) / (1000000000 >> 10); + + /* Then set the UTC time (seconds) plus the tickcount (fractional seconds */ + + flags = irqsave(); g_system_utc = tp->tv_sec; + g_tickcount = tickcount; + irqrestore(flags); } #endif diff --git a/nuttx/sched/clock_systimer.c b/nuttx/sched/clock_systimer.c index cfa019fc7..2061b1de6 100644 --- a/nuttx/sched/clock_systimer.c +++ b/nuttx/sched/clock_systimer.c @@ -85,20 +85,12 @@ uint32_t clock_systimer(void) uint32_t tickcount; #endif - /* Fetch the g_system_timer value from timer hardware, if available */ - -#ifdef CONFIG_RTC - - /* Check if the periodic timer is initialized +#ifdef CONFIG_RTC_HIRES + /* Fetch the g_system_timer value from timer hardware, if available. * - * Note that the unit of the g_system_timer and and up_rtc_getclock() do + * Note that the unit of the g_system_timer and and up_rtc_gettime() do * not have the same unit. */ - - if (g_rtc_enabled) - { - /* return up_rtc_getclock(); */ - } #endif #ifndef CONFIG_SYSTEM_UTC |