Source file: /~heha/basteln/PC/FunkUsb/FunkUsb.zip/FunkUsb/ve/DCF77.cpp

#include <string.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>

#include "FunkUsb.h"

Clocktime n;
Frame frame;

void Clocktime::IncSec() {
 if (++sec<60) return;
 sec=0;
 IncMin();
}
void Clocktime::IncMin() {
 if (++min<60) return;
 min=0;
 if (++hour<24) return;
 hour=0;
 IncDate();
}
void Clocktime::IncDate() {
 if (++wday>=7) wday=0;
 if (++day<=tage(month+1,year)) return;
 day=1;
 if (++month<12) return;
 month=0;
 if (++year<100) return;
 year=0;	// das wäre dann erst am 1.1.2100
};

// Alle Bits des Bytes xor-verknüpfen, liefert true bei ungerader Parität
// VR: nur W = R25:R24 — leider „sieht“ das der gcc-Optimierer nicht!
static bool parity(byte b) {	// xorbits
 b^= b>>4|b<<4;	// swap	   X X X X   73	62 51	40
 b^= b>>2;	// 2× lsr  X X X X   X	X  5173	4062
 b^= b>>1;	// lsr	   X X X X   X	X  X	40625173
 return bool(b&1);		// X-Bits ausblenden
}

// konvertiert BCD-Zahl in Binärform. Liefert >99 bei Pseudotetraden.
// VR: nur W
static byte bcd(byte b) {
 byte n = b&0x0F;	// Lo-Nibble isolieren
 if (n>=10) b=0xFF;	// Pseudotetrade = Fehler
 b&=0xF0;		// Hi-Nibble isolieren
 asm("lsr %0 $ add %1,%0 $ lsr %0 $ lsr %0 $ add %0,%1":"+r"(b):"r"(n));
 return b;		// Multiplikation ×10 nicht mit Laufzeitbibliothek
}

/***********************************
 * Taktfrequenz der CPU umschalten *
 ***********************************/
void CpuSlow() {
 if (CLKPR&8) return;	// Ist schon langsam
 cli();
 CLKPR=0x80;
 CLKPR=0x08;		// /256, 64 kHz Taktfrequenz, reicht zum Zeit messen
 TCNT0=GPIOR1;		// Nachteiler herausnehmen
 sei();			// TODO: ADU
}

void CpuFast() {
 if (!CLKPR&8) return;	// Schon schnell
 cli();
 CLKPR=0x80;
 CLKPR=0x00;		// 16,5 MHz, für USB und rechenintensive Operationen
 GPIOR1=TCNT0;		// Nachteiler einschieben
 sei();			// TODO: ADU
}

static void Vergleich() {
 char m = timeReport.t.min;
 char h = timeReport.t.hour;
 byte d = timeReport.t.ZoneMJD();
 if (++m>=60) {
  m=0; if (++h>=24) d++;
 }
 statusReport.someBits&=~4;
 if (m==n.min && h==n.hour && d==n.ZoneMJD()) statusReport.someBits|=4;
}

static WORD limits[3];
static unsigned long long *pData, *pOkay;

static void ToggleBit(void*mem,byte bitno) {
 byte *m = reinterpret_cast<byte*>(mem);
 m[bitno>>3] ^= 1<<(bitno&7);
}

void dcf77Poll() {

 if (GPIOR0&0x08) {
  GPIOR0&=~0x08;
  PRR=0x0A;		// ADC aktivieren
  ADMUX = 0x90 | (ANA_BIT==5?0:ANA_BIT==2?1:ANA_BIT==4?2:3);
  ADCSRA = CLKPR&8 ? 0xD8 : 0xDF;
 }
 if (GPIOR0&0x04) {	// Flanke erkannt, Zeitstempel (8kHz) in T0Capture
  n.IncSec();
#if 1
  WORD diff = T0Capture-lastStart;
  if (joyReport.joyKey) {	// Beginn "ganze" Sekunde
   if (diff > (WORD)(1.5*F_TMR)) {	// Minutenlücke?
    n.sec=0;
    memset(&frame,0,sizeof(frame));
    byte minu = historyReport.nMinu;
    if (minu&0x0F<10) minu++;
    minu+=0x10; if ((minu&0xF0)==0xA0) minu&=0x0F;	// sollte 0x0A ergeben
    historyReport.nMinu=minu;
    pData = &historyReport.Data[minu>>4];
    memset(pData,0xFF,8);
    pOkay = &historyReport.Okay[minu>>4];
    memset(pOkay,0,8);
    Vergleich();
    timeReport.t=n;	// müsste memcpy() kompilieren
   }else{
    if (n.sec<63) n.sec++;
   }
   lastStart = T0Capture;
  }else{
   unsigned hist_index=diff/(F_TMR/100);	// ungefähr
   if (hist_index>=50) hist_index=49;
   if (~historyReport.histo[hist_index]) historyReport.histo[hist_index]++;
   if (limits[0]<=diff && diff<=limits[2]) {
    ToggleBit(pOkay,n.sec);
    if (diff<limits[1]) {
     GPIOR0&=0x04;		// kurzer Impuls
     ToggleBit(pData,n.sec);
    }
   }else{
    ToggleBit(pData,n.sec);
   }
#if 0
	
    if (sync && !frame.recvbit)	{ // wenn neue Zeit empfangen wurde und 1. Bit nach Syncpause (= Start)
     timer0_cnt = 0;		// interner Timer auf Anfang			
					// weitere Prüfung eingebaut
     if (n.hour<24 && n.min<60 && (byte)(n.day-1)<31 && (byte)(n.month-1)<12) {
      if (comp_count == 1){  // empfangene Werte erstmal in Buffer "vgl"
       vgl = n;
       vgl.sec = 0; 		
      }
      if (comp_count==2) { // neu empfangene Werte mit Buffer "vgl" vergleichen
       if (!memcmp(&vgl.min,&n.min,6)) {
        cur = n;
	cur.sec = 0; 		// empfangene Uhrzeit für Anzeige übernehmen
	send_count ++;
	if (send_count == 100) send_count = 1;				
	sig_available = 99;		// Signalgüte hochsetzen
       }
       comp_count = 0;				// neues Spiel egal ob eingelesen oder nicht      
      }						
      comp_count++; // nach neuer Runde jetzt: (comp_count = 2)
     }							
     sync = 0;
    }			
   }
   LED_Port &= ~(1 << LED_Pin);	// LED aus
   timer1_stop ();			// liefert Impulsmesswert in µs
			// 150 ms
			
#endif
   if (GPIOR0&0x04) ToggleBit(&frame.data,frame.recvbit);
   switch (++frame.recvbit) {
    case 16: {
     if (timeReport.t.min==0) frame.GrabWetter0();
     else if (timeReport.t.min==2) frame.GrabWetter2();
    }break;
    case 21: frame.recvbit=24; break;
    case 39: frame.recvbit=42; break;
    case 42: frame.recvbit=41; break;
    case 41: {
     if (frame.wl&0x01
     || ((frame.inf&0x16)!=0x12 && (frame.inf&0x16)!=0x14)
     || parity(frame.min)
     || parity(frame.hod)
     || parity(frame.day ^ frame.wdm ^ frame.rok)) {
	// nichts tun!
     }else{	// Empfangene Zeit gültig => Interne Zeit setzen
      n.info = frame.inf&0x0F;
      n.min = bcd(frame.min&0x7F);
      n.hour = bcd(frame.hod&0x3F);
      n.day = bcd(frame.day>>2);
      n.wday = frame.wdm&7;
      n.month = bcd(frame.wdm>>3);
      n.year = bcd(frame.rok);
      if (timeReport.t.min==1) frame.GrabWetter1();
     }
    }break;
    case 64: frame.recvbit=40; break;
   }
  }
#endif
  GPIOR0&=0x04;
 }
}
Detected encoding: UTF-80