Quelltext /~heha/basteln/Haus/Telefon/Impulswahl→DTMF/mfv.zip/mfv2c/mfv2c.c

/* Programm für ATtiny45, „h#s“ Henrik Haftmann, TU Chemnitz
 * tabsize = 8, encoding = utf-8 (ohne Windows-Header BOM)
 090331	Erster Anfang als mfv2.c
+190531	CLIP funktioniert
~190610	CLIP funktioniert nicht am Telekom-Anschluss
+190617	phoneParallel() — Tiefentladeschutz für Elko
?190626	Nummer des Anrufers kann immer noch nicht mit 7³ gespeichert werden
 210206	Bei Binärgleichheit Kommentierung erweitert
 220317	Todos als Kommentar
 */
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/wdt.h> 
#include <util/delay.h>
#include <avr/fuse.h>
#include <avr/signature.h>
#include <string.h>

FUSES={
 0x5C,	// Taktteiler /8, kein Taktausgang, schnellster Startup, Resonator 3..8 MHz
 0x57,	// kein RESET, EEPROM-Inhalt behalten, kein Brownout
 0xFF,	// Keine Selbstprogrammierung
};	// {Startup: Reset = 272 CK + 64 ms (64068 µs), PowerDown = 258 CK (64 µs)}

/************
 * Hardware *
 ************/
/*
Anschlüsse:
PB0	(5)	-	Wählscheiben-Kontakte "nsi"+"nsa", 1 = unterbrochen
		AIN0	TODO: Empfindlichere Clip-Dekodierung via 4,7 nF (Fritzbox)
			und internem Pullup, f_HP = 1/(2πRC) = 850 Hz
PB1	(6)	OC1A	PWM-Tonausgang, reichlich (125 kHz) Trägerfrequenz
		AIN1	TODO: Gegenpol (als 5V-Ausgang) für den Analogvergleicher
PB2	(7)	ADC1	Amtsader La = Telefonader a, über 680 kΩ zur
			Abhebe- und Klingel-Detektierung,
			CLIP-Dekodierung sowie Minimalstromversorgung
			inklusive RC-Hochpass 33 kΩ + 1 nF (optional)
PB3	(2)	XIN	Resonator 4 MHz
PB4	(3)	XOUT	Resonator 4 MHz
PB5	(1)	!RESET	Erdtaste (optional) / Debug-Ausgang
GND	(4)		Telefonader b
Ucc	(8)		Amtsader Lb; Z-Diode 5,6 V und Elko 470 µF nach GND

TODO: Normalerweise wäre zu erwarten, dass der Analogvergleicher
auch mit Spannungen von 0,3 V jenseits der Betriebsspannungsgrenzen
zurecht kommt und korrekt arbeitet. Was zu testen wäre.
Ansonsten helfen ein Pulldown an AIN1 von 680 kΩ, der mit dem internen Pullup
(40 kΩ) 4,7 V bereitstellt, und ein Koppelwiderstand von 1 MΩ von AIN1 nach AIN0.
Die CLIP-Wechselspannung beträgt (-13,5±1,5)dBm an "BT3 termination" (600 Ω),
macht P = U*I = U*U/R = -13,5 dBm = 10^-1,35 mW = 45 µW; U = sqrt(P*R) = 160 mV = 450 mVss.
Für den A/D-Wandler (8 Bit, 5 V) mickrige 20 Count.
Die Terminierung ergibt sich durch das parallel geschaltete Läutewerk (Telefonklingel).
Bei Reflexionen (bei diesen hohen Frequenzen über 1,2 kHz) muss eine gesonderte
Wechselspannungs-Terminierung (600 Ω + 330 nF für f_HP = 850 Hz) herhalten.
Die Offsetspannung des Analogvergleichers ist nicht angegeben.
Nebenbei: CLIP-Dekodierung mit Analogverleicher ist Energie sparender:
I(ADC) = 170 µA; I(AC) = 40 µA; also nur ein Viertel.
Auch die CPU hat weniger zu tun, hält sie mit 1 mA/MHz den Löwenanteil.
Keiner der Timer hat eine Capture-Funktion; die Zeitmessung muss
von der CPU (am besten via Energie sparenden Timer0) ausgeführt werden. 

# Es ist schwieriger, einen Adapter zum Zwischenschalten
# (ohne Modifikation des Telefonapparates) zu bauen.
# Ein möglicher Trick dabei besteht darin, das Telefon zwischen W2 und b
# zu betreiben und den nsi-Kontakt an a abzugreifen.
# Das erspart das Unterdrücken der Wählimpulse,
# muss aber nicht mit jedem Telefon funktionieren, je nach Innenschaltung.

Die Erdtaste ist optional und ermöglicht einen schnellen Weg zum Rückruf.
(Sonst ist es die 6 in der dritten Shift-Ebene, also kurz 6³.)

Der EEPROM des AVR ermöglicht das Abspeichern von Nummern zur Kurzwahl.
Sonderfunktionen:
 LANG am Finger-Anschlag halten (bis zum ersten Piep):		Speichermodus:
  1..9,0 = Kurzwahl K1..K9, K0					Kurzwahl K0..9
 LANG×2 am Finger-Anschlag halten (bis zum zweiten Piep):
  1..8 = Kurzwahl M1..M8					Kurzwahl M1..8
  9 = '*' wählen						   Ortsvorwahl
  0 = '#' wählen						      Autowahl
 LANG×3 am Finger-Anschlag halten (bis zum dritten Piep):
  1..4 = 3. Kurzwahl A..D mit 'A' bis 'D' vorbelegt		 Kurzwahl A..D
  5 = Wahlwiederholung							   'A'
  6 = Rückruf								   'B'
  7 = Speichern der zuletzt gewählten Rufnummer (Ziffer folgt)		   'C'
  8 = Speichern einer neuen Rufnummer (Nummer folgt, dann LANG + Ziffer)   'D'
  9 = Letzte Rufnummer löschen (= Wahlwiederholung verhindern)		   '*'
  0 = Rückrufliste löschen						   '#'

Die Autowahl = Wahl beim Abnehmen des Telefonhörers ist mit Vorsicht zu nutzen.
Sie kann nur unterbunden werden durch:
 - Wahl bei aufgelegtem Telefonhörer
 - Eingehender Telefonanruf (Klingeln)
 - Mindestens 1 entgangener Anruf
Die Autowahl ist für den Einsatz als Oma-Telefon gedacht.
Andererseits kommen Omas gut mit Wählscheiben zurecht. Gerade weil's so zeitraubend ist.

Wahlen aus dem Kurzwahlspeicher gehen nicht in die Wahlwiederholung ein.
D.h. der Wahlwiederholungsspeicher merkt sich nur explizit gewählte Nummern.

Folgende interne Peripherie wird verwendet:
Ports: 2 Eingabe mit Pull-Up, 1 Ausgabe via Hardware-PWM
Timer 0: Frequenzsynthese = Stetige PWM-Änderung für Timer 1
Timer 1: High-Speed-Timer mit 32-MHz-PLL: Hardware-PWM-Ausgabe, mono
Totzeitgenerator: ungenutzt
Taktgenerator: Keramikoszillator mit bei Bedarf zugeschalteter High-Speed-PLL
Power-Management: Power-Save (onhook), Idle (offhook), CPU-Taktdrosselung
Analog-Digital-Wandler: Für onhook/offhook/Klingel-Detektion und CLIP
Analogvergleicher: TODO: CLIP-Dekodierung
Interrupts: Zählerüberlauf Timer 0, Watchdog-Timer, ADC fertig (nur für CLIP)
EEPROM: Nummernspeicher für Wahlwiederholung, 22 Kurzwahlen, Autowahl, Ortsvorwahl
	feste Adressen
	Beim ATtiny85 im Debug-Modus wird die gesamte CLIP-Nachricht im EEPROM abgelegt.
	Da die Fritzbox auch den Namen aus dem Telefonbuchspeicher übermittelt,
	steht dann hier der Name des letzten Anrufers im Klartext drin.
RAM: Nummernspeicher für CLIP, als Liste hintereinanderweg
Flash-Selbstprogrammierung: ungenutzt

In dieser Firmware ist die Resonatorfrequenz variabel gehalten;
getestet habe ich's aber nur mit 4 MHz.
Da der Vorläufer mfv2b auch mit anderen Frequenzen gut lief, wird's auch
funktionieren, nur bei der CLIP- und Rückruf-Funktion bin ich mir nicht sicher.
*/

// Signal-Zeiten in Sekunden
#define TON 0.14
#define PAUSE 0.06

typedef unsigned char byte;
typedef unsigned short word;
#define NOINIT __attribute__ ((section (".noinit")))

// Sinustabelle, Mittelwert und Amplitude = 73
// Mit der gewählten Amplitude läuft die Signal-Addition
// mit 100 % (hoher Ton) + 75 % (tiefer Ton) geradeso nicht über.
static PROGMEM const byte SinTab[256]={
 73, 75, 77, 78, 80, 82, 84, 85, 87, 89, 91, 92, 94, 96, 98, 99,
101,103,104,106,107,109,111,112,114,115,116,118,119,121,122,123,
125,126,127,128,129,131,132,133,134,135,136,137,137,138,139,140,
140,141,142,142,143,143,144,144,145,145,145,145,146,146,146,146,
146,146,146,146,146,145,145,145,145,144,144,143,143,142,142,141,
140,140,139,138,137,137,136,135,134,133,132,131,129,128,127,126,
125,123,122,121,119,118,116,115,114,112,111,109,107,106,104,103,
101, 99, 98, 96, 94, 92, 91, 89, 87, 85, 84, 82, 80, 78, 77, 75,
 73, 71, 69, 68, 66, 64, 62, 61, 59, 57, 55, 54, 52, 50, 48, 47,
 45, 43, 42, 40, 39, 37, 35, 34, 32, 31, 30, 28, 27, 25, 24, 23,
 21, 20, 19, 18, 17, 15, 14, 13, 12, 11, 10,  9,  9,  8,  7,  6,
  6,  5,  4,  4,  3,  3,  2,  2,  1,  1,  1,  1,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  1,  1,  1,  1,  2,  2,  3,  3,  4,  4,  5,
  6,  6,  7,  8,  9,  9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20,
 21, 23, 24, 25, 27, 28, 30, 31, 32, 34, 35, 37, 39, 40, 42, 43,
 45, 47, 48, 50, 52, 54, 55, 57, 59, 61, 62, 64, 66, 68, 69, 71};

#if F_CPU < 6000000
# define DIVSH 7	// 128 Takte pro Timer-Interrupt
// Heruntergeteilter CPU-Takt = 24..48 kHz (Resonator 3..6 MHz)
#else
# define DIVSH 8	// 256 Takte pro Timer-Interrupt
// Heruntergeteilter CPU-Takt = 24..48 kHz (Resonator 6..12 MHz)
#endif

#define FREQ(x) ((x)*65536.0*(1<<DIVSH)/F_CPU+0.5)
// DDS-Inkremente = Additionswerte auf Phasenwert alle F_CPU/(1<<DIVSH)
static PROGMEM const word Frequencies[20] = {
 FREQ( 697),
 FREQ( 770),
 FREQ( 852),
 FREQ( 941),
 FREQ(1209),
 FREQ(1336),
 FREQ(1477),
 FREQ(1633),
 FREQ(1046),	//c³
 FREQ(1109),
 FREQ(1175),	//d³
 FREQ(1245),
 FREQ(1319),	//e³
 FREQ(1397),	//f³ (Halbtonschritt)
 FREQ(1480),
 FREQ(1568),	//g³
 FREQ(1661),
 FREQ(1760),	//a³
 FREQ(1865),
 FREQ(1975),	//h³
};

// Funktionsprinzip: DDS = Digitale Frequenzsynthese
volatile register word addA	asm("r8");
volatile register word phaA	asm("r10");	// hoher Ton
volatile register word addB	asm("r12");
volatile register word phaB	asm("r14");	// tiefer Ton

volatile register byte buf_r	asm("r7");	// Puffer-Lesezeiger
volatile register byte anrufe	asm("r6");	// Anrufzähler
volatile register byte nsi	asm("r5");	// Nummernschaltkontakt
volatile register byte pinb	asm("r4");	// reflektiert PINB mit diesen Bits:
// 0	PB0	Nummernschalter "nsi" + "nsa" in Reihe, low-aktiv
// 2		abgehoben: A/D-Wandler an PB2 liefert mehr als 75 %
// 5	PB5	Erdtaste, low-aktiv
// Dabei ist PINB-Digitaleingabe an den Bits 1..4 gesperrt via DIDR0
volatile register byte opinb	asm("r3");	// Um Veränderungen an pinb zu erkennen
volatile register byte T_on	asm("r2");	// Rest-Ton/Pausenlänge, in 16 ms
#define Flags GPIOR0		// Bits für NSK() u.a.
//	7	Watchdog-Interrupt: 16 ms vergangen
//	6	Anruf beginnt: Wenn's klingelt oder gewählt wird
//	4	Erdtaste gedrückt
//	3	8³ = wahlfreie Nummer speichern
//	2	7³ = letzte Nummer speichern
//	1:0	Ebenenzähler 0..3
#define wend  GPIOR1		// Wähl-Timeout (onhook)
#define Zeit8 GPIOR2		// Zeitzähler für nsi+nsa

static byte Zeit16;	// Zeitzähler zum Abspeichern der letzten Rufnummer (für Wahlwiederholung)

static byte wahl[11];		// Puffer für Wählziffern
#define buf_w wahl[0]
// 1. Byte = Füllstand (buf_w)
// '*'=14, '#'=15, wie im EEPROM-Kurzwahlspeicher

extern void PutNib(byte*z,byte idx,char v);

// Puffer füllen, c=0..15
static void PutBuf(char c) {
 if (buf_w<20) PutNib(wahl+1,buf_w++,c);
}

extern char GetNib(const byte*z,byte idx);

// Puffer lesen, nicht aufrufen wenn leer!
// Aufruf mit freigegeben Interrupts (aus Hauptschleife) OK
static char GetBuf(void) {
 return GetNib(wahl+1,buf_r++);
}

extern byte x10(byte);

/******************
 * EEPROM-Zugriff *
 ******************/
// Um im EEPROM möglichst viele Kurzwahlen unterbringen zu können,
// werden die Ziffern nibbleweise gespeichert, das verdoppelt die Kapazität,
// erschwert aber die Verarbeitung.
// Die Kurzwahlen liegen allesamt an festen Adressen.

//Belegung des EEPROM:		Index	Kurzwahl
//x00	10	Letzte Nummer	0	5³
//x0A	10	K1		1	1¹
//x14	10	K2		2	2¹
//x1E	10	K3		3	3¹
//x28	10	K4		4	4¹
//x32	10	K5		5	5¹
//x3C	10	K6		6	6¹
//x46	10	K7		7	7¹
//x50	10	K8		8	8¹
//x5A	10	K9		9	9¹
//x64	10	K0		10	0¹
//x6E	10	M1		11	1²
//x78	10	M2		12	2²
//x82	10	M3		13	3²
//x8C	10	M4		14	4²
//x96	10	M5		15	5²
//xA0	10	M6		16	6²
//xAA	10	M7		17	7²
//xB4	10	M8		18	8²
//xBE	10	A		19	1³
//xC8	10	B		20	2³
//xD2	10	C		21	3³
//xDC	10	D		22	4³
//xE6	10	Vorwahl		23
//xF0	10	Autowahl	24
//xFA	2	frei
//xFC	1	Anzahl entgangener Anrufe
//xFD	1	Abhebezähler
//xFE	1	Watchdog-Timeout-Zähler (= kein CLIP)
//xFF	1	Reset-Zähler
//x1E0	32	CLIP-Daten (nur ATtiny85)

static void eewait(void) {
 while (EECR&2);
}
static byte eeread(void) {
 EECR|=1;
 return EEDR;
}
static void eewrite(void) {
 cli();
 EECR|=4;
 EECR|=2;
 sei();
}
static void eechange(byte b) {
 byte k=eeread();
 if (k!=b) {
  if (b==0xFF) EECR|=0x10;	// erase only
  else if (k==0xFF) EECR|=0x20;	// write only
  EEDR=b;
  eewrite();
  eewait();
  EECR=0;
 }
}


// Speichern der Nummer ab EEARL im kopflosen Format (FF-terminiert)
static void numSave(byte*z) {
 byte i=*z++;
// 1. Ziffernpuffer in ganzer Länge mit 0x0F auffüllen,
// mit dem Sonderfall '#' am Ende, dann 0x0D als 'falsches' Ende
 if (i && i!=20 && GetNib(z,i-1)==0x0F) PutNib(z,i++,0x0D);
 while (i!=20) PutNib(z,i++,0x0F);
// 2. Komplette Nummer speichern (nur Änderungen)
 for (i=0;i<10;i++) {
  eechange(z[i]);
  EEARL++;
 }
}


// <max> muss gerade sein!
static void numLoad(byte*z,byte max) {
 z++;
// 1. Nibbles vom EEPROM laden
 byte i;
 for (i=0;i<max>>1;i++) {
  z[i]=eeread();
  EEARL++;
 }
// 2. tatsächliche Rufnummern-Länge ermitteln
 for (i=max;i;) {
  char c=GetNib(z,--i);
  if (c!=0x0F) {
   if (c==0x0D && i && GetNib(z,i-1)==0x0F) --i;
   i++;
   break;
  }
 }
 *--z=i;	// 0..max möglich
}

/****************************
 * Rückruf-Liste bearbeiten *
 ****************************/
// Organisation der Rückruf-Liste:
// Nahtlose Aneinanderreihung entgangener und noch nicht "abgearbeiteter" Anrufe
// 1 Byte Länge (5 Bit) + Grund der Null-Länge (2 Bit):
// 0x00 = kein oder fehlerhaft empfangenes CLIP (Prüfsumme wird nicht ausgewertet)
// 0x20 = Grund 'O' = keine Durchleitung
// 0x40 = Grund 'P' = unterdrückt vom Anrufer
// 0x60 = sonstiger Grund
// Darauf folgen <Länge> Hex-Nibbles Rufnummer.
// Die gespeicherte Ortsvorwahl wird bei Gleichheit abgeschnitten.
// Bei ungerader Länge folgt ein ungenutztes Nibble (0) zur Byteausrichtung
// Da der Puffer mit Null initialisiert ist, führt fehlendes CLIP
// automatisch zum passenden Grund.
// Bei drohendem Pufferüberlauf wird nicht mehr gespeichert.
// Der Puffer reicht für durchschnittlich 25 Anrufe.
static byte lea[128];	// Liste entgangener Anrufe
static byte ovw[5];	// Ortsvorwahl (max. 8-stellig, 1. Byte = Länge)

static byte ealen(byte l) {	// Längen-Byte in Byte-Länge der Nummer
 return ((l&0x1F)+1>>1)+1;
}

static byte*eaindex(byte i) {	// Zum gegebenen Index „vorspulen“
 byte*z=lea;
 if (i) do z+=ealen(*z); while(--i);
 return z;
}

static byte IstRueckruf(const byte*z) {	// liefert buf_w wenn die aktuelle Nummer 
 if (*z++!=buf_w) return 0;
 byte i;
 for(i=0; i<buf_w; i++) if (GetNib(wahl+1,i)!=GetNib(z,i)) return 0;
 return buf_w;
}

// <clipbuf> zeigt auf 2 (Nummer des Anrufenden) oder 4 (Grund für das Fehlen)
static void AppendClip(const byte*clipbuf) {
// <anrufe> noch 0 beim 1. Klingeln
 byte*z=eaindex(anrufe);
 byte type=*clipbuf++&0x1F;
 byte len=*clipbuf++&0x1F;	// Nicht die wackligen höheren Bits beachten
 if (z+1+ealen(len)>=lea+sizeof lea) return; // Überlauf (in den Stack) verhindern
 byte i,j=0;
 if (type==2) {		// Rückrufnummer vorhanden
  if (len>20) return;		// Fehler
  for (i=0;i<len;i++) {
   byte c=*clipbuf++&0x1F;
   if (('0'&0x1F)<=c && c<=('9'&0x1F)) c&=0x0F;
   else if (c==('*'&0x1F)) c=0x0E;
   else if (c==('#'&0x1F)) c=0x0F;
   else continue;
   PutNib(z+1,j++,c);
  }
  *z=j;
// Testen ob die Rufnummer mit Ortsvorwahl beginnt
  byte k=ovw[0];
  if (!k) return;
  if (j<=k) return;
  for (i=0;i<k;i++) if (GetNib(z+1,i)!=GetNib(ovw+1,i)) return;
// Ortsvorwahl wegnehmen
  for (i=k;i<j;i++) PutNib(z+1,i-k,GetNib(z+1,i));
  *z=j-k;
 }else{		// keine Rückrufnummer
  if (len!=1) return;		// Fehler
  byte reason=*clipbuf&0x1F;
  switch (reason) {
   case 'O'&0x1F: j|=0x20;	// "unavailable"
   case 'P'&0x1F: j|=0x40;	// "private"
   default: j|=0x60;		// unknown reason
  }
  *z=j;
 }
}

static void DelRueckruf(void) {
 byte i;
 byte*z=lea;
 for(i=0;i<anrufe;) {
  byte*e=z+ealen(*z);
  if (IstRueckruf(z)) {
   memcpy(z,e,lea+sizeof lea-e);
   --anrufe;
  }else{
   z=e;
   i+=2;
  }
 }
 z=eaindex(anrufe); 
 memset(z,0,lea+sizeof lea-z);	// „Schwanz“ des Rückrufpuffers gelöscht halten
}

// liefert Nummer der Kurzwahl (1..22), 0 für keinen Treffer
static byte FindInKurzwahl(const byte*z) {
 byte idx,i;
 for (idx=22;idx;--idx) {
  EEARL=x10(idx);
  numLoad(wahl,20);
  if (buf_w=*z) for (i=0;i<*z;i++) if (GetNib(wahl+1,i)!=GetNib(z+1,i)) goto notfound;
  break;
notfound:;
 }
 buf_w=0;
 return idx;
}


/****************************
 * Kurzwahl laden/speichern *
 ****************************/

// 10 Bytes Puffer pro Nummer (20 Ziffern)
// idx zwischen 0 (letzte Nummer) und 24 (Autowahl)
static void KurzSpeichern(byte idx) {
 EEARL=x10(idx);	// × 10
 numSave(wahl);
 if (idx==23) memcpy(ovw,wahl,5);
}

static void KurzLaden(byte idx) {
 EEARL=x10(idx);	// × 10
 numLoad(wahl,20);	// 0..20 Ziffern
 DelRueckruf();
 buf_r=0;	// mit dem Abspielen beginnen
}

/**************************************
 * Frequenzsynthese = Sinustonausgabe *
 **************************************/
// CLD2 = Clock Logarithmus Dualis
#if F_CPU>=8000000
# define CLD2 3	// ÷ 8 = 1..1,99 MHz
#elif F_CPU>=4000000
# define CLD2 2	// ÷ 4 = 1..1,99 MHz
#else
# define CLD2 1	// ÷ 2 = 1..1,99 MHz
#endif

extern void clock_set(byte div);

static void clock_max(void) {clock_set(0);}
static void clock_1MHz(void) {clock_set(CLD2);}
static void clock_16us(void) {clock_set(CLD2+4);}

#define WDT_SEC(s) ((s)/0.016)

static void tonEin(void) {
 clock_max();
 PLLCSR|= 0x82;		// Low-Speed-Modus
 _delay_us(100);
 while (!(PLLCSR&1));
 PLLCSR|= 0x04;
 TCCR1  = 0x61;
 TCCR0B = 0x01;		// Timer0 mit Vorteiler 1 starten
 T_on=WDT_SEC(TON+PAUSE);// Länge setzen
 DDRB  |= 0x02;		// Ausgang aktivieren
}

static void tonAus(void) {
 DDRB  &=~0x02;		// Ausgang hochohmig
 TCCR0B = 0;		// Timer0 anhalten
 TCCR1  = 0;		// Timer1 aus
 PLLCSR = 0;		// PLL aus
 clock_1MHz();
}

// Startet Wählton für Ziffer z ("*"=14, "#"=15)
static void StartDTMF(char z) {
// z&=15;		// sollte nie vorkommen
 if (MCUCR&0x10) return;	// niemals im aufgelegten Zustand
 if (!(Flags&0x40)) {
  Flags|=0x40;
  if (anrufe) --anrufe;	// Anrufliste abbauen
 }
 static PROGMEM const byte Nr2HiLo[16]={
//   0    1    2    3    4    5    6    7    8    9    A    B    C    D    *    #
  0x53,0x40,0x50,0x60,0x41,0x51,0x61,0x42,0x52,0x62,0x70,0x71,0x72,0x73,0x43,0x63};
// High-Nibble  4  5  6  7
// Low-Nibble ┌───────────
//	    0 │ 1  2  3  A
//	    1 │ 4  5  6  B
//	    2 │ 7  8  9  C
//	    3 │ *  0  #  D
 byte i=pgm_read_byte(Nr2HiLo+z);		// umrechnen in die 2 Frequenzen
 addA=pgm_read_word(Frequencies+(i>>4));	// hoher Ton im High-Nibble
 addB=pgm_read_word(Frequencies+(i&15));	// tiefer Ton im Low-Nibble
 tonEin();
}

// Startet Hinweiston, z=0 für c³
static void StartTon(char z) {
 if (MCUCR&0x10) return;	// niemals im aufgelegten Zustand
 char okt=0;			// Oktave
 while (z<0) {z+=12; --okt;}
 while (z>=12) {z-=12; ++okt;}	// jetzt 0<=z<12
 addA=pgm_read_word(Frequencies+8+z);
 if (okt<0) addA>>=-okt;
 else if (okt>0) addA<<=okt;
 addB=addA;
 phaB=phaA;			// addieren lassen
 tonEin();
}

// Kernroutine der Frequenzsynthese
// Aufruf mit F_CPU/256 oder F_CPU/128, 20..40 kHz, reicht für HiFi-Ton
// Mit F_CPU/64 kommt der Controller nicht mehr hinterher,
// obwohl noch Rechenleistung frei sein müsste.
ISR(TIM0_COMPA_vect) {
// Tonlänge nominell 70 ms, Pause 30 ms
// T_on>=0 = Tonausgabe, sonst Pause
// if (T_on>=(byte)WDT_SEC(PAUSE)) {
#if 0
  byte a=pgm_read_byte(SinTab+((phaA+=addA)>>8));
  byte b=pgm_read_byte(SinTab+((phaB+=addB)>>8));
  OCR1A=a+b-(b>>2);		// hier ohne Runden
#else
  asm(
"	add	r10,r8	\n"
"	adc	r11,r9	\n"
"	add	%A1,r11	\n"
"	adc	%B1,r1	\n"
"	lpm		\n"
"	sub	%A1,r11	\n"	// ZH:ZL wiederherstellen
"	sbc	%B1,r1	\n"
"	add	r14,r12	\n"
"	adc	r15,r13	\n"
"	add	%A1,r15	\n"
"	adc	%B1,r1	\n"
"	lpm	r1,z	\n"	// gcc compiliert fäschlicherweise "lpm r1,Z+"
"	add	r0,r1	\n"
"	lsr	r1	\n"	// (kompensiert Kabeldämpfung für höhere Frequenzen)
"	lsr	r1	\n"
"	sbc	r0,r1	\n"	// "sbc" ist der Trick fürs Abrunden
"	clr	r1	\n"	// __zero_reg__ wiederherstellen
"	out	%0,r0	\n"
  ::"I" (_SFR_IO_ADDR(OCR1A)),"z"(SinTab):"1");
#endif
// }
}

/***********************************
 * Nummernschaltkontakt-Auswertung *
 ***********************************/
// Abarbeitung nach dem LIFO-Prinzip
static void Rueckruf(void) {
 byte *z,l,i=anrufe;
 for (;;) {
  if (!i) {
   if (!(DDRB&2)) StartTon(-6);
   return;		// Nichts (mehr) rückzurufen
  }
  z=eaindex(--i);	// Zum letzten Rückruf vorspulen
  l=*z&0x1F;
  if (l) break;
  *z=0;			// Unmögliche Rückrufe von hinten allesamt löschen
  --anrufe;
  if (!(DDRB&2)) StartTon(-12);	// Rückruf (der letzten Anrufe) nicht möglich
 }
 memcpy(wahl,z,11);
 DelRueckruf();
 KurzSpeichern(0);
 buf_r=0;	// mit dem Abspielen beginnen
}

// Nummernschaltkontakt an PB0 sowie Erdtaste an PB5 auswerten
// Aufruf alle 16 ms per Watchdog-Interrupt aus onhook- oder offhook-Schleife
static void NSK(void) {
 Flags&=~0x80;
 if (pinb&1 && !(opinb&1)) {	// Steigende Flanke: nsa losgelassen oder nsi unterbrochen
  ++nsi;		// Lo-Hi-Flanken zählen
  wend=0xFF;		// Globales Wähl-Timeout setzen
  Zeit8=WDT_SEC(0.2);	// zur Feststellung ob nsa oder nsi
 }else if (!(pinb&1) && opinb&1) {	// Fallende Flanke
  if (nsi) Zeit8=0;	// Kein Timeout wenn Lo nach 1. nsi
  else{
   Flags&=~1;		// Etappenzähler für „nsa halten“ rücksetzen
   Flags&=~2;
   Zeit8=WDT_SEC(1.5);
  }
 }else{			// keine Flanke
  if (Zeit8 && !--Zeit8) {	// Timeout
   if (nsi		// Nummernscheibe abgelaufen (nur bei Hi-Pegel)
   && --nsi) {		// Abzüglich letzter Lo-Hi-Flanke durch nsa-Kontakt
// Hier muss nsi für dänische oder schwedische Telefone angepasst werden
// Eigentliche Aktion
    if (Flags&0x10) {
     Flags&=~0x10;
     goto action3;
    }
    if (Flags&8 && !(Flags&1) && !(Flags&2)) goto save;
    if (Flags&8 || Flags&4) {	// nsi = Ziffer zum Kurzwahl-Speichern
     if (Flags&2) {
      if (Flags&1) {
       if (nsi<5) nsi+=18;	// 19..22
       else {nsi+=5; goto save1;}	// Möglichkeit des Abspeicherns von A,B,C,D,* und #
      }else{
       nsi+=10;			// 11..18
       if (nsi>18) nsi+=4;	// 23..24
      }
     }
     KurzSpeichern(nsi);
     StartTon(5);
     Flags&=~0x1F;
     goto raus;		// nicht wählen
    }else if (Flags&2) {
     if (Flags&1) switch (nsi) {	// LANG×3 = Halten am Fingeranschlag
action3:
      case 5: KurzLaden(0); goto raus;		// Wahlwiederholung
      case 6: buf_r=buf_w=0; KurzSpeichern(0); goto raus;	// Wahlwiederholung verhindern
      case 7: Flags|=4; StartTon(5); goto raus;	// Letzte Nummer speichern: Ziffer folgt
      case 8: Flags|=8; buf_r=buf_w=0; StartTon(0); goto raus;	// Nummer einspeichern: Nummer + Ziffer folgen
      case 9: Rueckruf(); goto raus;
      case 10: anrufe=0; memset(lea,0,sizeof lea); goto raus;
      default: KurzLaden(18+nsi); goto raus;		// 19..22 (A,B,C,D) wählen
     }else switch (nsi) {	// LANG×2
      case 9:
      case 10: nsi+=14-9; break;	// '*' oder '#'
      default: KurzLaden(10+nsi); goto raus;
     }
    }else if (Flags&1) {		// LANG
     KurzLaden(nsi);		// wählen lassen (1..10), Hauptschleife generiert Töne
     goto raus;
    }else save: if (nsi==10) nsi=0;
save1: PutBuf(nsi);	// Zählergebnis abspeichern (Hauptschleife generiert Ton anschließend)
    if (Flags&8) {
     buf_r=buf_w;	// Nicht wählen (Hauptschleife nicht in Aktion treten lassen)
     StartTon(4);
    }else Zeit16=WDT_SEC(2);	// Wahlwiederholungs-Abspeicher-TimeOut setzen (4 Sekunden)
raus:
    nsi=0;
   }else if (!(pinb&1)) {	// Vor nsi-Impulsen (Lo-Pegel)
    if (!(Flags&1 && Flags&2)) ++Flags;	// Zu lange gehalten: ignorieren, weiterpiepen
    StartTon(1+(Flags&3));	// Ton c/d/e
    Zeit8=WDT_SEC(1);
   }
  }
 }
 if (!T_on && !(pinb&0x20) && opinb&0x20) {	// Erdtaste gedrückt (simple Entprellung)
  Flags|= 0x10;			// Ebene 3 ansteuern
 }else if (pinb&0x20 && !(opinb&0x20) && Flags&0x10) {
  Flags&=~0x1F;
  if (anrufe) Rueckruf();
  else KurzLaden(0);
 }
}

// Bei Ablauf des Watchdog-Timers (meist 16 ms) wird die CPU geweckt
// und das Bit 7 im Flag-Register (GPIOR0) gesetzt.
ISR(WDT_vect,ISR_NAKED) {
 Flags|=0x80;
 reti();
}


static byte offhooklevel=0xC0;	// High bei 75 %

// Da einige Zeit vergangen ist, seit die Pullups aktiviert wurden,
// wird zunächst PINB eingelesen und der Analogwert an PB2 ausgewertet.
// Dann wird in Schritten von 48 µs (max. 12 ms)
// oder per Watchdog fest 16 ms gewartet.
// Die kürzere Zeit wird für die Klingel-Detektion
// = Wechselspannung 25 oder 50 Hz an PB2 benötigt; Flanken alle 20 oder 10 ms.
// In beiden Fällen mit abgeschaltetem A/D-Wandler und minimalem Stromverbrauch:
// Bei us48!=0 durch Heruntersetzen des Oszillatortakts und einer Busy-Loop
// bei us48==0 durch maximale(!) Oszillatorfrequenz und kürzester Hochlaufzeit.
// Heraus kommt die Funktion mit 1 MHz CPU-Takt, aktivierten Pullups
// und mit gestartetem A/D-Wandler, immer an PB2,
// sowie ausgewertetem Nummernschalter.
static void waitforwatchdog(byte us48) {
 opinb = pinb;		// PB0 einlesen, nachdem sich die Pegel eingeschwungen haben
 pinb  = PINB;
 while (!(ADCSRA&0x10));	// warten auf Ende, kann bis zu 50 µs dauern
 if (ADCH>=offhooklevel) pinb|=4;	// High bei 75 %
 ADCSRA= 0;		// ADC abschalten — sonst gibt's zusätzlichen Stromverbrauch.
 PORTB = 0;		// kein Pullup: Versorgungsstrom 120 µA fließt über die negative Gateschutzdiode
 DDRB  = 0x25;		// Floating-Eingänge auf Low festnageln, um Gateschutzdioden zu schonen
 if (us48) {
  clock_16us();
  _delay_loop_1(us48);
  DDRB &=~0x04;		// Pullup für A/D-Wandler voreilend vorbereiten
  PORTB|= 0x04;
 }else{
  clock_max();		// kürzestmögliche Hochlaufzeit
  sleep_cpu();		// Oszillator aus bis zum nächsten Watchdog-Interrupt
  DDRB &=~0x04;		// dann umgehend Pin 7 zur Messung vorbereiten
  PORTB|= 0x04;
  clock_16us();		// 16 µs pro Takt, gaanz langsam (4 × 16 µs)
 }
// Die Unterprogrammaufrufe genügen zur Verzögerung.
// Warten mit dem A/D-Konverter, bis sich der Pegel an Pin 7 stabilisiert hat.
// Der Kondensator wirkt als Tiefpass.
 clock_1MHz();		// (10 × 16 µs)
 DDRB  = 0;
 PORTB = 0x25;		// Restliche Pullups aktivieren
 ADCSRA= 0xD1;		// Start mit Teiler 2: 500 kHz, 2µs, ×25 = 50 µs, einmalig, ohne Interrupt
 if (Flags&0x80) NSK();// Man kann noch während des Klingelns wählen :-)
}

/********************
 * CLIP-Dekodierung *
 ********************/

// Der Interrupt dient nur zum Aufwecken; Interrupts werden/bleiben gesperrt.
// Dieser wird nur für die CLIP-Detektion benötigt, das ist Energie sparender
// als die Auswertung von ADCSRA.ADIF, denn während der CLIP-Erkennung
// steht nur die Ladung im Elko als Energiequelle zur Verfügung.
ISR(ADC_vect,ISR_NAKED) {
 asm volatile("ret");
}

static byte clip[64] NOINIT;	// letztes Byte = Prüfsumme
// CLIP-Dekodierung verlangt vom Mikrocontroller alles ab, daher ist's
// in Assembler (extern) geschrieben.

// Steckt die aktuelle Rufnummer in „lea“
static void saveClip(void) {
 byte i=2,j=clip[1]+2;
 if (j>sizeof clip) j=sizeof clip;
 do{
  if (clip[i]==2 || clip[i]==4) {
   AppendClip(clip+i);
   break;
  }else i+=clip[i+1]+2;	// Länge des unbekannten Chunks
 }while(i<j);
}

extern void acInit(void);
extern byte acMean(void);
#ifdef __AVR_ATtiny85__
static byte histo[32] NOINIT;
extern void acHisto(byte data[],byte len);
#endif
extern void acOnes(void);
extern byte acByte(void);

static void acDone(void) {
 ADMUX|= 0x20;		// Ergebnis links ausrichten um nur 8 Bit auswerten zu müssen
 ADCSRA= 0xD1;		// Start mit Ergebnis in 50 µs ohne Interrupt
 MCUCR = 0xB4;
 MCUCR = 0xB0;		// Sleep-Modus: PowerDown, kein BrownOut
// clock_1MHz();
}

// mit 1 MHz CPU-Takt
static void clipRead(void) {
// STELLSCHRAUBE, die entscheidend für die nötige Kapazität von C1 ist
 byte i=15;		// 32 ist am Thomson-Modem erprobt.
// Laut Standard sind es 250 ms nach dem Klingeln. Daher Minimum = 5.
// Kleinere Zahlen fressen mehr Zeit und mehr Strom für die Detektierung
 do{
  waitforwatchdog(0);	// je 16 ms
  if (pinb&4) return;	// Zweites Klingeln oder Abheben
 }while (--i);		// weitere ms energiesparend warten
#ifdef __AVR_ATtiny85__
 memset(histo,0,sizeof histo);
 memset(clip,0,sizeof clip);
#endif
 cli();
 wdt_reset();
// STELLSCHRAUBE, die entscheidend für die nötige Kapazität von C1 ist
 WDTCR = 0x0E;		// Watchdog mit Reset, 1 s (war am ATmega85 erforderlich)
 acInit();
#ifdef __AVR_ATtiny85__
 histo[31]=acMean();
 acHisto(histo,30);
#else
 acMean();
#endif
 acOnes();
#ifdef __AVR_ATtiny85__
 histo[30]=1;
#endif
 wdt_reset();		// nach 100 langen Nulldurchgängen
 byte sum=0;
 for (i=0;; i++) {
  byte c=acByte();
#ifdef __AVR_ATtiny85__
  histo[30]++;
#endif
  sum+=c;
  if (i<sizeof clip-1) clip[i]=c;
  if (i && i==clip[1]+2) break;
 }
 clip[sizeof clip-1]=sum;
 acDone();
 WDTCR = 0x1D;
 WDTCR = 0x40;		// Watchdog normal (gemessen 16,4 ms)
 sei();
 saveClip();
}

/************************************
 * Routinen für aufgelegten Zustand *
 ************************************/
// Handelt es sich beim Pegel an PB2 um Gleichspannung (permanent High oder auch Low) oder Klingelwechselspannung 25 Hz?
// Routine kehrt erst nach Ende des Klingelns zurück.
// Liefert Anzahl detektierter Schwingungen.
static byte wait_ringend(void) {
 byte i=0,j=0;	// geht mit PB2 = High in die Routine, d.h. opinb.2 ist nach waitforwatchdog() = 1
 for(;;){
  waitforwatchdog(100);	// 5 ms
  if (pinb&4 && !(opinb&4)) {	// (Erneuter) Lo-Hi-Wechsel
   ++j; i=0;		// Diese Flanken zählen
  }else if (!(pinb&4) && opinb&4) {	// Hi-Lo-Wechsel
   i=0;			// sollte der Compiler wegoptimieren
  }else{		// gleich geblieben
   if (++i==10) break;	// 16 ms × 10 = 160 ms ist ungefähr die Zeit, die das Kabelmodem braucht, um das Freizeichen aufzuschalten
  }
 }
 return j;
}

static void onhook(void) {
 MCUCR = 0xB4;
 MCUCR = 0xB0;		// Sleep-Modus: PowerDown, kein BrownOut
 ADMUX = 0x21;		// ADLAR, ADC1 = PB2
 ADCSRA= 0xD1;		// einmalig (wird in waitforwatchdog erneut gestartet)
 if (!(T_on&0x08)) {	// Wenn kein Watchdog-Reset (kein CLIP) …
  Flags&=~0x40;		// Aktiven Anruf beenden
 }
 buf_w = buf_r = 0;	// Rufnummer tilgen
 byte rend=0;		// Klingel-Timeout, zum Zählen verpasster Anrufe
 do{
  do{
   waitforwatchdog(0);
   if (rend && !--rend && Flags&0x40) {
    Flags&=~0x40;
    EEARL=252;
    eechange(++anrufe);	// Anruf verpasst
   }
   if (wend && !--wend) buf_w=0;	// Timeout für's Wählen bei aufgelegtem Telefonhörer: 255×16ms=4s
  }while (!(pinb&4));	// Bit 2 = Schaltschwelle 75%
// Das Ansteigen der Spannung am PB2 kann 3 Gründe haben:
// * Klingeln, * Hörer abheben, * Paralleles Telefon nimmt Hörer ab
// 16 ms und 75% sind zur Klingeldetektion möglicherweise knapp!
// In der Konstellation mit PB1 an 'a'-Ader statt am Mikrofon ist es besser,
// dort (via Pullup und kapazitiver Kopplung) Pegelwechsel zu detektieren.
  byte j=wait_ringend();	// kehrt erst nach 160 ms Ruhe zurück
  if (j>=4) {		// 4 Pegelwechsel gezählt: Klingeln
   if (!(Flags&0x40)) {	// Erstes Klingeln
    Flags|=0x40;	// bemerken
    clipRead();		// CLIP einlesen
   }
   rend=WDT_SEC(4);	// 4 Sekunden nach dem letzten Klingeln sei ein (verpasster) Anruf zu Ende
   continue;
  }
 }while (!(pinb&4));	// In der Schleife bleiben bis abgehoben wurde (PB2 = High)
}

/************************************
 * Routinen für abgehobenen Zustand *
 ************************************/

// Testen (mit dem A/D-Wandler) ob das High an Pin 7 tatsächlich mit einer
// ausreichenden, steigenden Betriebsspannung einhergeht.
// Wenn nicht ist es ein parallel geschaltetes Telefon,
// ein gezogener Stecker oder ein totes Amt;
// dann muss mit gaaanz langer Watchdog-Spanne
// auf das Ende dieses irregulären Zustandes gewartet werden,
// um den Elko möglichst nicht zu entladen.
// Dass währenddessen keine Wahl bei aufgelegtem Telefonhörer unterstützt wird
// ist kaum tragisch und erwartungsgemäß.
// Denn wenn dieser erst mal leer ist, kann der Mikrocontroller nicht mehr
// hochlaufen und bspw. Anrufe zählen.
// Das geht dann nur durch Abnehmen des Telefonhörers.

// Im Prinzip ein Designfehler des Mikrocontrollers: Es gibt keine
// Fuse-Einstellung, mit der der Mikrocontroller ab einer bestimmten Spannung
// loslegt UND mit einer deutlich niedrigeren weiterarbeiten kann (= Hysterese)
// UND wenig Strom dafür braucht; der Brown-Out-Detektor ist zu hungrig.

// Daten etwa:
// Mikrocontroller bei 3 V im PowerDown ohne Watchdog:	max. 2 µA
//				...	mit Watchdog:	max. 10 µA
//				...	dazu BrownOut:	+ 15 µA
//			... arbeitend bei 1 MHz:	500 µA
// Vom 680-kΩ-Widerstand kommen bei 45 V Amtsspannung:	60 µA

static byte adPin7 NOINIT;
static byte adRef NOINIT;

// Nachdem der Abfall der Speisespannung bei High an PB2 (vermeintlich abgehoben,
// aber eher parallel geschaltetes Telefon abgehoben, daher keine Durchstromung)
// bemerkt wurde, mit extrem geringer Stromaufnahme in langen Intervallen testen
// und verweilen, bis dieser Zustand beendet und genügend Spannung da ist.
// Andernfalls wird der Mikrocontroller abstürzen und kommt erst durch Abnehmen
// des Telefonhörers = Herstellung des Schleifenstroms wieder hoch.
static void phoneParallel(void) {
 tonAus();
 PORTB = 0;	// Keine ohmschen Verbraucher: Keine Pullups!
 DDRB  = 0x25;	// Eingänge zu Ausgängen machen und festnageln: Gateschutzdioden entlasten
 do{
  ADCSRA= 0;	// ADC aus
  WDTCR = 0x61;	// 8 s
  MCUCR = 0xB4;
  MCUCR = 0xB0;	// Sleep-Modus: PowerDown, kein BrownOut
  sleep_cpu();	// bis zum Watchdog
  ADCSRA= 0xF4;	// Start mit Teiler 16: 16 µs, ×13 = 208 µs
  WDTCR = 0x40;	// 16 ms (für den ADC wird offenbar 1 ms benötigt!! Sonst kommt Murks weil Referenzspannung falsch.)
  MCUCR = 0x28;	// ADC-Störunterdrückung
  sleep_cpu();	// bis zum Watchdog
 }while (ADCH>=0x3E);	// 0x3E: Hysterese von 2 (gegenüber 0x40)
}

static void handleADC(void) {
 switch (ADMUX) {
  case 0x21: {		// maß PB1 bzgl. Speisespannung?
   adPin7 = ADCH;
   ADMUX  = 0x2C;	// umschalten auf Referenzspannung 1,1 V
  }break;
  case 0x2C: {		// maß Referenzspannung bzgl. Speisespannung?
   byte b = adRef;
   byte a = ADCH;
   adRef  = a;
   if (a>=0x40 && a>b) {	// Ucc < 4,4 V (Uref > Ucc × 25%) und fallend
    phoneParallel();
   }
   ADMUX = 0x21;	// umschalten auf PB1
  }break;
  default: ADMUX = 0x21;
 }
}

static byte pause,index;	// nur offhook verwendet!

// Zyklisch aufzurufen: Arbeitet alle <anrufe> ab und generiert Zweiton-Töne
static void piepAnrufe(void) {
 if (Flags&0x40) return;	// Anruf wurde entgegen genommen: Nichts tun
 if (!anrufe) return;	// Keine Anrufe seit letztem Auflegen: Nichts tun
 if (nsi) return;
 if (Zeit8) return;
 if (buf_w) return;
 switch (--pause) {	// zunächst 256 Aufrufe nichtstun, zwischen den Pieptönen weniger
  case (byte)WDT_SEC(0.25): StartTon(0); return;
  case 0: break;
  default: return;
 }
 const byte*z=eaindex(index);
 byte l=*z;
 char f;
 if (!(l&0x1F)) f=-1-(l>>5);	// kein Rückruf möglich
 else f=FindInKurzwahl(z);
 StartTon(f);		// CLIP hörbar machen
 if (++index>=anrufe) index=0;
 else pause=WDT_SEC(0.5);
}

static void offhook(void) {
 EEARL = 253;
 eechange(eeread()+1);	// Abhebe-Zähler
#ifdef __AVR_ATtiny85__
 EEARH = 1;
 EEARL = 0;
 byte i;
 for (i=0; i<sizeof clip; i++) {
  eechange(clip[i]);
  ++EEARL;
 }
 for (i=0; i<sizeof histo; i++) {
  eechange(histo[i]);
  ++EEARL;
 }
 EEARH = 0;
#elif defined(DEBUG)
 EEARL = 0xE0;
 byte i;
 for (i=0; i<28; i++) {
  eechange(clip[i]);
  ++EEARL;
 }
#endif
 MCUCR = 0x20;		// Sleep-Modus: Idle (Timer läuft, Strom ist genug da)
 ADCSRA= 0xF1;		// /2 = 15 kHz / 13 = 1 kSa/s (reicht erstmal)
 T_on   = 0;
 byte no_dial=WDT_SEC(1);	// besser: auf Freizeichen warten
 //Bei Freizeichen müsste <anrufe> weitergezählt werden, wenn Flags.6 gesetzt!!
 byte a=anrufe;
 byte b=0;
 if (Flags&0x40 && a) {
 // Beim Abheben während des Klingelns nachgucken, ob CLIP im Rückrufspeicher liegt.
 // Wenn ja dann aus dieser Liste tilgen.
  byte*z=eaindex(a);
  byte l=*z&0x1F;
  if (l) {
   buf_r=l;
   memcpy(wahl,z,11);
   DelRueckruf();	// reduziert <anrufe>
   b=a-anrufe;		// Wenn etwas gelöscht wurde, dann piepsen.
  // Dann weiß man, dass dieser Anrufer bereits vorher versucht hatte anzurufen.
  // Den Ton hören beide Partner.
   KurzSpeichern(0);
  }
 }
 pause=WDT_SEC(1);
 index=0;
 for(;;) {		// Hauptschleife
  sleep_cpu();		// blockiert bis zu 16 ms — oder auch fast nicht bei laufender DDS
  if (Flags&0x80) {	// Watchdog-Interrupt?
   EEARL=252;
   eechange(anrufe);
   handleADC();		// Auch den A/D-Wandler per Watchdog-Timer abfragen.
// Sonst funktioniert der Vergleich mit der Referenzspannung nicht!!
// BUGBUG: DDRB hat kein Bit 6!!
   if (DDRB&0x40) break;	// wenn handleADC() phoneParallel() festgestellt hatte, dann raus hier
   pinb = PINB;
   if (adPin7>=offhooklevel) pinb|=4;	// High bei 75 %
   if (!(pinb&4) && !(opinb&4)) break;	// Low durch Auflegen
   if (T_on) {
    if (--T_on==(byte)WDT_SEC(PAUSE)-1) tonAus();	// Tonlänge reduzieren
   }
   if (no_dial && !--no_dial && !buf_w && !(Flags&0x40) && !anrufe) {
#ifndef DEBUG
    KurzLaden(24);	// Automatische Wahl beim Abheben
#endif
   }
   NSK();			// Nummernschaltkontakt-Auswertung
   if (Zeit16 && !--Zeit16) KurzSpeichern(0);
   piepAnrufe();
   opinb = pinb;
  }
  if (T_on) continue;		// Ton- oder Pausenausgabe läuft
  if (!no_dial) {
   if (b) {StartTon(b); b=0;}
   else if (buf_r!=buf_w) StartDTMF(GetBuf());	// Nächsten Ton + Pause ausgeben
  }
 }
 if (T_on>=(byte)WDT_SEC(PAUSE)) tonAus();
}

/*************************************
 * Initialisierung und Hauptschleife *
 *************************************/

void __init(void) __attribute__((naked,section (".init2")));
void __init(void) {
 asm volatile("clr r1");// Stack nicht initialisieren, um gleichermaßen auf ATtiny45 und ATtiny85 lauffähig zu sein
			// SPH:SPL ist bereits mit RAMEND initialisiert
 T_on   = MCUSR;	// retten in R2
 MCUSR = 0;
 WDTCR|= 0x18;
 WDTCR = 0x40;		// Watchdog auf Interrupt, 16 ms = 62 Hz (einzige Interruptquelle)
 PORTB = 0x25;		// Pullup für 3 Eingänge
}

static void hardwareInit(void) {
 eewait();
 EEARL = T_on&0x08?254:255;
#ifdef __AVR_ATtiny85__
 EEARH = 0;
#endif
 eechange(eeread()+1);	// Reset- oder Watchdog-Reset-Zähler
 EEARL = 252;
 byte a=eeread();
 if (a>32) a=0;
 anrufe= a;
 ACSR |= 0x80;		// Analogvergleicher ausschalten
 DIDR0 = 0x1E;		// diese digitalen Eingänge nicht nutzen
 TCCR0A= 0x02;		// Timer0: CTC
 OCR0A = (1<<DIVSH)-1;	// rund 32 kHz
 TIMSK = 0x10;		// Compare-Interrupt
 PCMSK = 0;		// hilft das zum Strom sparen? Nee.
 buf_r = 0;		// alle übrigen Register nullsetzen
 Flags = T_on&0x08 ? 0x40 : 0;
 nsi   = 0;
 Zeit8 = 0;
 wend  = 0;
#ifndef DEBUG
 EEARL = 230;		// 23×10
 numLoad(ovw,8);
#endif
 opinb = pinb = PINB;
}

int __attribute__((noreturn)) main(void) {
 hardwareInit();
 for(;;) {
  onhook();
  offhook();
 }
}
Vorgefundene Kodierung: UTF-80