#define __SFR_OFFSET 0
#include <avr/io.h>
/*************************
* Interrupts für S0-Bus *
*************************/
.global INT0_vect,INT1_vect,TIMER0_COMPA_vect,s0Init
#define D_EP 4 // Interrupt 16 Byte doppelt gepuffert
#define B_EP 3 // Isochron 64 Byte doppelt gepuffert
/*
Registerverwendung:
R2 usbCfg [5] = Phase des letzten 1-Bits
R3 rettet SREG
R4 rettet ZL
R5 rettet ZH
R6 Bit-Eimer Hören (Innere Doppelader, NT->TE)
R7 Bit-Eimer Sprechen (Äußere Doppelader, TE->NT)
R8 Wort-Adresse in UTAB = Bitnummer (Hör-Kanal, 0x74/2 + 0..47)
R9 temporär (rettet UENUM)
R10 Einsenzähler D-Kanal (um Stopfbits zu entfernen)
R11 [7:1] = Bitzähler D-Kanal, [0] = D-Datenrahmen aktiv
R12 Bit-Eimer D-Kanal
R13..R15 reserviert für E-Kanal
Für getrennte Synchronisation ist keine Zeit.
Synchronisiert wird auf „Hören“, ein Betrieb ohne Anzapfung der (äußeren)
Sprech-Doppelader ist daher möglich und auch vorgesehen.
D-Kanal: Datenblock beginnt und endet mit 0x7E,
innerhalb der Daten werden nach 5 1-Bits ein 0-Bit eingeschoben (Bitstopfen)
CPU-Taktfrequenz: 16 MHz
Zeiten: 1 Bit = 192 kHz = 5,2 µs = 83,3 Takte
1 Frame = 4 kHz = 250 µs = 4000 Takte
1 Byte (pro Kanal) = 8 kHz = 125 µs = 2000 Takte
1 Sample = 8 kSa/s × 2 Kanäle × 16 Bit = 32 kByte/s
D-Kanaldaten: max. 2 KByte/s, bei 16-Byte-Endpoints
USB-Interrupts mit ~ 120 Hz (8 ms)
*/
.macro SAVEPHASE
brcs 7f
bst r2,5
7:
.endm
.section .noinit
// Sample-Speicher
s1: .ds.b 1 // Sprechen B1 (Äußere Doppelader, TE->NT)
// „utab“ darf keine 200h-Grenze überstreichen.
.section .text // Hören Sprechen
utab: rjmp syncframe //1 0+! D-
rjmp ignore //2 0- L+
rjmp h_bit //3 B1-! 0+!
rjmp h_bit //4 B1 0-
rjmp h_bit //5 B1 B1-!
rjmp h_bit //6 B1 B1
rjmp h_bit //7 B1 B1
rjmp h_bit //8 B1 B1
rjmp h_bit //9 B1 B1
rjmp h_bit //10 B1 B1
rjmp in_e //11 E B1
rjmp in_d_s1 //12 D B1
rjmp in_a //13 A L+
rjmp usbH //14 0 D-
rjmp usbS //15 1 L+
rjmp usbE //16 B2 0-
rjmp tonH //17 B2 0+
rjmp tonS //18 B2 B2
rjmp ignore //19 B2 B2
rjmp ignore //20 B2 B2
rjmp ignore //21 B2 B2
rjmp ignore //22 B2 B2
rjmp ignore //23 B2 B2
rjmp in_e //24 E B2
rjmp in_d //25 D B2
rjmp ignore //26 0 L+
rjmp h_bit //27 B1 D-
rjmp h_bit //28 B1 L+
rjmp h_bit //29 B1 B1-
rjmp h_bit //30 B1 B1
rjmp h_bit //31 B1 B1
rjmp h_bit //32 B1 B1
rjmp h_bit //33 B1 B1
rjmp h_bit //34 B1 B1
rjmp in_e //35 E B1
rjmp in_d_s1 //36 D B1
rjmp usbH //37 0 L+
rjmp usbS //38 B2 D-
rjmp usbE //39 B2 L+
rjmp tonH //40 B2 B2-
rjmp tonS //41 B2 B2
rjmp ignore //42 B2 B2
rjmp ignore //43 B2 B2
rjmp ignore //44 B2 B2
rjmp ignore //45 B2 B2
rjmp in_e //46 E B2
rjmp in_d //47 D B2
rjmp null //48 L+ L+
.section .text
// H-L-Interrupts zur Synchronisation an S0
// Zeitforderung: < 83 Takte
// Geradeaus-Durchlauf = 57 Takte
// Worst-Case = 78 Takte (überschlagen, sicherlich etwas weniger)
INT1_vect:
INT0_vect:
in r3,SREG
movw r4,ZL
ldi ZL,1
out TCCR0B,ZL // Timer0 starten (falls nicht laufend)
in ZL,TCNT0
subi ZL,-90 // „Wecker“ fürs nächste Bit setzen
out OCR0A,ZL // (Ein Flankeninterrupt kommt ggf. eher)
ldi ZL,0x02
out TIFR0,ZL // eventuellen COMPA-Interrupt löschen!
rjmp 1f //(11)
// Timer0-Interrupts zum Fangen der Datenbits
TIMER0_COMPA_vect:
in r3,SREG
movw r4,ZL
in ZL,OCR0A
subi ZL,-84 // 16 MHz / 192 kHz = 83,3 (CPU-Takte!!)
out OCR0A,ZL //(5) „Wecker“ erneut stellen
1: //ldi ZL,0x21
//out EIFR,ZL // eventuelle Flanken-Interrupts löschen
in ZL,PIND // Pegel einfangen (Bits 0,1,4)
// jetzt Ternärwert Hören und Binärwert Sprechen in ZL: `...s..hh`
lsl r7
sbrc ZL,4
inc r7 // Sprechbit einsetzen
ori ZL,0xFC // `111111hh`
ldi ZH,1
add ZH,ZL // C=1 wenn beide h = 1 (Ruhepegel), sonst C=0
//Sprungverteiler:
bld ZL,0 // Zustand von PD0 nach T retten
mov ZL,r8
ldi ZH,pm_hi8(utab) // = 0
ijmp //(13)
// Rahmenanfang detektieren bzw. verifizieren
syncframe:
brcs stop // muss 0-Bit sein, also eine Flanke haben
bst ZH,5 // Aktuelle Phase
eor ZH,r2 // mit vorheriger Phase vergleichen
bst r2,5 // und aktuelle Phase sichern
sbrc ZH,5 // Aufeinanderfolgende ++ oder --?
rjmp stay // nein
cbi PORTD,5 // Tx-LED („Rahmen“) ein
ldi ZL,0x01
sts TCCR4B,ZL // PWM-Generator starten
sbi DDRB,6 // Tonausgabe Hören ein
sbi DDRD,7 // Tonausgabe Sprechen ein
rjmp next
// Ein B1-Bit (Hören) steht zur Verfügung
h_bit: SAVEPHASE
rol r6
rjmp next
// Echo-Bit (Hören) verfügbar: Nicht auswerten
in_e:
// Irgendein Bit verfügbar: Nicht auswerten
ignore:
SAVEPHASE
next: inc r8 // Bitnummer im Rahmen inkrementieren
stay: movw ZL,r4
out SREG,r3
reti //(9) (ab „ignore“)
stop: // Timer+Gespräch STOP, Bus idle
ldi ZL,0
out TCCR0B,ZL // Timer0 anhalten (Flanke muss kommen)
sbi PORTB,0 // Rx-LED („Gespräch“) aus
cbi DDRD,7 // Tonausgabe Sprechen hochohmig
cbi DDRB,6 // Tonausgabe Hören hochohmig
sts TCCR4B,ZL // PWM-Generator stoppen
rjmp stay
gstop: // Gespräch STOP
sbi PORTB,0 // Rx-LED („Gespräch“) aus
rjmp next
in_a: brcs gstop
bst r2,5
cbi PORTB,0 // Rx-LED („Gespräch“) ein
rjmp next
// B1 (Hören)
usbH: SAVEPHASE
sbrs r2,2 // USB-Soundkanal aktiv?
rjmp next // nein, kein USB
lds r9,UENUM // retten
ldi ZL,B_EP
sts UENUM,ZL // EP3 (doppelt gepuffert)
#ifdef LAW_AUDIO
mov ZL,r6
sts UEDATX,ZL // Hören (links)
#else
mov ZL,r6
ldi ZH,hi8(law)
lpm r7,Z
sts UEDATX,r7
ldi ZH,hi8(law+256)
lpm r7,Z
sts UEDATX,r7
#endif
sts UENUM,r9
rjmp next
// wie oben jedoch fü den Sprechkanal
usbS: SAVEPHASE
sbrs r2,2
rjmp next
lds r9,UENUM
ldi ZL,B_EP
sts UENUM,ZL // EP3 (doppelt gepuffert)
#ifdef LAW_AUDIO
lds ZL,s1
sts UEDATX,ZL // Sprechen (rechts)
#else
lds ZL,s1
ldi ZH,hi8(law)
lpm r7,Z
sts UEDATX,r7
ldi ZH,hi8(law+256)
lpm r7,Z
sts UEDATX,r7
#endif
sts UENUM,r9
rjmp next
// SOF-Interrupt testen und Paket absetzen
usbE: SAVEPHASE
sbrs r2,2
rjmp next
lds r9,UENUM
ldi ZL,B_EP
sts UENUM,ZL // EP3 (doppelt gepuffert)
lds ZL,UDINT
sbrs ZL,SOFI
rjmp 7f
ldi ZL,~(1<<SOFI)
sts UDINT,ZL
ldi ZL,0
sts UEINTX,ZL
7: sts UENUM,r9
rjmp next //max. (57)
// Ende des 48-Byte-Rahmens (der Hörleitung)
null: ldi ZL,pm_lo8(utab)
mov r8,ZL // Neues Frame: Bitzähler = 0
sbi PORTD,5 // Tx-LED („Rahmen“) aus
rjmp stay
// D-Bit (Hören) verfügbar UND B1-Byte (Sprechen) fertig
in_d_s1:
sts s1,r7 // abspeichern
// D-Bit (Hören) verfügbar
in_d: SAVEPHASE
movw ZL,r10
sbrc ZH,0 // Bitzähler aktiv?
rjmp 1f // beim Einlesen und Unstuff
dec ZL // Einsenzähler
brcs 9f // weiter nur Einsen zählen
ldi ZL,7
brne 9f // Einsenzähler war ungleich Null
sbr ZH,1 // Start-of-Frame (01111110) erkannt
ldi ZL,6
rjmp 9f
1: dec ZL
brcs 2f // 1-Bit ohne weitere Beachtung
ldi ZL,6
breq 9f // Nach 5 Einsen das 0-Bit ignorieren
2: rol r12 // Datenbit einschieben (verändert Z)
tst ZL // Null bei 6 aufeinanderfolgenden Einsen
breq 8f // bei ....0111111 anhalten (Stuff-Fehler)
subi ZH,-2 // Bits zählen
brhs 9f // fertig wenn Byte noch nicht voll
movw r10,ZL
lds r9,UENUM // retten
ldi ZH,D_EP // EP4 für D-Kanal
sts UENUM,ZH
sts UEDATX,r12 // Datenbyte übergeben
brcs 2f
ldi ZH,0
sts UEINTX,ZH // Commit
2: sts UENUM,r9
rjmp next //(34)
8: // USB-Datenblock zum Host absenden (Commit) und fertig
lds r9,UENUM // retten
ldi ZH,D_EP // EP4 für D-Kanal
sts UENUM,ZH
ldi ZH,0 // hier Doppelfunktion: Bitzähler deaktivieren
sts UEINTX,ZH
sts UENUM,r9
ldi ZL,0
9: movw r10,ZL
rjmp next //(28)
// Tonausgabe linker Kanal: Hören
tonH: SAVEPHASE
mov ZL,r6
ldi ZH,hi8(lawPWM+256)
lpm r7,Z
sts TC4H,r7
ldi ZH,hi8(lawPWM)
lpm r7,Z
sts OCR4B,r7
rjmp next
// Tonausgabe rechter Kanal: Sprechen
tonS: SAVEPHASE
lds ZL,s1
ldi ZH,hi8(lawPWM+256)
lpm r7,Z
sts TC4H,r7
ldi ZH,hi8(lawPWM)
lpm r7,Z
sts OCR4D,r7
rjmp next
// Von C aufrufbare Initialisierungsroutine: Alles außer Ports
s0Init: ldi r24,pm_lo8(utab)
mov r8,r24 // mit syncframe beginnen lassen
clr r11
ldi r24,84
out OCR0A,r24 // 83,3 CPU-Takte (16 MHz) pro Bit (192 kHz)
ldi r24,2
out TCCR0A,r24 // CTC-Modus
sts TIMSK0,r24 // Output-Compare-Interrupt aktivieren (Timer ist noch gestoppt)
ldi r24,0x0A
sts EICRA,r24 // Fallende Flanken lösen Interrupts aus
ldi r24,3
out EIMSK,r24 // INT0+1
ldi r24,0x34
out PLLFRQ,r24 // PLL mit 48 MHz für USB und Timer4
ldi r24,0x21
sts TCCR4A,r24 // HS-Timer4 initialisieren
ldi r24,0x01
sts TCCR4B,r24 // DEBUG, PWM-Ausgang aktiviert sich mit erstem Rahmenstart
ldi r24,0x29
sts TCCR4C,r24
sts TCCR4D,r1
ldi r24,0x40
sts TCCR4E,r24 // Erweiterter (11-Bit-)Modus
ldi r25,hi8(1023) // 3
ldi r24,lo8(1023) // 255
sts TC4H,r25
sts OCR4C,r24
sts OCR4B,r24 // DEBUG
sts OCR4D,r24 // DEBUG
ret
.section .progmem
space:
// Die law-Tabellen müssen an 100h-Grenzen beginnen.
// Das muss über das Linkerskript realisiert werden.
.ds.b 256-0xAC // .align geht hier nicht
// Lookup-Tabelle für „lineare“ Ausgabe der A-Law-Audiodaten
// „glättet“ 8-Bit-Wert in linearen 16-Bit-Wert für USB,
// rechtsschiebbar auf 11 Bit für PWM-D/A-Wandler
// Zur einfacheren Adressierung werden High- und Low-Teil
// in getrennten Tabellen hintereinander abgelegt.
.macro alaw rshift center
.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
.set m,m<<10|m<<6|m<<2 // 00MmmmMmmmMmmm00
.if e
.set m,m|0x4000 // 01MmmmMmmmMmmm00
.else
.set e,1 // „denormalisierte“ Zahl ohne führende 1
.endif
.set m,m>>(7-e) // 01MmmmMmmmMmmm00 ... 00000000MmmmMmmm
.if j&0x80 // Vorzeichen
.byte (\center-1-m)>>\rshift&0xFF
.else
.byte (\center+m)>>\rshift&0xFF
.endif
.set i,i+1
.endr
.endm
#ifndef LAW_AUDIO
// für Audio-Pipe von -32767 .. +32767 (16 Bit, 13 genutzt)
.type law,@object
law: alaw 0 // Low-Teile
alaw 8 // High-Teile
#endif
// für PWM-Analogausgabe von 0 .. 2047 (11 Bit)
.type lawPWM,@object
lawPWM: alaw 5 32768 // Low-Teile
alaw 13 32768// High-Teile
Detected encoding: UTF-8 | 0
|