Source file: /~heha/basteln/Haus/Telefon/Mithören-ISDN/Firmware.zip/hunz@mikrocontroller.net/main.S

#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-80