Source file: /~heha/basteln/Haus/Telefon/CLIP-Anzeige/clip.zip/clip_ac.cpp

/* CLIP für Analogvergleicher und 8-Bit-Timer0
   Projekt Rufnummern-Anzeige

Es ist für die korrekte Funktion extrem wichtig,
dass die beiden Analogvergleicher-Eingänge absolut symmetrisch
beschaltet sind. Im Experiment:
- Spannungsteiler 2× 10 kΩ von Ucc nach GND
  (macht 0,25 mA zusätzlichen Querstrom: Besser abschaltbar gestalten!)
- Vom Mittelpunkt jeweils 1× 33 kΩ zu AIN0 und AIN1
- Koppelkondensatoren jeweils 1× 6,8 nF nach b-Ader (= Ucc) und a-Ader,
  Zuordnung AIN0 / AIN1 egal.
Rechnerische Zeitkonstante: R×C = 0,22 ms , fg = 709 Hz

Der erste Ansatz verwendete nur 2 statt 4 Widerstände und nutzte
den integrierten Pullup bei einem der beiden AIN-Pins.
Dieses Konzept lief nur bei absolut glatter Betriebsspannung
vom Festspannungsregler einigermaßen brauchbar,
aber auch dann keineswegs so zuverlässig.
*/

#include "ra.h"

static void terminate() {
#ifdef TIMSK0
 TIMSK0&=~0x06;
#else
 TIMSK&=~0x18;
#endif
 if (stat>=3 && cnt) stat=0xFF;
 else stat=0;
}

// Zählerwert Timer0 für 1200 Hz (Baud)
#define T F_CPU/64/1200

// Flanke an Analogvergleicher
ISR(ANA_COMP_vect) {
 byte t=ACSRA;
 byte e=GPIOR0;
 if (!((t^e)&0x20)) return;	// Ohne Pegelwechsel an ACO ignorieren
 t=TCNT0L;
 GPIOR0=e^0x20;
 if (stat) {
  dif1=diff;
  diff=t-last;
  if (diff<T/6) {	// = 36 @ 16 MHz
   terminate();	// Zu hohe Frequenz: Wieder vorn anfangen, nicht nur ignorieren
   return;
  }
 }
 last=t;
 OCR0B=t+250;	// Zeitüberschreitung (zu tiefe Frequenz) feststellen lassen
 if (!stat) {
#ifdef TIMSK0
  TIFR0=0x04;
  TIMSK0|=0x04;
#else
  TIFR=0x08;
  TIMSK|=0x08;	// OCR0B aktivieren (für Timeout)
#endif
 }
 switch (stat) {
  case 0:	// Initialisierung: Erste Flanke
  cnt=akku=det=stat++;
  break;
  case 1: {	// Frequenzsuche: Trennschwelle zwischen hoch und tief
   add(akku,diff);
   adc0(det);	// 16-Bit-Zähler
   add(cnt,1);
   adc0(stat);	// 256 Flanken messen
  }break;
  case 2:	// Suche dauerhaft tiefe Frequenz gegenüber Trennschwelle
  if (diff>=det) {
   add(cnt,16);
   adc0(stat);
  }else cnt=0;	// Frequenz zu hoch	// einige Nulldurchgänge lang
  break;
  case 3:	// Suche nach Startbit
  if (diff<det && dif1<det) {
   ++stat;
   t+=T-20-dif1-diff;	// = 150 @ 16 MHz
   OCR0A=t;	// Das erste Datenbit kommt zu jener Zeit
#ifdef TIMSK0
   TIFR0=0x02;	// Anhängigen Interrupt löschen
   TIMSK0|=0x02;	// Beide Compare-Interrupts freigeben
#else
   TIFR=0x10;
   TIMSK|=0x10;	// OCR0A aktivieren
#endif
  }break;
 }
}

#define TOL 0

static bool bit() {
#if TOL==100
 return diff+dif1>=det+det;
#elif TOL
 return diff>det+TOL || diff>det-TOL && dif1>=det;
#else
 return diff>=det;
#endif
}

// Bitzeit, stat = 5..13
ISR(TIMER0_COMPA_vect) {
 akku>>=1;
 if (bit()) {
  akku|=0x80;	// Tiefe Frequenz = 1
 }
// sei();		// Ab hier darf der Pegelwechsel-Interrupt einschlagen
 switch (++stat) {
  case 14: {	// Rahmenfehler detektieren
   if (!(akku&0x80) || cnt>=2 && cnt==clip.data[1]+3) terminate();
   stat=3;	// Startbit suchen lassen
#ifdef TIMSK0
   TIMSK0&=~0x02;
#else
   TIMSK&=~0x10;// OCR0A deaktivieren
#endif
  }break;
  case 13: switch (cnt) {
   case 0: if (akku!=0x80) terminate(); break;
   case 1: if (akku>elemof(clip.data)-3) terminate(); break;
  }
  clip.data[cnt++]=akku; nobreak; 	// Byte ablegen und weiter
  default: OCR0A+=T;	// = 208 @ 16 MHz für das nächste Bit
 }
}

// Zu lange auf Flanke gewartet (F_CPU/64/250 = 1 kHz @ 16 MHz, = 1 ms)
// Vor dem Signal kommt es erwartungsgemäß zu vielen dieser Interrupts
ISR(TIMER0_COMPB_vect) {
 terminate();
}

void CLIP::init() {
 TCCR0B= 0x02;	// F_CPU/8/8; T = 1,024 ms für F_CPU = 16 MHz
 ACSRA = 0x18;	// Analogvergleicher: Interruptfreigabe, beide Flanken
}
void CLIP::done() {
 ACSRA = 0x80;	// Interrupts aus
 TCCR0B= 0x05;	// Standard-Vorteiler 1024
}
Detected encoding: UTF-80