⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rtc_experimental.c

📁 TDK 6521 SOC 芯片 DEMO程序
💻 C
字号:
/***************************************************************************
 * 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.         *
 ***************************************************************************/

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -