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

     1  /* Programm für ATtiny45, „h#s“ Henrik Haftmann, TU Chemnitz
     2   * tabsize = 8, encoding = utf-8 (ohne Windows-Header BOM)
     3   090331	Erster Anfang als mfv2.c
     4  +190531	CLIP funktioniert
     5  ~190610	CLIP funktioniert nicht am Telekom-Anschluss
     6  +190617	phoneParallel() — Tiefentladeschutz für Elko
     7  ?190626	Nummer des Anrufers kann immer noch nicht mit 7³ gespeichert werden
     8   210206	Bei Binärgleichheit Kommentierung erweitert
     9   220320	Umstellung auf C++14
    10   220328	Gleicher Quelltext für CLIP: c: mit ADC, d: mit Analogvergleicher
    11  -220421	Kurzen letzten nsi-Impuls detektieren (Österreicher Wählscheibe)
    12   */
    13  #include <avr/io.h>
    14  #include <avr/pgmspace.h>
    15  #include <avr/interrupt.h>
    16  #include <avr/sleep.h>
    17  #include <avr/wdt.h> 
    18  #include <util/delay.h>
    19  #include <avr/fuse.h>		// avr-size: 3 Bytes bei .data (falsch)
    20  #include <avr/signature.h>	// avr-size: 3 Bytes bei .text (falsch)
    21  #include <string.h>
    22  
    23  FUSES={
    24   0x5C,	// Taktteiler /8, kein Taktausgang, schnellster Startup, Resonator 3..8 MHz
    25   0x57,	// kein RESET, EEPROM-Inhalt behalten, kein Brownout
    26   0xFF,	// Keine Selbstprogrammierung
    27  };	// {Startup: Reset = 272 CK + 64 ms (64068 µs), PowerDown = 258 CK (64 µs)}
    28  
    29  /************
    30   * Hardware *
    31   ************/
    32  /*
    33  Anschlüsse:
    34  PB0	(5)	-	Wählscheiben-Kontakte "nsi"+"nsa", 1 = unterbrochen
    35  		AIN0	d: Empfindlichere Clip-Dekodierung via 4,7 nF (Fritzbox)
    36  			und internem Pullup, f_HP = 1/(2πRC) = 850 Hz
    37  PB1	(6)	OC1A	PWM-Tonausgang, reichlich (125 kHz) Trägerfrequenz
    38  		AIN1	d: Gegenpol (als 5V-Ausgang) für den Analogvergleicher
    39  PB2	(7)	ADC1	Amtsader La = Telefonader a, über 680 kΩ zur
    40  			Abhebe- und Klingel-Detektierung, Minimalstromversorgung,
    41  			c: CLIP-Dekodierung
    42  PB3	(2)	XIN	Resonator 4 MHz
    43  PB4	(3)	XOUT	Resonator 4 MHz
    44  PB5	(1)	!RESET	LVISP-Zugang
    45  		-	Erdtaste, LED-Flash, Debug-Ausgang
    46  GND	(4)		Telefonader b
    47  Ucc	(8)		Amtsader Lb; Z-Diode 5,6 V und Elko 470 µF nach GND
    48  
    49  Der Analogvergleicher kommt auch mit Spannungen von 0,3 V jenseits der
    50  Betriebsspannungsgrenzen zurecht. Getestet am ATtiny13, okay.
    51  Die CLIP-Wechselspannung beträgt (-13,5±1,5)dBm an "BT3 termination" (600 Ω),
    52  macht P = U*I = U*U/R = -13,5 dBm = 10^-1,35 mW = 45 µW; U = sqrt(P*R) = 160 mV = 450 mVss.
    53  Für den A/D-Wandler (10 Bit, 5 V) 40 Count.
    54  Die Terminierung ergibt sich durch das parallel geschaltete Läutewerk (Telefonklingel).
    55  Sowie zuschaltbar durch Fixierung von PB1 (auf High-Pegel) via R1.
    56  
    57  CLIP-Dekodierung mit Analogvergleicher ist Energie sparender:
    58  I(ADC) = 170 µA; I(AC) = 40 µA; also nur ein Viertel.
    59  Auch die CPU hat weniger zu tun, hält sie mit 1 mA/MHz den Löwenanteil.
    60  Keiner der Timer hat eine Capture-Funktion; die Zeitmessung muss
    61  von der CPU (am besten via Energie sparenden Timer0) ausgeführt werden. 
    62  Die Offsetspannung des Analogvergleichers ist nicht angegeben.
    63  
    64  # Es ist schwieriger, einen Adapter zum Zwischenschalten
    65  # (ohne Modifikation des Telefonapparates) zu bauen.
    66  # Ein möglicher Trick dabei besteht darin, das Telefon zwischen W2 und b
    67  # zu betreiben und den nsi-Kontakt an a abzugreifen.
    68  # Das erspart das Unterdrücken der Wählimpulse,
    69  # muss aber nicht mit jedem Telefon funktionieren, je nach Innenschaltung.
    70  
    71  Die Erdtaste ist optional und ermöglicht einen schnellen Weg zum Rückruf.
    72  (Sonst ist es die 6 in der dritten Shift-Ebene, also kurz 6³.)
    73  
    74  Der EEPROM des AVR ermöglicht das Abspeichern von Nummern zur Kurzwahl.
    75  Sonderfunktionen:
    76   LANG am Finger-Anschlag halten (bis zum ersten Piep):		Speichermodus:
    77    1..9,0 = Kurzwahl K1..K9, K0					Kurzwahl K0..9
    78   LANG×2 am Finger-Anschlag halten (bis zum zweiten Piep):
    79    1..8 = Kurzwahl M1..M8					Kurzwahl M1..8
    80    9 = '*' wählen						   Ortsvorwahl
    81    0 = '#' wählen						      Autowahl
    82   LANG×3 am Finger-Anschlag halten (bis zum dritten Piep):
    83    1..4 = 3. Kurzwahl A..D mit 'A' bis 'D' vorbelegt		 Kurzwahl A..D
    84    5 = Wahlwiederholung							   'A'
    85    6 = Rückruf								   'B'
    86    7 = Speichern der zuletzt gewählten Rufnummer (Ziffer folgt)		   'C'
    87    8 = Speichern einer neuen Rufnummer (Nummer folgt, dann LANG + Ziffer)   'D'
    88    9 = Letzte Rufnummer löschen (= Wahlwiederholung verhindern)		   '*'
    89    0 = Rückrufliste löschen						   '#'
    90  
    91  Die Autowahl = Wahl beim Abnehmen des Telefonhörers ist mit Vorsicht zu nutzen.
    92  Sie kann nur unterbunden werden durch:
    93   - Wahl bei aufgelegtem Telefonhörer
    94   - Eingehender Telefonanruf (Klingeln)
    95   - Mindestens 1 entgangener Anruf
    96  Die Autowahl ist für den Einsatz als Oma-Telefon gedacht.
    97  Andererseits kommen Omas gut mit Wählscheiben zurecht. Gerade weil's so zeitraubend ist.
    98  
    99  Wahlen aus dem Kurzwahlspeicher gehen nicht in die Wahlwiederholung ein.
   100  D.h. der Wahlwiederholungsspeicher merkt sich nur explizit gewählte Nummern.
   101  
   102  Folgende interne Peripherie wird verwendet:
   103  Ports: 2 Eingabe mit Pull-Up, 1 Ausgabe via Hardware-PWM
   104  Timer 0: Frequenzsynthese = Stetige PWM-Änderung für Timer 1
   105  Timer 1: High-Speed-Timer mit 32-MHz-PLL: Hardware-PWM-Ausgabe, mono
   106  Totzeitgenerator, USI, Flash-Selbstprogrammierung: ungenutzt
   107  Taktgenerator: Keramikoszillator mit bei Bedarf zugeschalteter High-Speed-PLL
   108  Power-Management: Power-Save (onhook), Idle (offhook), CPU-Taktdrosselung
   109  Analog-Digital-Wandler:
   110  	Messung Spannung an PB2: onhook/offhook/Klingel-Detektion
   111  	Messung der Betriebsspannung: Erkennung parallelgeschaltetes Telefon
   112  	c: CLIP-Flankenmessung
   113  Analogvergleicher:
   114  	d: CLIP-Flankenmessung
   115  Interrupts:
   116  	Zählerüberlauf Timer 0 => Nächster PWM-Wert,
   117  	Watchdog-Timer => verschiedene Timeouts,
   118  	c: ADC fertig => CPU aufwecken (nur für CLIP)
   119  	d: Analogvergleicher-Flanke => CPU aufwecken (nur für CLIP)
   120  EEPROM: Nummernspeicher für Wahlwiederholung, 22 Kurzwahlen, Autowahl, Ortsvorwahl
   121  	feste Adressen
   122  	Beim ATtiny85 wird die gesamte CLIP-Nachricht im EEPROM abgelegt.
   123  	Da die Fritzbox auch den Namen aus dem Telefonbuchspeicher übermittelt,
   124  	steht dann hier der Name des letzten Anrufers im Klartext drin.
   125  	Auslesbar mit einem AVR-Programmiergerät.
   126  RAM: Nummernspeicher für CLIP, als Liste hintereinanderweg
   127  Flash-Selbstprogrammierung: ungenutzt
   128  
   129  In dieser Firmware ist die Resonatorfrequenz variabel gehalten;
   130  getestet habe ich's aber nur mit 4 MHz.
   131  */
   132  
   133  // Signal-Zeiten in Sekunden
   134  constexpr float TON=0.14;
   135  constexpr float PAUSE=0.06;
   136  
   137  typedef unsigned char byte;
   138  typedef unsigned short word;
   139  #define NOINIT __attribute__((section(".noinit")))
   140  #define SINTAB __attribute__((section(".sintab")))
   141  
   142  // Sinustabelle, Mittelwert und Amplitude = 73
   143  extern const struct sintab_t{
   144   byte a[256] {};
   145   constexpr sintab_t() {
   146    constexpr float PI=__builtin_atan(1)*4;
   147    for (size_t x=0; x<256; x++)
   148      a[x]=__builtin_round(__builtin_sin(x*2*PI/256)*73)+73;
   149   }
   150  }SinTab SINTAB;
   151  // Mit der gewählten Amplitude läuft die Signal-Addition
   152  // mit 100 % (hoher Ton) + 75 % (tiefer Ton) geradeso nicht über.
   153  const sintab_t SinTab SINTAB;
   154  
   155  // Etwa 32 kHz Trägerfrequenz bei jeder Resonatorfrequenz (bis 8 MHz) realisieren
   156  constexpr byte T0DIV = F_CPU/32000;
   157  
   158  constexpr word FREQ(float x) {return x*65536*T0DIV/F_CPU+0.5;}
   159  // DDS-Inkremente = Additionswerte auf Phasenwert alle F_CPU/T0DIV
   160  static PROGMEM const word Frequencies[20] = {
   161   FREQ( 697),	//  Zeile mit 1 2 3 A
   162   FREQ( 770),	//  Zeile mit 4 5 6 B
   163   FREQ( 852),	//  Zeile mit 7 8 9 C
   164   FREQ( 941),	//  Zeile mit * 0 # D
   165   FREQ(1209),	// Spalte mit 1 4 7 *
   166   FREQ(1336),	// Spalte mit 2 5 8 0
   167   FREQ(1477),	// Spalte mit 3 6 9 #
   168   FREQ(1633),	// Spalte mit A B C D
   169   FREQ(1046),	//c³	Ab Index 9 gleichstufige Tonleiter aus 12 „Sekunden“
   170   FREQ(1109),	//cis³/des³
   171   FREQ(1175),	//d³
   172   FREQ(1245),	//dis³/es³
   173   FREQ(1319),	//e³
   174   FREQ(1397),	//f³ (Halbtonschritt)
   175   FREQ(1480),	//fis³/ges³
   176   FREQ(1568),	//g³
   177   FREQ(1661),	//gis³/as³
   178   FREQ(1760),	//a³	= 440*4
   179   FREQ(1865),	//ais³/b³
   180   FREQ(1975),	//h³
   181  };
   182  
   183  // Funktionsprinzip: DDS = Digitale Frequenzsynthese
   184  #define DDS_REG
   185  #ifdef DDS_REG
   186  volatile register word
   187  	addA	asm("r8"),
   188  	phaA	asm("r10"),	// hoher Ton
   189  	addB	asm("r12"),
   190  	phaB	asm("r14");	// tiefer Ton
   191  #else
   192  volatile word addA,phaA,addB,phaB NOINIT;
   193  #endif
   194  
   195  // Häufig benutzte globale Bytes werden in freien I/O-Adressen gehalten,
   196  // wobei nur die GPIORx bitadressierbar sind
   197  #define Flags	GPIOR0	// Bits für NSK() u.a.
   198  //	7	Watchdog-Interrupt aufgetreten: 16 ms vergangen
   199  //	6	Anruf beginnt: Wenn's klingelt oder gewählt wird
   200  //	5	Blitz-LED wurde eingeschaltet; Debug: CLIP: TLEVEL bestimmt
   201  //	4	Erdtaste solo gedrückt
   202  //	3	8³ = wahlfreie Nummer speichern
   203  //	2	7³ = letzte gewählte oder rückgerufene Nummer speichern
   204  //	1:0	Ebenenzähler 0..3 durch Festhalten am Fingeranschlag
   205  #define pb2	GPIOR1	// Aktivitätszustand (> 75 % A/D-Wert) an PB2 mit Historie in variabler Schrittweite
   206  #define buf_r	GPIOR2	// Puffer-Lesezeiger
   207  #define T_on	OCR0B	// Rest-Ton/Pausenlänge, in 16 ms
   208  #define anrufe	OCR1B	// Anrufzähler
   209  #define Zeit8	DT1A	// Zeitzähler für nsi+nsa
   210  #define NSI	DT1B	// Steigende Flanken an PB0 (nsi + einer extra für nsa)
   211  
   212  static byte pb0,pb5;	// Invertierter Zustand an PB0 und PB5 mit Historie in Watchdog-Schrittweite (16 ms)
   213  static byte Zeit16;	// Zeitzähler zum Abspeichern der letzten Rufnummer (für Wahlwiederholung)
   214  static byte rend NOINIT;// Wähl-Timeout, Klingel-Timeout, Flash-Timeout (onhook)
   215  
   216  static byte wahl[11];	// Puffer für bis zu 20 Wählziffern
   217  #define buf_w wahl[0]
   218  // 1. Byte = Füllstand (buf_w)
   219  // '*'=14, '#'=15, wie im EEPROM-Kurzwahlspeicher
   220  // Keine Vorsehung von Wählpausen oder Flash-Impulsen
   221  
   222  extern void PutNib(byte*z,byte idx,char v);
   223  
   224  // Puffer füllen, c=0..15
   225  static void PutBuf(char c) {
   226   if (buf_w<20) PutNib(wahl+1,buf_w++,c);
   227  }
   228  
   229  extern char GetNib(const byte*z,byte idx);
   230  
   231  // Puffer lesen, nicht aufrufen wenn leer!
   232  // Aufruf mit freigegeben Interrupts (aus Hauptschleife) OK
   233  static char GetBuf() {
   234   return GetNib(wahl+1,buf_r++);
   235  }
   236  
   237  extern byte x10(byte);
   238  
   239  /******************
   240   * EEPROM-Zugriff *
   241   ******************/
   242  // Um im EEPROM möglichst viele Kurzwahlen unterbringen zu können,
   243  // werden die Ziffern nibbleweise gespeichert, das verdoppelt die Kapazität,
   244  // erschwert aber die Verarbeitung. Sie sind als Klartext im Hexdump erkennbar.
   245  // Die Kurzwahlen liegen allesamt an festen Adressen.
   246  
   247  //Belegung des EEPROM:		Index	Kurzwahl
   248  //x00	10	Letzte Nummer	0	5³
   249  //x0A	10	K1		1	1¹
   250  //x14	10	K2		2	2¹
   251  //x1E	10	K3		3	3¹
   252  //x28	10	K4		4	4¹
   253  //x32	10	K5		5	5¹
   254  //x3C	10	K6		6	6¹
   255  //x46	10	K7		7	7¹
   256  //x50	10	K8		8	8¹
   257  //x5A	10	K9		9	9¹
   258  //x64	10	K0		10	0¹
   259  //x6E	10	M1		11	1²
   260  //x78	10	M2		12	2²
   261  //x82	10	M3		13	3²
   262  //x8C	10	M4		14	4²
   263  //x96	10	M5		15	5²
   264  //xA0	10	M6		16	6²
   265  //xAA	10	M7		17	7²
   266  //xB4	10	M8		18	8²
   267  //xBE	10	A		19	1³
   268  //xC8	10	B		20	2³
   269  //xD2	10	C		21	3³
   270  //xDC	10	D		22	4³
   271  //xE6	10	Vorwahl		23
   272  //xF0	10	Autowahl	24
   273  //xFA	2	frei
   274  //xFC	1	Anzahl entgangener Anrufe (= <anrufe>)
   275  //xFD	1	Abhebezähler
   276  //xFE	1	Watchdog-Timeout-Reset-Zähler (= kein CLIP oder Fehler)
   277  //xFF	1	Sonstige-Reset-Zähler (Stromausfälle, Steckzyklen oder Bugs)
   278  //Debug-Daten, nur ATtiny85
   279  //x100	64	CLIP-Daten wie empfangen + summierte Prüfsumme (muss 0 sein)
   280  //		0x80, gLen, { 1, 8, Datum+Uhrzeit },
   281  //			    { 2, nLen, Nummer },// Nummer des Anrufers
   282  //			    { 4, 1, [OP] },	// Grund für das Fehlen der Nummer
   283  //			    { 7, tLen, Text },	// Name vom Fritzbox-Telefonbuch
   284  //			    { 8, 1, O }, checksum
   285  //x140	30	Histogramm der Nulldurchgangszeiten, sollte 2 Höcker aufweisen
   286  //x15E	1	1 (Start) + 1 (1010-Phase) + 1 (1111-Phase) + Empfangene Bytes
   287  //x15F	1	Mittelwert der Nulldurchgangs-Zeiten während 1010-Phase,
   288  //		sollte mit der Delle zwischen den Höckern zusammenfallen
   289  //x160	128	lea = Liste entgangener Anrufe
   290  
   291  static inline void eewait() {
   292   while (EECR&2);
   293  }
   294  #define eeread() (EECR|=1,EEDR)
   295  //static inline byte eeread() {
   296  // EECR|=1;
   297  // return EEDR;
   298  //}
   299  static inline void eewrite() {
   300   cli();
   301   EECR|=4;
   302   EECR|=2;
   303   sei();
   304  }
   305  void eechange(byte b) {
   306   byte k=eeread();
   307   if (k==b) return;
   308   if (b==0xFF) EECR|=0x10;	// erase only
   309   else if (k==0xFF) EECR|=0x20;	// write only
   310   EEDR=b;
   311   eewrite();
   312   eewait();
   313   EECR=0;
   314  }
   315  
   316  
   317  // Speichern der Nummer ab EEARL im kopflosen Format (FF-terminiert)
   318  static void numSave(byte*z) {
   319   byte i=*z++;
   320  // 1. Ziffernpuffer in ganzer Länge mit 0x0F auffüllen,
   321  // mit dem Sonderfall '#' am Ende, dann 0x0D als 'falsches' Ende
   322   if (i && i!=20 && GetNib(z,i-1)==0x0F) PutNib(z,i++,0x0D);
   323   while (i!=20) PutNib(z,i++,0x0F);
   324  // 2. Komplette Nummer speichern (nur Änderungen)
   325   for (i=0;i<10;i++) {
   326    eechange(z[i]);
   327    EEARL++;
   328   }
   329  }
   330  
   331  
   332  // <max> muss gerade sein!
   333  static void numLoad(byte*z,byte max=20) {
   334   z++;
   335  // 1. Nibbles vom EEPROM laden
   336   byte i;
   337   for (i=0;i<max>>1;i++) {
   338    z[i]=eeread();
   339    EEARL++;
   340   }
   341  // 2. tatsächliche Rufnummern-Länge ermitteln
   342   for (i=max;i;) {
   343    char c=GetNib(z,--i);
   344    if (c!=0x0F) {
   345     if (c==0x0D && i && GetNib(z,i-1)==0x0F) --i;
   346     i++;
   347     break;
   348    }
   349   }
   350   *--z=i;	// 0..max möglich
   351  }
   352  
   353  /****************************
   354   * Rückruf-Liste bearbeiten *
   355   ****************************/
   356  // Organisation der Rückruf-Liste:
   357  // Nahtlose Aneinanderreihung entgangener und noch nicht "abgearbeiteter" Anrufe
   358  // 1 Byte Länge (5 Bit) + Grund der Null-Länge (2 Bit):
   359  // 0x00 = kein oder fehlerhaft empfangenes CLIP
   360  // 0x20 = Grund 'O' = keine Durchleitung (bspw. vom Ausland)
   361  // 0x40 = Grund 'P' = unterdrückt vom Anrufer
   362  // 0x60 = sonstiger Grund
   363  // Darauf folgen <Länge> Hex-Nibbles Rufnummer.
   364  // Die gespeicherte Ortsvorwahl wird bei Gleichheit abgeschnitten.
   365  // Bei ungerader Länge folgt ein ungenutztes Nibble (0) zur Byteausrichtung
   366  // Bei drohendem Pufferüberlauf wird nicht mehr gespeichert.
   367  // Der Puffer reicht für durchschnittlich 25 Anrufe.
   368  static byte lea[128] NOINIT;	// Liste entgangener Anrufe
   369  static byte ovw[5] NOINIT;	// Ortsvorwahl (max. 8-stellig, 1. Byte = Länge)
   370  
   371  static byte ealen(byte l) {	// Längen-Byte in Byte-Länge der Nummer inkl. Längenbyte
   372   return ((l&0x1F)+1>>1)+1;
   373  }
   374  
   375  static byte*eaindex(byte i) {	// Zum gegebenen Index „vorspulen“
   376   byte*z=lea;
   377   if (i) do z+=ealen(*z); while(--i);
   378   return z;
   379  }
   380  
   381  extern byte CompareN(const byte*,const byte*);
   382  extern byte KillVorwahl(byte*,const byte*);
   383  
   384  // Ryckruf statt Rückruf um mit avr-gcc 5.3 compilierbar zu bleiben
   385  static byte IstRyckruf(const byte*z) {	// liefert buf_w wenn die aktuelle Nummer
   386   return CompareN(z,wahl) ? 0 : buf_w;
   387  }
   388  
   389  // <clipbuf> zeigt auf 2 (Nummer des Anrufenden) oder 4 (Grund für das Fehlen)
   390  // <z> zeigt in <lea>
   391  static void ClipToLea(byte*z,const byte*clipbuf) {
   392   byte type=*clipbuf++;
   393   byte len=*clipbuf++;
   394   if (z+ealen(len)>lea+sizeof lea) return; // Überlauf (in den Stack) verhindern
   395   byte i,j=0;
   396   if (type==2) {		// Rückrufnummer vorhanden
   397    if (len>20) return;		// Fehler
   398    for (i=0;i<len;i++) {
   399     byte c=*clipbuf++;
   400     if ('0'<=c && c<='9') c-='0';
   401     else if ('A'<=c && c<='D') c-='A'-10;
   402     else if (c=='*') c=14;
   403     else if (c=='#') c=15;
   404     else continue;
   405     PutNib(z+1,j++,c);
   406    }
   407    *z=j;
   408  // Testen ob die Rufnummer mit Ortsvorwahl beginnt, und Ortsvorwahl wegnehmen
   409    KillVorwahl(z,ovw);
   410   }else{		// keine Rückrufnummer
   411    if (len!=1) return;		// Fehler
   412    byte reason=*clipbuf;
   413    switch (reason) {
   414     case 'O': j|=0x20;	// “unavailable”
   415     case 'P': j|=0x40;	// “private”
   416     default: j|=0x60;	// unknown reason
   417    }
   418    *z=j;
   419   }
   420  }
   421  
   422  static void DelRyckruf() {
   423   byte *x=lea,*z=x,a=anrufe;
   424   if (a) do{
   425    byte l=ealen(*z);
   426    if (IstRyckruf(z)) {
   427     z+=l;		// Quellzeiger vorrücken = Nummer löschen
   428     --anrufe;
   429    }else do *x++=*z++; while(--l);	// Bytes vorrücken
   430   }while(--a);
   431  }
   432  
   433  // liefert Nummer der Kurzwahl (1..24), 0 für keinen Treffer
   434  static byte FindInKurzwahl(const byte*z) {
   435   byte idx;
   436   for (idx=24;idx;--idx) {
   437    EEARL=x10(idx);
   438    numLoad(wahl);
   439    if (!CompareN(z,wahl)) break;
   440   }
   441   buf_w=0;
   442   return idx;
   443  }
   444  
   445  /****************************
   446   * Kurzwahl laden/speichern *
   447   ****************************/
   448  
   449  // 10 Bytes Puffer pro Nummer (20 Ziffern)
   450  // idx zwischen 0 (letzte Nummer) und 24 (Autowahl)
   451  static void KurzSpeichern(byte idx) {
   452   EEARL=x10(idx);	// × 10
   453   numSave(wahl);
   454   if (idx==23) memcpy(ovw,wahl,5);
   455  }
   456  
   457  static void KurzLaden(byte idx) {
   458   EEARL=x10(idx);	// × 10
   459   numLoad(wahl);	// 0..20 Ziffern
   460   DelRyckruf();
   461   buf_r=0;	// mit dem Abspielen beginnen
   462  }
   463  
   464  /**************************************
   465   * Frequenzsynthese = Sinustonausgabe *
   466   **************************************/
   467  constexpr char LOG2(unsigned v) {return 15-__builtin_clz(v);}
   468  // CLOG2 = Clock Logarithmus zur Basis 2
   469  constexpr char CLOG2 = LOG2(F_CPU/1000000);
   470  // 0 wenn F_CPU = 1..1,99 MHz
   471  // 1 wenn F_CPU = 2..3,99 MHz
   472  // 2 wenn F_CPU = 4..7,99 MHz (Standard)
   473  // 3 wenn F_CPU = 8..15,99 MHz
   474  // Bei (fest) 32 kHz Abtastrate (Syntheserate) muss die Resonatorfrequenz
   475  // >=2 MHz sein, damit die ISR schnell genug abgearbeitet wird.
   476  
   477  extern void clock_set(byte div);
   478  
   479  static void clock_max() {clock_set(0);}
   480  static void clock_1MHz() {clock_set(CLOG2);}	// Taktfrequenz 1..1,99 MHz
   481  static void clock_16us() {clock_set(CLOG2+4);}
   482  
   483  constexpr byte WDT_SEC(float s) {return s/0.016;}
   484  
   485  // ADC-Taktteiler während onhook() - je schneller desto besser
   486  constexpr char ALOG2 = LOG2(1000/500);		// 1 = ÷2 → 500 kHz → 38 kSa/s
   487  // ADC-Taktteiler während offhook() - Strom ist genug da
   488  constexpr char BLOG2 = LOG2(1000/125);		// 3 = ÷8 → 125 kHz → 9,6 kSa/s
   489  
   490  static void tonEin() {
   491   ADCSRA = 0xA0|BLOG2+CLOG2;	// ADC-Takt anpassen
   492   clock_max();
   493   PLLCSR|= 0x82;		// Low-Speed-Modus: 32 MHz (reicht)
   494   _delay_us(100);
   495   while (!(PLLCSR&1));
   496   PLLCSR|= 0x04;
   497   TCCR1  = 0x61;
   498   OCR0A  = T0DIV-1;	// rund 32 kHz
   499   TIMSK  = 0x10;		// Compare-Interrupt
   500   TCCR0A = 0x02;		// Timer0: CTC
   501   TCCR0B = 0x01;		// Timer0 mit Vorteiler 1 starten
   502   T_on=WDT_SEC(TON+PAUSE);// Länge setzen
   503   DDRB  |= 0x02;		// Ausgang aktivieren
   504  }
   505  
   506  static void tonAus() {
   507   TIMSK  = 0;		// Timer0: Kein Interrupt
   508   TCCR0A = 0;		// kein PWM
   509   TCCR0B = 0;		// aus
   510   DDRB  &=~0x02;		// Ausgang hochohmig
   511   TCCR1  = 0;		// Timer1 aus
   512   PLLCSR = 0;		// PLL aus
   513   clock_1MHz();
   514   ADCSRA = 0xA0|BLOG2;	// ADC-Takt anpassen
   515  }
   516  
   517  // Startet Wählton für Ziffer z ("*"=14, "#"=15)
   518  static void StartDTMF(char z) {
   519   if (MCUCR&0x10) return;	// niemals im aufgelegten Zustand
   520   if (!(Flags&0x40)) {
   521    Flags|=0x40;
   522    if (anrufe) --anrufe;	// Anrufliste abbauen
   523   }
   524   static PROGMEM const byte Nr2HiLo[16]={
   525  //   0    1    2    3    4    5    6    7    8    9    A    B    C    D    *    #
   526    0x53,0x40,0x50,0x60,0x41,0x51,0x61,0x42,0x52,0x62,0x70,0x71,0x72,0x73,0x43,0x63};
   527  // High-Nibble  4  5  6  7
   528  // Low-Nibble ┌───────────
   529  //	    0 │ 1  2  3  A
   530  //	    1 │ 4  5  6  B
   531  //	    2 │ 7  8  9  C
   532  //	    3 │ *  0  #  D
   533   byte i=pgm_read_byte(Nr2HiLo+z);		// umrechnen in die 2 Frequenzen
   534   addA=pgm_read_word(Frequencies+(i>>4));	// hoher Ton im High-Nibble
   535   addB=pgm_read_word(Frequencies+(i&15));	// tiefer Ton im Low-Nibble
   536   tonEin();
   537  }
   538  
   539  // Startet Hinweiston, z=0 für c³
   540  static void StartTon(char z) {
   541   if (MCUCR&0x10) return;	// niemals im aufgelegten Zustand
   542   char okt=0;			// Oktave
   543   for (;z<0;z+=12) --okt;	// Mit div_t ist's umständlicher
   544   for (;z>=12;z-=12) ++okt;	// jetzt 0<=z<12
   545   word a=pgm_read_word(Frequencies+8+z);
   546   for (;okt<0;++okt) a>>=1;
   547   for (;okt;--okt) a<<=1;
   548   addB=addA=a;
   549   phaB=phaA;			// addieren lassen
   550   tonEin();
   551  }
   552  
   553  // Kernroutine der Frequenzsynthese
   554  // Aufruf mit F_CPU/256 oder F_CPU/128, 20..40 kHz, reicht für HiFi-Ton
   555  // Mit F_CPU/64 kommt der Controller nicht mehr hinterher,
   556  // obwohl noch Rechenleistung frei sein müsste.
   557  ISR(TIM0_COMPA_vect) {
   558  // Tonlänge nominell 70 ms, Pause 30 ms
   559  // T_on>=0 = Tonausgabe, sonst Pause
   560  // if (T_on>=(byte)WDT_SEC(PAUSE)) {
   561  #ifdef DDS_REG
   562    asm(
   563  "	add	r10,r8	\n"
   564  "	adc	r11,r9	\n"
   565  "	add	r14,r12	\n"
   566  "	adc	r15,r13	\n"
   567  "	push	ZH	\n"
   568  "	push	ZL	\n"
   569  "	 ldi	ZH,hi8(SinTab)\n"
   570  "	 mov	ZL,r11	\n"
   571  "	 lpm		\n"
   572  "	 mov	ZL,r15	\n"
   573  "	 lpm	r1,z	\n"
   574  "	pop	ZL	\n"
   575  "	pop	ZH	\n"
   576  "	add	r0,r1	\n"
   577  "	lsr	r1	\n"	// (kompensiert Kabeldämpfung für höhere Frequenzen)
   578  "	lsr	r1	\n"
   579  "	sbc	r0,r1	\n"	// "sbc" ist der Trick fürs Abrunden
   580  "	out	%0,r0	\n"
   581    ::"I" (_SFR_IO_ADDR(OCR1A)));	// Undokumentiert: Leeres Constraint = Pointer
   582  #else
   583   byte a=pgm_read_byte(SinTab.a+((phaA+=addA)>>8));
   584   byte b=pgm_read_byte(SinTab.a+((phaB+=addB)>>8));
   585   OCR1A=a+b-(b>>2);		// hier ohne Runden
   586  #endif
   587  }
   588  
   589  /***********************************
   590   * Nummernschaltkontakt-Auswertung *
   591   ***********************************/
   592  // Abarbeitung nach dem LIFO-Prinzip
   593  static void Ryckruf() {
   594   byte *z;
   595   for (byte l,i=anrufe;;) {
   596    if (!i) {
   597     if (!(DDRB&2)) StartTon(-6);
   598     return;		// Nichts (mehr) rückzurufen
   599    }
   600    z=eaindex(--i);	// Zum letzten Rückruf vorspulen
   601    l=*z&0x1F;
   602    if (l) break;
   603    *z=0;			// Unmögliche Rückrufe von hinten allesamt löschen
   604    --anrufe;
   605    if (!(DDRB&2)) StartTon(-12);	// Rückruf (der letzten Anrufe) nicht möglich
   606   }
   607   memcpy(wahl,z,11);
   608   DelRyckruf();
   609   KurzSpeichern(0);
   610   buf_r=0;	// mit dem Abspielen beginnen
   611  }
   612  
   613  static void clearBuf() {
   614   buf_r=buf_w=0; 
   615  }
   616  
   617  static void ledOn() {
   618   if (PB5DEBUG) return;	// Nicht im Debugmodus
   619   if (DDRB&0x20) return;	// Nicht wenn bereits an oder aus
   620   if (!(PINB&0x20)) return;	// Nicht wenn Erdtaste gedrückt
   621   PORTB&=~0x20;
   622   DDRB |= 0x20;
   623   Flags|= 0x20;	// merken
   624  }
   625  
   626  static void ledOff() {
   627   if (!(Flags&0x20)) return;	// nicht eingeschaltet
   628   DDRB &=~0x20;
   629   PORTB|= 0x20;	// Pullup restaurieren
   630   Flags&=~0x20;	// zurücknehmen
   631  }
   632  
   633  // Konvertiert Impulszahl nach Ziffer; hier: 10 Pulse = 10, nicht 0
   634  // Hier muss <i> für schwedische oder neuseeländische Telefone angepasst werden
   635  static byte nsi_transcode(byte i) {
   636  // Schweden: Anordnung 0-1-2-3-4-5-6-7-8-9 (Quelle: Wikipedia)
   637  // if (!--i) i = 10;
   638  // Neuseeland: Anordnung 0-9-8-7-6-5-4-3-2-1
   639  // i = 11-i;
   640   return i;
   641  }
   642  
   643  // Nummernschaltkontakt an PB0 sowie Erdtaste an PB5 auswerten.
   644  // Die Historie dieser beiden Bits befindet sich in Schieberegistern.
   645  // Aufruf alle 16 ms per Watchdog-Interrupt aus onhook- oder offhook-Schleife.
   646  static void NSK() {
   647   Flags&=~0x80;		// Watchdog-Interrupt quittieren
   648   byte nsi=NSI;		// Lokales nsi
   649  // Entprellfunktion: Mindestens 32 ms Hi
   650   if ((pb0&7)==0b100) {	// nsi oder nsa öffnet [typ. 62 ms]
   651    ++nsi;		// Lo-Hi-Flanken zählen
   652    GIFR=0x20;		// Erkannte Pegelwechsel quittieren
   653    rend=0;		// Offhook-Wähl-Timeout setzen
   654    Zeit8=WDT_SEC(0.2);	// zur Feststellung ob nsa oder nsi
   655   }else if (nsi && (pb0&3)==0b01) {	// nsi schließt (kein extra Entprellen) [typ. 38 ms]
   656    Zeit8=0;		// Kein Timeout wenn Lo nach 1. nsi
   657   }else if (!nsi && (pb0&15)==0b0111) {	// nsa schließt: Mindestens 48 ms Lo
   658    if (Flags&0x10) {	// Erdtaste solo gedrückt?
   659     Flags&=~0x10;	// Solofunktion zurücknehmen
   660     Flags|= 1;		// Ebene 3 ansteuern
   661     Flags|= 2;
   662    }else{
   663     Flags&=~1;		// Ebene 0 ansteuern
   664     Flags&=~2;		// Etappenzähler für „nsa halten“ rücksetzen
   665    }
   666    Zeit8=WDT_SEC(1.5);
   667   }else{			// keine Flanke
   668    if (Zeit8 && !--Zeit8) {	// Timeout
   669     if (nsi) {		// Nummernscheibe abgelaufen (nur bei Hi-Pegel)
   670      if (!(GIFR&0x20)	// Kein < 16 ms kurzes letzes („übersehenes“) Low?
   671       && !--nsi) {	// Letzte Lo-Hi-Flanke durch nsa-Kontakt abziehen
   672       StartTon(-2);	// Fehler: Nur 1 langes Low detektiert: Wählscheibe weniger als 30° aufgezogen = Fehlbedienung
   673       goto raus;		// Oder soll man das als Sonderfunktion interpretieren?
   674      }
   675      if (nsi>10) {	// Zu viele Impulse?
   676       StartTon(-3);	// Fehler: Mehr als 10 Impulse, Sonderwählscheibe?
   677       goto raus;
   678      }
   679      nsi=nsi_transcode(nsi);
   680  // Eigentliche Aktion
   681      if (Flags&8 && !(Flags&1) && !(Flags&2)) goto putbuf0;
   682      if (Flags&8 || Flags&4) {	// nsi = Ziffer zum Kurzwahl-Speichern
   683       if (Flags&2) {
   684        if (Flags&1) {	// n³
   685         if (nsi<5) nsi+=18;	// 19..22 = Speicherplätze A-D
   686         else {nsi+=5; goto putbuf1;}	// 10..15 = Möglichkeit des Abspeicherns von A,B,C,D,* und #
   687        }else{		// n²
   688         nsi+=10;			// 11..18
   689         if (nsi>18) nsi+=4;	// 23..24 (Ortsvorwahl, Autowahl)
   690        }
   691       }
   692       KurzSpeichern(nsi);
   693       StartTon(5);
   694       Flags&=~0x0F;
   695       goto raus;		// nicht wählen
   696      }else if (Flags&2) {
   697       if (Flags&1) switch (nsi) {// n³ = ganz lange halten am Fingeranschlag
   698        case 5: KurzLaden(0); goto raus;		// Wahlwiederholung
   699        case 6: Ryckruf(); goto raus;
   700        case 7: Flags|=4; StartTon(5); goto raus;	// Letzte Nummer speichern: Kurzwahlspeicherplatz-Ziffer folgt
   701        case 8: Flags|=8; clearBuf(); StartTon(0); goto raus;	// Nummer einspeichern: Nummer + Kurzwahlspeicherplatz-Ziffer folgen
   702        case 9: clearBuf(); KurzSpeichern(0); goto raus;	// Wahlwiederholung verhindern
   703        case 10: anrufe=0; goto raus;
   704        default: KurzLaden(18+nsi); goto raus;		// 19..22 (A,B,C,D) wählen
   705       }else switch (nsi) {	// n²
   706        case 9:
   707        case 10: nsi+=14-9; break;	// '*' oder '#'
   708        default: KurzLaden(10+nsi); goto raus;
   709       }
   710      }else if (Flags&1) {	// n¹
   711       KurzLaden(nsi);		// wählen lassen (1..10), Hauptschleife generiert Töne
   712       goto raus;
   713      }else
   714  putbuf0: if (nsi==10) nsi=0;
   715  putbuf1: PutBuf(nsi);	// Zählergebnis abspeichern (Hauptschleife generiert Ton anschließend)
   716      if (Flags&8) {
   717       buf_r=buf_w;	// Nicht wählen (Hauptschleife nicht in Aktion treten lassen)
   718       StartTon(4);
   719      }else Zeit16=WDT_SEC(2);	// Wahlwiederholungs-Abspeicher-TimeOut setzen (4 Sekunden)
   720  raus:
   721      nsi=0;
   722     }else if (pb0&1) {		// Vor nsi-Impulsen (Lo-Pegel)
   723      if (!(Flags&1 && Flags&2)) ++Flags;	// Zu lange gehalten: ignorieren, weiterpiepen
   724      StartTon(1+(Flags&3));	// Töne d-dis-e
   725      ledOn();			// Bei jedem Timeout mitblitzen
   726      Zeit8=WDT_SEC(1);
   727     }
   728    }
   729   }
   730   NSI=nsi;
   731  // Entprellfunktion: Mindestens 4 × 16 ms = 64 ms Lo)
   732   if (!T_on && (pb5&31)==0b01111) {	// Erdtaste gedrückt
   733    if (!pb0) Flags|=0x10;	// Solo: Vermerken (n³ ansteuern lassen)
   734    else if (pb0==255) Flags|=1;	// Beim Festhalten des Nummernschalters: n¹
   735    else Flags|=2;		// Während Ablauf des Nummernschalters: n²
   736  // Entprellfunktion: Mindestens 4 × 16 ms = 64 ms Hi)
   737   }else if (!(pb5&15) && Flags&0x10) {	// Nummernschalter nicht zwischendurch betätigt?
   738    Flags&=~0x10;			// Flag löschen
   739    if (anrufe) Ryckruf();	// Solo-Aktion = Rückruf oder Wahlwiederholung
   740    else KurzLaden(0);
   741   }
   742   EEARL=252;
   743   eechange(anrufe);		// im EEPROM nachführen, falls geändert (auch im aufgelegten Zustand)
   744  }
   745  
   746  // Bei Ablauf des Watchdog-Timers (meist 16 ms) wird die CPU geweckt
   747  // und das Bit 7 im Flag-Register (GPIOR0) gesetzt.
   748  ISR(WDT_vect,ISR_NAKED) {
   749   Flags|=0x80;
   750   reti();
   751  }
   752  
   753  static void readPb2(byte adc1value) {
   754   constexpr byte offhooklevel=0xC0;	// High bei 75 %
   755   pb2 = pb2<<1 | (adc1value>=offhooklevel);	// High bei 75 %
   756  }
   757  
   758  static void readPins() {
   759   byte npinb = ~PINB;		// invertiert
   760   pb0 = pb0<<1 | npinb&1;
   761  #if PB5DEBUG
   762   if (DDRB&0x20) return;		// Wenn Ausgang dann Erdtaste NICHT einlesen
   763   pb5 = pb5<<1 | npinb>>5&1;
   764  #else
   765   if (Flags&0x20) {		// Wenn LED ein
   766    DDRB &=~0x20;			// LED aus, auf Eingang schalten
   767    PORTB|= 0x20;			// Pullup aktivieren
   768    _delay_us(100);		// etwas warten (Entladelast onhook selten, vertretbar)
   769   }
   770   pb5 = pb5<<1 | npinb>>5&1;	// Erdtaste einlesen
   771   if (Flags&0x20) {
   772    PORTB&=~0x20;			// Pullup aus
   773    DDRB |= 0x20;			// LED ein
   774   }
   775  #endif
   776  }
   777  
   778  // Da einige Zeit vergangen ist, seit die Pullups aktiviert wurden,
   779  // wird zunächst PINB eingelesen und der Analogwert an PB2 ausgewertet.
   780  // Dann wird in Schritten von 48 µs (max. 12 ms)
   781  // oder per Watchdog fest 16 ms gewartet.
   782  // Die kürzere Zeit wird für die Klingel-Detektion
   783  // = Wechselspannung 25 oder 50 Hz an PB2 benötigt; Flanken alle 20 oder 10 ms.
   784  // In beiden Fällen mit abgeschaltetem A/D-Wandler und minimalem Stromverbrauch:
   785  // Bei us48!=0 durch Heruntersetzen des Oszillatortakts und einer Busy-Loop
   786  // bei us48==0 durch maximale(!) Oszillatorfrequenz und kürzester Hochlaufzeit.
   787  // Heraus kommt die Funktion mit 1 MHz CPU-Takt, aktivierten Pullups
   788  // und mit gestartetem A/D-Wandler, immer an PB2,
   789  // sowie ausgewertetem Nummernschalter.
   790  static void waitforwatchdog(byte us48=0) {
   791   while (!(ADCSRA&0x10));// warten auf Ende, kann bis zu 50 µs dauern
   792   readPb2(ADCH);
   793   readPins();		// einlesen, nachdem sich alle Pegel eingeschwungen haben
   794   ADCSRA= 0;		// ADC abschalten — sonst gibt's zusätzlichen Stromverbrauch.
   795   if (!(DDRB&0x20)) PORTB&=~0x20;	// Gedrückte Erdtaste soll Elko nicht entladen
   796   PORTB&=~0x01;		// Aufgezogene Wählscheibe soll Elko nicht entladen
   797   PORTB&=~0x04;		// kein Pullup: Versorgungsstrom 120 µA fließt über die negative Gateschutzdiode
   798   DDRB |= 0x04;		// Floating-Eingang auf Low festnageln, um Gateschutzdiode zu schonen
   799   if (us48) {
   800    clock_16us();		// Taktvorteiler erhöhen für minimalen Stromverbrauch
   801    _delay_loop_1(us48);	// jede Runde dieses Delays benötigt 3×16µs
   802    DDRB &=~0x04;		// Pullup für A/D-Wandler früh vorbereiten
   803    PORTB|= 0x04;
   804   }else{
   805    clock_max();		// kürzestmögliche Hochlaufzeit spart am meisten Strom (!)
   806    sleep_cpu();		// Oszillator aus bis zum nächsten Watchdog-Interrupt
   807    DDRB &=~0x04;		// dann umgehend Pin 7 zur Messung vorbereiten
   808    PORTB|= 0x04;
   809    clock_16us();		// 16 µs pro Takt, gaanz langsam (4 × 16 µs), etwas warten
   810   }
   811  // Die Unterprogrammaufrufe genügen zur Verzögerung.
   812  // Warten mit dem A/D-Konverter, bis sich der Pegel an Pin 7 stabilisiert hat.
   813  // Der Kondensator am Pin 7 (wenn bestückt) wirkt als Tiefpass.
   814   clock_1MHz();		// (10 × 16 µs)
   815   PORTB|= 0x01;		// Restliche Pullups (in umgekehrter Reihenfolge) aktivieren
   816   if (!(DDRB&0x20)) PORTB|= 0x20;	// bei aktivem DDRB ist's die LED oder der Debug-Ausgang: Unbeeinflusst lassen
   817   ADCSRA= 0xD0|ALOG2;	// Start mit Teiler 2: 500 kHz, 2µs, ×25 = 50 µs, einmalig, ohne Interrupt
   818   if (Flags&0x80) {
   819    ledOff();		// LED hat nun 16 ms geleuchtet: Aus!
   820    NSK();		// (d: Irrtümliche Wählimpulse durch Klingeln werden später gekillt)
   821   }
   822  }
   823  
   824  /********************
   825   * CLIP-Dekodierung *
   826   ********************/
   827  
   828  // Der Interrupt dient nur zum Aufwecken; Interrupts werden/bleiben gesperrt.
   829  // Dieser wird nur für die CLIP-Detektion benötigt, das ist Energie sparender
   830  // als die Auswertung von ADCSRA.ADIF, denn während der CLIP-Erkennung
   831  // steht nur die Ladung im Elko als Energiequelle zur Verfügung.
   832  ISR(ADC_vect,ISR_NAKED) {
   833   asm volatile("ret");
   834  }
   835  ISR(ANA_COMP_vect,ISR_NAKED) {
   836   asm volatile("ret");
   837  }
   838  
   839  static byte clip[64] NOINIT;	// letztes Byte = Prüfsumme
   840  // CLIP-Dekodierung verlangt vom Mikrocontroller alles ab, daher ist's
   841  // in Assembler (extern) geschrieben.
   842  
   843  // Steckt die aktuelle Rufnummer in „lea“ (nicht in EEPROM)
   844  static void saveClip(byte*z) {
   845   byte i=2,j=clip[1]+2;	// j = Endindex der Nachricht
   846   if (j>sizeof clip) j=sizeof clip;
   847   do{
   848    if (clip[i]==2 || clip[i]==4) {
   849     ClipToLea(z,clip+i);
   850     return;
   851    }else i+=clip[i+1]+2;	// Länge des unbekannten Chunks
   852   }while(i<j);
   853  }
   854  
   855  extern void acInit();
   856  extern byte acMean();
   857  //Beim ATtiny85 gibt es Debug-Funktionalität: Logging im EEPROM
   858  #ifdef __AVR_ATtiny85__
   859  static byte histo[32] NOINIT;
   860  extern void acHisto(byte data[],byte len);
   861  #endif
   862  extern void acOnes();
   863  extern byte acByte();
   864  extern void acDone();
   865  
   866  // mit 1..2 MHz CPU-Takt
   867  static void clipRead(byte*z) {
   868  // STELLSCHRAUBE, die entscheidend für die nötige Kapazität von C1 ist
   869   byte i=34;	// 34 trifft am Thomson-Modem den Anfang der Schwingungen.
   870  // Laut Standard sind es 250 ms nach dem Klingeln. Daher Minimum = 5.
   871  // Laut „102s10.pdf“ sind's 500 ms, und das stimmt auch für's Thomson-Kabelmodem.
   872  // Kleinere Zahlen fressen mehr Zeit und mehr Strom für die Detektierung
   873  // d: Exakt platziert fällt die 470-µF-Elko-Spannung von 4,3 auf 3,2 V (642 ms)
   874  //    Das macht 805 µA mittlere Stromaufnahme, immerhin. Im Interruptbetrieb.
   875  //    Zu frühes Starten des Analogvergleichers frisst mehr Strom
   876  //    weil Interrupts sehr dicht kommen. Siehe Oszillogramm.
   877   do{
   878    waitforwatchdog();	// je 16 ms, aber das stimmt irgendwie nicht: 34×16 » 500!
   879    if (pb2&1) return;	// (Zweites Klingeln oder) Abheben
   880   }while (--i);		// weitere ms energiesparend warten
   881  #ifdef __AVR_ATtiny85__
   882   memset(histo,0,sizeof histo);
   883  # if 0
   884   memset(clip,0xFF,sizeof clip);	// memset mit Füllbyte!=0 wird nicht wegoptimiert
   885  # else
   886   asm(
   887  "	mov	r0,%1	\n"
   888  "	com	r1	\n"
   889  "0:	st	%a0+,r1	\n"
   890  "	dec	r0	\n"
   891  "	brne	0b	\n"
   892  "	com	r1	\n"
   893   ::"e"(clip),"r"(sizeof clip));
   894  # endif
   895  #endif
   896   cli();
   897   wdt_reset();
   898  // STELLSCHRAUBE, die entscheidend für die nötige Kapazität von C1 ist
   899   WDTCR = 0x0E;		// Watchdog mit Reset, 1 s (war am ATmega85 erforderlich)
   900   acInit();
   901  #ifdef __AVR_ATtiny85__
   902   histo[30]=1;
   903   histo[31]=acMean();	// 256 valide Nulldurchgänge detektieren, Mittelwert liefern
   904   acHisto(histo,30);	// 256 Nulldurchgänge messen und im Histogramm verteilen
   905   histo[30]=2;
   906  #else
   907   acMean();
   908  #endif
   909   acOnes();		// Folge von 100 tieffrequenten Nulldurchgängen finden
   910  #ifdef __AVR_ATtiny85__
   911   histo[30]=3;
   912  #endif
   913   wdt_reset();		// nach 100 langen Nulldurchgängen
   914   byte sum=0;
   915   for (i=0;; i++) {
   916    byte c=acByte();	// Das erste Byte wird nicht auf 0x80 geprüft
   917  #ifdef __AVR_ATtiny85__
   918    histo[30]++;
   919  #endif
   920    sum+=c;
   921    if (i<sizeof clip-1) clip[i]=c;
   922    if (i && i==clip[1]+2) break;	// Gesamtnachrichtenlänge erreicht
   923   }
   924  #ifdef __AVR_ATtiny85__
   925   if (i<sizeof clip-1) clip[i]=sum;
   926  #endif
   927   acDone();
   928   WDTCR = 0x1E;
   929   WDTCR = 0xC0;		// Watchdog normal (gemessen 16,4 ms)
   930   MCUCR = 0xB4;
   931   MCUCR = 0xB0;		// Sleep-Modus: PowerDown, kein BrownOut
   932   sei();
   933   if (!sum) saveClip(z);	// Nur bei korrekter Prüfsumme akzeptieren
   934  }
   935  
   936  /************************************
   937   * Routinen für aufgelegten Zustand *
   938   ************************************/
   939  // Handelt es sich beim Pegel an PB2 um Gleichspannung
   940  // (permanent High oder auch Low) oder Klingelwechselspannung 25 Hz?
   941  // Routine kehrt erst nach Ende des Klingelns zurück.
   942  // Liefert Anzahl detektierter Schwingungen.
   943  static byte wait_ringend() {
   944   byte i=0,j=0;	// geht mit PB2 = High in die Routine, d.h. pb2&1 ist nach waitforwatchdog() = 1
   945   for(;;){
   946    waitforwatchdog(100);	// 5 ms
   947    if ((pb2&3)==0b01) {	// (Erneuter) Lo-Hi-Wechsel
   948     ++j; i=0;		// Diese Flanken zählen
   949    }else if ((pb2&3)==0b10) {	// Hi-Lo-Wechsel
   950     i=0;			// sollte der Compiler wegoptimieren
   951    }else{		// gleich geblieben
   952     if (++i==10) break;	// 16 ms × 10 = 160 ms ist ungefähr die Zeit, die das Kabelmodem braucht, um das Freizeichen aufzuschalten
   953    }
   954   }
   955   return j;
   956  }
   957  
   958  static void onhook() {
   959   MCUCR = 0xB4;
   960   MCUCR = 0xB0;		// Sleep-Modus: PowerDown, kein BrownOut
   961   ADMUX = 0x21;		// ADLAR, ADC1 = PB2
   962   ADCSRA= 0xD0|ALOG2;	// einmalig (wird in waitforwatchdog erneut gestartet)
   963   clearBuf();		// Rufnummer tilgen
   964   for(;;){		// warte bis 75% Spannung an PB2 == ADC1
   965    waitforwatchdog();	// wertet Wählscheibe und Erdtaste aus
   966    if (pb2&1) {		// Spannung >= 75%: Wechsel- oder Gleichspannung?
   967  // Das Ansteigen der Spannung am PB2 kann 3 Gründe haben:
   968  // * Klingeln, * Hörer abheben, * Paralleles Telefon nimmt Hörer ab
   969  // 16 ms und 75% sind zur Klingeldetektion möglicherweise knapp!
   970  // In der Konstellation mit PB1 an 'a'-Ader statt am Mikrofon ist es besser,
   971  // dort (via Pullup und kapazitiver Kopplung) Pegelwechsel zu detektieren.
   972     /*byte j=*/wait_ringend();	// kehrt erst nach 160 ms Ruhe zurück
   973     if (pb2&1) return;	// raus wenn statisch >75%: Abgehoben oder Paralleles Telefon
   974  // TODO: Zusätzlich sollte beim Abheben das Freizeichen detektiert werden, wenn Flags.6 gesetzt
   975  // Es ist hier Sache von offhook(), den Fall „Paralleles Telefon“ zu verarbeiten,
   976  // obwohl der Hörer /dieses Telefons/ immer noch auf der Gabel liegt (=onhook).
   977     NSI=0;		// Durch AC-Kopplung „gewählte“ Nummer tilgen
   978     rend=0;		// 4 Sekunden nach dem letzten Klingeln sei ein (verpasster) Anruf zu Ende
   979  //   if (j<4) continue;	// Weniger als 4 Pegelwechsel: Störung, kein Klingeln (was soll das sein??)
   980     if (!(Flags&0x40)) {	// Erstes Klingeln?
   981      Flags|=0x40;	// bemerken
   982      byte*z=eaindex(anrufe);
   983      *z=0;		// Eintrag „Keine Rufnummer“ — noch nicht
   984      EEARL=252;
   985      eechange(++anrufe);	// Anruf verpasst (5 ms Schreibzeit) - einzige Stelle die inkrementiert!
   986      clipRead(z);	// CLIP einlesen, KANN FALLWEISE NICHT ZURÜCKKEHREN
   987     }
   988  // TODO: Lässigere 5 Sekunden passen derzeit nicht in <rend>
   989    }else{	// Spannung < 75%: 2 Zähler auswerten und in Schleife bleiben
   990     if (!++rend) {
   991      Flags&=~0x40;	// Klingel-Ende
   992  // Sofort mit dem Ablauf des Klingel-Timeouts losblitzen, so sieht man das Timeout
   993  // Es kann aber auch sein, dass das Gespräch bei zeitigerem Abheben weg ist
   994      if (anrufe) ledOn();	// LED 16 ms aufblitzen lassen, dann 4 s aus
   995      buf_w=0; // Timeout für's Wählen bei aufgelegtem Telefonhörer: 256×16ms=4s
   996     }
   997    }
   998   }
   999  }
  1000  
  1001  /****************************
  1002   * Feststellung Freizeichen *
  1003   ****************************/
  1004  // Testen (mit dem A/D-Wandler) ob das High an Pin 7 tatsächlich mit einer
  1005  // ausreichenden, steigenden Betriebsspannung einhergeht.
  1006  // Wenn nicht ist es ein parallel geschaltetes Telefon,
  1007  // ein gezogener Stecker oder ein totes Amt;
  1008  // dann muss mit gaaanz langer Watchdog-Spanne
  1009  // auf das Ende dieses irregulären Zustandes gewartet werden,
  1010  // um den Elko möglichst nicht zu entladen.
  1011  // Dass währenddessen keine Wahl bei aufgelegtem Telefonhörer unterstützt wird
  1012  // ist kaum tragisch und erwartungsgemäß.
  1013  // Denn wenn dieser erst mal leer ist, kann der Mikrocontroller nicht mehr
  1014  // hochlaufen und bspw. Anrufe zählen.
  1015  // Das geht dann nur durch Abnehmen des Telefonhörers.
  1016  
  1017  // Im Prinzip ein Designfehler des Mikrocontrollers: Es gibt keine
  1018  // Fuse-Einstellung, mit der der Mikrocontroller ab einer bestimmten Spannung
  1019  // loslegt UND mit einer deutlich niedrigeren weiterarbeiten kann (= Hysterese)
  1020  // UND wenig Strom dafür braucht; der Brown-Out-Detektor ist zu hungrig.
  1021  
  1022  // Daten etwa:
  1023  // Mikrocontroller bei 3 V im PowerDown ohne Watchdog:	max. 2 µA
  1024  //				...	mit Watchdog:	max. 10 µA
  1025  //				...	dazu BrownOut:	+ 15 µA
  1026  //			... arbeitend bei 1 MHz:	500 µA
  1027  // Vom 680-kΩ-Widerstand kommen bei 45 V Amtsspannung:	60 µA
  1028  constexpr byte DOWNLEVEL=0x40;
  1029  // Nachdem der Abfall der Speisespannung bei High an PB2 (vermeintlich abgehoben,
  1030  // aber eher parallel geschaltetes Telefon abgehoben, daher keine Durchstromung)
  1031  // bemerkt wurde, mit extrem geringer Stromaufnahme in langen Intervallen testen
  1032  // und verweilen, bis dieser Zustand beendet und genügend Spannung da ist.
  1033  // Andernfalls wird der Mikrocontroller abstürzen und kommt erst durch Abnehmen
  1034  // des Telefonhörers = Herstellung des Schleifenstroms wieder hoch.
  1035  static void phoneParallel() {
  1036   tonAus();
  1037   PORTB = 0;	// Keine ohmschen Verbraucher: Keine Pullups!
  1038   DDRB  = 0x05;	// Eingänge zu Ausgängen machen und festnageln: Gateschutzdioden entlasten
  1039   do{
  1040    ADCSRA= 0;	// ADC aus
  1041    WDTCR = 0xE1;	// 8 s (Maximum)
  1042    MCUCR = 0xB4;
  1043    MCUCR = 0xB0;	// Sleep-Modus: PowerDown, kein BrownOut
  1044    sleep_cpu();	// bis zum Watchdog
  1045    ADCSRA= 0xF4;	// Start mit Teiler 16: 16 µs, ×13 = 208 µs
  1046    WDTCR = 0xC0;	// 16 ms (für den ADC wird offenbar 1 ms benötigt!! Sonst kommt Murks weil Referenzspannung falsch.)
  1047    MCUCR = 0x28;	// ADC-Störunterdrückung
  1048    sleep_cpu();	// bis zum Watchdog
  1049   }while (ADCH>=DOWNLEVEL-2);	// 0x3E: Hysterese von 2 (gegenüber 0x40)
  1050  }
  1051  
  1052  // Anscheinend dauert es lange bis die Referenzspannung steht.
  1053  // Daher wird zwischen adPin7 und adRef alle 16 ms hin- und hergeschaltet.
  1054  static void handleADC() {
  1055   static byte adRef NOINIT;
  1056   switch (ADMUX) {
  1057    case 0x01: {		// maß ADC1 = PB2 bzgl. Speisespannung?
  1058     ADMUX |= 0x20;
  1059     readPb2(ADCH);
  1060     ADMUX  = 0x2C;	// umschalten auf Referenzspannung 1,1 V
  1061    }break;
  1062    case 0x2C: {		// maß Referenzspannung bzgl. Speisespannung?
  1063     byte b = adRef;
  1064     byte a = ADCH;
  1065     adRef  = a;
  1066     if (a>=DOWNLEVEL && a>b) {// Ucc < 4,4 V (Uref > Ucc × 25%) und fallend
  1067      phoneParallel();	// hinterlässt DDRB.0 gesetzt
  1068     }
  1069    }/*nobreak*/;
  1070    default: ADMUX = 0x01;	// umschalten auf ADC1 = PB2
  1071   }
  1072  }
  1073  
  1074  static byte f425;	// Alle 32 ms ein Bit, 8 Bit = 256 ms
  1075  extern bool mess425();
  1076  
  1077  static void m425() {	// Messung 425 Hz, c: mit ADC oder d: mit AC
  1078   if (f425&1) ledOn();	// bei vorherigem Freizeichen
  1079   f425=f425<<1|mess425();	// ggf. Abbruch wenn Messung nicht möglich
  1080   while (!(Flags&0x80)) sleep_cpu();	// warten bis Watchdog-Interrupt (16 ms)
  1081   handleADC();
  1082  }
  1083  
  1084  /************************************
  1085   * Routinen für abgehobenen Zustand *
  1086   ************************************/
  1087  
  1088  // Für die zyklische Ausgabe von Kenntönen für verpasste Anrufe
  1089  static byte pause,index NOINIT;	// nur offhook verwendet
  1090  
  1091  // Zyklisch aufzurufen: Arbeitet alle <anrufe> ab und generiert Zweiton-Töne
  1092  static void piepAnrufe() {
  1093   if (Flags&0x40) return;	// Anruf wurde entgegen genommen: Nichts tun
  1094   if (!anrufe) return;	// Keine Anrufe seit letztem Auflegen: Nichts tun
  1095   if (NSI) return;
  1096   if (Zeit8) return;
  1097   if (buf_w) return;
  1098   switch (--pause) {	// zunächst 256 Aufrufe nichtstun, zwischen den Pieptönen weniger
  1099    case (byte)WDT_SEC(0.25): StartTon(0); return;
  1100    case 0: break;
  1101    default: return;
  1102   }
  1103   const byte*z=eaindex(index);
  1104   byte l=*z;
  1105   char f;
  1106   if (--l>=19) f=-(1+(++l>>5));	// kein Rückruf möglich
  1107   else f=FindInKurzwahl(z);	// Rückwärtssuche im „Telefonbuch“
  1108   StartTon(f);			// CLIP-Eintrag auf diese Weise hörbar machen
  1109   if (++index>=anrufe) index=0;
  1110   else pause=WDT_SEC(0.5);
  1111  }
  1112  
  1113  static void offhook() {		// CPU-Taktfrequenz: 1..2 MHz
  1114  // Mögliches Problem: Speicheraktionen wenn paralleles Telefon abgehoben entlädt Elko unnötig
  1115  // Aber: Anders als durch (u.a. dadurch verursachten) Speisespannungsabfall ist's eh' nicht feststellbar
  1116   EEARL = 253;
  1117   eechange(eeread()+1);	// Abhebe-Zähler
  1118  #ifdef __AVR_ATtiny85__
  1119   EEARH = 1;
  1120   EEARL = 0;
  1121   byte i;
  1122   for (i=0; i<sizeof clip; i++) {
  1123    eechange(clip[i]);
  1124    ++EEARL;
  1125   }
  1126   for (i=0; i<sizeof histo; i++) {
  1127    eechange(histo[i]);
  1128    ++EEARL;
  1129   }
  1130   byte j=eaindex(anrufe)-lea;
  1131   for (i=0; i<j; i++) {
  1132    eechange(lea[i]);
  1133    ++EEARL;
  1134   }
  1135   for (;EEARL;++EEARL) eechange(0xFF);	// Rest löschen
  1136   EEARH = 0;
  1137  #endif
  1138   MCUCR = 0x20;		// Sleep-Modus: Idle (Timer kann laufen, Strom ist genug da)
  1139   ADCSRA= 0xF0|BLOG2;	// /8 = 125 kHz / 13 = 9,6 kSa/s
  1140   T_on   = 0;
  1141   byte no_dial=WDT_SEC(1);	// besser: auf Freizeichen warten
  1142   byte a=anrufe;
  1143   byte b=0;
  1144   if (Flags&0x40) {	// assert(a)
  1145  // Beim Abheben während des Klingelns nachgucken, ob CLIP im Rückrufspeicher liegt.
  1146  // Wenn ja dann alle Kopien davon aus dieser Liste tilgen.
  1147  // Eine möglicherweise (bei aufgelegtem Hörer, vor dem Klingeln)
  1148  // bis dahin gewählte Nummer wird überschrieben.
  1149    byte*z=eaindex(a-1);
  1150    byte l=*z;
  1151    if (--l<19) {		// 0 < l < 20
  1152     buf_r=++l;
  1153     memcpy(wahl,z,11);	// Für Vergleichsvorgang innerhalb DelRyckruf() in den Wahlspeicher kopieren
  1154     DelRyckruf();	// reduziert <anrufe>
  1155     b=a-anrufe;		// sollte >=1 sein, wenn CLIP erfolgreich
  1156     if (b) --b;		// Wenn mehr als 1 Anruf von derselben Nummer, dann piepsen, sonst nicht
  1157    // Dann weiß man, dass dieser Anrufer bereits vorher versucht hatte anzurufen.
  1158    // Den Ton hören beide Partner.
  1159     KurzSpeichern(0);	// Zur „Wahlwiederholung“ speichern
  1160  // (Als Ausnahme von der Regel, dass da nur selbst gewähltes hinein darf.
  1161  //  Der Grund ist, dass man diese Nummer während oder nach dem Gespräch
  1162  //  in einen Kurzwahlspeicher mit 7³ legen kann.)
  1163    }
  1164   }
  1165   pause=WDT_SEC(1);
  1166   index=0;
  1167   for(;;) {		// Hauptschleife
  1168    m425();	// blockiert bis zum Watchdog-Interrupt
  1169    ledOff();
  1170  // Sonst funktioniert der Vergleich mit der Referenzspannung nicht!!
  1171    if (DDRB&1) {	// wenn handleADC() phoneParallel() festgestellt hatte, dann raus hier
  1172     DDRB&=~1;		// PB0 = Ausgang = Kennung dafür, wegnehmen
  1173     break;
  1174    }
  1175    readPins();
  1176    if ((pb2&7)==0b100) break;	// Low durch Auflegen
  1177    if (T_on) {
  1178     if (--T_on==(byte)WDT_SEC(PAUSE)-1) tonAus();	// Tonlänge reduzieren
  1179    }
  1180    if (no_dial && !--no_dial && !buf_w && !(Flags&0x40) && !anrufe) {
  1181  #ifndef DEBUG
  1182     KurzLaden(24);	// Automatische Wahl beim Abheben
  1183  #endif
  1184    }
  1185    NSK();			// Nummernschaltkontakt-Auswertung
  1186    if (Zeit16 && !--Zeit16) KurzSpeichern(0);
  1187    piepAnrufe();
  1188    if (T_on) continue;		// Ton- oder Pausenausgabe läuft
  1189    if (no_dial) continue;
  1190    if (b) {StartTon(b); b=0;}	// Tonhöhe je nach Anzahl der Anrufversuche
  1191    else if (buf_r!=buf_w) StartDTMF(GetBuf());	// Nächsten Ton + Pause ausgeben
  1192   }
  1193   if (T_on>=(byte)WDT_SEC(PAUSE)) tonAus();
  1194  }
  1195  
  1196  /*************************************
  1197   * Initialisierung und Hauptschleife *
  1198   *************************************/
  1199  extern "C" void __init() __attribute__((naked,section(".init2")));
  1200  extern "C" void __init() {
  1201   asm volatile("clr r1");// Stack nicht initialisieren, um gleichermaßen auf ATtiny45 und ATtiny85 lauffähig zu sein
  1202  			// SPH:SPL ist bereits mit RAMEND initialisiert
  1203   Flags = MCUSR&0x08 ? 0x40 : 0;		// Reset-Ursache = Watchdog bedeutet: Timeout beim CLIP-Empfang
  1204   MCUSR = 0;
  1205   WDTCR|= 0x18;
  1206   WDTCR = 0xC0;		// Watchdog auf Interrupt, 16 ms = 62 Hz (einzige Interruptquelle)
  1207   PORTB = 0x25;		// Pullup für 3 Eingänge
  1208  }
  1209  
  1210  static void hardwareInit() {
  1211   eewait();
  1212   EEARH = 0;		// Auch für ATtiny25/45 so compilieren für Portierbarkeit; EEARH ist nach Reset uninitialisiert
  1213   EEARL = Flags&0x40 ? 254 : 255;
  1214   eechange(eeread()+1);	// Reset- oder Watchdog-Reset-Zähler, macht sei()
  1215   EEARL = 252;
  1216   byte a=eeread();
  1217   if (a>32) a=0;
  1218   anrufe= a;
  1219   if (!(Flags&0x40)) {
  1220    rend=0;		// Nach normalem Reset den ersten Blitz verzögern
  1221  #ifdef __AVR_ATtiny85__
  1222    EEARH = 1;
  1223    EEARL = sizeof clip+sizeof histo;
  1224    for (byte i=0; i<sizeof lea; i++) {
  1225     lea[i]=eeread();	// Luxus ATtiny85: Anrufliste wiederherstellen
  1226     ++EEARL;
  1227    }
  1228    EEARH = 0;
  1229  #else
  1230    memset(lea,0,a);	// Anrufliste besteht aus unbekannten Anrufen
  1231  #endif
  1232    EEARL = 230;		// 23×10: Ortsvorwahl
  1233    numLoad(ovw,8);
  1234   }			// Bei Watchdog-Reset bleibt <rend> und <lea> bestehen
  1235   ACSR |= 0x80;		// Analogvergleicher ausschalten
  1236   DIDR0 = 0b011110;	// diese digitalen Eingänge nicht nutzen
  1237   PCMSK = 0x01;		// Nur PB0 = Wählscheibe überwachen (ohne Interrupt)
  1238  }
  1239  
  1240  int main() __attribute__((OS_main));
  1241  int main() {
  1242   hardwareInit();
  1243   for(;;) {
  1244    onhook();		// Aufgelegter Hörer: Minimaler Stromverbrauch
  1245    offhook();		// Abgehobener Hörer: Normale Funktion
  1246    Flags&=~0x40;		// Aktiven Anruf beenden
  1247    rend=0xFF;		// Sofort blitzen
  1248   }
  1249  }
  1250  
Detected encoding: UTF-80