#define __SFR_OFFSET 0
#include <avr/io.h>
/* Referenzen:
http://wwwlehre.dhbw-stuttgart.de/~schulte/digiisdn.htm
http://www.virtualuniversity.ch/telekom/telefon/7.html
http://www.rhyshaden.com/isdn.htm
Hardware: ATtiny25 und Koppelschaltung (1), (2) oder (3)
Pin
1 PB5 !RESET !Reset (für ISP-Programmierung, erfordert Komparatoren) oder Sprechen+
2 PB3 Sprechen-
3 PB4 OC1B Tonausgabe Sprechen
4 GND
5 PB0 PCINT Hören-
6 PB1 OC1A Tonausgabe Hören
7 PB2 PCINT Hören+
8 Ucc
Plus oder Minus ist egal. Alle 3-4 Eingänge sind low-aktiv.
(1) Trafokopplung digital
Werden nur Trafos benutzt, sind 4 Eingänge erforderlich
und 1 Poti zur Vorgabe eines knappen High-Pegels.
Vorteil: Wenige Bauelemente.
Nachteil: Trafos = Spezialbauelemente, Einstellaufwand, Querstrom der Eingänge.
(2) Kapazitive Kopplung und LM339
Werden Kondensatoren und LM339 verwendet, genügen 3 Eingänge.
Damit bleibt !RESET zum ISP frei.
Vorteil: Kein Trafo, nur Standardbauteile, geringe Stromaufnahme.
Nachteil: Viele Bauelemente, keine echte Potenzialtrennung.
(3) Trafokopplung und LM339
Schaltung wie beim ISDN-Sniffer.
Vorteil: Perfekte Anschaltung, !RESET bleibt frei, geringe Stromaufnahme.
Nachteil: Viele Bauelemente, Trafos = Spezialbauelemente.
*/
#define lastpol r3
#define rxBH r4 // B-Daten Hören
#define rxBS r5 // B-Daten Sprechen
#define bitcnt r19
.set BITTIME,F_CPU/192000
.macro outi p,i
ldi r16,\i
out \p,r16
.endm
.section .fuse,"a",@progbits
.byte 0b11110001 // Kein Taktteiler, HF-PLL-Oszillator
.byte 0b01010111 // !RESET = Portpin, EESAVE
.byte 0b11111111 // unverändert
.section .signature,"a",@progbits
.byte SIGNATURE_2,SIGNATURE_1,SIGNATURE_0
.section .vectors
rjmp onReset //0 RESET
.word 0 //1 INT0
nop //2 PCINT
nop //3 TIMER1_COMPA
outi TCCR0B,1 //4
outi TCNT0,0 //6 Timer0 starten
outi TIFR,0x40 //8 Timer0-Compare-Interrupt löschen
// Compare-Interrupt für nächstes, flankenloses Bit:
in r16,PINB //A
// Bitmuster für Bits 2 und 0:
// "00" kommt nicht vor
// "01" = MAMI + = Null-Bit
// "10" = MAMI - = Null-Bit
// "11" = MAMI 0 = Eins-Bit
mov r17,r16 // Bits 5+3 (Sprechkanal) merken
com r16
andi r16,0b00000101
brne ZERO_BIT
ldi r16,0x80
cpi bitcnt,3 // Erste Bits müssen 0 sein, sonst Rahmenverlust
brsh DATABIT
SYNC_LOST:
outi TCCR0B,0 // Timer0 anhalten und nur noch auf Flanke warten
endframe:
clr bitcnt
// Interruptflag der Flankeninterrupts löschen
iret: ldi r17,0x20
out GIFR,r17
reti
/*
S0-Rahmen (Datenrate: 192 kBit/s, Rahmenlänge 48 Bit = 250 µs)
1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4
1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8
╔╤╤═╤╤╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╤═╗
H║0│0│B│B│B│B│B│B│B│B│E│D│A│0│1│b│b│b│b│b│b│b│b│E│D│0│B│B│B│B│B│B│B│B│E│D│0│b│b│b│b│b│b│b│b│E│D│L║
╟─┼─┼┬┼─┼┬┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─╢
S║D│L│0│0│B│B│B│B│B│B│B│B│L│D│L│0│0│b│b│b│b│b│b│b│b│L│D│L│B│B│B│B│B│B│B│B│L│D│L│b│b│b│b│b│b│b│b│L║
╚═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╧═╝
B = B1, b = B2, H = Hören, S = Sprechen, A = Aktivierungsindikator
D = Daten, E = Echo, L = Gleichspannungs-Ausgleichsbit
╤┬ = ab da Verletzung AMI-Kodierregel wenn Null
*/
ZERO_BIT:
// Gleiches Bitmuster wie vorhergehendes Nullbit?
// (MAMI-Koderegelverletzung = Synchronisierungskandidat)
cp r16,lastpol
breq CODE_VIOLATION
// Keine Koderegelverletzung: Polarität speichern
mov lastpol,r16
cpi bitcnt,0
breq SYNC_LOST // Rahmen hat noch nicht angefangen
inc bitcnt // jetzt ≥ 2
rjmp DATA0 // Mit 0-Datenbit weitermachen
CODE_VIOLATION:
// Gleiches Bitmuster wie vorangegangenes Nullbit?
cpi bitcnt,0
brne DATA0 // ignorieren und als Datenbit werten
inc bitcnt // jetzt ≡ 1
rjmp iret
DATA0: clr r16
DATABIT:
cpi bitcnt,48 // Rahmenende erreicht?
breq endframe // Ja, nächste Rahmensuche einleiten
lsl r16
rol rxBH // Datenbit einschieben
sbrs r17,5
cbr r17,1<<3
swap r17
lsl r17
rol rxBS // Datenbit einschieben
inc bitcnt
// Wenn PCM-Byte komplett dann ausgeben (nur B1, nicht B2)
cpi bitcnt,11
breq pwm_h
cpi bitcnt,13
breq pwm_s
cpi bitcnt,35 // Zweiter B1-Block
breq pwm_h
cpi bitcnt,37
breq pwm_s
rjmp iret
pwm_h: // Datenbyte vom Hör-Adernpaar fertig
mov ZL,rxBH
lpm
out OCR1A,r0
rjmp iret
pwm_s: // Datenbyte vom Sprech-Adernpaar fertig
mov ZL,rxBS
lpm
out OCR1B,r0
rjmp iret
onReset:
outi OCR0A,BITTIME // CTC-Länge setzen
outi TCCR0A,2 // CTC-Modus vorbereiten
// Timer1 als asynchroner High-Speed-PWM-Generator (Stereo)
outi PLLCSR,7 // 64 MHz für Timer1
outi TCCR1,0x61
outi GTCCR,0x60
outi OCR1A,128 // Mittelwert (50% PWM) anlegen
out OCR1B,r16
outi PORTB,0b00101101 // Pullups aktivieren
outi DDRB,0x12 // PWM-Ausgänge aktivieren
outi PCMSK,0b00000101 // Flanken nur auf Hören detektieren
outi MCUCR,0x20 // sleep aktivieren
out GIMSK,r16 // Pegelwechsel-Interrupts aktivieren
clr bitcnt
clr lastpol
ldi ZH,hi8(a_law)
sei
1: sleep
rjmp 1b
// Lookup-Tabelle für segmentierte Ausgabe der A-Law-Audiodaten
// „glättet“ 8-Bit-Wert in linearen 8-Bit-Wert für D/A-Wandler
.org 256 // Low-Teil = Index
.type a_law,@object
a_law:
.set i,0
.rept 256
.set j,i^0xD5 // 0x55-XORung + Vorzeichenwechsel
.set e,j>>4&7 // Exponent 0..7
.set m,j&0x0F // Mantisse 0000ABCD
.set m,m<<2|m>>2 // 00ABCDAB
.if e
.set m,m|0x40 // 01ABCDAB, führende 1
.else
.set e,1 // „denormalisierte“ Zahl ohne führende 1
.endif
.set m,m>>(7-e) // siehe unten
.if j&0x80 // Vorzeichen
.byte 127-m
.else
.byte 128+m
.endif
.set i,i+1
.endr
/* Bauvorschrift aus 0eeeABCD, eee=Exponent, ABCD=Mantisse, 12-Bit-Ergebnis
* 7 01ABCDABcdab
* 6 001ABCDAbcda
* 5 0001ABCDabcd
* 4 00001ABCdabc
* 3 000001ABcdab
* 2 0000001Abcda
* 1 00000001abcd
* 0 00000000abcd
* Kleinbuchstaben gehen bei 8-Bit-Ausgabe verloren. Vorzeichen extra
*/
| Detected encoding: UTF-8 | 0
|