#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: ATmega8 mit vorgeschaltetem LM393 bzw. LM339
und Kondensatorkopplung oder (besser) Trafokopplung
Pin
1 (PC6) RESET ISP-Programmierung
4 PD2 INT0 Komparator 1 innere Doppelader
5 PD3 INT1 Komparator 2 innere Doppelader
6 PD4 Komparator 3+4 äußere Doppelader (optional)
7 Ucc 5P
8 GND 00
9 (PB6) XTAL1 Quarz oder Resonator 16 MHz
10 (PB7) XTAL2 Quarz oder Resonator 16 MHz
15 PB1 OC1A PWM-Audioausgabe innere Doppelader
16 PB2 OC1B PWM-Audioausgabe äurere Doppelader
17 (PB3) MOSI ISP-Programmierung
18 (PB4) MISO ISP-Programmierung
19 (PB5) SCK ISP-Programmierung
20 AUcc 5P
22 GND 00
23:28 PC0:5 LEDs
*/
#define lastpol r3
#define rxBH r4 // B-Daten Hören
#define rxBS r5 // B-Daten Sprechen
#define bitcnt r19
.set PWM_PERIOD,F_CPU/16000 // 1000
.set BITTIME,F_CPU/192000
.macro outi p,i
ldi r16,\i
out \p,r16
.endm
.section .fuse,"a",@progbits
.byte 0b11111111 // Quarzoszillator
.byte 0b11011001 // unverändert
.section .signature,"a",@progbits
.byte SIGNATURE_2,SIGNATURE_1,SIGNATURE_0
.section .vectors
rjmp onReset //0 RESET
nop //1 INT0
// Starte Timer0 als Zeitgeber zum Abtasten von flankenlosen Folgebits
outi TCNT0,-BITTIME //2 INT1
outi TCCR0,1 //4
out TIFR,r16 //6 Interruptflag rücksetzen
out TIMSK,r16 //7 Interrupt freigeben
rjmp samp //8
// Überlaufinterrupt für nächstes, flankenloses Bit:
// Neuen Interruptzeitpunkt festlegen, dabei Interruptlatenz addieren
in r17,TCNT0 //9 TIMER0_OVF
subi r17,-BITTIME //A
out TCNT0,r17 //B
rjmp samp //C S0-Bus abtasten
.text
samp: in r16,PIND
// Interruptflags der Flankeninterrupts löschen
ldi r17,0xC0
out GIFR,r17
// Bitmuster für Bits 3 und 2:
// "00" kommt nicht vor
// "01" = MAMI + = Null-Bit
// "10" = MAMI - = Null-Bit
// "11" = MAMI 0 = Eins-Bit
mov r17,r16 // Bit 4 (Sprechkanal) merken
com r16
andi r16,0x0C
brne ZERO_BIT
ldi r16,0x80
cpi bitcnt,3 // Erste Bits müssen 0 sein, sonst Rahmenverlust
brsh DATABIT
SYNC_LOST:
outi TCCR0,0 // Timer0 anhalten und nur noch auf fallende Flanke warten
endframe:
clr bitcnt
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
reti
DATA0: clr r16
DATABIT:
cpi bitcnt,48 // Rahmenende erreicht?
breq endframe // Ja, nächste Rahmensuche einleiten
lsl r16
rol rxBH // Datenbit einschieben
lsl rxBS
sbrc r17,4
inc 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
reti
pwm_h: // Ausgabe PCM-Wert für Hörkanal an OC1A
// Datenbyte per Tabelle in Analogwert umrechnen (A-Law)
clr r16
ldi ZL,lo8(ALAW)
ldi ZH,hi8(ALAW)
add ZL,rxBH
adc ZH,r16
add ZL,rxBH
adc ZH,r16
lpm r0,Z+
lpm r1,Z+
// PWM-Ausgabe füttern
out OCR1AH, r1
out OCR1AL, r0
// LEDs flackern lassen
out PORTC, r0
reti
pwm_s: // Ausgabe PCM-Wert für Sprechkanal an OC1B
clr r16
ldi ZL,lo8(ALAW)
ldi ZH,hi8(ALAW)
add ZL,rxBS
adc ZH,r16
add ZL,rxBS
adc ZH,r16
lpm r0,Z+
lpm r1,Z+
out OCR1BH,r1
out OCR1BL,r0
reti
onReset:
// Als einer der wenigen AVRs benötigt ein ATmega8 eine Stack-Initialisierung
outi SPH,hi8(RAMEND)
outi SPL,lo8(RAMEND)
// Starte Timer1 für Pulsweitenmodulation bei 32 kHz
outi TCCR1A,0x82
outi TCCR1B,0x19
outi ICR1H,hi8(PWM_PERIOD-1)
outi ICR1L,lo8(PWM_PERIOD-1)
outi OCR1AH,hi8(PWM_PERIOD/2) // Mittelwert (50% PWM) anlegen
out OCR1BH,r16
outi OCR1AL,lo8(PWM_PERIOD/2)
out OCR1BL,r16
outi PORTD,0xFF // Pullups aktivieren
outi DDRB,0x06 // PWM-Portpins auf Ausgabe
outi DDRC,0x3F // LED-Portpins auf Ausgabe
// INT0/1-Interrupts bei fallender Flanke freigeben
outi MCUCR,0x0A
outi GICR,0xC0
clr bitcnt
clr lastpol
sei
1: rjmp 1b
Detected encoding: UTF-8 | 0
|