9
0
Fork 0

Add support for lo- and hi-res RTC hardware

git-svn-id: https://nuttx.svn.sourceforge.net/svnroot/nuttx/trunk@4005 7fd9a85b-ad96-42d3-883c-3090e2eb8679
This commit is contained in:
patacongo 2011-10-01 22:09:00 +00:00
parent e468782ea0
commit 4762f40a5e
13 changed files with 750 additions and 307 deletions

View File

@ -2121,3 +2121,8 @@
* configs/sam3u/touchscreen - This is the configuration that I plan to use * configs/sam3u/touchscreen - This is the configuration that I plan to use
to verify the SAM3U-EK touchscreen driver. However, as of this writing, to verify the SAM3U-EK touchscreen driver. However, as of this writing,
there is no touchscreen driver for the board. 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.

View File

@ -12,7 +12,7 @@
<h1><big><font color="#3c34ec"> <h1><big><font color="#3c34ec">
<i>NuttX RTOS Porting Guide</i> <i>NuttX RTOS Porting Guide</i>
</font></big></h1> </font></big></h1>
<p>Last Updated: September 13, 2011</p> <p>Last Updated: October 1, 2011</p>
</td> </td>
</tr> </tr>
</table> </table>
@ -1989,18 +1989,30 @@ CONFIG_SYSTEM_UTC=y
<h4>4.1.20.2 Hardware</h4> <h4>4.1.20.2 Hardware</h4>
<p> <p>
To enable hardware module use option: To enable hardware module use the following configuration options:
<p> <p>
<ul><pre> <ul><dl>
CONFIG_RTC=y <dt><code>CONFIG_RTC</code>
</pre></ul> <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 &quot;seed&quot; 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> <p>
which requires the following three base functions to read time: which requires the following three base functions to read time:
</p> </p>
<ul> <ul>
<li><code>up_rtcinitialize()</code></li> <li><code>up_rtcinitialize()</code></li>
<li><code>up_rtc_gettime()</code>. UTC time in seconds.</li> <li><code>up_rtc_time()</code>. UTC time in seconds.</li>
<li><code>up_rtc_getclock()</code>. Replacement for <code>g_system_tick</code></li> <li><code>up_rtc_gettime()</code>. Replacement for <code>g_system_tick</code></li>
</ul> </ul>
<p> <p>
This module depends on <code>CONFIG_SYSTEM_UTC=y</code>. 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. Running at rate of system base timer, used for time-slicing, and so forth.
</p> </p>
<p> <p>
If hardware RTC is present (<code>CONFIG_RTC</code>) and enabled, then after successful If hardware RTC is present (<code>CONFIG_RTC</code>) and and high-resolution timeing
initiliazation variables are overriden by calls to <code>up_rtc_getclock()</code> which is 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. running continously even in power-down modes.
</p> </p>
<p> <p>
In the case of <code>CONFIG_RTC</code> is set the <code>g_tickcount</code> and <code>g_system_utc</code> keep In the case of <code>CONFIG_RTC_HIRES</code> is set the <code>g_tickcount</code> and
counting at rate of a system timer, which however, is disabled in power-down <code>g_system_utc</code> keep counting at rate of a system timer, which however, is
mode. By comparing this time and RTC (actual time) one may determine the disabled in power-down mode.
actual system active time. To retrieve that variable use: By comparing this time and RTC (actual time) one may determine the actual system active time.
To retrieve that variable use:
</p> </p>
<ul><pre> <ul><pre>
<li><code>clock_gettime(CLOCK_ACTIVETIME, tp)</code> <li><code>clock_gettime(CLOCK_ACTIVETIME, tp)</code>
@ -4239,10 +4253,35 @@ build
</ul> </ul>
<h2>Device Drivers</h2> <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 &quot;seed&quot; 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> <h3>SPI driver</h3>
<ul> <ul>
<li> <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. on the SPI bus. No locking or SPI configuration will be performed.
It is not necessary for clients to lock, re-configure, etc.. It is not necessary for clients to lock, re-configure, etc..
</li> </li>

View File

@ -6,6 +6,7 @@
* *
* With extensions, modifications by: * With extensions, modifications by:
* *
* Copyright (C) 2011 Gregory Nutt. All rights reserved.
* Author: Gregroy Nutt <gnutt@nuttx.org> * Author: Gregroy Nutt <gnutt@nuttx.org>
* *
* Redistribution and use in source and binary forms, with or without * 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 #else
# define stm32_i2c_disablefsmc() (0) # define stm32_i2c_disablefsmc(priv) (0)
# define stm32_i2c_enablefsmc(ahbenr) # define stm32_i2c_enablefsmc(ahbenr)
#endif #endif

View File

@ -33,14 +33,7 @@
* *
************************************************************************************/ ************************************************************************************/
/** \file /* The STM32 RTC Driver offers standard precision of 1 Hz or High Resolution
* \author Uros Platise
* \brief STM32 Real-Time Clock
*
* \addtogroup STM32_RTC
* \{
*
* 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 * operating at rate up to 16384 Hz. It provides UTC time and alarm interface
* with external output pin (for wake-up). * with external output pin (for wake-up).
* *
@ -53,9 +46,13 @@
* - time is a combination of clock and upper bits stored in backuped domain * - time is a combination of clock and upper bits stored in backuped domain
* with unit of 1 [s] * 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/config.h>
#include <nuttx/arch.h> #include <nuttx/arch.h>
#include <nuttx/irq.h> #include <nuttx/irq.h>
@ -74,21 +71,78 @@
#include "stm32_rtc.h" #include "stm32_rtc.h"
#include "stm32_waste.h" #include "stm32_waste.h"
/************************************************************************************
* Pre-processor Definitions
************************************************************************************/
/* Configuration ********************************************************************/
#if defined(CONFIG_STM32_BKP) #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.
*/
#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 * Private Data
************************************************************************************/ ************************************************************************************/
/** Variable determines the state of the LSE oscilator. /* Callback to use when the alarm expires */
* Possible errors:
#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 * - on start-up
* - during operation, reported by LSE interrupt * - during operation, reported by LSE interrupt
*/ */
@ -99,113 +153,265 @@ volatile bool g_rtc_enabled = false;
* Private Functions * Private Functions
************************************************************************************/ ************************************************************************************/
/************************************************************************************
* Name: stm32_rtc_beginwr
*
* Description:
* Enter configuration mode
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
************************************************************************************/
static inline void stm32_rtc_beginwr(void) static inline void stm32_rtc_beginwr(void)
{ {
/* Previous write is done? */ /* Previous write is done? */
while( (getreg16(STM32_RTC_CRL) & RTC_CRL_RTOFF)==0 ) up_waste();
/* Enter Config mode, Set Value and Exit */ while ((getreg16(STM32_RTC_CRL) & RTC_CRL_RTOFF) == 0)
modifyreg16(STM32_RTC_CRL, 0, RTC_CRL_CNF); {
up_waste();
}
/* Enter Config mode, Set Value and Exit */
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) 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) static inline void stm32_rtc_wait4rsf(void)
{ {
modifyreg16(STM32_RTC_CRL, RTC_CRL_RSF, 0); modifyreg16(STM32_RTC_CRL, RTC_CRL_RSF, 0);
while( !(getreg16(STM32_RTC_CRL) & RTC_CRL_RSF) ) up_waste(); 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 ); uint64_t frac;
uint32_t cnt;
if (source & RTC_CRL_OWF) { uint16_t ovf;
putreg16( getreg16(RTC_TIMEMSB_REG) + 1, RTC_TIMEMSB_REG );
} /* Break up the time in seconds + milleconds into the correct values for our use */
if (source & RTC_CRL_ALRF) { frac = ((uint64_t)tp->tv_nsec * CONFIG_RTC_FREQUENCY) / 1000000000;
/* Alarm */ cnt = (tp->tv_sec << RTC_CLOCKS_SHIFT) | ((uint32_t)frac & (CONFIG_RTC_FREQUENCY-1));
} ovf = (tp->tv_sec >> (32 - RTC_CLOCKS_SHIFT));
/* Clear pending flags, leave RSF high */ /* Then return the broken out time */
putreg16( RTC_CRL_RSF, STM32_RTC_CRL ); regvals->cnth = cnt >> 16;
return 0; 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
/************************************************************************************ /************************************************************************************
* Public Function - Initialization * 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);
}
#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 */
putreg16(RTC_CRL_RSF, STM32_RTC_CRL);
return 0;
}
#endif
/************************************************************************************
* 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) int up_rtcinitialize(void)
{ {
/* For this initial version we use predefined value */ /* For this initial version we use predefined value */
uint32_t prescaler = STM32_RTC_PRESCALER_MIN; uint32_t prescaler = STM32_RTC_PRESCALER_MIN;
/* Set access to the peripheral, enable power and LSE */ /* Set access to the peripheral, enable power and LSE */
stm32_pwr_enablebkp();
stm32_rcc_enablelse();
// \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 #ifdef CONFIG_RTC_HIRES
stm32_rtc_wait4rsf(); stm32_pwr_enablebkp();
#endif
/* Configure prescaler, note that these are write-only registers */ stm32_rcc_enablelse();
stm32_rtc_beginwr(); /* TODO: Get state from this function, if everything is
putreg16(prescaler >> 16, STM32_RTC_PRLH); * okay and whether it is already enabled (if it was disabled
putreg16(prescaler & 0xFFFF, STM32_RTC_PRLL); * reset upper time register)
stm32_rtc_endwr(); */
/* Configure Overflow Interrupt */
irq_attach(STM32_IRQ_RTC, stm32_rtc_overflow_isr);
up_enable_irq(STM32_IRQ_RTC);
/* Previous write is done? This is required prior writing into CRH */ g_rtc_enabled = true;
while( (getreg16(STM32_RTC_CRL) & RTC_CRL_RTOFF)==0 ) up_waste();
modifyreg16(STM32_RTC_CRH, 0, RTC_CRH_OWIE);
/* Alarm Int via EXTI Line */
// STM32_IRQ_RTCALR /* 41: RTC alarm through EXTI line interrupt */
return OK; /* 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 */
while ((getreg16(STM32_RTC_CRL) & RTC_CRL_RTOFF) == 0)
{
up_waste();
}
modifyreg16(STM32_RTC_CRH, 0, RTC_CRH_OWIE);
/* Alarm Int via EXTI Line */
/* STM32_IRQ_RTCALR 41: RTC alarm through EXTI line interrupt */
return OK;
} }
/** Get time (counter) value /************************************************************************************
* * Name: up_rtc_time
* \return time, where the unit depends on the prescaler value *
**/ * Description:
* Get the current time in seconds.
clock_t up_rtc_getclock(void) *
* 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; irqstate_t flags;
uint16_t cnth; uint16_t cnth;
@ -243,98 +449,176 @@ clock_t up_rtc_getclock(void)
while (cntl < tmp); while (cntl < tmp);
irqrestore(flags); 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 /************************************************************************************
* * Name: up_rtc_gettime
* \param time The unit depends on the prescaler value *
**/ * 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(); irqstate_t flags;
putreg16(newclock >> 16, STM32_RTC_CNTH); uint32_t ls;
putreg16(newclock & 0xFFFF, STM32_RTC_CNTL); uint32_t ms;
stm32_rtc_endwr(); uint16_t ovf;
} uint16_t cnth;
uint16_t cntl;
uint16_t tmp;
time_t up_rtc_gettime(void) /* 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:
*/
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
/************************************************************************************
* 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)
{ {
/* Fetch time from LSB (hardware counter) and MSB (backup domain) struct rtc_regvals_s regvals;
* Take care on overflow of the LSB: irqstate_t flags;
* - 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(); /* Break out the time values */
uint32_t time_lsb = up_rtc_getclock(); up_rtc_breakout(tp, &regvals);
uint32_t time_msb = getreg16(RTC_TIMEMSB_REG);
/* Then write the broken out values to the RTC counter and BKP overflow register
irqrestore( irqs ); * (hi-res mode only)
*/
/* Use the upper bits of the LSB and lower bits of the MSB
* structured as: flags = irqsave();
* time = time[31:18] from MSB[13:0] | time[17:0] from time_lsb[31:14] stm32_rtc_beginwr();
*/ putreg16(regvals.cnth, STM32_RTC_CNTH);
putreg16(regvals.cntl, STM32_RTC_CNTL);
time_lsb >>= RTC_CLOCKS_SHIFT; stm32_rtc_endwr();
time_msb <<= (32-RTC_CLOCKS_SHIFT); #ifdef CONFIG_RTC_HIRES
time_msb &= ~((1<<(32-RTC_CLOCKS_SHIFT))-1); putreg16(regvals.ovf, RTC_TIMEMSB_REG);
#endif
return time_msb | time_lsb; irqrestore(flags);
return OK;
} }
void up_rtc_settime(time_t newtime) /************************************************************************************
* 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);
{ {
/* Do reverse compared to gettime above */ struct rtc_regvals_s regvals;
irqstate_t flags;
uint32_t time_lsb = newtime << RTC_CLOCKS_SHIFT | int ret = -EBUSY;
(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 );
}
/** Set ALARM at which time ALARM callback is going to be generated /* Is there already something waiting on the ALARM? */
*
* 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)
{
stm32_rtc_beginwr();
putreg16(atclock >> 16, STM32_RTC_ALRH);
putreg16(atclock & 0xFFFF, STM32_RTC_ALRL);
stm32_rtc_endwr();
return up_rtc_getclock();
}
/** Set alarm output pin */ if (g_alarmcb == NULL)
void stm32_rtc_settalarmpin(bool activate) {
{ /* No.. Save the callback function pointer */
}
#endif // defined(CONFIG_STM32_BKP) g_alarmcb = callback;
/** \} */
/* Break out the time values */
up_rtc_breakout(tp, &regvals);
/* 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

View File

@ -268,6 +268,7 @@ CONFIG_WIRELESS=y
# #
CONFIG_RTC=y CONFIG_RTC=y
CONFIG_RTC_HIRES=y CONFIG_RTC_HIRES=y
CONFIG_RTC_FREQUENCY=16384
CONFIG_SYSTEM_UTC=y CONFIG_SYSTEM_UTC=y
# #

View File

@ -714,9 +714,8 @@ int sif_main(int argc, char *argv[])
struct timespec t_active; struct timespec t_active;
clock_gettime(CLOCK_ACTIVETIME, &t_active); clock_gettime(CLOCK_ACTIVETIME, &t_active);
fprintf(stderr, "rtc time = %u / %u, active = %u / %u, time / systick = %u / %u\n", fprintf(stderr, "rtc time = %u, active = %u / %u, time / systick = %u / %u\n",
up_rtc_gettime(), up_rtc_getclock(), up_rtc_time(), t_active.tv_sec, t_active.tv_nsec,
t_active.tv_sec, t_active.tv_nsec,
time(NULL), clock_systimer() ); time(NULL), clock_systimer() );
return -1; return -1;
} }

View File

@ -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); EXTERN uint32_t clock_systimer(void);
#endif #endif

View File

@ -4,6 +4,11 @@
* Copyright(C) 2011 Uros Platise. All rights reserved. * Copyright(C) 2011 Uros Platise. All rights reserved.
* Author: Uros Platise <uros.platise@isotel.eu> * 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 * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions * modification, are permitted provided that the following conditions
* are met: * are met:
@ -44,19 +49,51 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <time.h>
#include <nuttx/clock.h>
/**************************************************************************** /****************************************************************************
* Pre-processor Definitions * 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 #ifdef CONFIG_RTC_HIRES
#define RTC_CLOCKS_SHIFT 14 # 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 * Public Types
****************************************************************************/ ****************************************************************************/
/* The form of an alarm callback */
typedef void (alarmcb_t)(void);
/**************************************************************************** /****************************************************************************
* Public Variables * Public Variables
@ -83,32 +120,92 @@ extern "C" {
#define EXTERN extern #define EXTERN extern
#endif #endif
/**************************************************************************** /************************************************************************************
* Name: up_rtcinitialize * Name: up_rtcinitialize
* *
* Description: * Description:
* Initialize the periodic timer interface. This function is called once * Initialize the hardware RTC per the select configuration. This function is
* from the clock_initialize() function. * called once during the OS initialization sequence
*
* Input Parameters:
* None
* *
* Returned Value: * 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 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_gettime(void); EXTERN time_t up_rtc_time(void);
EXTERN void up_rtc_settime(time_t time);
EXTERN clock_t up_rtc_setalarm(clock_t atclock); /************************************************************************************
* 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
*
************************************************************************************/
/* This callback is provided by the clock module and called by the RTC ISR */ #ifdef CONFIG_RTC_HIRES
EXTERN int up_rtc_gettime(FAR struct timespec *tp);
#endif
EXTERN void clock_rtcalarmcb(clock_t clock); /************************************************************************************
* 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 int up_rtc_settime(FAR const struct timespec *tp);
/************************************************************************************
* 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
EXTERN int up_rtc_setalarm(FAR const struct timespec *tp, alarmcb_t callback);
#endif
#undef EXTERN #undef EXTERN
#if defined(__cplusplus) #if defined(__cplusplus)

View File

@ -77,7 +77,7 @@
* power down modes. Unit is 1 second. * power down modes. Unit is 1 second.
*/ */
#ifdef CONFIG_RTC #ifdef CONFIG_SYSTEM_UTC
# define CLOCK_ACTIVETIME 1 # define CLOCK_ACTIVETIME 1
#endif #endif

View File

@ -113,7 +113,12 @@ int clock_gettime(clockid_t clock_id, struct timespec *tp)
if (clock_id == CLOCK_REALTIME && 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 #ifndef CONFIG_SYSTEM_UTC
/* Get the elapsed time since power up (in milliseconds) biased /* Get the elapsed time since power up (in milliseconds) biased
* as appropriate. * as appropriate.
*/ */
@ -151,47 +156,18 @@ int clock_gettime(clockid_t clock_id, struct timespec *tp)
tp->tv_sec = (time_t)secs; tp->tv_sec = (time_t)secs;
tp->tv_nsec = (long)nsecs; tp->tv_nsec = (long)nsecs;
#else /* if CONFIG_SYSTEM_UTC=y */ #else /* CONFIG_SYSTEM_UTC */
#ifdef CONFIG_RTC /* 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_HIRES
if (g_rtc_enabled) if (g_rtc_enabled)
{ {
/* up_rtc_gettime() returns the time in seconds and up_rtc_getclock() /* Get the hi-resolution time from the RTC */
* 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.
*/
clock_t rtc_frac; /* Current fractional seconds in RTC ticks */ ret = up_rtc_gettime(tp);
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);
} }
else else
#endif #endif
@ -209,17 +185,16 @@ int clock_gettime(clockid_t clock_id, struct timespec *tp)
tp->tv_sec = system_utc; tp->tv_sec = system_utc;
tp->tv_nsec = tickcount * (1000000000/TICK_PER_SEC); tp->tv_nsec = tickcount * (1000000000/TICK_PER_SEC);
} }
#endif #endif /* CONFIG_SYSTEM_UTC */
sdbg("Returning tp=(%d,%d)\n", sdbg("Returning tp=(%d,%d)\n", (int)tp->tv_sec, (int)tp->tv_nsec);
(int)tp->tv_sec, (int)tp->tv_nsec);
} }
/* CLOCK_ACTIVETIME is non-standard. Returns active UTC time, which is /* CLOCK_ACTIVETIME is non-standard. Returns active UTC time, which is
* disabled during power down modes. Unit is 1 second. * 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) else if (clock_id == CLOCK_ACTIVETIME && g_rtc_enabled && tp)
{ {
/* Disable interrupts while g_system_utc and g_tickcount are sampled /* Disable interrupts while g_system_utc and g_tickcount are sampled

View File

@ -54,6 +54,12 @@
* Definitions * 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) */ /* Standard time definitions (in units of seconds) */
#define SEC_PER_MIN ((time_t)60) #define SEC_PER_MIN ((time_t)60)
@ -85,11 +91,11 @@
****************************************************************************/ ****************************************************************************/
#if CONFIG_SYSTEM_UTC #if CONFIG_SYSTEM_UTC
volatile time_t g_system_utc = 0; volatile time_t g_system_utc;
#else #else
volatile clock_t g_system_timer = 0; volatile clock_t g_system_timer;
struct timespec g_basetime = {0,0}; struct timespec g_basetime;
uint32_t g_tickbias = 0; uint32_t g_tickbias;
#endif #endif
/************************************************************************** /**************************************************************************
@ -102,11 +108,11 @@ uint32_t g_tickbias = 0;
#if CONFIG_SYSTEM_UTC #if CONFIG_SYSTEM_UTC
#if TICK_PER_SEC > 32767 #if TICK_PER_SEC > 32767
volatile uint32_t g_tickcount = 0; volatile uint32_t g_tickcount;
#elif TICK_PER_SEC > 255 #elif TICK_PER_SEC > 255
volatile uint16_t g_tickcount = 0; volatile uint16_t g_tickcount;
#else #else
volatile uint8_t g_tickcount = 0; volatile uint8_t g_tickcount;
#endif #endif
#endif /* CONFIG_SYSTEM_UTC */ #endif /* CONFIG_SYSTEM_UTC */
@ -114,7 +120,7 @@ volatile uint8_t g_tickcount = 0;
* Private Functions * Private Functions
**************************************************************************/ **************************************************************************/
/**************************************************************************** /****************************************************************************
* Function: clock_timer * Function: incr_utc
* *
* Description: * Description:
* This function must be called once every time the real * This function must be called once every time the real
@ -139,9 +145,57 @@ static inline void incr_utc(void)
#endif #endif
/**************************************************************************** /****************************************************************************
* Private Functions * Function: clock_inittime
*
* Description:
* Get the initial time value from the best source available.
*
****************************************************************************/ ****************************************************************************/
#ifdef CONFIG_RTC
#ifdef CONFIG_RTC_HIRES
static inline void clock_inittime(FAR struct timespec *tp)
{
/* Get the complete time from the hi-res RTC. */
(void)up_rtc_gettime(tp);
}
#else
static inline void clock_inittime(FAR struct timespec *tp)
{
/* Get the seconds (only) from the lo-res RTC */
tp->tv_sec = up_rtc_time();
tp->tv_nsec = 0;
}
#endif /* CONFIG_RTC_HIRES */
#else /* CONFIG_RTC */
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
*/
jdn = clock_calendar2utc(CONFIG_START_YEAR, CONFIG_START_MONTH,
CONFIG_START_DAY);
/* Set the base time as seconds into this julian day. */
tp->tv_sec = jdn * SEC_PER_DAY;
tp->tv_nsec = 0;
}
#endif /* CONFIG_RTC */
/**************************************************************************** /****************************************************************************
* Public Functions * Public Functions
****************************************************************************/ ****************************************************************************/
@ -156,48 +210,26 @@ static inline void incr_utc(void)
void clock_initialize(void) void clock_initialize(void)
{ {
#ifndef CONFIG_SYSTEM_UTC
time_t jdn = 0;
#endif
/* Initialize the real time close (this should be un-nesssary except on a
* restart).
*/
#ifdef CONFIG_SYSTEM_UTC #ifdef CONFIG_SYSTEM_UTC
g_system_utc = 0; struct timespec ts;
#else
g_system_timer = 0;
#endif #endif
/* Do we have hardware RTC support? */ /* Initialize the RTC hardware */
#ifdef CONFIG_RTC #ifdef CONFIG_RTC
#ifndef CONFIG_SYSTEM_UTC
# error In order to support hardware RTC system must have set the CONFIG_SYSTEM_UTC=y
#endif
up_rtcinitialize(); up_rtcinitialize();
#endif #endif
#ifndef CONFIG_SYSTEM_UTC /* Initialize the time value */
/* Get the EPOCH-relative julian date from the calendar year, #ifdef CONFIG_SYSTEM_UTC
* month, and date clock_inittime(&ts);
*/ g_system_utc = ts.tv_sec;
g_tickcount = ((ts.tv_nsec > 10) * CLOCKS_PER_SEC) / (1000000000 >> 10);
jdn = clock_calendar2utc(CONFIG_START_YEAR, CONFIG_START_MONTH, #else
CONFIG_START_DAY); clock_inittime(&g_basetime);
g_system_timer = 0;
/* Set the base time as seconds into this julian day. */ g_tickbias = 0;
g_basetime.tv_sec = jdn * SEC_PER_DAY;
g_basetime.tv_nsec = 0;
/* These is no time bias from this time. */
g_tickbias = 0;
#endif #endif
} }

View File

@ -43,6 +43,8 @@
#include <time.h> #include <time.h>
#include <errno.h> #include <errno.h>
#include <debug.h> #include <debug.h>
#include <arch/irq.h>
#include "clock_internal.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; int ret = OK;
@ -116,12 +118,13 @@ int clock_settime(clockid_t clock_id, const struct timespec *tp)
#ifdef CONFIG_RTC #ifdef CONFIG_RTC
if (g_rtc_enabled) if (g_rtc_enabled)
{ {
up_rtc_settime(tp->tv_sec); up_rtc_settime(tp);
} }
else else
#endif #endif
g_system_utc = tp->tv_sec; {
g_system_utc = tp->tv_sec;
}
#endif #endif
sdbg("basetime=(%d,%d) tickbias=%d\n", 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. * 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) 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_system_utc = tp->tv_sec;
g_tickcount = tickcount;
irqrestore(flags);
} }
#endif #endif

View File

@ -85,20 +85,12 @@ uint32_t clock_systimer(void)
uint32_t tickcount; uint32_t tickcount;
#endif #endif
/* Fetch the g_system_timer value from timer hardware, if available */ #ifdef CONFIG_RTC_HIRES
/* Fetch the g_system_timer value from timer hardware, if available.
#ifdef CONFIG_RTC
/* Check if the periodic timer is initialized
* *
* 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. * not have the same unit.
*/ */
if (g_rtc_enabled)
{
/* return up_rtc_getclock(); */
}
#endif #endif
#ifndef CONFIG_SYSTEM_UTC #ifndef CONFIG_SYSTEM_UTC