Source file: /~heha/basteln/Haus/Telefon/Mithören-ISDN/Firmware.zip/ATtiny25/isdn25.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: 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-80