?? rtc_experimental.c
字號(hào):
/***************************************************************************
* This code and information is provided "as is" without warranty of any *
* kind, either expressed or implied, including but not limited to the *
* implied warranties of merchantability and/or fitness for a particular *
* purpose. *
* *
* Copyright (C) 2005 Teridian Semiconductor Corp. All Rights Reserved. *
***************************************************************************/
//**************************************************************************
// DESCRIPTION: 71M652x POWER METER - Real Time Clock Routines.
// This experimental RTC adjustment algorithm was invented too close
// to the release date to be tested. If it works, it may reduce
// the average error when compared to the older adjustment method.
//
// AUTHOR: MTF/RGV
//
// HISTORY: See end of file.
//**************************************************************************
// File: RTC.C
//
#include "options.h"
#include "library.h" // get LRC calculation from the source
#include "meter.h"
#include "freq.h" // to handle rtc adjustment from line's edge count
#include "lcd.h"
#if TIMERS
#include "stm.h" // use for timing the write
#elif TMR1
#include "tmr1.h" // timer 1 can be used for timing the write
#elif TMR0
#include "tmr0.h" // timer 1 can be used for timing the write
#elif REAL_TIME_DATE
#error need timers to set the clock
#endif
#include "irq.h" // interrupt control
#include "rtc.h" // test the function definitions against the code
#include FILE_DEFINING_RTC
#if OPERATING_TIME
// display the number of hours of operation
void operating_lcd (void)
{
LCD_Number_Max (OperatingSeconds / 36, 2); // Display max digits.
LCD_2DP (); // Two decimal places.
}
#endif
#if REAL_TIME_DATE
/*** External functions referenced by this module ***/
// See includes
/*** External variables referenced by this module ***/
// Options.h must define:
// RTC_COPY, a structure in xdata RAM of type RTC_t that contains the
// system's clock variables. The demo code, defines RTC_COPY in options_gbl.h
// In the demo code, RTC_COPY is near the other meter data, in
// the data structure "Totals", defined in meter.h
// Some compensation data might also be needed: slow, trim, and trim_count
// These are nonvolatile because they are used to compensate the clock for
// the time the unit was off. SInce they're nonvolatile, they need to be
// in the nonvolatile memory.
// If COMPENSATION is nonzero, enabling temperature compensation, options.h
// must also somehow define:
// Y_Cal_Deg2 is 1 ppb => 1 Y_Cal_Deg2 is 1 ppb.
// Y_Cal_Deg1 is 10 ppb => 10 Y_Cal_Deg1 is 1 ppb.
// Y_Cal_Deg0 is 100 ppb => 100 Y_Cal_Deg0 is 1 ppb.
// In the demo code, these are in the "Parameter_t" section of Totals,
// defined in meter.h
/*** Public functions declared within this module ***/
// See "rtc.h".
/*** Public variables declared within this module ***/
// rtc_ready, a bool set once per second by the isr.
/*** Private functions declared within this module ***/
static void RTClk_Write_Delay (void) small reentrant;
/*** Private variables declared within this module ***/
volatile bool rtc_ready;
static volatile bool rtc_adjusting_now;
static volatile uint8x_t RTC_idx = 0;
// display the date on the LCD
void date_lcd (void)
{
LCD_Command (LCD_CLEAR);
LCD_Year (RTC_COPY.Year);
LCD_Month (RTC_COPY.Month);
LCD_Date (RTC_COPY.Date);
}
// display the time on the LCD
void time_lcd (void)
{
LCD_Command (LCD_CLEAR);
LCD_Hour(RTC_COPY.Hour);
LCD_Min (RTC_COPY.Min);
LCD_Sec (RTC_COPY.Sec);
}
void RTClk_Reset (void) // Reset RTC to defaults, if necessary.
{
#if !RTC_LINE_LOCKED // if it's line-locked, don't do compensation
struct RTC_t xdata rtc_temp;
// save the last valid clock reading
memcpy_xx((uint8x_t*)&rtc_temp, (uint8x_t*)&RTC_COPY, sizeof(RTC_COPY));
#endif
RTClk_Read ();
if (RTC_COPY.Year == 0) // has the clock been set since it was first powered?
{
// Power to the clock failed, so set it.
// This could copy the last saved date and time, but that could
// cause the mistaken belief that the clock was "losing time" when the battery was bad.
memset_x ((uint8x_t *) &RTC_COPY, 0x01, sizeof (RTC_COPY));
RTClk_Write ();
#if !RTC_LINE_LOCKED // if it's line-locked, don't do compensation
trim_count = 0; // restart the trimming
second_count = 0;
#endif
Status |= CLOCK_UNSET;
}
#if !RTC_LINE_LOCKED // if it's line-locked, don't do compensation
// It needs to know how long the power was off.
if (0 != ((POWER_BAD | CLOCK_UNSET) & Status))
{
// Either the last running date, or the current time is bad
// Also, the trim was not saved, so it needs to be recalculated.
#if RTC_COMPENSATION
// deltaT, the difference in temperature from the calibration
// temperature, is cleared to zero at start-up, so it stays zero
// until it is measured. The constant drift (Y-Cal_Deg0) runs
// right away, though.
RTC_Compensation (); // Calculate compensation of RTC.
#else
// if no other clock compensation,
// calculate a constant clock drift
trim_value = (int32_t) (Y_Cal_Deg0 * 100L);
#endif
}
else
{
// Compensate for the time off by simulating the normal adjustment.
// It uses the last valid trim, which was collected when
// the temperature was running, more accurate than
// the trim for the calibration temperature.
// Multiplication must be used, rather than a loop, because
// the unit could be unpowered for millions of seconds while in
// storage.
float delta_trim = (float)Delta_Seconds (&rtc_temp, &RTC_COPY);
int16_t seconds;
irq_disable();
delta_trim = (delta_trim * (float)trim_value) + (float)trim_count;
seconds = (int16_t)(delta_trim / 1.0e9);
second_count += seconds;
trim_count = (int32_t)(delta_trim - (1.0e9 * ((float)seconds)));
irq_enable();
}
#endif
RTC_idx = 0;
}
void RTClk_Write (void)
{
#if RTC_COMPENSATION
// clear the adjustment counts, because there's no error
// when the clock is set, right?
RTC_Adjust_Trim (TRUE, trim_value);
#endif
RTC_idx = sizeof(RTC)-1;
RTClk_Write_Delay ();
}
#if CE_OFF
bool RTC_Tic (void) // called to detect a 1-second tick.
{
if (rtc_ready)
{
rtc_ready = FALSE;
return (TRUE);
}
else
return (FALSE);
}
#endif // ce_off
#pragma save
#pragma NOAREGS
static void RTClk_Write_Delay (void) small reentrant
{
#if M6520
WE = 0x00; // Write Enable RTC.
#endif
RTC[ RTC_idx ] = *(RTC_idx + ((uint8x_t*)&RTC_COPY));
// keep it from overwriting I/O
if (RTC_idx > 0)
{
// Switched to software timers to use less code, and free a timer.
// The two is the minimum reliable number of clock ticks.
#if TIMERS
if(NULL != stm_start (2, 0, RTClk_Write_Delay))
--RTC_idx;
else
RTC_idx = 0;
#elif TMR0 || TMR1
// 397us = 13/32768 of a second, the minimum time to write
tmr_start (microseconds2tmr_reg(500), 0, RTClk_Write_Delay);
--RTC_idx;
#else
#error need a timer
#endif
}
}
#pragma restore
void RTClk_Read (void)
{
uint8_t xdata sec;
uint8_t xdata old_sec;
if(RTC_idx != 0)
return;
sec = -1;
do
{
old_sec = sec;
sec = RTC[ 0 ];
} while (sec != old_sec);
memcpy_xx ((uint8x_t *) &RTC_COPY, RTC, sizeof (RTC));
}
#if RTC_COMPENSATION
// This is the RTC's temperature compensation. It's updated in run_meter()
// in meter.c, running each time the meter measures the temperature.
// It figures the clock's compensation in parts per billion.
// Y_FREQ = XTAL_FREQ * {[Y_DEG2 * deltaT + Y_DEG1] * deltaT + Y_CAL}.
//
// Y_Cal_Deg2 is 1 ppb => 1 Y_Cal_Deg2 is 1 ppb.
// Y_Cal_Deg1 is 10 ppb => 10 Y_Cal_Deg1 is 1 ppb.
// Y_Cal is 100 ppb => 100 Y_Cal is 1 ppb.
//
// deltaT is 0.1 degrees => deltaT / 10 is degrees C.
//
void RTC_Compensation (void) // Do temperature compensation of RTC.
{
int32_t value;
value = (((((int32_t) Y_Cal_Deg2 * deltaT) / 100 + (int32_t) Y_Cal_Deg1) * deltaT) + (int32_t) Y_Cal_Deg0 * 100);
RTC_Adjust_Trim (FALSE, value);
}
void RTC_Adjust_Trim (bool clr_cnt, int32_t value)
{
irq_disable(); // Begin critical section.
// using a signed value means that opposite signs cancel, as they should
trim_value = value;
if (clr_cnt)
{
trim_count = 0;
second_count = 0;
}
irq_enable(); // End critical section.
}
#endif // RTC_COMPENSATION.
#if !RTC_LINE_LOCKED
// time since midnight, january 1, 2000
int32_t Julian (struct RTC_t xdata *ptm)
{
int32_t a, y, m, j;
a = (14 - (*ptm).Month) / 12L;
y = (int32_t)(*ptm).Year + 6800L - a;
m = (int32_t)(*ptm).Month + (12 * a) - 3;
// julian days since Jan 1, 2000; 5.8e6 years range in 32-bit signed no.
j = (int32_t)(*ptm).Date
+ (((153 * m) + 2)/5)
+ (365 * y)
+ (y / 4)
- (y / 100)
+ (y / 400)
- 2483590;
#if 1
// julian seconds... 68 years range in a 32-bit signed number
j = (j * 86400)
+ (((int32_t) (*ptm).Hour - 12) * 3600)
+ ((int32_t) (*ptm).Min * 60)
+ (int32_t) (*ptm).Sec;
#elif 0
// julian millihours... 244 years range in a 32-bit signed number
j = (((j * 24) + ((int32_t) (*ptm).Hour - 12)) * 1000)
+ ((((int32_t) (*ptm).Min * 50) + 1) / 3)
+ ((((int32_t) (*ptm).Sec * 5) + 9) / 18);
#elif 0
// julian decihours... 24,497 years range in a 32-bit signed number
j = (((j * 24) + ((int32_t)(*ptm).Hour - 12)) * 10)
+ (((int32_t) (*ptm).Min + 3) / 6);
#endif
return j;
}
int32_t Delta_Seconds (struct RTC_t xdata *start, struct RTC_t xdata *end)
{
return (Julian(end) - Julian(start)); // delta time.
}
#endif
#endif // REAL_TIME_DATE
#if OPERATING_TIME || REAL_TIME_DATE
#define BILLION (1000000000L)
#pragma save
#pragma NOAREGS
// called from the interrupt decode routines in io65??.c
void rtc_isr (void) small reentrant
{
// This produces a calibration signal, usable when the
// pulse output is disabled. It's needed to set the compensation.
// It is enabled automatically after autocalibration.
// It must be first in this routine to reduce timing uncertainties.
#if 0
DIO_7 ^= 1; // toggle pulse output
#endif
#if REAL_TIME_DATE
rtc_ready = TRUE;
#if !RTC_LINE_LOCKED // at least a constant adjustment is the default
// adjust the clock in real time
trim_count += trim_value;
if (trim_count < 0) // same speed as checking a flag
{
// keep maximum error +/- 0.5 seconds, +/- 5.8 ppm/day
if (trim_count < (-BILLION / 2)) // -500.0e8 equals -1/2 second
{
second_count -= 1;
trim_count += BILLION;
}
}
else
{
// keep maximum error +/- 0.5 seconds, +/- 5.8 ppm
if (trim_count > (BILLION / 2)) // 500.0e8 = 1/2 second
{
second_count += 1;
trim_count -= BILLION;
}
}
// catch up on adjustments;
// Adjustments have to be distributed, no more than one per two seconds
// Normally this would be done just by the slow rate of adjustment,
// but the unit might be in storage for years. After that, it would
// have many seconds of adjustment at once.
if (rtc_adjusting_now)
{
rtc_adjusting_now = FALSE;
}
else
{
if (second_count < 0)
{
second_count += 1;
// Pulse decrement.
#if M6520
WE = 0x00; // Write Enable RTC.
#endif
RTC_ADJUST = RTC_DEC_SEC;
rtc_adjusting_now = TRUE;
}
else if (second_count > 0)
{
second_count -= 1;
// Pulse increment.
#if M6520
WE = 0x00; // Write Enable RTC.
#endif
RTC_ADJUST = RTC_INC_SEC;
rtc_adjusting_now = TRUE;
}
}
#else
// This locks the RTC to the line frequency
// This requires no calibration.
{
// get the nominal counts per second, calculated in meter\freq.c
int8_t cnt = (int8_t)main_edge_cnt_nom;
// Are mains data available? (demo code is often run without mains)
if (cnt != 0) // from meter\freq.c
{
// how much error has accumulated?
MainEdgeCount -= (long) cnt; // remove this second's counts
if (MainEdgeCount < (long)(-cnt)) // clock is too fast
{
MainEdgeCount += (long)cnt; // adjust the measurement
#if M6520
WE = 0x00; // Write Enable RTC.
#endif
RTC_ADJUST = RTC_DEC_SEC; // Decrement the seconds.
}
else if (MainEdgeCount > (long)cnt) // clock is too slow
{
MainEdgeCount -= (long)cnt; // adjust the measurement
#if M6520
WE = 0x00; // Write Enable RTC.
#endif
RTC_ADJUST = RTC_INC_SEC; // increment second.
}
}
}
#endif // RTC_LINE_LOCKED
#endif // REAL_TIME_DATE
#if OPERATING_TIME
OperatingSeconds += 1; // count seconds of operation
#endif
}
#pragma restore
#endif // REAL_TIME_DATE || OPERATING_TIME
/***************************************************************************
* $Log: rtc_experimental.c,v $
* Revision 1.1 2006/10/13 00:47:30 tvander
* Removed compile options for 6530, 6515;
* renamed 6511 and 6513 to trace11 and trace13;
* Binary verified unchanged from previous version.
*
*
* Copyright (C) 2006 Teridian Semiconductor Corp. All Rights Reserved. *
* this program is fully protected by the United States copyright *
* laws and is the property of Teridian Semiconductor Corporation. *
***************************************************************************/
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -