/* 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-8 | 0
|