Yahoo Groups archive

Lpc2000

Index last updated: 2026-04-28 23:31 UTC

Message

Re: [lpc2000] RTC problem in LPC2148

2006-01-31 by Tom Walsh

Sean wrote:

>Hello everyone,
>
>I've got a bit of a strange problem with the RTC in my setup.  I have a 
>dedicated lithium battery going to the Vbat pin to power the RTC, and it 
>usually works fine, however periodically the clock changes to an invalid 
>value and stops running.  Usually this value is something like "Year:129 
>Month:00 Day:16 Hour:02 Min:09 Sec:08", and unless I reset the RTC to a 
>valid value it stops running.  This will usually happen after several hours 
>of Vcc absent (i.e. device powered off) or after a day or two running 
>constantly.  Nothing else is disrupted.  I have a 3.6V lithium running 
>through a diode which drops the voltage at the pin down to 3.165V when Vcc 
>present and to 3.232V when Vcc absent (implying that more current is drawn 
>when the micro is powered on??)
>
>  
>
Diode switching is needed to maintain the Vbat when Vdd (3.3v main) is 
absent and to switch over to the battery when Vdd disappears.  It sounds 
like you have that working?  It will take two diodes (1N4148A) and a 
resistor (560ohm) to do this properly.  Cathodes of both diodes go to 
Vbat line, anode of one diode goes to Vdd, anode of other diode is 
series with 560ohm to positive terminal of Lithium cell.

This is half the solution, the hardware half.  I found that my clock 
would also "explode" on occasion and seemingly at random.  The solution 
I took was a bit more aggressive in the software.  Study my clock 
routines, especially awakenClock() and sleepClock().  Essentially, when 
the clock registers are not needed, they are "disconnected".

=============== begin RTCfuncs.c ===================
/*************************************************************
 * RealTime awareness functions.  Provide for elapsed time
 * intervals as well as reading data & time from RTC.
 *
 * (C) 2005 - Tom Walsh <tom@...>
 * May be used for hobby or commercial purposes as long as
 * this notice remains intact.
*************************************************************/
#include <LPC21xx.h>
#include <lpcRTC.h>
#include <time.h>
#include <sys/times.h>
#include    <sysdefs.h>
#include <system.h>

#define    CCR_CLKEN        BIT(0)
#define    CCR_CTCRST        BIT(1)
#define    CCR_CTTEST0        BIT(2)
#define    CCR_CTTEST1        BIT(3)
#define    CCR_CLKSRC        BIT(4)
#define    T0_MS_DIV        (PCLK / 10000)


long systemTickCounter;

static inline void sleepClock(void)
{// place RTC on 32kHz xtal and disconnect power.
    RTCCCR = (CCR_CLKEN | CCR_CLKSRC);
        // power off the clock registers.
    PCONP &= ~PCRTC;
}

static inline void awakenClock(void)
{// prepare clock for interactive use.
    RTCCCR = (CCR_CLKEN | CCR_CLKSRC);
        // power up the clock registers.
    PCONP |= PCRTC;
}

static void startMsTimer (void)
{// tick timer, 1ms heartbeat for the system.
        // connect VIC to Timer0
    Timer0VectAddr = (uint32_t)timer0ISR;    // address of the ISR
    Timer0VectCntl = VIC_ENABLE | VIC_TIMER0;
    VICIntSelect &= ~VIC_BIT(VIC_TIMER0);  // Timer0 selected as IRQ
    VICIntEnable = VIC_BIT(VIC_TIMER0);    // Timer0 interrupt enabled
    T0TCR = TCR_RESET;                        // reset & disable timer 0
    T0PC = 0;
    T0PR = T0_MS_DIV;
    T0CCR = 0;    // timer mode.
    T0EMR = 0;    // no external match.
    T0MR0 = 9;
    T0MCR = 3;    // reset and interrupt on terminal count.
    T0TCR = TCR_ENABLE;
        // init the system ticks.
    systemTickCounter = 0;
}

void setElapsed (int HowLong, struct TIMER * timer, timertype_t TimerType)
{ /* set for a TimeElapsed event. */
    timer->HowLong = HowLong;
    timer->Type = TimerType;
    if (timer->Type == TimerInMilli) timer->SetTime = times (0);
    else timer->SetTime = time (0);
}

bool timeElapsed (struct TIMER * timer)
{ /* return True if preset interval has expired. */
time_t TestTimerNow, TestTimeStart;
    if (timer->Type == TimerInMilli) TestTimerNow = times (0);
    else TestTimerNow = time (0);
    TestTimeStart = timer->SetTime;
     if (((TestTimeStart>TestTimerNow) ? (TestTimerNow + 
(~TestTimeStart)) : (TestTimerNow - TestTimeStart)) > timer->HowLong) {
        return True;
    }
    return False;
}

void initRTC(void)
{// start clock so that the sytsem may use it.
#ifdef HAS_CLOCK
bool nonsense = False;
rtcCTIME0_t    ctime0;
rtcCTIME1_t    ctime1;
rtcCTIME2_t    ctime2;
struct tm newTime;
time_t resetTime;
    awakenClock();
        // see if clock contains nonsense values.
        // grab time as consolidated to avoid misses.
    ctime0 = RTCCTIME0; ctime1 = RTCCTIME1; ctime2 = RTCCTIME2;
        // leisurely tear the packed time apart into individual time.
    if ((ctime0.seconds < 0) || (ctime0.seconds > 59)) nonsense = True;
    if ((ctime0.minutes < 0) || (ctime0.minutes > 59)) nonsense = True;
    if ((ctime0.hours < 0) || (ctime0.hours > 23)) nonsense = True;
    if ((ctime1.dayOfMonth < 1) || (ctime1.dayOfMonth > 31)) nonsense = 
True;
    if ((ctime1.month < 1) || (ctime1.month > 12)) nonsense = True;
    if ((ctime1.year < 1980) || (ctime1.year > 2050)) nonsense = True;
    if ((ctime0.dayOfWeek < 0) || (ctime0.dayOfWeek > 6)) nonsense = True;
    if ((ctime2.dayOfYear < 0) || (ctime2.dayOfYear > 366)) nonsense = True;
    sleepClock ();
    if (nonsense) {
        // set the clock to Jan 1, 2006 00:00:00
        resetTime = 1136073600l;
        localtime_r (&resetTime, &newTime);
        newTime.tm_year += 1900;
        newTime.tm_mon += 1;
        newTime.tm_yday += 1;
        writeRTC (&newTime);
    }
#endif
        // start realtime heartbeat (1ms period).
    startMsTimer();
}

/**************************************************
 *
 * readRTC & writeRTC are supporting functions
 * for the newlib stubs.
 *
**************************************************/

void readRTC (struct tm *theTime)
{// read clock registers and return tm structure.
#ifdef HAS_CLOCK
rtcCTIME0_t    ctime0;
rtcCTIME1_t    ctime1;
rtcCTIME2_t    ctime2;
    awakenClock();
        // grab time as consolidated to avoid misses.
    ctime0 = RTCCTIME0; ctime1 = RTCCTIME1; ctime2 = RTCCTIME2;
        // leisurely tear the packed time apart into individual time.
    theTime->tm_sec = ctime0.seconds;
    theTime->tm_min = ctime0.minutes;
    theTime->tm_hour = ctime0.hours;
    theTime->tm_mday = ctime1.dayOfMonth;
    theTime->tm_mon = ctime1.month;
    theTime->tm_year = ctime1.year;
    theTime->tm_wday = ctime0.dayOfWeek;
    theTime->tm_yday = ctime2.dayOfYear;
    theTime->tm_isdst = False;
    sleepClock ();
#endif
}

void writeRTC (struct tm *newTime)
{// set clock to new values.
#ifdef HAS_CLOCK
    awakenClock();
        // grab time as consolidated to avoid misses.
    RTCTCR->seconds = newTime->tm_sec;
    RTCTCR->minutes = newTime->tm_min;
    RTCTCR->hours = newTime->tm_hour;
    RTCTCR->dayOfMonth = newTime->tm_mday;
    RTCTCR->month = newTime->tm_mon;
    RTCTCR->year = newTime->tm_year;
    RTCTCR->dayOfWeek = newTime->tm_wday;
    RTCTCR->dayOfYear = newTime->tm_yday;
    sleepClock ();
#endif
}

================== snip ========================

The above functions are designed to work with the newlib gettimeofday(), 
settimeofday(), localtime(), etc..

My clock has been running for several months without failure.  It has 
lost about 5 minutes, so I need to tweak the crystal capacitance...


What also may be of note is how I setup my power control during system 
initialization:

   PCONP = ((PCTIM0 | PCTIM1 | PCUART0 | PCUART1 | PCRTC |
          PCSPI1 | PCAD0) & ~PCONP_MASK);

Hope this helps,

TomW


-- 
Tom Walsh - WN3L - Embedded Systems Consultant
http://openhardware.net, http://cyberiansoftware.com
"Windows? No thanks, I have work to do..."
----------------------------------------------------

Attachments

Move to quarantaine

This moves the raw source file on disk only. The archive index is not changed automatically, so you still need to run a manual refresh afterward.