Arbeiten mit dem RTC

Allgemeines

Da der RTC (Real-Time-Clock) in der LPC900-Familie nur als 16-bit Down-Counter aufgebaut ist, müssen entsprechende Routinen für das Umsetzen in Stunden-/Tagesberechnung selbst durch­ge­führt werden. Da die Genauigkeit des internen RC-Oscillators bei ± 2,5 % liegt, kann schon nach 5 Stunden eine Abweichung von ± 8 min entstehen (siehe dazu auch Kapitel 10.13 [3]). Aus diesem Grund empfielt es sich, den RTC über einen externen Quarz (z. B. 32,769 kHz) zu betreiben.

Anpassungen für die Verwendung des externen Quarzes

Damit der Simulator mit dem RC-Oscillator und der RTC mit dem externen Quarz arbeitet, muss die START900.a51 im Projekt verwendet werden. Dieses Assembler­mo­dul ent­hält das Konfigurationsbyte UCFG1, mit dessen Einstellung die Auswahl für den ver­wen­deten Quarz vorgenommen wird. Die Bits FOC0-2 müssen auf „3“ (Interner RC Oscillator) gesetzt werden (siehe Abbildung 66).

Abbildung 66  Auszug aus der START900.a51

F !! Eine ausführliche Beschreibung zur START900.a51 finden sie in Kapitel 8.11 „Startup-Code“ [3], die Beschreibung zum UCFG1 in Kapitel 10 „Extern Taktbe­schal­tung“ [3]. !!

Die Frequenz für den externen Uhren-Quarz wird unter Options for Target im Reiter Target eingegeben (siehe Abbildung 67). Die Auswahl externen Quarzes für den RTC erfolgt über das SFR RTCCON (siehe Kapitel 10.13 „Real Time Clcok „ [3]).

Abbildung 67  Options for Target, Reiter Target

Arbeiten mit den Baugruppen MCB900/ EPM900

Bei Verwendung der MCB900 Baugruppe wird der externe Quarz in der Position Q1 eingelötet. Die Kondensatoren (22 pF) können entweder auf die Positionen C5 und C6 (SMD) oder auf die Positionen P3.1 und P3.0 (verdrahtet) gelötet werden.

Wird die EPM900 Baugruppe verwendet, muss nur der Quarz auf der Position Q1 einge­lö­tet werden. Die beiden Kondensatoren sind schon bestückt (Position C8, C9). Zudem müssen die beiden Jumper auf die Position „ext. XTAL“ gesteckt wer­den.

Aufbau einer Software-Uhr

Bei Verwendung der Quarzfrequenz von 32,769 kHz werden 256 Zählimpulse pro Se­kun­de am Down-Counter des RTC erzeugt (32768 Hz/128). Somit wird bei jedem Zähl­schritt eine 1/256 Sekunde gezählt. Um z. B. eine Auflösung von 10 ms mit dem Down-Counter des RTCs zu erreichen, muss ein externer Quarz von 128 kHz oder einem Vielfachen davon verwendet werden.

Die Uhr soll im ersten Schritt Stunden, Minuten und Sekunden enthalten. Die Verwal­tung dieser Informationen erfolgt in der Struktur stWatch (siehe Listing 77, ). Die Da­ten werden im Datentyp „unsigned char“ abgespeichert (siehe ).

 

struct stWATCH

{

  unsigned char ucSec;

  unsigned char ucMin;

  unsigned char ucHour;

};

Listing 77  Aufbau der Struktur stUHR

Erstellen sie ein Programm, bei dem der RTC jede Sekunde einen Interrupt auslöst. Die Zeit soll in der Struktur aktualisiert werden. Die Initialisierung des RTCs soll über die Funk­tion „v_InitRTC()“ erfolgen. Die externe Frequenz für den RTC wird in der De­fi­ni­tion „RTC_FREQ“ ange­ge­ben (siehe Listing 78, ). In der De­finition „RTC_RELOAD“ wird der Reloadwert für die SFR RTCL und RTCH be­rech­net. Der Reloadwert wird vor dem Teilen um „1“ verringert, da erst beim Wechsel von 0x0000 nach 0xFFFF der Unterlauf erkannt und somit die ISR ausgelöst wird.

Die Struktur wird global im C-Modul angelegt (siehe Listing 79, ). Der RTC wird in der Funktion „v_InitRTC()“ initialisiert (siehe ). Der Reloadwert wird mit Hilfe der De­finition berechnet (siehe ƒ).

F !! Vergessen sie nicht, den WDT auszuschalten, da dieser die gleiche ISR verwendet. !!

Nach der Initalisierung läuft das Programm in der while(1) Schleife (siehe ). Löst der RTC die ISR-Routine aus, wird zuerst das Flag gelöscht (siehe ). Danach wird über­prüft, ob schon 59 Sekunden verstrichen sind (siehe ). Ist das nicht der Fall, wird die Variable „stWatch.ucSec“ um Eins erhöht. Andernfalls wird die Variable auf „0“ gesetzt, und es findet eine Überprüfung statt, ob schon 59 Minu­ten verstrichen sind (siehe ). Ist dies nicht der Fall, wird die Variable „stWatch.ucMin“ um Eins erhöht. Die gleiche Überprüfung findet dann nochmals für die Stunden statt (siehe ˆ).

Projektname

Verzeichnis

Verwendete Sourcemodule

Watch

Test_Watch

Test_Watch.c

START900.a51

Tabelle 72  Projekt Watch

 

 

 

// Definitionen

#define RTC_FREQ 32768 // Angabe in Hz

#define RTC_RELOAD ((RTC_FREQ-1)/128)

 

struct stWATCH

{

  unsigned char ucSec;

  unsigned char ucMin; 

  unsigned char ucHour;

};

Listing 78  Inhalt von Test_Watch.h

 

 

 

 

 

 

 

 

 

 

ƒ

ƒ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ˆ

#include <REG932.H>

#include <Test_Watch.H>

 

struct stWATCH stWatch;

 

void v_InitRTC(void)

{

   WDCON = 0x00; // WDT ausschalten !!

   WFEED1= 0xa5;

   WFEED2= 0x5a;

 

   // Berechnung des eigentlichen RTC Taktes

   RTCL = (unsigned char) (RTC_RELOAD);

   RTCH = (unsigned char) ((RTC_RELOAD) >> 8); 

 

   // RTCS1 = 1 (low. Freq. Quarz) + Interrupt freigeben

   RTCCON = 0x42;       

   RTCCON = RTCCON | 1;  // RTC starten

   EWDRT = 1;

}

 

void main(void)

{

   v_InitRTC();

   EA = 1;       // Allgemeine Interruptsperre aufheben

   while(1);

}

 

void v_RTC_ISR(void) interrupt 10 using 1

{

   RTCCON = 0x43;   // ISR-Flag loeschen

   if (stWatch.ucSec < 59) // Ueberpruefung auf Ueberlauf (sec)

      stWatch.ucSec++;

   else

   {

      stWatch.ucSec = 0;

      if (stWatch.ucMin < 59) // Ueberpruefung auf Ueberlauf (min)

         stWatch.ucMin++;

      else

      {

         stWatch.ucMin= 0;

         if (stWatch.ucHour < 23) // Ueberpruefung auf Ueberlauf

           stWatch.ucHour++;      // (hour)

         else

           stWatch.ucHour = 0;  

      }

   }

}

Listing 79  Inhalt von Test_Watch.c

Mit diesem Verfahren wird die Verwaltung der Uhr in die ISR verlagert. Soll jetzt noch eine Verwaltung der Tage, Monate und Jahre erfolgen, wird immer mehr Programmzeit in der ISR benötigt.

Es empfiehlt sich deswegen, die Auswertung der Zeit außerhalb der ISR durchzuführen und die Zeit in Sekunden mit Hilfe einer Variablen vom Datentyp unsigned long zu spei­chern. Die Variable dieses Datentyps kann 4294967295 Sekunden aufnehmen, bis ein Überlauf statt­fin­det. Dies entspricht einer Zeit von rund 136 Jahren. Dieses Verfahren wird auch beim PC und bei einigen anderen RTC (z. B. von Dallas) eingesetzt. In der ISR des RTC wird nur die Variable inkrementiert.

Erstellen sie ein Programm bei dem Zeit mit der Variablen „ulRTCTime“ verwaltet wird. In der ISR wird die Variable um Eins erhöht.

Projektname

Verzeichnis

Verwendete Sourcemodule

RTCSec

Test_RTCSec

Test_RTCSec.c

 START900.a51

Tabelle 73  Projekt RTCSec

 

 

 

 

 

#include <REG932.H>

#include <TestWatchSec.h>

 

unsigned long ulRTCTime;

 

void v_InitRTC(void)

{

   WDCON = 0x00; // WDT ausschalten !!  

   WFEED1= 0xa5;  

   WFEED2= 0x5a;   // Berechnung des eigentlichen RTC Taktes  

   // Berechnung des eigentlichen RTC Taktes  

   RTCL = (unsigned char) (RTC_RELOAD);

   RTCH = (unsigned char) ((RTC_RELOAD) >> 8);    

   // RTCS1 = 1 (low. Freq. Quarz) + Interrupt freigeben  

   RTCCON = 0x42;          

   RTCCON = RTCCON | 1;  // RTC starten  

   EWDRT = 1;

}

 

void main(void)

{  

   v_InitRTC();  

   EA = 1;       // Allgemeine Interruptsperre aufheben  

   while(1);

}

 

void v_RTC_ISR(void) interrupt 10 using 1

{  

   RTCCON = 0x43;   // ISR-Flag loeschen

   ulRTCTime++; 

}

Listing 80  Inhalt von Test_RTCSec.c

Die Umrechnung der Variablen „ulRTCTime“ auf die jeweilige Zeit muss per Software erfolgen. Fügen sie nun die Funktion „v_GetTime()“ hinzu, die die Zeitumrechnung durchführt (siehe Listing 81, ). Die Funktion zerlegt „ulRTCTime“ in Sekunden (siehe k), Minuten (sie­he l) und Stunden (siehe m) und weist das Ergebnis dem jeweiligen Element der Struk­­tur zu. Als Übergabeparameter wird ein Pointer auf die Struktur stWATCH ver­wen­det. Das Ergebnis der Berechnung wird in dieser Struktur ab­ge­spei­chert.

F !! Die Funktionsweise der Struktur sowie die Adressierung mit Hilfe eines Pointers können sie in Kapitel 13, „Strukturen“ [1] nachlesen. !!

 

 

 

 

k

 

l

n

m

void v_GetTime(struct stWATCH *pWatch)

{

   unsigned long ulMin, ulHour;

   unsigned int uiDays;

   ulMin = ulRTCTime / 60;

   pWatch->ucSec = ulRTCTime - (60 * ulMin); // Berechnung sec

   ulHour = ulMin / 60;

   pWatch->ucMin = ulMin - (60 * ulHour);    // Berechnung min

   uiDays = ulHour / 24;

   pWatch->ucHour = ulHour - (24 * uiDays);  // Berechnung hours

}

Listing 81  Funktion v_GetTime()

Die Berechung der Tage, Monate und Jahre hängt von den Schaltjahren ab. Um hier eine Vereinfachung bezüglich der Softwareauswertung zu erreichen, wurde eine Formel ent­wi­ckelt, die nur ein bestimmtes Zeit­fenster abdeckt. Es beginnt beim Referenz­da­tum 01.01.1970 (Unix-Timestamp) und en­det am 19.01.2038 (ausgetestet).

F !! Dieser Bereich ist vollkommend ausreichend, da die Hardware meist nur für 10 Jahre aus­gelegt wird. !!

Um Tag, Monat, Jahr und den Wochentag auswerten und abspeichern zu können, muss die Struktor stWATCH erweitert werden (siehe Listing 82, ).

 

 

 

 

 

 

 

// Definitionen

struct stWATCH

{

  unsigned char ucSec;

  unsigned char ucMin; 

  unsigned char ucHour;

  unsigned char ucWeekday;

  unsigned char ucDay;

  unsigned char ucMonth;

  unsigned int  uiYear;

};

Listing 82  Inhalt von Test_RTCSec.h

Um den Wochentag zu errechnen, muss zur Anzahl der ermittelten Tage der Wert „4“ hin­zugezählt werden (der 01.01.1970 war ein Donnerstag). Der Gesamtwert wird dann Mo­du­lo 7 geteilt. Das Ergebnis kann die Werte „0“ bis „6“ enthalten, wobei „0“ den Sonntag repräsentiert.

 

 

 

 

k

l

m

n

 

 

o

p

 

q

 

void v_GetTime(void)

{

   ...

   uiDays = ulHour / 24;

   pWatch->ucHour = ulHour - (24 * uiDays);  // Berechnung hours

   pWatch->ucWeekday = (uiDays  + 4) % 7;    // Berechnung weekday

   uiDays = uiDays + 365 + 366;              // ab 01.01.1968

   ucSchaltPeri = uiDays / 1461;             // ((4 * 365) + 1)

   uiDaysLYear = uiDays % ((4 * 365) + 1);

   if ((uiDaysLYear >= (31 + 29)))

       ucSchaltPeri++;

 

   pWatch->uiYear = ((uiDays - ucSchaltPeri) / 365);

   uiDays = uiDays - (pWatch->uiYear * 365) - ucSchaltPeri;

   if ((uiDays <= 365) && (uiDays >= 60))

              uiDays++;

   pWatch->uiYear += 68;

}

Listing 83  Auszug aus Funktion v_GetTime()

Für die Berechnung der Schaltjahre werden die Tage ab dem 01.01.1968 gezählt. Die in „v_GetTime()“ (siehe siehe Listing 81, n) ermittelten Tage müssen um 731 (365 + 366) er­höht wer­den (siehe Listing 83, k). Danach wird „uiDays“ durch 1461 geteilt, um die Anzahl der Schaltjahre zu ermitteln (siehe l). Als nächstes müssen die Tage ermittelt werden, die seit dem letzten Schaltjahr vergangen sind (siehe m). Liegt das Datum über dem 29.02. eines Schaltjahres, muss zudem die Anzahl der Schaltjahre um eins erhöht werden (siehe n).

Das aktuelle Jahr kann jetzt aus der Anzahl der ermittelten Tage minus der Anzahl Schalt­jahrtage durch 365 errechnet werden (siehe o).

Die Anzahl der Tage seit Beginn des Jahres wird in p ermittelt.

Da die Formel vom 01.01.1968 ausgeht, muss noch der Wert „68“ addiert werden (siehe q).

Um den aktuellen Monat und den Tag zu ermitteln, wird eine Schleife durchlaufen. In einem Array sind die Anzahl der abgelaufenen Tage pro Monat abgespeichert (siehe Listing 84, ). Im Januar sind 0 Tage, im Februar schon 31 Tage aus dem Januar enthal­ten.

 

unsigned  int DaysToMonth[13] =

{ 0,31,59,90,120,151,181,212,243,273,304,334,365};

Listing 84  Array DaysToMonth

Die while-Schleife (siehe Listing 85, ) wird jetzt solange durchlaufen, bis die Anzahl der Tage kleiner ist als der Monat Tage hat. Bei jedem Schleifendurchlauf wird der Monat (stWatch.ucMonth) um Eins dekrementiert (siehe k). Der Monat wird als Index im Array „DaysToMonth“ verwendet (siehe l). Zudem wird überprüft, ob ein Schaltjahr vorhanden ist und der Monat über Februar liegt (siehe m). Ist dies der Fall, wird die Anzahl der Tage um Eins erhöht (siehe n).

Ist die Bedingung der while-Schleife nicht mehr gegeben (false), so ist der Monat gefunden. Es kann jetzt noch der aktuelle Tag innerhalb des Monats ermittelt werden (siehe o).

 

 

 

 

 

 

k

l

m

n

 

o

 

void v_GetTime(struct stWATCH *pWatch)

{

   ...

   pWatch->ucMonth = 13;

   uiDay2Month = 366;

   while (uiDays < uiDay2Month)

   {

     pWatch->ucMonth--;

     uiDay2Month = DaysToMonth[pWatch->ucMonth];

     if ((pWatch->ucMonth >= 2) && ((pWatch->uiYear % 4) == 0))

        uiDay2Month++;

   }

   pWatch->ucDay = uiDays - uiDay2Month + 1;

}

Listing 85  Auszug aus Funktion v_GetTime()

Für die Umwandlung eines Datums und einer Uhrzeit in einen long-Wert wird die Funk­tion „ul_SetTime()“ verwendet (siehe Listing 86, ). Die Funktion verwendet die Anga­ben in der Struktur stWatch (siehe k) und errechnet daraus einen long-Wert. Dieser wird an die aufrufende Funktion zurückgegeben (siehe l) .

 

 

 

 

k

k

k

k

 

k

 

l

unsigned long ul_SetTime(struct stWATCH *pWatch)

{

  unsigned long iday;

  unsigned long val;

 

  iday = 365 * (pWatch->uiYear - 70) +

             DaysToMonth[pWatch->ucMonth] + (pWatch->ucDay - 1);

  iday = iday + (pWatch->uiYear - 69) / 4;

  if ((pWatch->ucMonth > 1) && ((pWatch->uiYear % 4) == 0))

    iday++;

  val = pWatch->ucSec + 60 * pWatch->ucMin +

        3600 * (pWatch->ucHour + 24 * iday);

  return val;

}

Listing 86  Funktion ul_SetTime()

Erstellen sie das Projekt aus Tabelle 74, bei dem die Funktionen für den RTC und die ISR in einem eigenen Sourcemodul lie­gen. Auf die Variable ulRTCTime soll nur noch innerhalb des C-Moduls RTCLIB.c zugegriffen werden dürfen. Die Variable wird in die­sem Fall mit dem Gültigkeitsbereich „static“ versehen (siehe Listing 87, ). Die Funk­tionsweise von static können sie dem Kapitel 8.3 „Variablen [3] nachlesen. Das Aus­lesen bzw. das Setzen der Zeit kann nur noch über die Funktionen „v_GetTime()“ (siehe k) und „v_SetTime()“ (siehe l) erfolgen.

Projektname

Verzeichnis

Verwendete Sourcemodule

RTCLib

Test_RTCLib

Test_RTCLib.c

 START900.a51

 

..\Library

RTCLib.c

Tabelle 74  Projekt RTCLib

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

k

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

l

#include <REG932.H>

#include <RTCLib.h>

 

// Definitionen

unsigned  int DaysToMonth[13] =

{ 0,31,59,90,120,151,181,212,243,273,304,334,365};

 

static unsigned long ulRTCTime;

 

void v_InitRTC(void)

{

   WDCON = 0x00; // WDT ausschalten !!  

   WFEED1= 0xa5;  

   WFEED2= 0x5a;  

 

 // Berechnung des eigentlichen RTC Taktes  

   RTCL = (unsigned char) (RTC_RELOAD);

   RTCH = (unsigned char) ((RTC_RELOAD) >> 8);  

  

   // RTCS1 = 1 (low. Freq. Quarz) + Interrupt freigeben  

   RTCCON = 0x42;          

   RTCCON = RTCCON | 1;  // RTC starten  

   EWDRT = 1;

}

 

void v_GetTime(struct stWATCH *pWatch)

{

   unsigned long ulMin, ulHour;

   unsigned int uiDays, uiDay2Month, uiDaysLYear;

   unsigned char ucSchaltPeri;

 

   ulMin = ulRTCTime / 60;

   pWatch->ucSec = ulRTCTime - (60 * ulMin); // Berechnung sec

   ulHour = ulMin / 60;

   pWatch->ucMin = ulMin - (60 * ulHour);    // Berechnung min

   uiDays = ulHour / 24;

   pWatch->ucHour = ulHour - (24 * uiDays);  // Berechnung hours

   pWatch->ucWeekday = (uiDays  + 4) % 7;    // Berechnung weekday

   uiDays = uiDays + 365 + 366;              // ab 01.01.1968

   ucSchaltPeri = uiDays / ((4 * 365) + 1);

 

   uiDaysLYear = uiDays % ((4 * 365) + 1);

   if ((uiDaysLYear >= (31 + 29)))

       ucSchaltPeri++;

   pWatch->uiYear = ((uiDays - ucSchaltPeri)  / 365);

   uiDays = uiDays - (pWatch->uiYear * 365) - ucSchaltPeri;

   if ((uiDays <= 365) && (uiDays >= 60))

      uiDays++;

 

   pWatch->uiYear += 68;

   pWatch->ucMonth = 13;

   uiDay2Month = 366;

   while (uiDays < uiDay2Month)

   {

      pWatch->ucMonth--;

      uiDay2Month = DaysToMonth[pWatch->ucMonth];

       if ((pWatch->ucMonth >= 2) && ((pWatch->uiYear % 4) == 0))

          uiDay2Month++;

   }

   pWatch->ucDay = uiDays - uiDay2Month + 1;

}

 

void v_SetTime(struct stWATCH *pWatch)

{

   unsigned long iday;

   iday = 365 * (pWatch->uiYear - 70) +

   DaysToMonth pWatch->ucMonth] + (pWatch->ucDay - 1);

   iday = iday + (pWatch->uiYear - 69) / 4;

   if ((pWatch->ucMonth > 1) && ((pWatch->uiYear % 4) == 0))

       iday++;

   ulRTCTime = pWatch->ucSec + 60 * pWatch->ucMin + 3600 *

               (pWatch->ucHour + 24 * iday);

}

 

void v_RTC_ISR(void) interrupt 10 using 1

{  

   RTCCON = 0x43;   // ISR-Flag loeschen

   ulRTCTime++; 

}

Listing 87  Inhalt von RTCLib.c

Die Definition der RTC-Frequenz, der Struktur, sowie der RTC-Funktionen sind im H-File RTCLib.h enthalten (siehe Listing 88).

 

 

// Definitionen

#define RTC_FREQ 32768 // Angabe in Hz

#define RTC_RELOAD ((RTC_FREQ-1)/128)

 

struct stWATCH

{

  unsigned char ucSec; 

  unsigned char ucMin;   

  unsigned char ucHour;

  unsigned char ucWeekday;

  unsigned char ucDay;

  unsigned char ucMonth;

  unsigned int  uiYear;

};

enum enRTCMONTH {JAN=0, FEB, MAR, APR, MAI, JUN, JUL, AUG, SEP,

                 OCT, NOV, DEC};

 

// Deklarationen

extern void v_GetTime(struct stWATCH *pWatch);

extern void v_InitRTC(void);

extern void v_SetTime(struct stWATCH *pWatch);

Listing 88  Inhalt von RTCLib.h

Um die RTC-Funktionen bekannt zu machen, wird das H-File „RTCLib.h“ am Anfang in das Sourcemodul mit eingebunden (siehe Listing 89, ). Vor der Initialisierung des RTCs wird zuerst die Zeit in der Struktur „stWatch“ vorbelegt, in diesem Beispiel auf den 01.10.2002, 14:00:00 (siehe k) und dann mit Hilfe der Funktion „v_SetTime()“ gesetzt (siehe l). Danach wird der RTC initialisiert (siehe m) und die allgemeine Interruptsperre aufgehoben (siehe n).

 

 

 

 

 

 

k

k

k

k

k

k

 

l

 

m

n

#include <REG932.H>

#include <RTCLib.h>

 

void main(void)

{

   struct stWATCH stWatch;

   // Zeit auf 14:00:00 01.10.2004 setzen

   stWatch.ucSec = 0;

   stWatch.ucMin = 0;

   stWatch.ucHour = 14;

   stWatch.ucDay = 1;

   stWatch.ucMonth = OCT;  // JAN == 0!!

   stWatch.uiYear = 104;   // 2004 - 1970

 

   v_SetTime(&stWatch);    // Zeit umrechnen

 

   v_InitRTC();  // RTC initialisieren

   EA = 1;       // Allgemeine Interruptsperre aufheben  

 

   while(1);

}

Listing 89  Inhalt von TestRTCLib.c

Erweitern sie das Projekt so, dass die Uhrzeit auf dem LCD ausgegeben wird. Für die Aus­gabe auf der LCD muss noch das Sourcemodul „LCDLib_4bitPort.c“ zum Pro­jekt hinzugefügt werden. Damit nicht ständig das LCD neu beschrieben wird, findet eine Überprüfung der Sekunden statt. In der Variablen ucOldSec (siehe Listing 90, ) wird zuerst ein Wert außerhalb des gültigen Sekundenbereichs, in diesem Beispiel 0xFF,  ein­getragen. Wird jetzt das erste Mal die Zeit über die Funktion „v_GetTime()“ geholt (sie­he k), ist die Überprüfung zwischen den aktuellen Sekunden und dem abgespeicher­tem Wert „wahr“. Somit wird der Programmteil innerhalb der if-Abfrage abgearbeitet. Als erstes werden die aktuellen Sekunden in der Variablen ucOldSec abgespeichert (siehe m). Danach erfolgt die Ausgabe auf der LCD mit Hilfe der Funktion „printf()“ (siehe n).

F !! Der cast auf (int) (siehe n) wird bei Variablen vom Datentyp char in Zusam­men­hang mit printf() benötigt. Andernfalls arbeitet printf() nicht korrekt. !!

Projektname

Verzeichnis

Verwendete Sourcemodule

RTCLib

Test_RTCLib

Test_RTCLib.c

 START900.a51

 

..\Library

RTCLib.c, LCDLib_4bitPort.c

Tabelle 75  Projekt RTCLib erweitert um LCD-Ausgabe

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

k

l

 

m

 

 

n

#include <REG932.H>

#include <RTCLib.h>

#include <lcd_def.h>

#include <stdio.h>

 

void main(void)

{

   unsigned char ucOldSec = 0xFF;

   struct stWATCH stWatch;

 

   // Zeit auf 14:00:00 01.10.2004 setzen

   stWatch.ucSec = 0;

   stWatch.ucMin = 0;

   stWatch.ucHour = 14;

   stWatch.ucDay = 1;

   stWatch.ucMonth = OCT;

   stWatch.uiYear = 104; // 2004 - 1970

 

   v_SetTime(&stWatch);   // Zeit umrechnen

   IP0 = 0x40;

   v_InitRTC();  // RTC initialisieren

   EA = 1;       // Allgemeine Interruptsperre aufheben  

  

  // Initialisierung des Port 2 (LCD) auf Bidirektional

  P2M1 = 0;

  P2M2 = 0;

 

  // Initialisierung des HD44780 Controllers

  uc_LCDIni(_2LINE | _7DOTS);

 

   while(1)

   {

     v_GetTime(&stWatch);

     if (stWatch.ucSec != ucOldSec)

     {

        ucOldSec = stWatch.ucSec;

        // Loeschen des HD44780 Speichers.

        uc_Clrscr();

        printf("%.2d:%.2d:%.2d", (int)stWatch.ucHour,

               (int)stWatch.ucMin, (int)stWatch.ucSec);

     }

   }

   while(1);

}

Listing 90  Inhalt von TestRTCLib.c erweitert um LCD-Ausgabe

Auswertung eines Alarms bzw. Schaltzeit

Meist wird der RTC nicht nur zur Anzeige der aktuellen Uhrzeit verwendet, er wertet vielmehr gespeicherte Zeiten aus und gibt bei einer Übereinstimmung mit der aktuellen Uhrzeit ein Schaltsignal aus. Dieses kann nun einen Alarm auslösen oder z. B. ein Relais schalten. Dieses Schaltsignal kann z. B. zu einem bestimmten Tag zu einer bestimmten Uhrzeit kommen, täglich um 8 Uhr und um 22 Uhr, jeden Sonntag um 12 Uhr usw.

Erstellen sie das Projekt aus Tabelle 76, das es , eine Alarmzeit zu spei­chern und bei einer Übereinstimmung mit der aktuellen Uhrzeit ein Schaltsignal ausgibt.

Die Ver­wal­tung der Alarm-Zeit und die Speicherung des Ereignisses wird mit Hilfe der Struk­tur „stRTC_ALARM“ durchgeführt (siehe Listing 91). Die Alarmzeit wird im Element „ulTime“ gespeichert (siehe Listing 91, ). Die Information, ob es sich um ein einmaliges Ereignis oder um ein wiederkehrendes Er­ei­gnis handelt, wird im Element „ucDayAndActiv“ gespeichert (siehe k). Für die wiederkehrenden Ereignisse und die Ein­stellung, ob beim Alarm der Portpin gesetzt oder gelöscht werden soll, ist der Auf­zäh­lungs­typ „enALARMSET“ vorhanden (siehe l).

 

 

 

 

 

k

 

 

l

// Definitionen

...

 

struct stRTC_ALARM

{

  unsigned long ulTime;

  unsigned char ucDayAndActiv;

};

         

enum enALARMSET { SIGNAL_OFF=0x00, SIGNAL_ON=0x01,

           MONDAY=0x02, TUESDAY=0x04, WEDNESDAY=0x08,

           THURSDAY=0x10,FRIDAY=0x20, SATURDAY=0x40, SUNDAY=0x80};

Listing 91  Erweiterung in RTCLib.h

Im Sourcemodul RTCLIB.c wird die Struktur „stAlarm“ definiert (siehe Listing 92, ). Mit der Funktion „b_ SetAlarmTime()“ (siehe k) wird die Struktur „stWATCH“ in ei­nen 32-bit Wert gewandelt und dem Element „ulTime“ zugewiesen (siehe l). Der Übergabeparameter „ucDayInfo“ wird direkt dem Element „ucDayAndActiv“ zuge­wie­sen (siehe m).

 

 

k

 

 

 

 

 

 

 

 

 

 

l

m

...

struct stRTC_ALARM stAlarm;

...

bit b_SetAlarmTime(struct stWATCH *pWatch,

                   unsigned char ucDayInfo)

{

   unsigned long iday;

   iday = 365 * (pWatch->uiYear - 70) +

          DaysToMonth[pWatch->ucMonth] + (pWatch->ucDay - 1);

   iday = iday + (pWatch->uiYear - 69) / 4;

   if ((pWatch->ucMonth > 1) && ((pWatch->uiYear % 4) == 0))

      iday++;

   iday = pWatch->ucSec + 60 * pWatch->ucMin +

          3600 * (pWatch->ucHour + 24 * iday);

   stAlarm.ulTime = iday;

   stAlarm.ucDayAndActiv = ucDayInfo;

   return 1;

}

Listing 92  Funktion b_ SetAlarmTime()

Die Auswertung der Alarmzeit erfolgt in der ISR des RTC. Ist in der Struktur „stAlarm“ eine Zeit eingetragen (siehe Listing 93, ), wird überprüft, ob es sich um einen täglichen Alarm handelt. Die Überprüfung findet in der switch-case Anweisung statt (siehe k). Ist dies der Fall, wird die Variable „btDayFound“ auf „1“ gesetzt (siehe l). Ist „btDayFound“ gesetzt, wird nun noch überprüft, ob die aktuelle Zeit mit dem der Alarmzeit übereinstimmt (siehe m). Stimmt die Alarmzeit mit der aktuellen Zeit überein, wird die globale Variable „btAlarm“ gesetzt bzw. gelöscht (siehe n).

Handelt es sich um einen einmaligen Alarm, wird nur der Alarmwert „stAlarm.ulTime“ mit dem aktuellen Wert „ulRTCTime” verglichen (siehe ). Sind die beiden Werte gleich, liegt ein Alarm vor. Die globale Variable „btAlarm“ wird gesetzt bzw. gelöscht (siehe ).

 

 

 

 

 

 

 

 

 

 

k

 

 

l

 

l

 

l

 

l

 

l

 

l

 

l

 

m

 

 

 

 

 

 

n

 

 

 

 

void v_RTC_ISR(void) interrupt 10 using 1

{  

   RTCCON = 0x43;   // ISR-Flag loeschen

   ulRTCTime++; 

   if (stAlarm.ulTime != 0) // Alarmzeit eingetragen

   {

     if (stAlarm.ucDayAndActiv & 0xFE) // taeglicher ALARM

     {

       struct stWATCH stActTime;

       bit btDayFound = 0;

       v_GetTime(&stActTime);

       switch(stActTime.ucWeekday)

       {

       case 0: if (stAlarm.ucDayAndActiv & SUNDAY)

                 btDayFound = 1; break; 

       case 1: if (stAlarm.ucDayAndActiv & MONDAY)

                 btDayFound = 1; break; 

       case 2: if (stAlarm.ucDayAndActiv & TUESDAY)

                 btDayFound = 1; break; 

       case 3: if (stAlarm.ucDayAndActiv & WEDNESDAY)

                 btDayFound = 1; break; 

       case 4: if (stAlarm.ucDayAndActiv & THURSDAY)

                 btDayFound = 1; break; 

       case 5: if (stAlarm.ucDayAndActiv & FRIDAY)

                 btDayFound = 1; break; 

       case 6: if (stAlarm.ucDayAndActiv & SATURDAY)

                 btDayFound = 1; break; 

       }

       if (btDayFound == 1)

       {

          struct stWATCH stAlarmTime;

          v_GetTime(&stAlarmTime);

          if (stActTime.ucSec  == stAlarmTime.ucSec &&

              stActTime.ucMin  == stAlarmTime.ucMin &&

              stActTime.ucHour == stAlarmTime.ucHour)

              btAlarm = stAlarm.ucDayAndActiv & 0x01;

       } 

     }

     else if (stAlarm.ulTime == ulRTCTime) // einmaliger Alarm

        btAlarm = stAlarm.ucDayAndActiv & 0x01;

   }

}

Listing 93  ISR v_RTC_ISR()

Für das Setzen der Alarmzeit sowie der aktuellen Zeit wird die Struktur stWatch benö­tigt (siehe Listing 94, ). Zuerst wird die Alarmzeit gesetzt (siehe k), danach die aktuelle Zeit (siehe l). Erst dann wird der RTC gestartet. In der ersten while(1)-Schleife (siehe m) wird nun solange gewartet, bis „btAlarm“ auf „1“ gesetzt wird.

 

 

 

 

 

 

 

 

 

 

 

 

 

k

 

 

 

 

 

 

 

 

l

 

 

 

 

m

#include <REG932.H>

#include <RTCLib.h>

 

void main(void)

{

   struct stWATCH stWatch;

 

   // Alarmzeit setzen  14:01:12 01.10.2004 setzen

   stWatch.ucSec = 12;

   stWatch.ucMin = 0;

   stWatch.ucHour = 14;

   stWatch.ucDay = 1;

   stWatch.ucMonth = OCT;

   stWatch.uiYear = 104; // 2004 - 1970

   b_SetAlarmTime(&stWatch, THURSDAY | SIGNAL_ON);

 

   // Zeit auf 14:00:00 01.10.2004 setzen

   stWatch.ucSec = 0;

   stWatch.ucMin = 0;

   stWatch.ucHour = 14;

   stWatch.ucDay = 1;

   stWatch.ucMonth = OCT;

   stWatch.uiYear = 104; // 2004 - 1970

   v_SetTime(&stWatch);   // aktuelle Zeit setzen

   IP0 = 0x40;

   v_InitRTC();  // RTC initalisieren

   EA = 1;       // Allgemeine Interruptsperre aufheben  

 

   while(1)

   {

     if (btAlarm == SIGNAL_ON)

       while(1);

   } 

}

Listing 94  Inhalt von Test_RTCAlarm.c

Erstellen sie jetzt das Projekt aus Tabelle 76.

Projektname

Verzeichnis

Verwendete Sourcemodule

RTCAlarm

Test_RTCAlarm

Test_RTCAlarm.c

 START900.a51

 

..\Library

RTCLib.c

Tabelle 76  Projekt RTCAlarm

Auswerten von mehreren Alarm-/Schaltzeiten

Sollen mehrere Alarmzeiten gespeichert werden, z. B. erste Schaltzeit Montag 6:00, SIGNAL_ON, zweiter Alarm Montag 22:00, SIGNAL_OFF, usw. muss ein Array von Strukturelementen verwendet werden, die diese Alarm-Schaltzeiten aufnimmt (siehe Listing 95, ). In diesem Beispiel können 20 Einträge durchgeführt werden. Zudem wird die Variable „ucAlarmInfo“ (siehe k) benötigt, die das Array verwaltet.

 

 

 

 

 

k

#include <REG932.H>

#include <RTCMultiLib.h>

 

// Definitionen

...

struct stRTC_ALARM idata stAlarm[20];

unsigned char ucAlarmInfo =0;

...

Listing 95  Auszug aus RTCMultiLib.c

In der Funktion b_SetAlarmTime() ist jetzt eine Überprüfung vorhanden, ob noch ein Alarm­eintrag zulässig ist (siehe Listing 96, ). Wenn kein weiterer Eintrag mehr auf­ge­nom­men werden kann, liefert die Funktion den Wert „0“ zurück. Die Berechnung der mö­glichen Einträge wird mit Hilfe des sizeof()-Operators durchgeführt. Eine Be­schrei­bung zur Funktions­wei­se dieses Operators finden sie in Kapitel 11.9 [1]. Nach der Um­rech­nung wird der Wert in das Array geschrieben (siehe k) und die Variable „ucAlarmInfo“ um eins erhöht (siehe l).

 

 

 

 

 

 

 

 

 

 

 

 

k

k

l

bit b_SetAlarmTime(struct stWATCH *pWatch,

                    unsigned char ucDayInfo)

{

  unsigned long iday;

  if (ucAlarmInfo > (sizeof(stAlarm)/sizeof(struct stRTC_ALARM)))

      return 0;

  iday = 365 * (pWatch->uiYear - 70) +

         DaysToMonth[pWatch->ucMonth] + (pWatch->ucDay - 1);

  iday = iday + (pWatch->uiYear - 69) / 4;

  if ((pWatch->ucMonth > 1) && ((pWatch->uiYear % 4) == 0))

       iday++;

  iday = pWatch->ucSec + 60 * pWatch->ucMin +

         3600 * (pWatch->ucHour + 24 * iday);

  stAlarm[ucAlarmInfo].ulTime = iday;

  stAlarm[ucAlarmInfo].ucDayAndActiv = ucDayInfo;

  ucAlarmInfo++;

  return 1;

}

Listing 96  Funktion b_SetAlarmTime()

In der ISR des RTCs wird nun zusätzlich mit Hilfe der Variablen „ucOff” überprüft, ob ei­ner der Ein­träge im Array zuschlägt (siehe Listing 97, ). Diese Schleife wird solange durch­lau­fen, bis alle Einträge im Array überprüft wurden.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

void v_RTC_ISR(void) interrupt 10 using 1

{  

   unsigned char ucOff;

   struct stWATCH stActTime;

 

   RTCCON = 0x43;   // ISR-Flag loeschen

   ulRTCTime++; 

 

   v_GetTime(&stActTime);

   for (ucOff = 0; ucOff < ucAlarmInfo; ucOff++)

   {

      if (stAlarm[ucOff].ulTime != 0) // Alarmzeit eingetragen

      {

        if (stAlarm[ucOff].ucDayAndActiv & 0xFE)

        {  // taeglicher ALARM

          bit btDayFound = 0;

          switch(stActTime.ucWeekday)

          {

          case 0: if (stAlarm[ucOff].ucDayAndActiv & SUNDAY)

                    btDayFound = 1; break; 

          case 1: if (stAlarm[ucOff].ucDayAndActiv & MONDAY)

                    btDayFound = 1; break; 

          case 2: if (stAlarm[ucOff].ucDayAndActiv & TUESDAY)

                    btDayFound = 1; break; 

          case 3: if (stAlarm[ucOff].ucDayAndActiv & WEDNESDAY)

                    btDayFound = 1; break; 

          case 4: if (stAlarm[ucOff].ucDayAndActiv & THURSDAY)

                    btDayFound = 1; break; 

          case 5: if (stAlarm[ucOff].ucDayAndActiv & FRIDAY)

                    btDayFound = 1; break; 

          case 6: if (stAlarm[ucOff].ucDayAndActiv & SATURDAY)

                    btDayFound = 1; break; 

          }

          if (btDayFound == 1)

          {

             struct stWATCH stAlarmTime;

             v_GetTime(&stAlarmTime);

             if (stActTime.ucSec  == stAlarmTime.ucSec &&

                 stActTime.ucMin  == stAlarmTime.ucMin &&

                 stActTime.ucHour == stAlarmTime.ucHour)

             {

                 btAlarm = stAlarm[ucOff].ucDayAndActiv & 0x01;

                 break;

             }    

          }

       } 

     }

     else if (stAlarm[ucOff].ulTime == ulRTCTime)

     {  // einmaliger Alarm

        btAlarm = stAlarm[ucOff].ucDayAndActiv & 0x01;

        break;

     }

   }

}

Listing 97  ISR des RTCs

Erstellen sie das Projekt aus Tabelle 77 und geben sie zwei Alarmzeiten ein..

Projektname

Verzeichnis

Verwendete Sourcemodule

RTCMultiAlarm

Test_RTCMultiAlarm

Test_RTCMultiAlarm.c

 START900.a51

 

..\Library

RTCMultiLib.c

Tabelle 77  Projekt RTCMultiAlarm