Source file: /~heha/basteln/Haus/Telefon/Impulswahl→DTMF/mfv.zip/mfv3a/mfv3a.c

/* Programm für ATtiny25, „h#s“ Henrik Haftmann, TU Chemnitz
 * tabsize = 8, encoding = utf-8 (ohne Windows-Header BOM)
 090331	Anfang
 */
#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>

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)	-	Relais oder pnp-Transistor, L-aktiv
PB1	(6)	OC1A	PWM-Tonausgang, reichlich (125 kHz) Trägerfrequenz
PB2	(7)	ADC1	Amtsader La über Widerstand 680 kΩ
PB3	(2)	XIN	Resonator 4 MHz
PB4	(3)	XOUT	Resonator 4 MHz
PB5	(1)	ADC0	Erdtaste / Wählen bei aufgelegtem Hörer
GND	(4)		Telefonader b
Ucc	(8)		Amtsader Lb;Z-Diode 5,6 V und Elko ≥ 100 µF nach GND

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							Kurzwahl
 LANG×2 am Finger-Anschlag halten (bis zum zweiten Piep):
  1..4 = "A".."D" wählen
  5 = Wahlwiederholung							Autowahl
  6 = Anrufliste / Letzte Rufnummer löschen (= Wahlwiederholung verhindern)
  7 = Speichern der zuletzt gewählten Rufnummer (Ziffer folgt)
  8 = Speichern einer neuen Rufnummer (Nummer folgt, dann LANG + Ziffer)
  9 = "*" wählen
  0 = "#" wählen

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

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 so ziemlich alles
Analogvergleicher: ungenutzt
Interrupts: Zählerüberlauf Timer 0, Watchdog-Timer
EEPROM: Nummernspeicher für 10 Kurzwahlen und 1 Wahlwiederholung, 1 Autowahl,
feste Adressen
Flash-Selbstprogrammierung: ungenutzt
*/

// 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
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[12] = {
 FREQ( 697),
 FREQ( 770),
 FREQ( 852),
 FREQ( 941),
 FREQ(1209),
 FREQ(1336),
 FREQ(1477),
 FREQ(1633),
 FREQ(1046),	//c³
 FREQ(1175),	//d³
 FREQ(1319),	//e³
 FREQ(1397),	//f³ (Halbtonschritt)
};

// Funktionsprinzip: DDS
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");
volatile register byte opinb	asm("r3");
volatile register byte Ton	asm("r2");	// Rest-Ton/Pausenlänge, in 16 ms
#define Flags GPIOR0		// Bits für NSK()
//	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..2
#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)

typedef union{
 struct{
  word dc;	// 0..65535	Gleitender Mittelwert
  int  ac;	// -1023..+1023	Momentanabweichung vom Mittelwert = Addierwert
 };
 struct{
  byte dcl,dch,acl;
  char ach;
 };
}adcval;
static adcval adc0,adc1;
void onadc(adcval*v) {
 v->dc+=v->ac=ADC-(v->dc>>6);	// Neuer Wechselanteil und gleitender Mittelwert
}
static byte dis1h,dis1l;

static char numbers[21];		// Puffer für Wählziffern
#define buf_w numbers[0]

// Puffer füllen
static void PutBuf(char c) {
 if (buf_w<20) numbers[1+buf_w++]=c;
}

static void eewait(void) {
 while (EECR&2);
}

static void eewrite(void) {
 cli();
 EECR=4;
 EECR|=2;
 sei();
}

// Erlaubte c: /[0-9*#A-D]/
static char toNibble(char c) {
 if (c=='*') return 10;
 if (c=='#') return 11;
 if ('0'<=c && c<='9') return c-'0';
 if ('A'<=c && c<='D') return c-'A'+12;
 return -1;
}

// Erlaubte n: 0..15
static char fromNibble(char n) {
 if (n<10) return n+'0';
 if (n==10) return '*';
 if (n==11) return '#';
 if (n<16) return n-12+'A';
 return 0;
}

// 10 Bytes Puffer pro Nummer (20 Ziffern)
// idx zwischen 0 (letzte Nummer) und 10 (Kurzwahl 0)
static void KurzSpeichern(byte idx) {
 eewait();
 EEARL=(idx<<3)+(idx<<1);	// *10
 byte i;
 Flags&=~0x80;	// Nullterminierung erkennen
 for (i=0;i<20;i++) {
  char c=numbers[i];
  if (!c) Flags|=0x80;	// Nullterminierung erkannt
  char n;
  if (Flags&0x80) n=15;	// Nibbles sind 'F'-terminiert
  else n=toNibble(c);
  if (!(i&1)) EEDR=n<<4;	// direkt lesbar beim Lesen des EEPROMs
  else{
   EEDR|=n;
   eewrite();
   eewait();
   ++EEARL;
  }
 }
}

static void KurzLaden(byte idx) {
 eewait();
 EEARL=(idx<<3)+(idx<<1);	// *10
 byte i,j;
 for (i=j=0;i<20;i++) {
  char n;
  if (!(i&1)) {
   EECR|=1;
   n=EEDR>>4;
  }else n=EEDR&15;
  if (n!=15) j=i+1;	// letzte gültige Ziffer auffinden
  numbers[i]=fromNibble(n);
 }
 if (j<20) numbers[j]=0;	// "FFF" abhacken
 buf_r=0;	// mit dem Abspielen beginnen
}

static void StartTon(char z);

// Nummernschaltkontakt-Auswertung, Aufruf mit F_CPU/65536
// Der A/D-Wandler liefert die Werte für adc1
// Maximum = nsa (0 V über Telefon)
// Nahe Maximum = Normalfall (2..7 V über Telefon)
// Niedriger = nsi (12 V über Telefon durch Z-Diode)
static void NSK(void) {
 if (Flags&0x40) {	// Neues Paar A/D-Werte
  Flags&=~0x40;
  if (adc1.dch>dis1h) {	// nsa betätigt? (Start Nummernscheibe aufziehen)
   nsi=0;
   Zeit8=0;
   Flags&=~3;		// Etappenzähler für „nsa halten“ rücksetzen
  }else if (nsi) {	// nsa losgelassen? (Nummernscheibe abgelaufen)
// Eigentliche Aktion
   if (Flags&4) {	// nsi = Ziffer zum Kurzwahl-Speichern
    KurzSpeichern(nsi);
    Flags&=0xF0;
    StartTon(19);
    goto raus;		// nicht wählen
   }else if (Flags&2) switch (nsi) {	// LANG×2 = Ganz langes Halten am Fingeranschlag
    case 10: numbers[0]=0; KurzSpeichern(0); goto raus;	// Wahlwiederholung verhindern
    case 1: KurzLaden(0); goto raus;		// Wahlwiederholung
    case 2: Flags=Flags&0xF0|4; StartTon(19); goto raus;	// Letzte Nummer speichern: Ziffer folgt
    case 3: Flags=Flags&0xF0|8; buf_r=0; StartTon(16); goto raus;	// Nummer einspeichern: Nummer + Ziffer folgen
    default: nsi+=10-4;		// A,B,C,D,*,# wählen
   }else if (Flags&1) {		// LANG
    KurzLaden(nsi);		// wählen lassen (1..10), Hauptschleife generiert Töne
    goto raus;
   }else if (nsi==10) nsi=0;
   PutBuf(nsi+'0');		// Zählergebnis abspeichern (Hauptschleife generiert Ton anschließend)
   if (Flags&8) {
    // Nicht wählen (Hauptschleife nicht in Aktion treten lassen)
    StartTon(18);
   }else Zeit16=4*F_CPU/65536;	// Wahlwiederholungs-Abspeicher-TimeOut setzen (5 Sekunden)
raus:;
  }
 }else if (adc1.dch<dis1l) {
  if (!++Zeit8) {
   if (!(Flags&2)) ++Flags;	// Zu lange gehalten: ignorieren, weiterpiepen
   if (Flags&8) Flags|=0x0E;	// Einspeicher-Modus? Umschalten zum Abspeichern wie LANG×2 + 2
   StartTon(15+(Flags&3));	// Ton 16 oder 17 
  }
 }
// if (ckey&1 && key&1) {	// nsi (Nummernschalter) gerade geöffnet?
//  nsi++;			// Anzahl der Öffnungen zählen
//  Zeit8=0;			// Zeitmessung für nsa verhindern
// }
}

// Wechselweise ADC0 (PB5) und ADC1 (PB2) abfragen (600 Hz)
//ISR(ADC_vect) {
// if (ADMUX&1) {
//  adc0=ADCH;	// Da ADMUX erst mit dem Interrupt wirksam wird, ist hier der Wert von ADC0!
//  ADMUX&=~1;
// }else{
//  adc1=ADCH;	// Beim ersten ISR-Aufruf ist's ADC0, beim dritten ADC1
//  Flags|=0x40;
//  ADMUX|=1;
// }
//}

// Kernroutine der Frequenzsynthese
// Aufruf mit F_CPU/256 = 15625 .. 78125 Hz
ISR(TIM0_OVF_vect) {
// Tonlänge nominell 70 ms, Pause 30 ms
// Ton>=0 = Tonausgabe, sonst Pause
 if (Ton>=(byte)(PAUSE*F_CPU/65536)) {
#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
 }
 if (CLKPR || !--Teiler) {
  asm volatile("rcall __vector_1");	// kein ((void(*)(void))__vector)(), denn das führt zum weiträumigen Register-Push
 }
}

ISR(_VECTOR(1),ISR_NOBLOCK) {
 if (Ton) --Ton;
// NSK();		// Nummernschaltkontakt-Auswertung
 if (Zeit16 && !--Zeit16) KurzSpeichern(0);
}

static void pwm_on(void) {
 if (!(DDRB&0x02)) {
  cli();
  CLKPR=0x80;
  CLKPR=0;
  PLLCSR|=0x82;
// _delay_us(100);
  while (!(PLLCSR&1));
  PLLCSR|=0x04;
  TCCR1=0x61;
  DDRB|=0x02;			// Ausgang aktivieren
  sei();
 }
}

static void pwm_off(void) {
 if (DDRB&0x02) {
  cli();
  DDRB&=~0x02;			// Ausgang hochohmig
  TCCR1=0;			// Timer1 aus
  PLLCSR=0;			// PLL aus
  CLKPR=0x80;
  CLKPR=0x08;			// Taktteiler /256 (15 .. 78 kHz)
  sei();
 }
}

// Startet Ton für Ziffer z
static void StartTon(char z) {
 static PROGMEM const byte Nr2HiLo[20]={
//   0    1    2    3    4    5    6    7    8    9    *   #    A    B    C    D    ..Hinweistöne..
  0x53,0x40,0x50,0x60,0x41,0x51,0x61,0x42,0x52,0x62,0x43,0x63,0x70,0x71,0x72,0x73,0x88,0x99,0xAA,0xBB};
// 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 n=toNibble(z);
 if (n<16) z=n;
 if ((byte)z>=20) return;	// sollte nie vorkommen
 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
 phaA=phaB;
 pwm_on();
 Ton=(TON+PAUSE)*F_CPU/65536;	// Tonausgabe in ISR starten
}

static void sleep(void) {
 PORTB|= 0x20;
 sleep_cpu();
 PORTB&=~0x20;
}

EMPTY_INTERRUPT(PCINT0_vect);
EMPTY_INTERRUPT(WDT_vect);

static void wdt_set(byte v) {
 cli();
 wdt_reset();
 WDTCR = 0x18;
 WDTCR = v;
 sei();
}

static void wdt_off(void) {
 wdt_set(0);
}

static void eecount(byte a) {
 eewait();
 EEARL=a;
 EECR|=1;
 EEDR++;
 eewrite();
}
static void eestore(byte a, byte v) {
 eewait();
 EEARL=a;
 EEDR=v;
 eewrite();
}

/************************************
 * Routinen für aufgelegten Zustand *
 ************************************/

// Handelt es sich beim Pegel an PB2 um Gleichspannung (permanent High oder auch Low)
// oder Klingelwechselspannung 25 Hz / 50 Hz?
// Routine kehrt erst nach Ende des Klingelns zurück.
// Liefert Anzahl detektierter Schwingungen.
// Kann sein, dass sich statisches High durch ein parallel geschaltetes und abgehobenes Telefon ergibt,
// das wird in offhook() verarbeitet.
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;
}

// Im aufgelegten Zustand warten.
// Außer dem Watchdog läuft KEINE Peripherie, KEIN Brownout-Detektor und auch KEIN Quarz,
// außer mal kurz wenn's etwas zu tun gibt.
static void onhook(void) {
 cli();
 MCUCR = 0x84;		// BOD Sleep: 15 µA einsparen
 MCUCR = 0xB0;		// Sleep-Modus: PowerDown (nur PCINT2)
 PORTB|= 0x01;		// Relais aus
 PORTB&=~0x04;		// Pullup aus
 DIDR0&=~0x04;		// Digitaler Eingang: Nur PB2
 PCMSK = 0x04;
 GIMSK = 0x20;		// Pegelwechsel-Interrupt zum Aufwecken
 ADCSRA= 0;		// A/D-Wandler aus
 TCCR0B= 0;		// Timer0 aus
 PRR   = 0x0F;		// Alle Peripherie aus
 sei();
 for(;;){
// Erstes Klingeln oder Abheben abwarten
  eecount(0x72);
  while (!(PINB&0x04)) sleep();	// warten bis high
  eecount(0x73);
  WDTCR = 0x4A;		// Watchdog-Interrupt nach 64 ms um Klingeln vom Abheben zu unterscheiden
  w();
  if (PINB&0x04) return;	// High: Abgehoben, Low: CLIP detektieren
  wdt_set(0x4D);		// 0,5 s warten und Energie sparen
  for(;;){
   sleep();
   if (PINB&0x04) return;
  }				// Im Normalfall Timeout
  clipdetect();			// Der Elko muss als einziger Energiespeicher herhalten
  for(;;) {
   w2();			// Weiteres Klingeln abwarten
   if (PINB&0x04) return;
  }
// Ende des Klingelns: Anruf verpasst
  wdt_off();
  ++anrufe;
 }
}

static void wait_adc(void) {
#if 0
 do sleep();
 while (!(Flags&0x40));
 Flags&=~0x40;
#else
 while (!(ADCSRA&0x10));// warte auf Interruptflag
 ADCSRA=ADCSRA;		// Interruptflag löschen
 if (ADMUX&0x01) {
  ADMUX&=~0x01;
  if (ADMUX&0x20) ADMUX&=~0x20;
  else onadc(&adc0);
 }else{
  ADMUX|= 0x01;
  onadc(&adc1);
 }
#endif
}

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

// Testen (mit dem A/D-Wandler) ob das High an Pin 7 mit einer
// steigenden Betriebsspannung einhergeht.
// Wenn nicht ist es ein parallel geschaltetes Telefon oder ein totes Amt;
// dann sollte mit waitforwatchdog mit ganz langer Watchdog-Spanne
// auf das Ende dieses irregulären Zustandes gewartet werden,
// um den Elko möglichst nicht zu entladen.
// Denn wenn dieser erst mal leer ist, kann der Mikrocontroller nicht mehr
// hochlaufen und bspw. Anrufe zählen.

static byte adPin7 NOINIT;
static byte adRef NOINIT;

static void phoneParallel(void) {
 tonAus();
 PORTB = 0;	// Keine ohmschen Verbraucher
 DDRB  = 0x25;
 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: {
   adPin7 = ADCH;
   ADMUX  = 0x2C;
  }break;
  case 0x2C: {
   byte b = adRef;
   byte a = ADCH;
   adRef  = a;
   if (a>=0x40 && a>b) {	// Ucc < 4,4 V und fallend
    phoneParallel();
   }
   ADMUX = 0x21;
  }break;
  default: ADMUX = 0x21;
 }
}

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

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;
 if (--pause) return;	// zunächst 256 Aufrufe nichtstun, zwischen den Pieptönen weniger
 StartTon(1);		// Anzahl Anrufe melden
 if (++index>=anrufe>>1) index=0;
 else pause=WDT_SEC(0.5);
}

static void offhook(void) {
 EEARL = 253;
 eechange(eeread()+1);	// Abhebe-Zähler
 MCUCR = 0x20;		// Sleep-Modus: Idle (für Timer, Strom ist genug da)
 adRef = 0xFF;
 ADCSRA= 0xF4;		// /16 = 62 kHz / 25 = 2,5 kSa/s
 byte no_dial=WDT_SEC(1);	// besser: auf Freizeichen warten
 //Bei Freizeichen müsste <anrufe> weitergezählt werden, wenn Flags.6 gesetzt!!
 wait_adc();
 eestore(0x7C,ADCSRA);
 eestore(0x7D,'b');
 word i=1000;
 do wait_adc();		// 208 µs * 1000 = 208 ms
 while(--i);		// Gleichspannungswert aus 500 Wandlungen ermitteln
 dis1h=adc1.dch+10;	// Schaltschwellen ermitteln
 dis1l=adc1.dch-30;
 eecount(0x7D);
 eestore(0x7E,dis1l);	// DEBUG
 for(;;) {			// Hauptschleife
  sleep_cpu();
  if (Flags&0x80) {
   EEARL=252;
   eechange(anrufe);
   handleADC();
  wait_adc();
  if (adc1.dch<dis1l) return;	// TODO: Low-Länge messen!!
  if (Ton) continue;		// Ton- oder Pausenausgabe läuft
  char c=numbers[buf_r];	// Nächsten Ton + Pause ausgeben
  if (c) {
   StartTon(c);
   buf_r++;
  }else{	// Puffer leer, nichts zu tun
   pwm_off();
   buf_r=0;
   numbers[0]=0;
  }
 }
}

/*************************************
 * Initialisierung und Hauptschleife *
 *************************************/
static void hardwareInit(void) {
 Ton   = 0;
 WDTCR = 0x40;		// Watchdog auf Interrupt, 16 ms = 62 Hz
 PORTB = 0x25;		// Pullup für 2 Eingänge
 DDRB |= 0x01;		// Relais-Ausgang
 eewait();
 EEARL = 255;
#ifdef __AVR_ATtiny85__
 EEARH = 0;
#endif
 eechange(eeread()+1);	// 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 = 0;
 nsi   = 0;
 Zeit8 = 0;
 wend  = 0;
 opinb = pinb = PINB;
}

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