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 durchgefü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.
Damit der Simulator mit dem RC-Oscillator und der RTC mit dem externen Quarz arbeitet, muss die START900.a51 im Projekt verwendet werden. Dieses Assemblermodul enthält das Konfigurationsbyte UCFG1, mit dessen Einstellung die Auswahl für den verwendeten 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 Taktbeschaltung“ [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
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 eingelötet werden. Die beiden Kondensatoren sind schon bestückt (Position C8, C9). Zudem müssen die beiden Jumper auf die Position „ext. XTAL“ gesteckt werden.
Bei Verwendung der Quarzfrequenz von 32,769 kHz werden 256 Zählimpulse pro Sekunde am Down-Counter des RTC erzeugt (32768 Hz/128). Somit wird bei jedem Zählschritt 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 Verwaltung dieser Informationen erfolgt in der Struktur stWatch (siehe Listing 77, ). Die Daten 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 Funktion „v_InitRTC()“ erfolgen. Die externe Frequenz für den RTC wird in der Definition „RTC_FREQ“ angegeben (siehe Listing 78, ). In der Definition „RTC_RELOAD“ wird der Reloadwert für die SFR RTCL und RTCH berechnet. 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 Definition 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 überprü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 Minuten 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 speichern. Die Variable dieses Datentyps kann 4294967295 Sekunden aufnehmen, bis ein Überlauf stattfindet. 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 (siehe l) und Stunden (siehe m) und weist das Ergebnis dem jeweiligen Element der Struktur zu. Als Übergabeparameter wird ein Pointer auf die Struktur stWATCH verwendet. Das Ergebnis der Berechnung wird in dieser Struktur abgespeichert.
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 entwickelt, die nur ein bestimmtes Zeitfenster abdeckt. Es beginnt beim Referenzdatum 01.01.1970 (Unix-Timestamp) und endet am 19.01.2038 (ausgetestet).
F !! Dieser Bereich ist vollkommend ausreichend, da die Hardware meist nur für 10 Jahre ausgelegt 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“ hinzugezählt werden (der 01.01.1970 war ein Donnerstag). Der Gesamtwert wird dann Modulo 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) erhöht werden (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 Schaltjahrtage 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 enthalten.
|
|
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 Funktion „ul_SetTime()“ verwendet (siehe Listing 86, ). Die Funktion verwendet die Angaben 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 liegen. Auf die Variable ulRTCTime soll nur noch innerhalb des C-Moduls RTCLIB.c zugegriffen werden dürfen. Die Variable wird in diesem Fall mit dem Gültigkeitsbereich „static“ versehen (siehe Listing 87, ). Die Funktionsweise von static können sie dem Kapitel 8.3 „Variablen [3] nachlesen. Das Auslesen 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 Ausgabe auf der LCD muss noch das Sourcemodul „LCDLib_4bitPort.c“ zum Projekt 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, eingetragen. Wird jetzt das erste Mal die Zeit über die Funktion „v_GetTime()“ geholt (siehe k), ist die Überprüfung zwischen den aktuellen Sekunden und dem abgespeichertem 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 Zusammenhang 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
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 speichern und bei einer Übereinstimmung mit der aktuellen Uhrzeit ein Schaltsignal ausgibt.
Die Verwaltung der Alarm-Zeit und die Speicherung des Ereignisses wird mit Hilfe der Struktur „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 Ereignis handelt, wird im Element „ucDayAndActiv“ gespeichert (siehe k). Für die wiederkehrenden Ereignisse und die Einstellung, ob beim Alarm der Portpin gesetzt oder gelöscht werden soll, ist der Aufzählungstyp „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 einen 32-bit Wert gewandelt und dem Element „ulTime“ zugewiesen (siehe l). Der Übergabeparameter „ucDayInfo“ wird direkt dem Element „ucDayAndActiv“ zugewiesen (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
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 Alarmeintrag zulässig ist (siehe Listing 96, ). Wenn kein weiterer Eintrag mehr aufgenommen 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 Beschreibung zur Funktionsweise dieses Operators finden sie in Kapitel 11.9 [1]. Nach der Umrechnung 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 einer der Einträge im Array zuschlägt (siehe Listing 97, ). Diese Schleife wird solange durchlaufen, 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