Source file: /~heha/basteln/Haus/Telefon/Impulswahl→DTMF/mfv.zip/mfv2c/mfv2cd.cpp

/* 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
 220320	Umstellung auf C++14
 220328	Gleicher Quelltext für CLIP: c: mit ADC, d: mit Analogvergleicher
-220421	Kurzen letzten nsi-Impuls detektieren (Österreicher Wählscheibe)
 */
#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>		// avr-size: 3 Bytes bei .data (falsch)
#include <avr/signature.h>	// avr-size: 3 Bytes bei .text (falsch)
#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	d: 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	d: Gegenpol (als 5V-Ausgang) für den Analogvergleicher
PB2	(7)	ADC1	Amtsader La = Telefonader a, über 680 kΩ zur
			Abhebe- und Klingel-Detektierung, Minimalstromversorgung,
			c: CLIP-Dekodierung
PB3	(2)	XIN	Resonator 4 MHz
PB4	(3)	XOUT	Resonator 4 MHz
PB5	(1)	!RESET	LVISP-Zugang
		-	Erdtaste, LED-Flash, Debug-Ausgang
GND	(4)		Telefonader b
Ucc	(8)		Amtsader Lb; Z-Diode 5,6 V und Elko 470 µF nach GND

Der Analogvergleicher kommt auch mit Spannungen von 0,3 V jenseits der
Betriebsspannungsgrenzen zurecht. Getestet am ATtiny13, okay.
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 (10 Bit, 5 V) 40 Count.
Die Terminierung ergibt sich durch das parallel geschaltete Läutewerk (Telefonklingel).
Sowie zuschaltbar durch Fixierung von PB1 (auf High-Pegel) via R1.

CLIP-Dekodierung mit Analogvergleicher 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. 
Die Offsetspannung des Analogvergleichers ist nicht angegeben.

# 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, USI, Flash-Selbstprogrammierung: ungenutzt
Taktgenerator: Keramikoszillator mit bei Bedarf zugeschalteter High-Speed-PLL
Power-Management: Power-Save (onhook), Idle (offhook), CPU-Taktdrosselung
Analog-Digital-Wandler:
	Messung Spannung an PB2: onhook/offhook/Klingel-Detektion
	Messung der Betriebsspannung: Erkennung parallelgeschaltetes Telefon
	c: CLIP-Flankenmessung
Analogvergleicher:
	d: CLIP-Flankenmessung
Interrupts:
	Zählerüberlauf Timer 0 => Nächster PWM-Wert,
	Watchdog-Timer => verschiedene Timeouts,
	c: ADC fertig => CPU aufwecken (nur für CLIP)
	d: Analogvergleicher-Flanke => CPU aufwecken (nur für CLIP)
EEPROM: Nummernspeicher für Wahlwiederholung, 22 Kurzwahlen, Autowahl, Ortsvorwahl
	feste Adressen
	Beim ATtiny85 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.
	Auslesbar mit einem AVR-Programmiergerät.
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.
*/

// Signal-Zeiten in Sekunden
constexpr float TON=0.14;
constexpr float PAUSE=0.06;

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

// Sinustabelle, Mittelwert und Amplitude = 73
extern const struct sintab_t{
 byte a[256] {};
 constexpr sintab_t() {
  constexpr float PI=__builtin_atan(1)*4;
  for (size_t x=0; x<256; x++)
    a[x]=__builtin_round(__builtin_sin(x*2*PI/256)*73)+73;
 }
}SinTab SINTAB;
// Mit der gewählten Amplitude läuft die Signal-Addition
// mit 100 % (hoher Ton) + 75 % (tiefer Ton) geradeso nicht über.
const sintab_t SinTab SINTAB;

// Etwa 32 kHz Trägerfrequenz bei jeder Resonatorfrequenz (bis 8 MHz) realisieren
constexpr byte T0DIV = F_CPU/32000;

constexpr word FREQ(float x) {return x*65536*T0DIV/F_CPU+0.5;}
// DDS-Inkremente = Additionswerte auf Phasenwert alle F_CPU/T0DIV
static PROGMEM const word Frequencies[20] = {
 FREQ( 697),	//  Zeile mit 1 2 3 A
 FREQ( 770),	//  Zeile mit 4 5 6 B
 FREQ( 852),	//  Zeile mit 7 8 9 C
 FREQ( 941),	//  Zeile mit * 0 # D
 FREQ(1209),	// Spalte mit 1 4 7 *
 FREQ(1336),	// Spalte mit 2 5 8 0
 FREQ(1477),	// Spalte mit 3 6 9 #
 FREQ(1633),	// Spalte mit A B C D
 FREQ(1046),	//c³	Ab Index 9 gleichstufige Tonleiter aus 12 „Sekunden“
 FREQ(1109),	//cis³/des³
 FREQ(1175),	//d³
 FREQ(1245),	//dis³/es³
 FREQ(1319),	//e³
 FREQ(1397),	//f³ (Halbtonschritt)
 FREQ(1480),	//fis³/ges³
 FREQ(1568),	//g³
 FREQ(1661),	//gis³/as³
 FREQ(1760),	//a³	= 440*4
 FREQ(1865),	//ais³/b³
 FREQ(1975),	//h³
};

// Funktionsprinzip: DDS = Digitale Frequenzsynthese
#define DDS_REG
#ifdef DDS_REG
volatile register word
	addA	asm("r8"),
	phaA	asm("r10"),	// hoher Ton
	addB	asm("r12"),
	phaB	asm("r14");	// tiefer Ton
#else
volatile word addA,phaA,addB,phaB NOINIT;
#endif

// Häufig benutzte globale Bytes werden in freien I/O-Adressen gehalten,
// wobei nur die GPIORx bitadressierbar sind
#define Flags	GPIOR0	// Bits für NSK() u.a.
//	7	Watchdog-Interrupt aufgetreten: 16 ms vergangen
//	6	Anruf beginnt: Wenn's klingelt oder gewählt wird
//	5	Blitz-LED wurde eingeschaltet; Debug: CLIP: TLEVEL bestimmt
//	4	Erdtaste solo gedrückt
//	3	8³ = wahlfreie Nummer speichern
//	2	7³ = letzte gewählte oder rückgerufene Nummer speichern
//	1:0	Ebenenzähler 0..3 durch Festhalten am Fingeranschlag
#define pb2	GPIOR1	// Aktivitätszustand (> 75 % A/D-Wert) an PB2 mit Historie in variabler Schrittweite
#define buf_r	GPIOR2	// Puffer-Lesezeiger
#define T_on	OCR0B	// Rest-Ton/Pausenlänge, in 16 ms
#define anrufe	OCR1B	// Anrufzähler
#define Zeit8	DT1A	// Zeitzähler für nsi+nsa
#define NSI	DT1B	// Steigende Flanken an PB0 (nsi + einer extra für nsa)

static byte pb0,pb5;	// Invertierter Zustand an PB0 und PB5 mit Historie in Watchdog-Schrittweite (16 ms)
static byte Zeit16;	// Zeitzähler zum Abspeichern der letzten Rufnummer (für Wahlwiederholung)
static byte rend NOINIT;// Wähl-Timeout, Klingel-Timeout, Flash-Timeout (onhook)

static byte wahl[11];	// Puffer für bis zu 20 Wählziffern
#define buf_w wahl[0]
// 1. Byte = Füllstand (buf_w)
// '*'=14, '#'=15, wie im EEPROM-Kurzwahlspeicher
// Keine Vorsehung von Wählpausen oder Flash-Impulsen

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() {
 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. Sie sind als Klartext im Hexdump erkennbar.
// 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 (= <anrufe>)
//xFD	1	Abhebezähler
//xFE	1	Watchdog-Timeout-Reset-Zähler (= kein CLIP oder Fehler)
//xFF	1	Sonstige-Reset-Zähler (Stromausfälle, Steckzyklen oder Bugs)
//Debug-Daten, nur ATtiny85
//x100	64	CLIP-Daten wie empfangen + summierte Prüfsumme (muss 0 sein)
//		0x80, gLen, { 1, 8, Datum+Uhrzeit },
//			    { 2, nLen, Nummer },// Nummer des Anrufers
//			    { 4, 1, [OP] },	// Grund für das Fehlen der Nummer
//			    { 7, tLen, Text },	// Name vom Fritzbox-Telefonbuch
//			    { 8, 1, O }, checksum
//x140	30	Histogramm der Nulldurchgangszeiten, sollte 2 Höcker aufweisen
//x15E	1	1 (Start) + 1 (1010-Phase) + 1 (1111-Phase) + Empfangene Bytes
//x15F	1	Mittelwert der Nulldurchgangs-Zeiten während 1010-Phase,
//		sollte mit der Delle zwischen den Höckern zusammenfallen
//x160	128	lea = Liste entgangener Anrufe

static inline void eewait() {
 while (EECR&2);
}
#define eeread() (EECR|=1,EEDR)
//static inline byte eeread() {
// EECR|=1;
// return EEDR;
//}
static inline void eewrite() {
 cli();
 EECR|=4;
 EECR|=2;
 sei();
}
void eechange(byte b) {
 byte k=eeread();
 if (k==b) return;
 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=20) {
 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
// 0x20 = Grund 'O' = keine Durchleitung (bspw. vom Ausland)
// 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
// Bei drohendem Pufferüberlauf wird nicht mehr gespeichert.
// Der Puffer reicht für durchschnittlich 25 Anrufe.
static byte lea[128] NOINIT;	// Liste entgangener Anrufe
static byte ovw[5] NOINIT;	// Ortsvorwahl (max. 8-stellig, 1. Byte = Länge)

static byte ealen(byte l) {	// Längen-Byte in Byte-Länge der Nummer inkl. Längenbyte
 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;
}

extern byte CompareN(const byte*,const byte*);
extern byte KillVorwahl(byte*,const byte*);

// Ryckruf statt Rückruf um mit avr-gcc 5.3 compilierbar zu bleiben
static byte IstRyckruf(const byte*z) {	// liefert buf_w wenn die aktuelle Nummer
 return CompareN(z,wahl) ? 0 : buf_w;
}

// <clipbuf> zeigt auf 2 (Nummer des Anrufenden) oder 4 (Grund für das Fehlen)
// <z> zeigt in <lea>
static void ClipToLea(byte*z,const byte*clipbuf) {
 byte type=*clipbuf++;
 byte len=*clipbuf++;
 if (z+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++;
   if ('0'<=c && c<='9') c-='0';
   else if ('A'<=c && c<='D') c-='A'-10;
   else if (c=='*') c=14;
   else if (c=='#') c=15;
   else continue;
   PutNib(z+1,j++,c);
  }
  *z=j;
// Testen ob die Rufnummer mit Ortsvorwahl beginnt, und Ortsvorwahl wegnehmen
  KillVorwahl(z,ovw);
 }else{		// keine Rückrufnummer
  if (len!=1) return;		// Fehler
  byte reason=*clipbuf;
  switch (reason) {
   case 'O': j|=0x20;	// “unavailable”
   case 'P': j|=0x40;	// “private”
   default: j|=0x60;	// unknown reason
  }
  *z=j;
 }
}

static void DelRyckruf() {
 byte *x=lea,*z=x,a=anrufe;
 if (a) do{
  byte l=ealen(*z);
  if (IstRyckruf(z)) {
   z+=l;		// Quellzeiger vorrücken = Nummer löschen
   --anrufe;
  }else do *x++=*z++; while(--l);	// Bytes vorrücken
 }while(--a);
}

// liefert Nummer der Kurzwahl (1..24), 0 für keinen Treffer
static byte FindInKurzwahl(const byte*z) {
 byte idx;
 for (idx=24;idx;--idx) {
  EEARL=x10(idx);
  numLoad(wahl);
  if (!CompareN(z,wahl)) break;
 }
 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);	// 0..20 Ziffern
 DelRyckruf();
 buf_r=0;	// mit dem Abspielen beginnen
}

/**************************************
 * Frequenzsynthese = Sinustonausgabe *
 **************************************/
constexpr char LOG2(unsigned v) {return 15-__builtin_clz(v);}
// CLOG2 = Clock Logarithmus zur Basis 2
constexpr char CLOG2 = LOG2(F_CPU/1000000);
// 0 wenn F_CPU = 1..1,99 MHz
// 1 wenn F_CPU = 2..3,99 MHz
// 2 wenn F_CPU = 4..7,99 MHz (Standard)
// 3 wenn F_CPU = 8..15,99 MHz
// Bei (fest) 32 kHz Abtastrate (Syntheserate) muss die Resonatorfrequenz
// >=2 MHz sein, damit die ISR schnell genug abgearbeitet wird.

extern void clock_set(byte div);

static void clock_max() {clock_set(0);}
static void clock_1MHz() {clock_set(CLOG2);}	// Taktfrequenz 1..1,99 MHz
static void clock_16us() {clock_set(CLOG2+4);}

constexpr byte WDT_SEC(float s) {return s/0.016;}

// ADC-Taktteiler während onhook() - je schneller desto besser
constexpr char ALOG2 = LOG2(1000/500);		// 1 = ÷2 → 500 kHz → 38 kSa/s
// ADC-Taktteiler während offhook() - Strom ist genug da
constexpr char BLOG2 = LOG2(1000/125);		// 3 = ÷8 → 125 kHz → 9,6 kSa/s

static void tonEin() {
 ADCSRA = 0xA0|BLOG2+CLOG2;	// ADC-Takt anpassen
 clock_max();
 PLLCSR|= 0x82;		// Low-Speed-Modus: 32 MHz (reicht)
 _delay_us(100);
 while (!(PLLCSR&1));
 PLLCSR|= 0x04;
 TCCR1  = 0x61;
 OCR0A  = T0DIV-1;	// rund 32 kHz
 TIMSK  = 0x10;		// Compare-Interrupt
 TCCR0A = 0x02;		// Timer0: CTC
 TCCR0B = 0x01;		// Timer0 mit Vorteiler 1 starten
 T_on=WDT_SEC(TON+PAUSE);// Länge setzen
 DDRB  |= 0x02;		// Ausgang aktivieren
}

static void tonAus() {
 TIMSK  = 0;		// Timer0: Kein Interrupt
 TCCR0A = 0;		// kein PWM
 TCCR0B = 0;		// aus
 DDRB  &=~0x02;		// Ausgang hochohmig
 TCCR1  = 0;		// Timer1 aus
 PLLCSR = 0;		// PLL aus
 clock_1MHz();
 ADCSRA = 0xA0|BLOG2;	// ADC-Takt anpassen
}

// Startet Wählton für Ziffer z ("*"=14, "#"=15)
static void StartDTMF(char z) {
 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
 for (;z<0;z+=12) --okt;	// Mit div_t ist's umständlicher
 for (;z>=12;z-=12) ++okt;	// jetzt 0<=z<12
 word a=pgm_read_word(Frequencies+8+z);
 for (;okt<0;++okt) a>>=1;
 for (;okt;--okt) a<<=1;
 addB=addA=a;
 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)) {
#ifdef DDS_REG
  asm(
"	add	r10,r8	\n"
"	adc	r11,r9	\n"
"	add	r14,r12	\n"
"	adc	r15,r13	\n"
"	push	ZH	\n"
"	push	ZL	\n"
"	 ldi	ZH,hi8(SinTab)\n"
"	 mov	ZL,r11	\n"
"	 lpm		\n"
"	 mov	ZL,r15	\n"
"	 lpm	r1,z	\n"
"	pop	ZL	\n"
"	pop	ZH	\n"
"	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
"	out	%0,r0	\n"
  ::"I" (_SFR_IO_ADDR(OCR1A)));	// Undokumentiert: Leeres Constraint = Pointer
#else
 byte a=pgm_read_byte(SinTab.a+((phaA+=addA)>>8));
 byte b=pgm_read_byte(SinTab.a+((phaB+=addB)>>8));
 OCR1A=a+b-(b>>2);		// hier ohne Runden
#endif
}

/***********************************
 * Nummernschaltkontakt-Auswertung *
 ***********************************/
// Abarbeitung nach dem LIFO-Prinzip
static void Ryckruf() {
 byte *z;
 for (byte l,i=anrufe;;) {
  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);
 DelRyckruf();
 KurzSpeichern(0);
 buf_r=0;	// mit dem Abspielen beginnen
}

static void clearBuf() {
 buf_r=buf_w=0; 
}

static void ledOn() {
 if (PB5DEBUG) return;	// Nicht im Debugmodus
 if (DDRB&0x20) return;	// Nicht wenn bereits an oder aus
 if (!(PINB&0x20)) return;	// Nicht wenn Erdtaste gedrückt
 PORTB&=~0x20;
 DDRB |= 0x20;
 Flags|= 0x20;	// merken
}

static void ledOff() {
 if (!(Flags&0x20)) return;	// nicht eingeschaltet
 DDRB &=~0x20;
 PORTB|= 0x20;	// Pullup restaurieren
 Flags&=~0x20;	// zurücknehmen
}

// Konvertiert Impulszahl nach Ziffer; hier: 10 Pulse = 10, nicht 0
// Hier muss <i> für schwedische oder neuseeländische Telefone angepasst werden
static byte nsi_transcode(byte i) {
// Schweden: Anordnung 0-1-2-3-4-5-6-7-8-9 (Quelle: Wikipedia)
// if (!--i) i = 10;
// Neuseeland: Anordnung 0-9-8-7-6-5-4-3-2-1
// i = 11-i;
 return i;
}

// Nummernschaltkontakt an PB0 sowie Erdtaste an PB5 auswerten.
// Die Historie dieser beiden Bits befindet sich in Schieberegistern.
// Aufruf alle 16 ms per Watchdog-Interrupt aus onhook- oder offhook-Schleife.
static void NSK() {
 Flags&=~0x80;		// Watchdog-Interrupt quittieren
 byte nsi=NSI;		// Lokales nsi
// Entprellfunktion: Mindestens 32 ms Hi
 if ((pb0&7)==0b100) {	// nsi oder nsa öffnet [typ. 62 ms]
  ++nsi;		// Lo-Hi-Flanken zählen
  GIFR=0x20;		// Erkannte Pegelwechsel quittieren
  rend=0;		// Offhook-Wähl-Timeout setzen
  Zeit8=WDT_SEC(0.2);	// zur Feststellung ob nsa oder nsi
 }else if (nsi && (pb0&3)==0b01) {	// nsi schließt (kein extra Entprellen) [typ. 38 ms]
  Zeit8=0;		// Kein Timeout wenn Lo nach 1. nsi
 }else if (!nsi && (pb0&15)==0b0111) {	// nsa schließt: Mindestens 48 ms Lo
  if (Flags&0x10) {	// Erdtaste solo gedrückt?
   Flags&=~0x10;	// Solofunktion zurücknehmen
   Flags|= 1;		// Ebene 3 ansteuern
   Flags|= 2;
  }else{
   Flags&=~1;		// Ebene 0 ansteuern
   Flags&=~2;		// Etappenzähler für „nsa halten“ rücksetzen
  }
  Zeit8=WDT_SEC(1.5);
 }else{			// keine Flanke
  if (Zeit8 && !--Zeit8) {	// Timeout
   if (nsi) {		// Nummernscheibe abgelaufen (nur bei Hi-Pegel)
    if (!(GIFR&0x20)	// Kein < 16 ms kurzes letzes („übersehenes“) Low?
     && !--nsi) {	// Letzte Lo-Hi-Flanke durch nsa-Kontakt abziehen
     StartTon(-2);	// Fehler: Nur 1 langes Low detektiert: Wählscheibe weniger als 30° aufgezogen = Fehlbedienung
     goto raus;		// Oder soll man das als Sonderfunktion interpretieren?
    }
    if (nsi>10) {	// Zu viele Impulse?
     StartTon(-3);	// Fehler: Mehr als 10 Impulse, Sonderwählscheibe?
     goto raus;
    }
    nsi=nsi_transcode(nsi);
// Eigentliche Aktion
    if (Flags&8 && !(Flags&1) && !(Flags&2)) goto putbuf0;
    if (Flags&8 || Flags&4) {	// nsi = Ziffer zum Kurzwahl-Speichern
     if (Flags&2) {
      if (Flags&1) {	// n³
       if (nsi<5) nsi+=18;	// 19..22 = Speicherplätze A-D
       else {nsi+=5; goto putbuf1;}	// 10..15 = Möglichkeit des Abspeicherns von A,B,C,D,* und #
      }else{		// n²
       nsi+=10;			// 11..18
       if (nsi>18) nsi+=4;	// 23..24 (Ortsvorwahl, Autowahl)
      }
     }
     KurzSpeichern(nsi);
     StartTon(5);
     Flags&=~0x0F;
     goto raus;		// nicht wählen
    }else if (Flags&2) {
     if (Flags&1) switch (nsi) {// n³ = ganz lange halten am Fingeranschlag
      case 5: KurzLaden(0); goto raus;		// Wahlwiederholung
      case 6: Ryckruf(); goto raus;
      case 7: Flags|=4; StartTon(5); goto raus;	// Letzte Nummer speichern: Kurzwahlspeicherplatz-Ziffer folgt
      case 8: Flags|=8; clearBuf(); StartTon(0); goto raus;	// Nummer einspeichern: Nummer + Kurzwahlspeicherplatz-Ziffer folgen
      case 9: clearBuf(); KurzSpeichern(0); goto raus;	// Wahlwiederholung verhindern
      case 10: anrufe=0; goto raus;
      default: KurzLaden(18+nsi); goto raus;		// 19..22 (A,B,C,D) wählen
     }else switch (nsi) {	// n²
      case 9:
      case 10: nsi+=14-9; break;	// '*' oder '#'
      default: KurzLaden(10+nsi); goto raus;
     }
    }else if (Flags&1) {	// n¹
     KurzLaden(nsi);		// wählen lassen (1..10), Hauptschleife generiert Töne
     goto raus;
    }else
putbuf0: if (nsi==10) nsi=0;
putbuf1: 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 (pb0&1) {		// Vor nsi-Impulsen (Lo-Pegel)
    if (!(Flags&1 && Flags&2)) ++Flags;	// Zu lange gehalten: ignorieren, weiterpiepen
    StartTon(1+(Flags&3));	// Töne d-dis-e
    ledOn();			// Bei jedem Timeout mitblitzen
    Zeit8=WDT_SEC(1);
   }
  }
 }
 NSI=nsi;
// Entprellfunktion: Mindestens 4 × 16 ms = 64 ms Lo)
 if (!T_on && (pb5&31)==0b01111) {	// Erdtaste gedrückt
  if (!pb0) Flags|=0x10;	// Solo: Vermerken (n³ ansteuern lassen)
  else if (pb0==255) Flags|=1;	// Beim Festhalten des Nummernschalters: n¹
  else Flags|=2;		// Während Ablauf des Nummernschalters: n²
// Entprellfunktion: Mindestens 4 × 16 ms = 64 ms Hi)
 }else if (!(pb5&15) && Flags&0x10) {	// Nummernschalter nicht zwischendurch betätigt?
  Flags&=~0x10;			// Flag löschen
  if (anrufe) Ryckruf();	// Solo-Aktion = Rückruf oder Wahlwiederholung
  else KurzLaden(0);
 }
 EEARL=252;
 eechange(anrufe);		// im EEPROM nachführen, falls geändert (auch im aufgelegten Zustand)
}

// 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 void readPb2(byte adc1value) {
 constexpr byte offhooklevel=0xC0;	// High bei 75 %
 pb2 = pb2<<1 | (adc1value>=offhooklevel);	// High bei 75 %
}

static void readPins() {
 byte npinb = ~PINB;		// invertiert
 pb0 = pb0<<1 | npinb&1;
#if PB5DEBUG
 if (DDRB&0x20) return;		// Wenn Ausgang dann Erdtaste NICHT einlesen
 pb5 = pb5<<1 | npinb>>5&1;
#else
 if (Flags&0x20) {		// Wenn LED ein
  DDRB &=~0x20;			// LED aus, auf Eingang schalten
  PORTB|= 0x20;			// Pullup aktivieren
  _delay_us(100);		// etwas warten (Entladelast onhook selten, vertretbar)
 }
 pb5 = pb5<<1 | npinb>>5&1;	// Erdtaste einlesen
 if (Flags&0x20) {
  PORTB&=~0x20;			// Pullup aus
  DDRB |= 0x20;			// LED ein
 }
#endif
}

// 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=0) {
 while (!(ADCSRA&0x10));// warten auf Ende, kann bis zu 50 µs dauern
 readPb2(ADCH);
 readPins();		// einlesen, nachdem sich alle Pegel eingeschwungen haben
 ADCSRA= 0;		// ADC abschalten — sonst gibt's zusätzlichen Stromverbrauch.
 if (!(DDRB&0x20)) PORTB&=~0x20;	// Gedrückte Erdtaste soll Elko nicht entladen
 PORTB&=~0x01;		// Aufgezogene Wählscheibe soll Elko nicht entladen
 PORTB&=~0x04;		// kein Pullup: Versorgungsstrom 120 µA fließt über die negative Gateschutzdiode
 DDRB |= 0x04;		// Floating-Eingang auf Low festnageln, um Gateschutzdiode zu schonen
 if (us48) {
  clock_16us();		// Taktvorteiler erhöhen für minimalen Stromverbrauch
  _delay_loop_1(us48);	// jede Runde dieses Delays benötigt 3×16µs
  DDRB &=~0x04;		// Pullup für A/D-Wandler früh vorbereiten
  PORTB|= 0x04;
 }else{
  clock_max();		// kürzestmögliche Hochlaufzeit spart am meisten Strom (!)
  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), etwas warten
 }
// Die Unterprogrammaufrufe genügen zur Verzögerung.
// Warten mit dem A/D-Konverter, bis sich der Pegel an Pin 7 stabilisiert hat.
// Der Kondensator am Pin 7 (wenn bestückt) wirkt als Tiefpass.
 clock_1MHz();		// (10 × 16 µs)
 PORTB|= 0x01;		// Restliche Pullups (in umgekehrter Reihenfolge) aktivieren
 if (!(DDRB&0x20)) PORTB|= 0x20;	// bei aktivem DDRB ist's die LED oder der Debug-Ausgang: Unbeeinflusst lassen
 ADCSRA= 0xD0|ALOG2;	// Start mit Teiler 2: 500 kHz, 2µs, ×25 = 50 µs, einmalig, ohne Interrupt
 if (Flags&0x80) {
  ledOff();		// LED hat nun 16 ms geleuchtet: Aus!
  NSK();		// (d: Irrtümliche Wählimpulse durch Klingeln werden später gekillt)
 }
}

/********************
 * 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");
}
ISR(ANA_COMP_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“ (nicht in EEPROM)
static void saveClip(byte*z) {
 byte i=2,j=clip[1]+2;	// j = Endindex der Nachricht
 if (j>sizeof clip) j=sizeof clip;
 do{
  if (clip[i]==2 || clip[i]==4) {
   ClipToLea(z,clip+i);
   return;
  }else i+=clip[i+1]+2;	// Länge des unbekannten Chunks
 }while(i<j);
}

extern void acInit();
extern byte acMean();
//Beim ATtiny85 gibt es Debug-Funktionalität: Logging im EEPROM
#ifdef __AVR_ATtiny85__
static byte histo[32] NOINIT;
extern void acHisto(byte data[],byte len);
#endif
extern void acOnes();
extern byte acByte();
extern void acDone();

// mit 1..2 MHz CPU-Takt
static void clipRead(byte*z) {
// STELLSCHRAUBE, die entscheidend für die nötige Kapazität von C1 ist
 byte i=34;	// 34 trifft am Thomson-Modem den Anfang der Schwingungen.
// Laut Standard sind es 250 ms nach dem Klingeln. Daher Minimum = 5.
// Laut „102s10.pdf“ sind's 500 ms, und das stimmt auch für's Thomson-Kabelmodem.
// Kleinere Zahlen fressen mehr Zeit und mehr Strom für die Detektierung
// d: Exakt platziert fällt die 470-µF-Elko-Spannung von 4,3 auf 3,2 V (642 ms)
//    Das macht 805 µA mittlere Stromaufnahme, immerhin. Im Interruptbetrieb.
//    Zu frühes Starten des Analogvergleichers frisst mehr Strom
//    weil Interrupts sehr dicht kommen. Siehe Oszillogramm.
 do{
  waitforwatchdog();	// je 16 ms, aber das stimmt irgendwie nicht: 34×16 » 500!
  if (pb2&1) return;	// (Zweites Klingeln oder) Abheben
 }while (--i);		// weitere ms energiesparend warten
#ifdef __AVR_ATtiny85__
 memset(histo,0,sizeof histo);
# if 0
 memset(clip,0xFF,sizeof clip);	// memset mit Füllbyte!=0 wird nicht wegoptimiert
# else
 asm(
"	mov	r0,%1	\n"
"	com	r1	\n"
"0:	st	%a0+,r1	\n"
"	dec	r0	\n"
"	brne	0b	\n"
"	com	r1	\n"
 ::"e"(clip),"r"(sizeof clip));
# endif
#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[30]=1;
 histo[31]=acMean();	// 256 valide Nulldurchgänge detektieren, Mittelwert liefern
 acHisto(histo,30);	// 256 Nulldurchgänge messen und im Histogramm verteilen
 histo[30]=2;
#else
 acMean();
#endif
 acOnes();		// Folge von 100 tieffrequenten Nulldurchgängen finden
#ifdef __AVR_ATtiny85__
 histo[30]=3;
#endif
 wdt_reset();		// nach 100 langen Nulldurchgängen
 byte sum=0;
 for (i=0;; i++) {
  byte c=acByte();	// Das erste Byte wird nicht auf 0x80 geprüft
#ifdef __AVR_ATtiny85__
  histo[30]++;
#endif
  sum+=c;
  if (i<sizeof clip-1) clip[i]=c;
  if (i && i==clip[1]+2) break;	// Gesamtnachrichtenlänge erreicht
 }
#ifdef __AVR_ATtiny85__
 if (i<sizeof clip-1) clip[i]=sum;
#endif
 acDone();
 WDTCR = 0x1E;
 WDTCR = 0xC0;		// Watchdog normal (gemessen 16,4 ms)
 MCUCR = 0xB4;
 MCUCR = 0xB0;		// Sleep-Modus: PowerDown, kein BrownOut
 sei();
 if (!sum) saveClip(z);	// Nur bei korrekter Prüfsumme akzeptieren
}

/************************************
 * 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() {
 byte i=0,j=0;	// geht mit PB2 = High in die Routine, d.h. pb2&1 ist nach waitforwatchdog() = 1
 for(;;){
  waitforwatchdog(100);	// 5 ms
  if ((pb2&3)==0b01) {	// (Erneuter) Lo-Hi-Wechsel
   ++j; i=0;		// Diese Flanken zählen
  }else if ((pb2&3)==0b10) {	// 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() {
 MCUCR = 0xB4;
 MCUCR = 0xB0;		// Sleep-Modus: PowerDown, kein BrownOut
 ADMUX = 0x21;		// ADLAR, ADC1 = PB2
 ADCSRA= 0xD0|ALOG2;	// einmalig (wird in waitforwatchdog erneut gestartet)
 clearBuf();		// Rufnummer tilgen
 for(;;){		// warte bis 75% Spannung an PB2 == ADC1
  waitforwatchdog();	// wertet Wählscheibe und Erdtaste aus
  if (pb2&1) {		// Spannung >= 75%: Wechsel- oder Gleichspannung?
// 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 (pb2&1) return;	// raus wenn statisch >75%: Abgehoben oder Paralleles Telefon
// TODO: Zusätzlich sollte beim Abheben das Freizeichen detektiert werden, wenn Flags.6 gesetzt
// Es ist hier Sache von offhook(), den Fall „Paralleles Telefon“ zu verarbeiten,
// obwohl der Hörer /dieses Telefons/ immer noch auf der Gabel liegt (=onhook).
   NSI=0;		// Durch AC-Kopplung „gewählte“ Nummer tilgen
   rend=0;		// 4 Sekunden nach dem letzten Klingeln sei ein (verpasster) Anruf zu Ende
//   if (j<4) continue;	// Weniger als 4 Pegelwechsel: Störung, kein Klingeln (was soll das sein??)
   if (!(Flags&0x40)) {	// Erstes Klingeln?
    Flags|=0x40;	// bemerken
    byte*z=eaindex(anrufe);
    *z=0;		// Eintrag „Keine Rufnummer“ — noch nicht
    EEARL=252;
    eechange(++anrufe);	// Anruf verpasst (5 ms Schreibzeit) - einzige Stelle die inkrementiert!
    clipRead(z);	// CLIP einlesen, KANN FALLWEISE NICHT ZURÜCKKEHREN
   }
// TODO: Lässigere 5 Sekunden passen derzeit nicht in <rend>
  }else{	// Spannung < 75%: 2 Zähler auswerten und in Schleife bleiben
   if (!++rend) {
    Flags&=~0x40;	// Klingel-Ende
// Sofort mit dem Ablauf des Klingel-Timeouts losblitzen, so sieht man das Timeout
// Es kann aber auch sein, dass das Gespräch bei zeitigerem Abheben weg ist
    if (anrufe) ledOn();	// LED 16 ms aufblitzen lassen, dann 4 s aus
    buf_w=0; // Timeout für's Wählen bei aufgelegtem Telefonhörer: 256×16ms=4s
   }
  }
 }
}

/****************************
 * Feststellung Freizeichen *
 ****************************/
// 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
constexpr byte DOWNLEVEL=0x40;
// 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() {
 tonAus();
 PORTB = 0;	// Keine ohmschen Verbraucher: Keine Pullups!
 DDRB  = 0x05;	// Eingänge zu Ausgängen machen und festnageln: Gateschutzdioden entlasten
 do{
  ADCSRA= 0;	// ADC aus
  WDTCR = 0xE1;	// 8 s (Maximum)
  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 = 0xC0;	// 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>=DOWNLEVEL-2);	// 0x3E: Hysterese von 2 (gegenüber 0x40)
}

// Anscheinend dauert es lange bis die Referenzspannung steht.
// Daher wird zwischen adPin7 und adRef alle 16 ms hin- und hergeschaltet.
static void handleADC() {
 static byte adRef NOINIT;
 switch (ADMUX) {
  case 0x01: {		// maß ADC1 = PB2 bzgl. Speisespannung?
   ADMUX |= 0x20;
   readPb2(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>=DOWNLEVEL && a>b) {// Ucc < 4,4 V (Uref > Ucc × 25%) und fallend
    phoneParallel();	// hinterlässt DDRB.0 gesetzt
   }
  }/*nobreak*/;
  default: ADMUX = 0x01;	// umschalten auf ADC1 = PB2
 }
}

static byte f425;	// Alle 32 ms ein Bit, 8 Bit = 256 ms
extern bool mess425();

static void m425() {	// Messung 425 Hz, c: mit ADC oder d: mit AC
 if (f425&1) ledOn();	// bei vorherigem Freizeichen
 f425=f425<<1|mess425();	// ggf. Abbruch wenn Messung nicht möglich
 while (!(Flags&0x80)) sleep_cpu();	// warten bis Watchdog-Interrupt (16 ms)
 handleADC();
}

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

// Für die zyklische Ausgabe von Kenntönen für verpasste Anrufe
static byte pause,index NOINIT;	// nur offhook verwendet

// Zyklisch aufzurufen: Arbeitet alle <anrufe> ab und generiert Zweiton-Töne
static void piepAnrufe() {
 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>=19) f=-(1+(++l>>5));	// kein Rückruf möglich
 else f=FindInKurzwahl(z);	// Rückwärtssuche im „Telefonbuch“
 StartTon(f);			// CLIP-Eintrag auf diese Weise hörbar machen
 if (++index>=anrufe) index=0;
 else pause=WDT_SEC(0.5);
}

static void offhook() {		// CPU-Taktfrequenz: 1..2 MHz
// Mögliches Problem: Speicheraktionen wenn paralleles Telefon abgehoben entlädt Elko unnötig
// Aber: Anders als durch (u.a. dadurch verursachten) Speisespannungsabfall ist's eh' nicht feststellbar
 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;
 }
 byte j=eaindex(anrufe)-lea;
 for (i=0; i<j; i++) {
  eechange(lea[i]);
  ++EEARL;
 }
 for (;EEARL;++EEARL) eechange(0xFF);	// Rest löschen
 EEARH = 0;
#endif
 MCUCR = 0x20;		// Sleep-Modus: Idle (Timer kann laufen, Strom ist genug da)
 ADCSRA= 0xF0|BLOG2;	// /8 = 125 kHz / 13 = 9,6 kSa/s
 T_on   = 0;
 byte no_dial=WDT_SEC(1);	// besser: auf Freizeichen warten
 byte a=anrufe;
 byte b=0;
 if (Flags&0x40) {	// assert(a)
// Beim Abheben während des Klingelns nachgucken, ob CLIP im Rückrufspeicher liegt.
// Wenn ja dann alle Kopien davon aus dieser Liste tilgen.
// Eine möglicherweise (bei aufgelegtem Hörer, vor dem Klingeln)
// bis dahin gewählte Nummer wird überschrieben.
  byte*z=eaindex(a-1);
  byte l=*z;
  if (--l<19) {		// 0 < l < 20
   buf_r=++l;
   memcpy(wahl,z,11);	// Für Vergleichsvorgang innerhalb DelRyckruf() in den Wahlspeicher kopieren
   DelRyckruf();	// reduziert <anrufe>
   b=a-anrufe;		// sollte >=1 sein, wenn CLIP erfolgreich
   if (b) --b;		// Wenn mehr als 1 Anruf von derselben Nummer, dann piepsen, sonst nicht
  // Dann weiß man, dass dieser Anrufer bereits vorher versucht hatte anzurufen.
  // Den Ton hören beide Partner.
   KurzSpeichern(0);	// Zur „Wahlwiederholung“ speichern
// (Als Ausnahme von der Regel, dass da nur selbst gewähltes hinein darf.
//  Der Grund ist, dass man diese Nummer während oder nach dem Gespräch
//  in einen Kurzwahlspeicher mit 7³ legen kann.)
  }
 }
 pause=WDT_SEC(1);
 index=0;
 for(;;) {		// Hauptschleife
  m425();	// blockiert bis zum Watchdog-Interrupt
  ledOff();
// Sonst funktioniert der Vergleich mit der Referenzspannung nicht!!
  if (DDRB&1) {	// wenn handleADC() phoneParallel() festgestellt hatte, dann raus hier
   DDRB&=~1;		// PB0 = Ausgang = Kennung dafür, wegnehmen
   break;
  }
  readPins();
  if ((pb2&7)==0b100) 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();
  if (T_on) continue;		// Ton- oder Pausenausgabe läuft
  if (no_dial) continue;
  if (b) {StartTon(b); b=0;}	// Tonhöhe je nach Anzahl der Anrufversuche
  else if (buf_r!=buf_w) StartDTMF(GetBuf());	// Nächsten Ton + Pause ausgeben
 }
 if (T_on>=(byte)WDT_SEC(PAUSE)) tonAus();
}

/*************************************
 * Initialisierung und Hauptschleife *
 *************************************/
extern "C" void __init() __attribute__((naked,section(".init2")));
extern "C" void __init() {
 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
 Flags = MCUSR&0x08 ? 0x40 : 0;		// Reset-Ursache = Watchdog bedeutet: Timeout beim CLIP-Empfang
 MCUSR = 0;
 WDTCR|= 0x18;
 WDTCR = 0xC0;		// Watchdog auf Interrupt, 16 ms = 62 Hz (einzige Interruptquelle)
 PORTB = 0x25;		// Pullup für 3 Eingänge
}

static void hardwareInit() {
 eewait();
 EEARH = 0;		// Auch für ATtiny25/45 so compilieren für Portierbarkeit; EEARH ist nach Reset uninitialisiert
 EEARL = Flags&0x40 ? 254 : 255;
 eechange(eeread()+1);	// Reset- oder Watchdog-Reset-Zähler, macht sei()
 EEARL = 252;
 byte a=eeread();
 if (a>32) a=0;
 anrufe= a;
 if (!(Flags&0x40)) {
  rend=0;		// Nach normalem Reset den ersten Blitz verzögern
#ifdef __AVR_ATtiny85__
  EEARH = 1;
  EEARL = sizeof clip+sizeof histo;
  for (byte i=0; i<sizeof lea; i++) {
   lea[i]=eeread();	// Luxus ATtiny85: Anrufliste wiederherstellen
   ++EEARL;
  }
  EEARH = 0;
#else
  memset(lea,0,a);	// Anrufliste besteht aus unbekannten Anrufen
#endif
  EEARL = 230;		// 23×10: Ortsvorwahl
  numLoad(ovw,8);
 }			// Bei Watchdog-Reset bleibt <rend> und <lea> bestehen
 ACSR |= 0x80;		// Analogvergleicher ausschalten
 DIDR0 = 0b011110;	// diese digitalen Eingänge nicht nutzen
 PCMSK = 0x01;		// Nur PB0 = Wählscheibe überwachen (ohne Interrupt)
}

int main() __attribute__((OS_main));
int main() {
 hardwareInit();
 for(;;) {
  onhook();		// Aufgelegter Hörer: Minimaler Stromverbrauch
  offhook();		// Abgehobener Hörer: Normale Funktion
  Flags&=~0x40;		// Aktiven Anruf beenden
  rend=0xFF;		// Sofort blitzen
 }
}
Detected encoding: UTF-80