|
|
Programme C |
|
|
|
dESµ DCF 77 Funkuhrempfänger |
|
|
|
|
|
|
|
|
|
|
|
am PIO-Pin des IPC@CHIP |
|
|
|
|
|
|
|
Dieses Programm ermöglicht es, die DCF77-Funkuhr an einem PIO-Pin der IPC@CHIP WebServer zu betreiben. In dem Einstellungsteil des Quellcodes kann angegeben werden, welcher PIO-Pin verwendet werden soll. |
|
|
|
|
|
/* dcf77.cpp
--------- Version 22.04.2004
Borland IDE C++ 3.1 oder 5.02
www.GoBlack.de, D.Schwarzer
-------------------------------------------
Ziel-Hardware: IPC@CHIP WebServer bei dem
das dESµ DCF77 Funkuhrmodell
an einem PIO-Eingang ange-
schlossen ist
voreingestellt: PIO 13, DCF-highaktiv
-------------------------------------------*/
// Bevor dieses Programm compiliert wird, sollten die nach-
// folgenden Werte entsprechend gesetzt werden.
// Die Vorgabewerte beziehen sich auf IPC@CHIP WebServer,
// bei denen das dESµ DCF77-Modell an einem PIO-Eingänge
// angeschlossen ist.
// 1. Wann werden das 100ms bzw. das 200ms Signal der
// DCF-Uhr aktiv, bei log 0 oder bei log 1? Geben Sie
// den Wert mit 0 oder 1 hinter der #define-Variablen
// an.
#define aktiv 1
// 2. Zwischen welchen beiden Toleranztwerten soll das
// Programm ein 100ms bzw. ein 200ms -Signal akzep-
// tieren, ohne einen Fehler zu melden? Die Werte sind
// abhängig von der Empfangsqualität des DCF-Signals
#define min100 50
#define max100 120
#define min200 150
#define max200 220
// 3. Welcher Wert soll zur Erkennung für das 2000ms
// Minutensignal ausreichen? Über den Maximalwert
// wird erkannt, wenn kein DCF-Signal vorliegt.
#define min2000 1700
#define max2000 2200
// 4. Nach einem Empfangsfehler startet das Programm neu
// um das nächste Minutensignal zu suchen. Wie oft soll
// dies geschehen, bis das Programm abbricht?
// Dieser Abbruch tritt auch dann in Kraft, wenn
// kein DCF-Signal eintrifft.
#define timeout 3
// 5. Welches Torbit 0-7 oder PIO-Bit 0-13 soll für das
// Uhrensignal benutzt werden?
#define torbit 13
// 6. Sollen alle Ausgaben angezeigt werden, oder nur das
// Resultat, nachdem die Zeit ermittelt wurde?
// 0 -nur das Resultat, 1 -alles anzeigen
#define verbose 1
// --------------------------------------------------------
// Die nachfolgenden Setzungen betreffen das vorliegende
// Programm
#pragma option -1 // 80186 Code erzeugen
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
// globale Variablen
// -----------------
union REGS inregs; // ..C-SoftInterrupt-Funktion
union REGS outregs;
struct SREGS segregs;
// ..DCF-Zeitinformationen
unsigned int ptbword=0; // 15Bit PTB-Meldungen
unsigned char flaggen=0; // 5Bit Ank.Schaltsek,
// Zone2, Zone1,
// Ank. MEZ/MESZ,
// Antenne
char* wotag[]= {"So","Mo","Di","Mi","Do","Fr","Sa","So"};
unsigned char sekunde=0;
unsigned char minute =0;
unsigned char stunde =0;
unsigned char tag =0;
unsigned char tagnr =0;
unsigned char monat =0;
unsigned int jahrt =2000;
unsigned int jahr =0;
// ..Benutzer SoftINT-Handler
volatile unsigned ms; // ms -Zähler
unsigned old_es; // Adr. alter Handler HIGH
unsigned old_bx; // Adr. alter Handler LOW
// ========================================================
// Funktionen der Programmausführung bei IPC-WebServern
// ----------------------------------------------------
// setStdio()
// setzt den Fokus auf das Standard Ein-/Ausgabegerät.
// 2- Applikationsprogramm, 3- Betriebssystem
void setStdio (char kanal)
{
asm{ mov ah,11h
mov al,kanal
int 0A0h }
}
// api_sleep()
// unterbricht die Ausführung des Programms für eine in
// Millisekunden angegebene Zeit. Parallel arbeitende
// Programm des Multitasksystems werden hiervon nicht
// betroffen, sondern erhalten die Arbeitsleistung der
// CPU. Diese Funktion sollte zwingend anstelle der
// C-Funktion delay() benutzt werden. Letztere behindert
// das Betriebssystem des SC12.
void api_sleep(int ms)
{
asm{ mov ah, 00h
mov bx, ms
int 0ADh }
}
// ========================================================
// Funktion des DCF-Eingangstores
// ------------------------------
// Der DCF77-Empfänger muss an einem Eingang des IPC@CHIP
// WebServers angeschlossen werden. Je nach verwendetem
// Eingang müssen die nachfolgenden zwei Funktionen ange-
// passt werden. Diese beiden Funktionen werden von main()
// erwartet.
// 1. initTor() mit dieser Funktion wird das verwendete
// Eingangstor oder Torbit initialisiert
// 2. leseTor() diese Funktion liest das Bit oder das Tor
// und gibt das DCF-Bit als niedrigstes Bit einer 16Bit-
// Variablen zurück, deren weitere Bits auf 0 gesetzt
// sind.
// Die vorliegenden Funktionen können mit einem der PIO-Bits
// des IPC@CHIP verwendet werden.
// --------------------------------------------------------
// initTor()
// setzt eines der PIO-Bit der IPC-WebServer als Eingang
// ohne Pull-Up oder Pulldown-Widerstand. Benutzt wird das
// PIO-Bit, das in 'Torbit' angegeben ist.
void initTor (void)
{
asm{ mov dx, 01h
mov cl, torbit
shl dx, cl // als PIO ausgewählt
mov ah, 82h // SoftINT A2 Fkt 80
mov al, 01h // Eingangstor ohne alles
int 0A2h }
}
// leseTor()
// Liest die Zustände an allen PIO Eingängen der IPC-Web-
// Server isoliert das DCF-Bit und gibt es als 0.Bit zu-
// rück.
unsigned leseTor (void)
{
asm{ mov ah, 82h // SoftINT A1 Fkt 80
mov bx, 0FFFFh // wAND
mov cx, 0000h // wXOR
int 0A1h // PIO-Werte holen
mov cl, torbit
shr ax, cl // DCF-Uhrbit, 0.Bit
and ax, 0001h } // .. HighBits gelöscht
return(_AX);
}
// ========================================================
// Funktionen der DOS-Uhr
// ----------------------
// Nachdem die DCF-Zeit ermittelt wurde können mit ihr die
// DOS-Uhr und / oder die Echtzeituhr gesetzt werden. Die
// hier vorliegenden Funktionen setzten die DOS-Uhr.
// setzeZeit()
// nachdem die Zeit der Funkuhr empfangen wurde, übertragen
// sie diese beiden Funktionen in den Zeitspeicher des SC12
// Betriebsystems. Hier wird die Zeit auf Quarzgenauigkeit
// fortgeschrieben (bis zu einem erneuten Empfang).
void setzeZeit (void)
{
asm{ mov ah, 2Dh // SoftINT 21 Fkt 2D
mov ch, stunde
mov cl, minute
mov dh, sekunde
int 21h }
}
void setzeDatum (void)
{
asm{ mov ah, 2Bh // SoftINT 21 Fkt 2B
mov cx, jahr
mov dl, tag
mov dh, monat
int 21h }
}
// ========================================================
// Funktionen der DCF77-Funkuhr
// ----------------------------
// 1. Funktionen des Timers
// ------------------------
// Um die logischen Signale der Funkuhr zu ermitteln wird
// ein Timer der IPC@CHIP WebServer benutzt. Dessen Zähler
// wird beim Erkennen eines Signalwechsels an der DCF-Uhr
// auf Null gesetzt. Der Timer schreibt ihn dann im Milli-
// sekundentakt fort. Wird der Zähler bei Eintritt eines
// weiteren Signalwechsels gelesen, gibt er die Zeit dieses
// Intervalls an. Mit dieser Zeit kann erkannt werden, ob
// es sich um ein 0-Intervall (100ms), 1-Intervall (200ms),
// oder ein Minutensignal (1800-1900ms) handelte.
// interrupt handler()
// die Funktion wird vom abgelaufenen Timer jede ms
// aufgerufen und und erhöht die Variable ms. Aus dem
// eigenen Programm kann die Variable ms auf einen Anfangs-
// wert, normal 0, gesetzt oder gelesen werden.
void interrupt handler (void)
{
ms++;
}
// init_msCount()
// installiert den InterruptHandler, fragt die System-
// frequenz für den Timer ab und initialisiert einen
// der Timer 0 od.1
void init_msCount (int timer)
{
// Interrupt-Handler für benutzten Timer setzen
inregs.h.ah = 0x84; // SoftINT A1 Fkt 84
inregs.x.cx = 1; // Anzahl new INT
inregs.x.dx = 8+timer; // Timer 0 od.1
segregs.es = FP_SEG(handler); // Handler-adr.high
inregs.x.bx = FP_OFF(handler); // Handler-adr.low
int86x(0xA1, &inregs, &outregs, &segregs);
old_es = segregs.es; // alte Adresse high
old_bx = outregs.x.bx; // alte Adresse low
// Timertakt des Systems zur Berechnung der Wertes 1ms
unsigned long tfreq;
inregs.h.ah = 0x8A; // SoftINT A1 Fkt 8A
inregs.h.al = 1; // Timerfrequenz
int86x(0xA1, &inregs, &outregs, &segregs);
tfreq= outregs.x.dx; // wegen 32Bit
tfreq=((tfreq<<16)+outregs.x.ax)/500;
// benutzten Timer voreinstellen
inregs.h.ah = 0x85; // SoftINT A1 Fkt 85
inregs.h.al = timer; // Timer 0 od. 1
inregs.x.dx = 0x0003; // DauerLauf, TimerINT
inregs.x.cx = (unsigned)tfreq; // Abbruchwert
int86x(0xA1, &inregs, &outregs, &segregs);
}
// oldHandler()
// setzt am Ende des Programms die Adressen des
// ursprünglichen SoftINT-Handlers wieder ein.
void oldHandler(int timer)
{
inregs.h.ah = 0x84; // SoftINT A1 Fkt 84
inregs.x.dx = 8+timer; // Timer 0 od.1
segregs.es = old_es; // Handler-adr.high
inregs.x.bx = old_bx; // Handler-adr.low
int86x(0xA1, &inregs, &outregs, &segregs);
}
// starttimer(), stoptimer()
// startet den benutzten timer, mit den gesetzten Werten.
// löscht zuvor den ms-Zähler.
void starttimer(int timer)
{
ms=0; // ms-Zähler auf 0
asm { mov ax, timer
mov ah, 86h // SoftINT A1 Fkt 86
int 0A1h }
}
void stoptimer(int timer)
{
asm{ mov ax timer
mov ah, 87h // SoftINT A1 Fkt 87
int 0A1h }
}
// 2. Funktionen der DCF-Signalerkennung
// -------------------------------------
// Die Zeitintervalle des Zählers werden durch die Funktion
// getDFC() in logische Zustände gewandelt. Die Funktion
// decodeDCF() sammelt die Zustände nach ihrer, dem DCF-
// Protokoll entsprechenden Bedeutung und setzt daraus die
// BCD-codierten Zeitinformationen zusammen. Über die
// Funktion BCDtoHEX() können diese in die hexadezimale
// Codierung gewandelt werden.
// BCDtoHEX()
// die DCF-Zeitangaben werden im BCD-Format übermittelt
// DOS-Uhren benutzen das HEX-Format. Die Funktion erzeugt
// aus einer maximal 4-stelligen gepackten BDC-Zahl
// eine Hexzahl.
unsigned BCDtoHEX(unsigned bcd)
{
bcd= ((bcd&0xF000)>>12)*1000+
((bcd&0x0F00)>> 8)* 100+
((bcd&0x00F0)>> 4)* 10+ (bcd&0x000F);
return(bcd);
}
// getDCF()
// Die Funktion stellt fest, ob es sich bei dem übergebenen
// Wert in ms um ein 0-Signal (100ms) oder um ein 1-Signal
// (200ms)des DCF-Empfängers handelt. Zudem wird das of-
// Signal überprüft. Es darf nur in der 58.Sekunde einen
// Wert >1000 besitzen (Minutensignal). Werte außerhalb der
// angegebenen Toleranzbereiche sprechen für ein gestörtes
// Signal. In diesem Fall wird -1 zurückgegeben.
int getDCF (unsigned on, unsigned of, unsigned s)
{
if (of >1000 && s!=58) return(-1);
if (on >= min100 && on <= max100) return(0);
if (on >= min200 && on <= max200) return(1);
return (-1);
}
// decodeDCF()
// die Funktion stellt die empfangenen DCF-Bits zu den
// enthaltenen Informationspaketen Min, Std, Tag,
// Monat, Jahr, usw .. zusammen und kontrolliert deren
// Prüfsummen.
// Rückgaben: 0 - alles ok
// -1 - übergebenes Bit war weder 0 noch 1
// -2 - Sekunde 20 Zeitstartinformation falsch
// -3 - Prüfsumme Minuten falsch
// -4 - Prüfsumme Stunden falsch
// -5 - Prüfsumme Datum falsch
// -6 - Sekunde größer als 58
int decodeDCF (int s, int bit)
{
static unsigned pbit=0;
if (bit !=1 && bit !=0) return(-1);
pbit = pbit^bit; // Prüfsumme berechnen
// PTB-Wort und Flaggen
if (s>= 0 && s<=14) ptbword = (ptbword>>1)+(bit<<14);
else if (s>=15 && s<=19) flaggen = (flaggen>>1)+(bit<<4);
// Bit 20: Start Zeitinfo, immer 1
else if (s==20){ if(!bit) return(-2);
pbit = 0; }
else if (s>=21 && s<=27) minute = (minute>>1)+(bit<<6);
else if (s==28) {if(pbit) return(-3);}
else if (s>=29 && s<=34) stunde = (stunde>>1)+(bit<<5);
else if (s==35) {if(pbit) return(-4);}
else if (s>=36 && s<=41) tag = (tag>>1)+(bit<<5);
else if (s>=42 && s<=44) tagnr = (tagnr>>1)+(bit<<2);
else if (s>=45 && s<=49) monat = (monat>>1)+(bit<<4);
else if (s>=50 && s<=57) jahr = (jahr>>1)+(bit<<7);
else if (s==58){
if(pbit) return(-5);
minute = BCDtoHEX(minute);
stunde = BCDtoHEX(stunde);
tag = BCDtoHEX(tag);
monat = BCDtoHEX(monat);
jahr = BCDtoHEX(jahr)+jahrt;
}
else return(-6); // Sek > 58
return( 0); // alles i.o.
}
// printDCF()
// gibt die zusammengestellten DCF-Zeitinformationen zum
// Bildschirm aus.
void printDCF(void)
{
char* R =" R : es sendet die ";
char* A1 =" A1: Wechsel MEZ/MESZ - ";
char* Z1 =" Z1: Zeitzone - ";
char* Z2 =" Z2: Zeitzone - ";
char* A2 =" A2: Schaltsekunde - ";
char* txt ="zur vollen Stunde";
char* nein ="nein";
char* mez ="MEZ";
char* mesz ="MESZ";
int maske=0x4000;
printf("\n\n PTB-Code: ");
while (maske){
if(ptbword&maske) printf("1");
else printf("0");
maske = maske>>1;
}
if(flaggen & 0x01) printf("\n%sReserveantenne",R);
else printf("\n%sBetriebsantenne",R);
if(flaggen & 0x02) printf("\n%s%s",A1,txt);
else printf("\n%s%s",A1,nein);
if(flaggen & 0x04) printf("\n%s%s",Z1,mesz);
else printf("\n%s%s",Z1,mez);
if(flaggen & 0x08) printf("\n%s%s",Z2,mez);
else printf("\n%s%s",Z2,mesz);
if(flaggen & 0x10) printf("\n%s%s",A2,txt);
else printf("\n%s%s\n",A2,nein);
printf("\n Datum: %s %0.2d:%0.2d:%0.4d",
wotag[tagnr], tag, monat, jahr);
printf("\n Zeit: %0.2d:%0.2d:%0.2d",
stunde, minute, sekunde);
}
// ========================================================
// Funktion main()
// ---------------
// Hier wird durch Polling alle 10ms das Torbit des
// Funkuhrempfängers abgefragt, während gleichzeitig ein
// Timer die ablaufende Zeit in Millisekunden misst.
// Nachdem die jeweiligen Eingangssignalgrenzen fest-
// gestellt sind, wird anhand des Timerwertes überprüft,
// ob die Signalbreiten den geforderten Zeitlängen von 100
// bzw 200ms +/- Toleranz entsprechen.
// Ist kein Empfänger angeschlossen, oder laufen die Sig-
// nale aus den Toleranzgrenzen, weil ein schlechter
// Empfang vorliegt, bricht das Programm nach timeout
// Versuchen die Minutenmarke zu finden, ab.
// Konnte ein vollständiges DCF-Zeitprotokoll empfangen
// werden, so werden dessen Zeit und Datum in die DOS-Uhr
// übertragen, sowie das Protokoll angezeigt.
void main (void)
{
const char timer = 1; // benutzter Timer
char *pgmname ="\r\n[dcf77] ";
char *line ="\r\n ---------------------------------";
char *start ="gestartet .. ";
char val ='o'; // Bereitschaftszeichen
int verb =verbose; // Flagge: alles anzeigen =1
int akt =aktiv; // dcf77: aktiv bei 0 od.1
int to =timeout; // Anzahl der Versuche
int bit =0; // aktuelles Bit von dcf77
int sync =0; // nach Minutenmarke =1
int s =0; // Sekundenzähler nach sync
int err =0; // Fehlerart des Signals
unsigned torbyte =0; // Wert des EingabeTores
unsigned on =0; // ms des Aktiv-Signals
unsigned of =0; // ms des Inaktiv-Signals
// Programmvorbereitung
printf("%s",pgmname);
initTor(); // EingabeTor initialisieren
setStdio(2); // Terminalfokus auf Programm
init_msCount(timer); // ms-Zähler init
if(verb) printf("%s",line); else printf("%s",start);
// Programmstart
sync = bit = s = 0;
starttimer(timer);
while(!(sync==1 && s>=59)){
on = of = err = 0;
// Die beiden nachfolgenden Schleifen werden nach
// max2000 ms abgebrochen, wenn kein Signal anliegt.
// dcf77 -Aktivsignal abwarten
while((torbyte^(!akt))== 1 && ms<max2000){
torbyte=leseTor();
api_sleep(10);
}
on=ms; ms=0;
// dcf77 -Inaktivsignal abwarten
while((torbyte^(!akt))== 0 && ms<max2000){
torbyte=leseTor();
api_sleep(10);
}
of=ms; ms=0;
if(sync){
bit= getDCF(on,of,s); // Signalkontrolle
err= decodeDCF(s,bit); // Auswertung
if(err){sync=0; to--;
if(!to){err=1; break;} // Timeout
}
s++;
}
else if(of>min2000 && of<max2000){ // Minutenmarke ??
s=0; sync=1; // sync setzen
if(verb) printf("\n Minutenbeginn off: %u\n", of);
continue;
}
// Ausgabe wenn Verbose = 1
if(verb){
if(!sync)printf ("\n -- ");
else printf ("\n %0.2u ", s);
printf ("on: %0.4u ms | off: %0.4u ms |summe: %u ms",
on, of, on+of);
if(err) printf(" Fehler: %d",err);
}
// Ausgabe wenn Verbose = 0
else {
if(!sync){val^=0x20; printf("\b%c",val);}
else {printf("\b\b%0.2u",s);}
}
}
// Abschluss des Programms
stoptimer(timer); // Timer anhalten
oldHandler(timer); // init alten IRQ
if(!err){
setzeZeit(); // DOS-Uhr Zeit setzen
setzeDatum(); // DOS-Uhr Datum setzen
printf("%s",line); // Linie
printDCF(); // Zeitausgabe
printf("%s",line); // Linie
}
else printf("Abbruch wegen Timeout\r\n");
printf("%sEnde\r\n",pgmname); // Endemeldung
setStdio(3); // Fokus Betriebssystem
}
|
.de