Programme C


dESµ Modelle              

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
}
www..de