;Firmware für ATmega16 auf LPT2USB, Parallel-zu-USB-Konverter
;für Drucker und (später) USB-Speicher - haftmann#software
;Zu übersetzen mit `avrasm2 -fI -FD"%d.%m.%y" lpt2usb.a90´
;Zu brennen mit PonyProg2000, alle CKSEL-Häkchen entfernen
;ECP, EPP zz. nicht unterstützt!
;Rote LED stets an, grüne LED blinkt nicht bei Datenverkehr
;Anzeigen FIFO-Füllstand (mit MIN und MAX) wären nett...
;060825 Erste lauffähige Version
;060826 IEEE1284-Aushandlung und Geräte-ID
;060827 Datenrate-Anzeige
#define DEBUG
#define FULL_FRAME ;1-ms-Rahmen komplett ausnutzen
;Hardware:
;Pinzuordnung:
;PortA: EzHost-Datenport
; 0*: Spannungsüberwachung (zusätzlich; analog)
;PortB: LPT-Datenport
;PortC:
.equ BSY = 7 ;7-3: LPT-Statusport Bits 7-3
.equ ACK = 6
.equ P_E = 5
.equ ONL = 4
.equ ERR = 3
.equ SEL = 2 ;2-0: LPT-Steuerport Bits 3-1
.equ INI = 1 ;mit /RESET verbunden per Lötbrücke
.equ A_F = 0
;PortD:
.equ LedG= 7 ;*?LED grün (Verbindung)
.equ WR = 6 ;EzHost /WR
.equ RD = 5 ;EzHost /RD
.equ A0 = 4 ;EzHost A0
.equ LedR= 3 ;LED rot (Spannung)
.equ STB = 2 ;*LPT-Steuerport Bit 0 (Strobe = INT0)
; 1*: serielles Port TxD
; 0*: serielles Port RxD
;* = Verwendung der pinspezifischen Sonderfunktion
.equ CPUCLK = 12000000
.equ BaudRate = 38400 ;Baudrate serielle Schnittstelle (Debug)
#define ZERO YH
#define UPPERCOLOR 0x67 ;Farbe in oberer Bildhälfte: Grau auf Zyan
;Tastenfunktionen im HyperTerminal zz:
;^L: Bild neu aufbauen
;^R: Mikrocontroller-Reset (in etwa)
; r: Byte-Zähler rücksetzen
; p: Strobe auslösen, 1 Byte "drucken"
; d: 1284-Geräte-ID lesen und anzeigen (Voraussetzung: Puffer leer)
; e: EzBits anzeigen
.NOLIST
.include "m16def.inc"
.include "makros.i90"
.LIST
.def EzBits = r22 ;verschiedene Melde-Bits (für SL811)
.equ LOW_SPEED = 0
.equ SLAVE_FOUND = 1
;.equ SLAVE_ENUMERATED = 2
.equ BUFFER_EMPTY = 3 ;LPT: Sektorpuffer leer
.equ BUFFER_FULL = 4 ;LPT: Sektorpuffer voll (8fach Ringpuffer)
.equ HAVE_1284_ID = 5 ;Sektor enthält 1284-ID-String
.equ USBMEM_FOUND = 6 ;Emulation eines Remote-PC für Total Commander
.equ PRINTER_FOUND = 7 ;Emulation eines Paralleldruckers
.def UART_InChar = r23
.def ONES = r12 ;lauter Einsen
;Bei allen "aktiven" Portzuständen bleiben die LEDs aus...
;Zur schnellen Reaktion auf Strobe-Impulse:
.def DataCatch = r14 ;Aufgeschnappter Wert bei Strobe H-L
.def StatusResp = r15 ;PortC-Ausgabe bei eintretendem Interrupt
.dseg ;AVR-ASM fängt freundlicherweise bei 60h an zu zählen
#define MAX_DEVDAT 256 ;Bytes Platz für Geräte-Infos, Gerätezahl ca. 32
#define MAX_DESC 512 ;Puffergröße für das Holen von Deskriptoren (1 Sektor)
#define LPTTIMEO 10 ;Millisekunden?
;Bei 1 KB Platz muss etwas gehaushaltet werden.
;Wir brauchen ja auch noch einen Sektorpuffer (512 Bytes)
;und Verwaltungsinfo für den FAT-Zugriff.
SetupDat: .byte 8 ;Fester Transferpuffer für Setup EP0
DevPrinter: .byte 2 ;Zeiger auf Drucker-DevDat (oder USB-Stick)
EpOutIdx: .byte 1 ;Index des OUT-Endpoints
EpInIdx: .byte 1 ;Index des IN-Endpoints (später)
LptStatus: .byte 1 ;Puffer für "GET_PORT_STATUS"-Request
LptTimeOut: .byte 1 ;TimeOut-Zähler
LptSent: .byte 4 ;Byte-Zähler
LptRecv: .byte 4 ;Byte-Zähler
Zyklus: .byte 1 ;Drehstrich-Zähler sowie Aktualisierungsbits
LastStatus: .byte 1 ;Statusanzeige-Cache
LastSent: .byte 3 ;24-bit-Zahlen zur Datenratemessung
LastRecv: .byte 3
.def LptPtrRdL = r8 ;Sektor-Zeiger zum USB-Transfer
.def LptPtrRdH = r9
.def LptPtrWrL = r10 ;Sektor-Zeiger zum Einschreiben
.def LptPtrWrH = r11
;Verwaltungsdaten für USB-Speicher
#define cbwSignature 0
#define cbwTag 4
#define cbwDataTransferLength 8
#define cbwFlags 12
#define cbwLun 13
#define cbwLength 14
#define cbwCB 15
#define cbwStructSize 31
#define cswSignature 0
#define cswTag 4
#define cswDataResidue 8
#define cswStatus 12
#define cswStructSize 13
gCBW: .byte cbwStructSize ;"Command Block Wrapper"
gCSW: .byte cswStructSize ;"Command Status Wrapper"
;Gerätedaten (aus Geräte-Beschreibern)
DevDat: .byte MAX_DEVDAT
.org 0x200
Sektor: .byte 512 ;Sektorpuffer 200h..3FFh
SektorE: ;Endadresse+1 (0x400)
Stack: .byte 0x60 ;Stack(s) 96 Byte
.cseg
;Adresse Null
rjmp main
.dw (__YEAR__+20)*512+__MONTH__*32+__DAY__
;Interruptserviceroutine zum Auffangen der Daten bei Strobe (ohne rjmp)
.org INT0addr ;=2
in DataCatch,PinB ;Zustand der Datenleitungen auffangen
out PortC,StatusResp;BUSY auf High, ACK auf Low
reti
;Warten in ganzen Millisekunden
;PE: R16 = Anzahl Millisekunden
Wait_ms:
push r16
;Energie sparen - mit sleep?
#if 1
wms1: rcall Idle
in r16,TIFR
jbrc r16,1,wms1 ;Warte auf Output-Compare-Interrupt
outi TIFR,1<<1 ;Interruptanforderung löschen
#else
push r17
ldi r17,0x0D
wms1: rcall HostRead ;SOF-Interrupt
jbrc r16,4,wms1
ldi r16,1<<4 ;SOF-Bit löschen
rcall HostWrite
pop r17
#endif
pop r16
djnz r16,Wait_ms
ret
;=============================
;== EzHost SL811-spezifisch ==
;=============================
ClearInterrupts:
;Häufig benutzter Ausgabebefehl
;PE: -
;VR: R16,R17
ldi r17,0x0D
ldi r16,0xFF
HostWrite:
;Schreiben zum Host-Controller, Signalverlauf:
; Daten schreiben Daten lesen
; __ __________ ______ __ __________ ___ __
;D __X____A_____X__D___ __X____A_____>--<_D_>--<__
; ____ ________ ____ ______________
;A0 \______/ \______/
; ______ _____ _ ______ ________________
;WR \__/ \__/ \__/
; ____________________ _______________ ___
;RD \______/
;PE: R17=Adresse
; R16=Daten
;VR: -
;Ta: 18 (~ 2 µs)
out PortA,r17 ;(1)
cbi PortD,A0 ;(2)
cbi PortD,WR ;(2)
sbi PortD,WR ;(2)
sbi PortD,A0 ;(2)
out PortA,r16 ;(1)
;SL811DataWriteCycle:
cbi PortD,WR ;(2)
sbi PortD,WR ;(2)
ret ;(4)
HostRead:
;Lesen vom Host-Controller, Signalverlauf siehe oben
;PE: R17 = Adresse
;PA: R16 = Daten
;VR: R16
;Ta: 21 (~ 2 µs)
out PortA,r17 ;(1)
cbi PortD,A0 ;(2)
cbi PortD,WR ;(2)
sbi PortD,WR ;(2)
sbi PortD,A0 ;(2)
out DDRA, ZERO ;(1) auf Eingabe schalten
cbi PortD,RD ;(2)
nop ;(1)
nop
in r16, PinA ;(1)
sbi PortD,RD ;(2)
out DDRA, ONES ;(1) zurück auf Ausgabe schalten
ret ;(4)
.macro SL811WriteK ;Adresse, Datenbyte (beides Konstanten)
ldi r17,@0
ldi r16,@1
rcall HostWrite
.endm
.macro SL811Write ;Adresse (Konstante), Datenbyte in R16
ldi r17,@0
rcall HostWrite
.endm
.macro SL811Read ;Adresse (Konstante), Ergebnis in R16
ldi r17,@0
rcall HostRead
.endm
.macro EZUSB_Delay ;Millisekunden (Konstante)
ldi r16,@0
rcall Wait_ms
.endm
;Blocktransfer vom Host zum Speicher:
;PE: X=Speicheradresse, R17=SL811-Startadresse, R18=Transferlänge
;VR: X+=R18, R16, R17+=R18
HostInS:
tst r18
breq hi_e
push r18
hi_l: rcall HostRead ;(25)
st X+,r16 ;(2)
inc r17 ;(1)
djnz r18,hi_l ;(3) -> 31 Takte pro Byte (= 2,6 µs)
pop r18
hi_e: ret
;Blocktransfer vom Speicher zum Host:
;PE: X=Speicheradresse, R17=SL811-Startadresse, R18=Transferlänge
;VR: X+=R18, R16, R17+=R18
HostOutS:
tst r18
breq ho_e
push r18
ho_l: ld r16,X+ ;(2)
rcall HostWrite ;(22)
inc r17 ;(1)
djnz r18,ho_l ;(3) -> 28 Tykte pro Byte (= 2,3 µs)
pop r18
ho_e: ret
UsbReset:
;Rücksetz-Zustand auf dem USB für 50 ms halten
SL811Read 0x05
sbr r16,0b00001000
rcall HostWrite
push r16
EZUSB_Delay 50
pop r16
cbr r16,0b00001000
rcall HostWrite
rjmp ClearInterrupts
SL811_Init:
;Initialisiert den Chip
SL811WriteK 0x0F,0xAE
SL811WriteK 0x0E,0xE0 ;SOF-Zähler (1 ms)
rjmp ClearInterrupts
speed_detect:
;Aufruf nur mit EzBits=0
cbi PortD,LedG ;LED aufblitzen lassen
;hier: SL811 mit Datenzyklus "wachküssen", Suspend-Bit löschen
SL811WriteK 0x0D,1<<6 ;Bit 6 löschen
rcall HostRead ;R17 steht noch
jbrc r16,6,sd1 ;Gerät entdeckt!
sd5:
EZUSB_Delay 2
sbi PortD,LedG ;LED nach 2 ms ausschalten
;hier: SL811 Suspend-Bit setzen -> Tiefschlaf
;SL811WriteK 0x05,0x40
EZUSB_Delay 198 ;LED 198 ms ausgeschaltet lassen
ret
sd1:
;Zunächst sollte etwas gewartet und noch einmal Präsenz geprüft werden.
EZUSB_Delay 10
;Bei wackeligem Zustand würde das Bit wieder 1 werden, das ist so gewollt!
rcall HostRead ;R17 steht noch
jbrs r16,6,sd5 ;zurück wenn nicht sicher gesteckt!
#ifdef DEBUG
push r16
rcall SendStrAfter
.db "Attach ",0
pop r16
#endif
;Gerät gefunden: erst mal Speed ermitteln
sbrs r16,7 ;0=Low-Speed, 1=Full-Speed
sbr EzBits,1<<LOW_SPEED
;Speed einstellen, USB-Reset ausführen
ldi r16,0xAE
sbrc EzBits,LOW_SPEED
ldi r16,0xEE ;Low-Speed: D+ und D- tauschen
SL811Write 0x0F
ldi r16,0b00001000 ;Bus-Reset (SE0)
sbrc EzBits,LOW_SPEED
ldi r16,0b00101000 ;Low-Speed
SL811Write 0x05
EZUSB_Delay 50
ldi r16,0b00000001 ;SOF-Freigabe
sbrc EzBits,LOW_SPEED
ldi r16,0b00100001 ;Low-Speed
rcall HostWrite ;R17 steht noch
EZUSB_Delay 10
;LED schalten?
sbr EzBits,1<<SLAVE_FOUND
;cbr EzBits,1<<SLAVE_ENUMERATED
;Braucht man das folgende überhaupt???
SL811WriteK 0x00,0x01 ;start generate SOF or EOP
EZUSB_Delay 25 ;Hub required approx. 24.1ms
;Ein Bus-Reset erzeugt ebenfalls Interrupts der Marke "Nonpresent Device"
rcall ClearInterrupts
#ifdef DEBUG
jbrs EzBits,LOW_SPEED,sd2
rcall SendStrAfter
.db "Full",0,0
rjmp sd3
sd2: rcall SendStrAfter
.db "Low",0
sd3: rcall SendStrAfter
.db "-Speed",0,0
#endif
ret
#define PID_SETUP 0xD0
#define PID_IN 0x90
#define PID_OUT 0x10
;Ep-Struktur
;4 Bytes Endpoint-Nummern
; 7 Richtung (1 = IN)
; 6 DataToggle-Bit (nur bei Bulk)
; 5-4 Typ 00=control, 01=iso, 10=bulk, 11=int
; 3-0 Endpoint-Nummer 0..15
;4 Bytes Endpoint-Längen
;Dev-Struktur (bei Multifunktionsgeräten gibt's durchaus mehrere!
;1 Byte USB-Adresse; Bit7=Low-Speed-Bit
;3 Byte Klasse-Subklasse-Protokoll
;3 Byte Config-Interface-AltSetting
;1 Byte Anzahl Endpoints (außer EP0)
;(n+1)*2 Bytes Ep-Strukturen
// Anstelle von "typedef struct": Komponenten-Offsets
#define devUsbAddr 0 ;Bit7=Preamble (Low-Speed), Bit6-0=USB-Adresse
#define devClass 1 ;zur Auffindung der Geräte-Art
#define devSubclass 2
#define devProtocol 3
#define devConfiguration 4 ;zum Setzen der Konfiguration
#define devInterface 5
#define devAltSetting 6
#define devNumEndpoints 7 ;EP0 nicht mitzählend!
#define devStructSize 8
#define epNumber 0 ;epNumber siehe unten!
#define epLength 1 ;16-bit-FIFO-Größen kann der SL811 ohnehin nicht
#define epStructSize 2
;epNumber: Bitfeld:
; 7: Richtung (1 = IN) - nicht ausgewertet!
; 6: Togglebit (bei ISO nicht ausgewertet)
; 5-4: Transfertyp (0=CONTROL, 1=ISO, 2=BULK, 3=INT)
; 3-0: Endpoint-Nummer
#define DATA0START 0x10 ;nur ein Hostsystem wird zz. verwendet
TogToggle:
;Toggle-Bit in EP-Struktur sowie R19 kippen
;PE: Z=Ep-Zeiger
; R19=Kopie davon
;PA: epNumber sowie R19 geändert
;VR: R16,R17,R19
ldi r16,0b01000000 ;Data0-Toggle
ldd r17,Z+epNumber
eor r17,r16
std Z+epNumber,r17 ;Data0 toggeln
eor r19,r16
ret
.def pid=r20
ux_PreOut:
;Vorbereitung für OUT- oder SETUP-Transfer
;PE: Z=Ep-Zeiger
; X=Daten
; r18=Datenlänge (Maximum aus FIFO-Größe und W=wDataLen)
; r19=epNumber (umgearbeitet)
; pid=r20
;PA: r16=0T0100111, T=Togglebit, X vorgerückt
;VR: R16,R17,X
;Wenn Daten vorhanden, dann in Puffer schreiben (immer bei EP0)
ldi r17,DATA0START ;Daten-Bereich
rcall HostOutS
ldi r16,0b00100111
sbrc r19,6 ;Momentaner Data-Toggle-Wert
sbr r16,0b01000000 ;Data-Toggle einsetzen
ret
ux_PostOut:
;bei erfolgreicher Ausführung eines OUT- oder SETUP-Transfers
;PE: R18=Geschriebene Bytes
; R19=epNumber
; Z=Ep-Zeiger
;PA: W=verbleibende Bytes
; ZF=1 wenn W==0
; Togglebit getoggelt (außer bei ISO)
;VR: R16,R17;R19(Togglebit)
sbrs r19,4 ;Bei ISO nicht toggeln
rcall TogToggle
sub WL,r18
sbc WH,ZERO ;ZF=1 wenn W=0 (Besonderheit von ATmega SBC!)
ret
ux_PostIn:
;bei erfolgreicher Ausführung eines IN-Transfers (Toggle-Bit bereits geprüft)
;PE: R18=Zu lesende Bytes
; R19=epNumber
; Z=Ep-Zeiger
; X=Datenpuffer
;PA: W=verbleibende Bytes
; ZF=1 wenn W==0 (Puffer voll) oder verkürzt gelesen
; X vorgerückt
; Togglebit getoggelt (außer bei ISO)
;VR: R16,R17,R18;R19(Togglebit)
SL811Read 0x04 ;Verbliebener Puffer-Platz
push r16 ;Bei Rest (r16!=0) ZF=1 liefern
sub r18,r16 ;Übertragene Bytes
brcc ux9
#ifdef DEBUG
mov r16,r18 ;Zu viel Rest!
rcall ohex
ldi r16,'!'
rcall putchar
#endif
clr r18
ux9: ldi r17,DATA0START ;Daten-Bereich
rcall HostInS
rcall ux_PostOut
pop r16
breq ux_e ;ZF=1 für Puffer voll
cpi r16,1 ;Rest verblieben? (via CY!)
brcs ux_e ;Bei R16=0 ist CY=1 und ZF=0
sez ;ansonsten Z setzen
ux_e: ret
usbXfer:
;USB-Transfer (alle Arten), zz. wird nur die erste USB-Engine des SL811 genutzt
;Routine kümmert sich nicht um initiale Togglebits und blockiert bis Ende
;PE: Z=Dev-Zeiger
; X=Daten-Zeiger (bei pid==PID_SETUP faktisch immer SetupDat)
; W=Datenlänge (16 bit, bei pid==PID_SETUP immer 8)
; r18=Endpoint-Index, 0=EP0, 1=EPx, bei pid==PID_SETUP immer 0)
; r20=pid (PID_IN, PID_OUT oder PID_SETUP)
;PA: X=Daten-Zeiger vorgerückt
; W=Datenlänge=0 oder Rest (bei verkürztem PID_IN, oder NAK)
; CY=1 bei Fehler, dann R16=USB-Transferstatus (SL811-Register 03h)
;VR: R16-R17,W,X
;Im Innern:
; R16 = Kommando oder Status
; R18 = Teiltransferlänge
; R19 = (etwaige) Kopie von epNumber
; R21 = Wiederholungszähler
#ifdef DEBUG
ldi r16,'X'
;rcall putchar
#endif
pushhl Z
push r18
push r19
push r21
ldi r21,50 ;Wiederholungen für EP0 NAK und TimeOut
ldd r16,Z+devUsbAddr
;Ep-Zeiger ermitteln
add r18,r18 ;*epStructSize
addi r18,devStructSize
add ZL,r18
adc ZH,ZERO ;Z = Ep-Zeiger
;ISO-Modus extrahieren: nach R19.4
ldd r19,Z+epNumber
sbrc r19,5
cbr r19,1<<4 ;bei BULK oder INT das Bit4 löschen
;Preamble-Bit umkopieren
cbr r19,1<<7 ;DIR-Bit wird nicht benötigt!
sbrc r16,7 ;Ab jetzt R19.4=ISO-Bit, R19.3-0=Endpoint
sbr r19,1<<7 ;sowie R19.7=Preamble und R19.6=Togglebit
;USB-Adresse setzen
cbr r16,1<<7 ;Preamble-Bit ausschalten
SL811Write 0x04 ;USB-Adresse setzen
uxOuterLoop:
;Teil-Transferlänge ermitteln
ldd r18,Z+epLength
cp WL,r18 ;Wie schön, dass man CP verketten kann!
cpc WH,ZERO
brcc ux0 ;Gesamtlänge >= FIFO-Länge
mov r18,WL ;Gesamtlänge < FIFO-Länge: einsetzen!
ux0:
;Hier müsste anhand der Ressourcen-Verfügbarkeit USB-A oder USB-B
;ausgewählt werden. Da müsste die USB-Adresse erst hier gesetzt werden?
;Ob der SL811 so "schlau" ist, die Reihenfolge einzuhalten??!!
mov r16,r19
andi r16,0b00001111 ;EP
or r16,pid
SL811Write 0x03 ;Endpoint+PID setzen
ldi r16,DATA0START
SL811Write 0x01 ;Transferadresse setzen
mov r16,r18
SL811Write 0x02 ;Transferlänge setzen
;Je nach pid (r20) Kommandobyte ermitteln
ldi r16,0b00100011 ;svw. "ux_PreIn": nur "cmd" liefern
cpi pid,PID_IN
breq ux1
rcall ux_PreOut ;OUT/SETUP vorbereiten sowie "cmd" liefern
ux1:
;SyncSOF könnte auch entfernt werden, wenn genug Zeit für Bulktransfer bleibt
;Bei Wiederholungen ist's allerdings Quatsch!
#ifdef FULL_FRAME
jbrs EzBits,LOW_SPEED,ux7
cpi pid,PID_SETUP
breq ux8 ;hier: Setup-Transfers immer hinter SOF
cpi r21,50 ;Eine Wiederholung?
brne ux8 ;SyncSOF stehen lassen!
;if (SL811Read(0x0F)>=((r18+8?)*10)>>6) r16&=~0x20;
;Ohne diese Prüfung kann es zu "Babble" kommen.
;Bei Verwendung beider USB-Einheiten muss der andere Kanal eingerechnet werden!
mov r17,r18
addi r17,8 ;fraglich!
pushw r1,r0
push r16
ldi r16,10*4
mul r16,r17
SL811Read 0x0F ;R16 = Rest-Zeit
cp r16,r1
pop r16
popw r1,r0
brcs ux8 ;Nicht genügend Zeit: Bit stehen lassen
ux7: cbr r16,0b00100000 ;SyncSOF-Bit entfernen
ux8:
#else
sbrc EzBits,LOW_SPEED
cbr r16,0b00100000 ;SyncSOF-Bit entfernen
#endif
sbrc r19,7 ;Low-Speed-Gerät (hinter HUB)?
sbr r16,0b10000000 ;Preamble-Bit setzen
sbrc r19,4 ;ISO?
sbr r16,0b00010000 ;ISO-Bit setzen
;Für Low-Speed muss man hier alle Interrupts löschen,
;sonst gibt's (zumindest) einen Disconnect-Interrupt. Oder SE0/Idle??
push r16
rcall ClearInterrupts
pop r16
SL811Write 0x00 ;Los geht's! Start-Kommando ausgeben
uxInnerLoop:
;Alle Warteschleifen werden mit einem Aufruf von "Idle" garniert,
;um die parallele Schnittstelle zu bedienen.
rcall Idle
SL811Read 0x0D
mov r17,r16
andi r17,0b01100000 ;Resume oder Remove
brne uxe0 ;raus mit Fehler!
jbrc r16,0,uxInnerLoop ;in Schleife bleiben bis DONE USB-A
SL811WriteK 0x0D,0x01 ;Diesen Interrupt löschen
SL811Read 0x03 ;Status lesen
jbrc r16,0,no_ack
;ACK auswerten (der "Glücksfall")
cpi pid,PID_IN
brne uxAckOut ;bei OUT: nächste Runde?
;Prüfen des Togglebits (nicht bei ISO)
;Das Togglebit bei IN-Transfers ist ohnehin fehlerhaft beim SL811 implementiert!
;Wie soll man den "falschen" IN-Transfer ignorieren können?
;(Lt. USB-Spec. darf der SL811 kein ACK senden, wenn das Togglebit falsch ist.)
jbrs r19,4,uxAckIn ;ISO: Toggle-Bit ignorieren
swap r16
lsr r16 ;Sequence-Bit auf Bit6 bringen
eor r16,r19 ;Erwartetes Togglebit einsetzen
jbrs r16,6,uxe ;muss Null sein, sonst Fehler (r16=??)
uxAckIn: ;Problem: Host hat bereits geACKt.
rcall ux_PostIn ;Daten aus SL811 abholen
rjmp ux4 ;mit ZF=1 wenn fertig
uxAckOut:
rcall ux_PostOut
ux4: brne uxol ;weitere Runde wenn nicht vollständig versendet
ux5: clc ;alles OK
rjmp uxp
uxe0: ldi r16,1<<4 ;Bit4: Gerät entfernt
uxe: ;Fehlermeldung (CY=1) - USB-Baum rücksetzen!?
#ifdef DEBUG
push r16
ldi r16,'<'
rcall putchar
pop r16
push r16
rcall ohex
ldi r16,'>'
rcall putchar
pop r16
#endif
sec
uxp: pop r21
pop r19
pop r18
pophl Z
ret
no_ack:
cpi pid,PID_IN
breq uxi
;Zur Wiederholung Datenzeiger zurück
;(alternativ könnte man das erneute Laden unterdrücken...)
sub XL,r18
sbc XH,ZERO
;Bei EP0 Wiederholungen versuchen (sonst nicht)
uxi: mov r17,r19
andi r17,0b00001111 ;EP0?
brne uxe ;nein, raus mit Fehler
jbrs r16,6,uxo ;NAK? Wiederholung!
jbrc r16,2,uxe ;Kein TimeOut? Fehler!
uxo: dec r21 ;6 Versuche
breq uxe ;Fehler
#ifdef DEBUG
cpi r21,1 ;Letzte Wiederholung?
brne uxol
ldi r16,'r'
rcall putchar ;anzeigen!
#endif
uxol: rjmp uxOuterLoop
ep0Xfer:
;PE: X=Datenpuffer
; SetupDat-Puffer gefüllt
; Z=Dev-Zeiger
;PA: W=Residuum (tmp.)
;VR: R16-R19,W
push r20
pushhl X
std Z+devStructSize+epNumber,ZERO ;Data-Toggle stets 0
clr r18 ;EP0
pushhl X
ldihl X,SetupDat
ld r19,X ;bmRequestType
andi r19,0x80 ;Bit7=Richtungsbit, 1=IN
ori r19,PID_OUT ;künftige PID ermitteln
ldihl W,8
ldi pid,PID_SETUP
rcall usbXfer
ld WH,-X ;wLengthH
ld WL,-X ;wLengthL
pophl X
brcs e0e ;Raus bei Fehler
mov r16,WL
or r16,WH
ldi pid,PID_IN ;Richtung für Status immer IN, wenn keine Daten
breq e0z
mov pid,r19 ;PID_OUT oder PID_IN, je nachdem
rcall usbXfer
brcs e0e ;Raus bei Fehler
ldi pid,PID_OUT^PID_IN ;80h
eor pid,r19 ;Richtung für Status
e0z: pushhl W
ldihl W,0 ;keine Daten
ldi r16,0b01000000 ;Data-Toggle stets 1
std Z+devStructSize+epNumber,r16
rcall usbXfer
pophl W
e0e:
#ifdef DEBUG
push r16
ldi r16,'e'
brcc eee
ldi r16,'f'
eee: ;rcall putchar
pop r16
#endif
pophl X
pop r20
ret
.undef pid
ep0Xfer5: ;5x Wiederholung (eigentlich für STALL oder Sequence)
push r20
ldi r20,5
ex2: rcall ep0Xfer
brcc ex1
dec r20
sbrs r16,4 ;Abgezogen? Nicht wiederholen!
brne ex2
ex1: pop r20
#ifdef DEBUG
brcc ex3
mov r18,r16 ;Fehlerkode retten
pushhl Z
rcall SendStrAfter
.db "EP0-Transfer",0,0
ldi YL,SetupDat
ldi r17,8
ex4: rcall ospac
ld r16,Y+
rcall ohex ;SetupDat-Puffer ausgeben
djnz r17,ex4
rcall SendStrAfter
.db " Fehler Kode ",0
mov r16,r18
rcall ohex
rcall ocrlf
pophl Z
mov r16,r18
ex3:
#endif
ret
;====================
;== USB-spezifisch ==
;====================
OutDeviceRequest:
clr r16
clr r19
SimpleDeviceRequest:
;Einfache (datenlose) Requests ausführen
;PE: R16=bmRequestType
; R17=bRequest
; R18=wValueL
; R19=wIndexL
ldi YL,SetupDat
st Y+,r16 ;bmRequestType
st Y+,r17 ;bRequest
st Y+,r18 ;wValueL = Adresse
clr r16
st Y+,r16 ;wValueH = 0
st Y+,r19 ;wIndexL
st Y+,r16 ;wIndexH
st Y+,r16 ;wLengthL
st Y+,r16 ;wLengthH
rjmp ep0Xfer5 ;X ist ohne Daten egal
SetAddress:
;Adresse des USB-Gerätes setzen (es hat zurzeit die USB-Adresse 0)
;PE: R18=Adresse (1..127)
; Z=Dev-Zeiger
;PA: Z->devUsbAddr modifiziert
;VR: X,R16-R20
push r18
ldi r17,5 ;bRequest = SET_ADDRESS
rcall OutDeviceRequest
pop r18
brcs sae ;raus bei Fehler
ldd r17,Z+devUsbAddr
or r17,r18 ;Low-Speed-Bit stehen lassen
std Z+devUsbAddr,r17
EZUSB_Delay 2
sae: ret
GetDesc:
;Beschreiber beschaffen
;PE: R19=Beschreiber-Typ, 1=Gerät(18 Bytes), 2=Konfiguration (lang)
; R18=String-Nummer (bei String)
; W=Anzahl erwartete Bytes
; Z=Dev-Zeiger
;PA: CY=1: Fehler
; X=Descriptor (Puffer und Zeiger bereit zum Parsen)
; W=Anzahl freigebliebener Bytes
;VR: R16-R20,X
ldi r16,0x80 ;bmRequestType
ldi r17,0x06 ;bRequest
ldi r20,0 ;wIndexL
ldi r21,0 ;wIndexH
GetD2: ldihl X,Sektor
GetD3: ldi YL,SetupDat
st Y+,r16 ;bmRequestType
st Y+,r17 ;bRequest
st Y+,r18
st Y+,r19
st Y+,r20
st Y+,r21
st Y+,WL
st Y+,WH
rcall ep0Xfer5
ret
ParseEp:
;Endpoint-Beschreiber (7 Bytes) parsen
;PE: X=Zeiger auf Endpoint-Descriptor
; Z=Ep-Zeiger (Speicherbereich mit Null initialisiert)
;PA: X um 7 Bytes vorgerückt wenn OK (ZF=1)
; ZF=0 wenn kein Endpoint-Beschreiber
;VR: R16,R17,X
ld r16,X+ ;bLength müsste 7 sein
ld r17,X+ ;bDescriptorType
cpi r17,5 ;"Endpoint"?
brne pepe ;Ende mit Parsen
ld r16,X+ ;bEndpointAddress (Bit7=IN)
ld r17,X+ ;bmAttributes (ISO=1, BULK=2, INT=3)
swap r17
or r16,r17
std Z+epNumber,r16 ;mit Togglebit=0
ld r16,X+ ;wMaxPacketSizeL
ld r17,X+ ;wMaxPacketSizeH
tst r17
brne limit
cpi r16,240 ;absolut maximale Transfergröße für EzHost
brcs nolimit
limit: ldi r16,240
nolimit:std Z+epLength,r16
ld r16,X+ ;bInterval ignorieren
sez
pepe: ret
EnumUsbDev:
;PE: DevDat gelöscht
rcall UsbReset ;mit 2. Reset wird beim Stick aus TimeOut ein CRC-Fehler
EZUSB_Delay 50
#ifdef DEBUG
rcall SendStrAfter
.db ", Enum ",0
#endif
clr r2 ;USB-Adressen-Zähler
ldihl Z,DevDat
NextAddress: ;Umsonstmarke, zz. keine Hub-Unterstützung
inc r2 ;Beginnend mit Adresse 1
;Wäre es nicht richtiger, erst mal eine Adresse zu setzen??
;ldi r16,64 ;Vorgabegröße für EP0, Full-Speed
;sbrc EzBits,LOW_SPEED
; ldi r16,8 ;Vorgabegröße für EP0, Low-Speed
stdi Z+devStructSize+epLength,64 ;Vorgabegröße für EP0
;Low-Speed-Geräte hinter HUBs liefern ebenfalls kurze EP0-Datenblöcke!
clr r3 ;Konfigurations-Zähler
;Gerätebeschreiber lesen
clr r18
ldi r19,1 ;DEVICE
ldihl W,8
rcall GetDesc ;Beschreiber beschaffen (erste 8 Bytes)
brcs eue ;Fehler!
#ifdef DEBUG
ldi r16,'d'
rcall putchar
#endif
adiw XH:XL,4
ld r16,X+
std Z+devClass,r16
ld r16,X+
std Z+devSubclass,r16
ld r16,X+
std Z+devProtocol,r16
ld r16,X+ ;EP0-Größe setzen
std Z+devStructSize+epLength,r16
mov r18,r2
rcall SetAddress
brcs eue
#ifdef DEBUG
ldi r16,'a'
rcall putchar
#endif
;Gerätebeschreiber lesen (noch einmal, für NumConfigurations)
clr r18
ldi r19,1 ;DEVICE
ldihl W,18
rcall GetDesc ;Beschreiber beschaffen
brcs eue ;Fehler!
#ifdef DEBUG
ldi r16,'d'
rcall putchar
#endif
;Gerätebeschreiber parsen (nur die "interessanten" Felder)
adiw XH:XL,17
ld r4,X ;bNumConfigurations (normal: 1)
NextConfiguration:
;Konfigurationsbeschreiber lesen (in einem Rutsch)
mov r18,r3
ldi r19,2 ;CONFIGURATION
ldihl W,MAX_DESC ;Descriptor-Platz
rcall GetDesc
brcs eue
#ifdef DEBUG
ldi r16,'c'
rcall putchar
#endif
ld r16,X+
cpi r16,9
brne eue
ld r16,X+
cpi r16,2
brne eue
;Konfigurationsbeschreiber parsen
clr WL ;beim Start ist Z=gültiger Dev-Zeiger
clr WH
adiw XH:XL,2
ld r6,X+ ;bNumInterfaces
ld r16,X+ ;bConfigurationValue
std Z+devConfiguration,r16
adiw XH:XL,3 ;iConfiguration,bmAttributes,MaxPower überspr.
clr r7
NextInterface:
;Hier müsste ein Interface-Descriptor sein!
ld r16,X+ ;bLength (=9)
ld r17,X+ ;bDescriptorType
cpi r17,4 ;"Interface"
breq pok
pee: inc r3 ;Nächste Konfiguration (selten)
djnz r4,NextConfiguration
rcall ospac
clc ;Ende mit Parsen
ret
eue: rcall SendStrAfter
.db "Fehler!",0
rcall ocrlf
sec
ret
pok: mov r16,WL
or r16,WH
breq ncop ;Das erste Mal ist W=0
pushhl X ;Bei jeder weiteren Runde Z=Dev-Zeiger machen
pushhl Z
movwhl X,W ;Quelle
ldi r16,devStructSize+epStructSize
cop: ld r17,X+ ;Eigentlich braucht man nur die Adresse und FIFO-Größe kopieren
st Z+,r17
djnz r16,cop
pophl Z
pophl X
ncop: movwhl W,Z ;retten zur Kopie
ld r16,X+ ;bInterfaceNumber
std Z+devInterface,r16
ld r16,X+ ;bAlternateSetting
std Z+devAltSetting,r16
ld r5,X+ ;bNumEndpoints
std Z+devNumEndpoints,r5
ld r16,X+ ;bInterfaceClass
tst r16
breq pcl
std Z+devClass,r16
pcl: ld r16,X+ ;bInterfaceSubclass
tst r16
breq psc
std Z+devSubclass,r16
psc: ld r16,X+ ;bInterfaceProtocol
tst r16
breq ppr
std Z+devProtocol,r16
ppr: ld r16,X+ ;iInterface (ignorieren)
;Hier müssten jetzt <r5> Endpoint-Descriptoren folgen
adiw ZH:ZL,devStructSize+epStructSize ;hinter EP0
tst r5
breq pq
NextEndpoint:
rcall ParseEp
brne pee
adiw ZH:ZL,2 ;nächster Endpoint
djnz r5,NextEndpoint
pq: rjmp NextInterface ;nächstes Interface (Multifunktion oder AltSets)
;=========================================
;== Gelesene USB-Strukturen durchkämmen ==
;=========================================
;LocateDevice: Gerät in den Device-Daten suchen
;Sucht die erste Struktur mit devClass=R17, devSubclass=R18 und devProtocol=R19
;PE: Z=DevDat
;PA: CY=0: Z=Dev-Zeiger auf Drucker-Interface, sonst CY=1
;VR: R16,Z
lp_n: ldd r16,Z+devNumEndpoints
add r16,r16 ;*epStructSize
addi r16,devStructSize+epStructSize
add ZL,r16
adc ZH,ZERO
LocateDevice:
ldd r16,Z+devUsbAddr
cpi r16,1
brcs lp_e ;Ende der Liste erreicht
ldd r16,Z+devClass
cp r16,r17
brne lp_n
ldd r16,Z+devSubclass
cp r16,r18
brne lp_n
ldd r16,Z+devProtocol
cp r16,r19
brne lp_n
lp_e: ret
LocateEndpoint:
;Sucht Endpoint nach Richtung und Typ
;PE: R19=Richtung und Typ (Bit 7 sowie 5-4), Z=DevDat
;PA: CY=0: R18=Endpoint-Index, sonst CY=1
;VR: Z,r16
// BYTE NumEp = Dev->NumEndpoints;
// BYTE i;
// EpStruct *Ep = Dev->Endpoints;
// for (i=0; i != NumEp; ) {
// i++; Ep++;
// if (Ep->Number&0xB0 == 0x20) { // Bulk Out
// EpOutIdx=i;
// goto raus;
// }
// }
// goto Weitersuchen;
//raus:
ldd r16,Z+devNumEndpoints
adiw ZH:ZL,devStructSize
clr r18 ;Ep-Index-Zähler
rjmp le_f ;am Fuß anfangen
le_l: inc r18
adiw ZH:ZL,epStructSize
ldd r17,Z+epNumber
eor r17,r19 ;muss in Richtung und Typ stimmen
andi r17,0b10110000 ;Nummer und Togglebit ausblenden
breq le_e
le_f: cp r16,r18
brne le_l ;mit CY=0
clr r18
sec
le_e: ret
LocateEndpoints:
;OUT- und IN-Endpoint suchen (IN "optional")
;PE: Z=Dev-Zeiger
;PA: CY=1: Kein OUT-Endpoint
; [EpOutIdx]=Out-Index (meistens 1)
; [EpInIdx] =In-Index (meistens 2, 0 wenn nicht vorhanden)
;OUT-Endpoint suchen
movwhl W,Z
ldi r19,0b00100000 ;BULK OUT
rcall LocateEndpoint
sts EpOutIdx,r18 ;Index speichern
brcs les_e
#ifdef DEBUG
ldd r18,Z+epLength
rcall SendStrAfter
.db ", Bulk-Out(",0
mov r16,r18
rcall SendByte
ldi r16,')'
rcall putchar
#endif
;IN-Endpoint suchen
movwhl Z,W
ldi r19,0b10100000 ;BULK IN
rcall LocateEndpoint
sts EpInIdx,r18
brcs les_i ;wenn nicht vorhanden, ignorieren
#ifdef DEBUG
ldd r18,Z+epLength
rcall SendStrAfter
.db "+Bulk-In(",0
mov r16,r18
rcall SendByte
ldi r16,')'
rcall putchar
#endif
les_i:
#ifdef DEBUG
rcall SendStrAfter
.db " entdeckt",0
#endif
movwhl Z,W
clc
les_e: ret
SetConfig_AltSetting:
;Setzt Konfiguration (aktiviert Gerät) und wählt alternative Einstellung
;PE: Z=Dev-Zeiger
ldi r17,9 ;Set_Configuration
ldd r18,Z+devConfiguration
rcall OutDeviceRequest
brcs sca_e
ldi r16,1 ;bmRequestType=OUT,Interface
ldi r17,11 ;Set_Interface (Alt.Setting)
ldd r18,Z+devAltSetting
ldd r19,Z+devInterface
rcall SimpleDeviceRequest
brcc sca_e
ldd r18,Z+devAltSetting ;Bei AltSetting=0 Fehler ignorieren
cp ZERO,r18 ;Bei AltSetting<>0 Fehler melden
#ifdef DEBUG
brcs sca_e
movwhl W,Z
rcall SendStrAfter
.db ", aktiviert",0
movwhl Z,W
clc
#endif
sca_e: ret
DeviceRequest_Reset:
;Gemeinsames Rücksetzen für Massenspeicher und Drucker
;PE: R17=bRequest (einzig unterschiedlich)
;PA: Fehler kann offenbar ignoriert werden
ldi r16,0b00100001 ;bmRequestType
clr r18
ldd r19,Z+devInterface
rcall SimpleDeviceRequest
#ifdef DEBUG
brcs sp_j ;Reset-Fehler ignorieren
movwhl W,Z
rcall SendStrAfter
.db ", rckgesetzt",0
movwhl Z,W
sp_j:
#endif
ret
#ifdef DEBUG
SendStr_gefunden:
rcall SendStrAfter
.db " gefunden",0
ret
#endif
ScanKnownDevices:
;Sucht in den Strukturen die erste mit devClass=7
;PE: Z=DevDat
;PA: Z=Dev-Zeiger auf Drucker-Interface (insbes. bei Multifunktionsgeräten)
; PRINTER_FOUND gesetzt
; kein PRINTER_FOUND wenn nicht gefunden
;VR: R16,Z
ldi r17,7 ;Class=7
ldi r18,1 ;Subclass=1
ldi r19,2 ;Protocol=2 bevorzugen
movwhl W,Z ;Anfang retten
rcall LocateDevice
brcc InitPrinter
movwhl Z,W ;Anfang herstellen
ldi r19,1 ;Protocol=1 alternativ
rcall LocateDevice
brcc InitPrinter
movwhl Z,W
ldi r17,8 ;Class=8
ldi r18,6 ;Subclass=6 (Kamera QV2800UX: 5)
ldi r19,0x50 ;Protocol=0x50: Bulk-Only Transport (QV2800: 0)
rcall LocateDevice
brcc InitMassStorage
rcall SendStrAfter
.db "Kein Drucker!",0 ;Meldung
rjmp ocrlf
InitPrinter:
;PE: Z=DevDat-Zeiger auf geeignetstes (möglichst bidirektionales)
; Drucker-Interface
#ifdef DEBUG
movwhl W,Z ;retten
rcall SendStrAfter
.db "Drucker",0
rcall SendStr_gefunden
movwhl Z,W
#endif
rcall LocateEndpoints
brcs sp_e
stshl DevPrinter,Z
rcall SetConfig_AltSetting
brcs sp_e
ldi r17,2 ;bRequest
rcall DeviceRequest_Reset
sbr EzBits,(1<<PRINTER_FOUND)|(1<<BUFFER_EMPTY)|(1<<BUFFER_FULL)
ldihl X,Sektor ;Zeiger initialisieren
movwhl LptPtrRd,X
movwhl LptPtrWr,X
outi DDRC,0b11111000 ;Statusleitungen Ausgänge!
cbi PortC,7 ;BSY low: bereit zum Datenempfang
rcall UpdateStatus
rcall Read1284DevId
cbr EzBits,1<<BUFFER_FULL
sp_ok:
#ifdef DEBUG
rcall SendStrAfter
.db ", OK.",0
rcall ocrlf
#endif
ret
sp_e:
#ifdef DEBUG
rcall SendStrAfter
.db " - Fehler!",0,0
rcall ocrlf
#endif
ret
InitMassStorage:
#ifdef DEBUG
movwhl W,Z ;retten
rcall SendStrAfter
.db "Massenspeicher",0,0
rcall SendStr_gefunden
movwhl Z,W
#endif
rcall LocateEndpoints
brcs sp_e
lds r16,EpInIdx
cpi r16,1
brcs sp_e ;Fehler, wenn kein IN-Endpoint vorhanden
stshl DevPrinter,Z
ldi r17,0b11111111 ;bRequest
rcall DeviceRequest_Reset
;LUN interessiert hier nicht. Seriennummer evtl.
sbr EzBits,1<<USBMEM_FOUND
ldi YL,gCBW
ldihl Z,gDefaultCBW
ldi r17,15
ims_1: lpm r16,Z+
st Y+,r16
djnz r17,ims_1
rjmp sp_ok ;OK ausgeben
gDefaultCBW:
.dd 0x43425355 ;Signatur
.dd 0x08154711 ;Tag (beliebig)
.dd 0x00000200 ;1 Sektor
.db 0x80,0 ;Flags (IN), LUN
.db 0x10,0 ;Länge des CB, Füllbyte
DoEnumSlave:
;Versuch der Enumeration; Aufruf mit SLAVE_FOUND=0
rcall speed_detect
sbrs EzBits,SLAVE_FOUND
ret
ldihl Z,DevDat+MAX_DEVDAT
ldihl W,MAX_DEVDAT
loesch: st -Z,ZERO
sbiw WH:WL,1
brne loesch
rcall EnumUsbDev ;mit gelöschtem DevDat-Puffer
brcs des1
#ifdef DEBUG
ldihl Z,DevDat
rcall ShowDevDat
#endif
ldihl Z,DevDat
rcall ScanKnownDevices ;Ersten Drucker lokalisieren
sbrc EzBits,PRINTER_FOUND
rjmp ShowPrinterDevDat
sbrs EzBits,USBMEM_FOUND
des1: sbi PortD,LedG ;LED aus bei Enum-Fehler (vorerst)
ret
;=============================
;== (Hyper)Terminal-Anzeige ==
;=============================
HideDevId:
;Löschen der 1284-ID vom Bildschirm; LeaveScroll vorausgesetzt!
ldi r16,13
ldi r17,4
rcall GotoXY
rcall SendLineDel ;Von da bis Zeilenende löschen
rcall hd_1 ;Zwei weitere Zeilen löschen
hd_1: rcall ocrlf
rjmp SendLineDel
ShowDevId:
;1284-Geräte-ID anzeigen
;Voraussetzung: HAVE_1284_ID gesetzt, kein LeaveScroll!
rcall LeaveScroll
ldi r17,UPPERCOLOR^0x0C
rcall SendColor
rcall HideDevId ;Ggf. alte Anzeige weglöschen
ldi r16,13
ldi r17,4
rcall GotoXY
ldihl X,Sektor
ld WH,X+
ld WL,X+
sbiw WH:WL,2
rd_l: ld r16,X+ ;String-Ausgabe
rcall putchar
sbiw WH:WL,1
brne rd_l
rjmp EnterScroll
CheckSlaveDetach:
;Prüfen, ob Verbindung besteht
SL811Read 0x0D ;Interruptstatusregister
sbrs r16,6
ret ;0: Gerät hängt noch dran!
;hier: SL811 Suspend-Bit setzen -> Tiefschlaf
;SL811WriteK 0x05,0x40 funktioniert nicht
rcall SL811_Init ;wieder auf Full-Speed
;Sonst wird nach dem Anstecken der Maus kein Full-Speed-Gerät erkannt
#ifdef DEBUG
rcall SendStrAfter
.db " Detach",0
rcall ocrlf
#endif
jbrc EzBits,PRINTER_FOUND,csd1
rcall LeaveScroll ;Angezeigten Deskriptor löschen
ldi r17,UPPERCOLOR^0x0C
rcall SendColor
ldi r16,12
ldi r17,3
rcall GotoXY
rcall SendLineDel ;Löschen vom Kursor zum Zeilenende
rcall HideDevId
rcall EnterScroll
csd1: clr EzBits
;hier BUSY-Bit setzen
out DDRC, ZERO ;alles hochohmig machen (wie Stecker ziehen)
out PortC,ONES ;überall Pullups
sbi PortD,LedG ;LED aus
out DDRB, ZERO ;Eingänge (falls bidirektional)
ret
UpdateStatus:
;Druckerstatus holen und an die drei Statuspins legen
ldshl Z,DevPrinter
ldi r16,0b10100001 ;bmRequestType: klassenspezifisch
ldi r17,1 ;bRequest = GET_PORT_STATUS
ldi r18,0 ;wValueL
ldi r19,0 ;wValueH
ldd r20,Z+devInterface;wIndexL
ldi r21,0 ;wIndexH
ldihl W,1 ;wLength
ldihl X,LptStatus ;Ziel-Puffer
rcall GetD3 ;Status beschaffen
brcs use ;Fehler! (Was tun?)
or WL,WH ;Restlänge Null (1 Byte geliefert?)
brne use ;nein (Was tun?)
ld r16,X ;Da ist der Status
andi r16,0b00111000
mov r17,StatusResp
andi r17,0b11000111
or r17,r16
mov StatusResp,r17
;Umständliches Übertragen dreier Bits, ohne Interrupts sperren zu müssen:
sbrc r17,3
sbi PortC,3
sbrs r17,3
cbi PortC,3
sbrc r17,4
sbi PortC,4
sbrs r17,4
cbi PortC,4
sbrc r17,5
sbi PortC,5
sbrs r17,5
cbi PortC,5
use: ret
ShowOneDevDat:
;Einen DevDat-Eintrag anzeigen
;PE: Z=DevDat-Zeiger
ldd r17,Z+devNumEndpoints
lsl r17 ;*epStructSize
addi r17,devStructSize+epStructSize
sdd1: rcall ospac
ld r16,Z+
rcall ohex
djnz r17,sdd1
ret
#ifdef DEBUG
sdd2: rcall ShowOneDevDat
ShowDevDat:
rcall ocrlf
ldd r16,Z+devUsbAddr
tst r16 ;Null?
brne sdd2 ;Eintrag gültig (enumeriert)
ret
#endif
ShowPrinterDevDat:
rcall LeaveScroll ;Angezeigten Deskriptor löschen
ldi r17,UPPERCOLOR^0x0C
rcall SendColor
ldi r16,12
ldi r17,3
rcall GotoXY
ldshl Z,DevPrinter
rcall ShowOneDevDat
rcall EnterScroll
ret
;=============================
;== Parallelport-spezifisch ==
;=============================
;Angekommenes Drucker-Byte zum Puffer durchreichen
;Voraussetzung des Aufrufs: PRINTER_FOUND, BSY=High
;VR: X
;Könnte eigentlich auch die ISR erledigen, oder?
;Ta: ca.36 (3µs), BSY-High-Zeit min. 18 (1,5µs)
HandleLptByte:
sbrc EzBits,BUFFER_FULL;(2)
ret ; Puffer voll: Nichts tun!
movwhl X,LptPtrWr ;(1) Schreibzeiger
st X+,DataCatch ;(2) Byte abspeichern
cbr XH,0x04 ;(1) Bei Pufferende "zurückklappen"
sbr XH,0x02 ;(1)
movwhl LptPtrWr,X ;(1) abspeichern
eor XL,LptPtrRdL ;(1) Voll?
eor XH,LptPtrRdH ;(1)
or XH,XL ;(1)
breq hl1 ;(1) Nicht voll
cbi PortC,BSY ;(2) BUSY auf Low, bereit für nächstes Byte
sbi PortC,ACK ;(2) /ACK auf High (löst im PC Interrupt aus)
rjmp hl2 ;(2)
hl1: sbr EzBits,1<<BUFFER_FULL ;Voll ab jetzt
;(ab hier ist etwas Zeit für den PC, die ISR kann arbeiten)
hl2: cbr EzBits,(1<<BUFFER_EMPTY)|(1<<HAVE_1284_ID)
;Bytes zählen
push r0 ;(2)
lds r0,LptRecv ;(2)
inc r0 ;(1)
sts LptRecv,r0 ;(2)
brne hl4 ;(2)
lds r0,LptRecv+1
inc r0
sts LptRecv+1,r0
brne hl4
lds r0,LptRecv+2
inc r0
sts LptRecv+2,r0
brne hl4
lds r0,LptRecv+3
inc r0
sts LptRecv+3,r0
hl4: lds XL,Zyklus ;(2)
sbr XL,1<<7 ;(1)
sts Zyklus,XL ;(2)
pop r0 ;(2)
jbis PortC,BSY,HandleLptByte ;(3) Flinker PC? Kurzschluss!
ret ;(4)
HandleUsbTransfer:
;Voraussetzung: PRINTER_FOUND gesetzt
sbrc EzBits,BUFFER_EMPTY
ret ;Nichts tun wenn leer
;Länge des anstehenden Transfers ermitteln (max. 40h = EP-Größe)
movwhl X,LptPtrRd ;Lesezeiger
movwhl W,LptPtrWr ;Schreibzeiger
sub WL,XL
sbc WH,XH ;Differenz = Länge, 0 entspricht 200h
sbrs EzBits,BUFFER_FULL
brcc hu2
ldihl W,SektorE ;Bei CY=1 oder VOLL Länge bis Pufferende ansetzen
sub WL,XL ;Hier kann niemals 0 herauskommen!
sbc WH,XH
hu2:
;Begrenzung der Länge auf 40h (weil Senderoutine eh' blockiert)
#if 0
mov r16,WH
rcall ohex
mov r16,WL
rcall ohex
ldi r16,'#'
rcall putchar
#endif
;tst WH ;Achtung: ATmega's SBC "verkettet" ZF!
;brne hu3 ;High-Teil=1 (256..511) oder=2 (512)
cpi WL,0x40 ;eigentlich: FIFO-Größe benutzen!
cpc WH,ZERO
brcc hu3 ;bei >=40h sofort senden
;Bei kleinen Mengen abwarten per TimeOut, nicht sofort senden
lds r16,LptTimeOut
dec r16
breq hu4 ;Null: Transfer ausführen
sts LptTimeOut,r16
ret ;Beim nächsten Mal...
hu3: ldi WL,0x40
mov WH,ZERO
hu4: ldi r16,LPTTIMEO
sts LptTimeOut,r16 ;Timeout-Zähler auf Maximum
;Übertragung des Datenblocks (NAK ist ebenfalls möglich! Und üblich.)
ldshl Z,DevPrinter
lds r18,EpOutIdx
ldi r20,PID_OUT
push WL
;hier eigentlich LED-Blinken anstoßen
sbi PortD,LedG
rcall UsbXfer ;hier kann sich per Yield LptPtrWr ändern!
cbi PortD,LedG
pop WL
brcs hue ;NAK o.ä.
#if 0
mov r16,WL
rcall ohex
#endif
cbr XH,0x04 ;vorgerücktes X ggf. auf Sektoranfang
sbr XH,0x02 ;(2->2, 3->3, 4->2)
movwhl LptPtrRd,X
;Puffer nicht mehr voll
jbrc EzBits,BUFFER_FULL,hu6
cbi PortC,BSY ;nächstes Byte erwarten
sbi PortC,ACK ;PC-Interrupt auslösen
cbr EzBits,1<<BUFFER_FULL
hu6:
;Noch Bytes im Puffer? Wenn nein, BUFFER_EMPTY setzen
eor XL,LptPtrWrL
eor XH,LptPtrWrH
or XH,XL
brne hu7
sbr EzBits,1<<BUFFER_EMPTY
;Statistik aktualisieren (eigentlich bräuchte man noch Bytes/Sekunde)
hu7: ldi YL,LptSent
ldi r17,4 ;DWORD
clc
hul: ld r16,Y
adc r16,WL
st Y+,r16
clr WL
djnz r17,hul
SetZyklusBit7:
ldi r16,1<<7
SetZyklusBit:
lds r17,Zyklus
or r17,r16
sts Zyklus,r17
hue: ret
;==========================
;== IEEE1284-Aushandlung ==
;==========================
Read1284DevId:
;1284-DevID lesen und anzeigen (kein Platz im RAM zum Zwischenpuffern)
;Voraussetzung: PRINTER_FOUND, BUFFER_EMPTY und(!) BUFFER_FULL gesetzt
;PA: CY=0: geglückt, und String besteht aus mindestens 1 Zeichen
;VR: R16-R20, Z - Benutzt Sektorpuffer!
ldshl Z,DevPrinter
ldi r16,0b10100001 ;bmRequestType: klassenspezifisch
ldi r17,0 ;bRequest = GET_DEVICE_ID
ldd r18,Z+devConfiguration ;wValueL
dec r18 ;0-basiert (unsauber!!)
ldi r19,0 ;wValueH
ldd r20,Z+devAltSetting;wIndexL
ldd r21,Z+devInterface;wIndexH
ldihl W,MAX_DESC ;wLength
rcall GetD2 ;ID beschaffen
brcs rd_e
ld WH,X+ ;Länge in Big-Endian am Anfang
ld WL,X+
sbiw WH:WL,3 ;Diese Bytes zählen mit
brcs rd_e ;Länge zu kurz
adiw WH:WL,2
cpi WH,2
brcc rd_e ;Zu lang
sbr EzBits,1<<HAVE_1284_ID
rcall ShowDevId
clc
ret
rd_e: sec
ret
TwoNibbles:
;PE: R16=auszugebendes Byte (Host taktet mit AF, Device antwortet mit ACK)
;PA: CY=1: Zeitüberschreitung, oder Host beendet Modus mit SEL=L
;VR: Z,R17 (R16 wenn CY=1)
rcall Nibble
brcs hn_e
Nibble:
;Gibt das Low-Nibble von R16 aus
;PA: CY=1 wenn AF zu langsam ODER SEL=Low geht; R16 "geswapt"
swap r16 ;Mit dem Low-Teil beginnen
mov r17,r16
asr r17 ;Bit7 stehen lassen, übrige Bits 1 runter
ori r17,0b01000111 ;ACK auf High, Pull-Ups belassen
;warten bis AF low wird, max. 10 ms
ldihl Z,CPUCLK/7/100 ;10ms {7 = Takte pro Runde}
tn_w: sbiw ZH:ZL,1 ;(2)
jbic PinC,SEL,tn_e ;(2)
sbic PinC,A_F ;(1)
brne tn_w ;(2)
breq tn_e ;Zeit abgelaufen
;Jetzt ACK auf Low und Daten anlegen
cbr r17,1<<ACK
out PortC,r17 ;Nibble ausgeben, ACK=LOW
;warten bis AF high wird, max. 10 ms
ldihl Z,CPUCLK/7/100 ;10ms {7 = Takte pro Runde}
tn_v: sbiw ZH:ZL,1 ;(2)
jbic PinC,SEL,tn_e ;(2)
sbis PinC,A_F ;(1)
brne tn_v ;(2)
breq tn_e ;Zeit abgelaufen
;Jetzt ACK auf High legen
ldi r17,0b01010111 ;Windows will es unbedingt SO haben!
out PortC,r17
ret ;CY sollte 0 sein
tn_e:
#ifdef DEBUG
in r16,PinC ;Leitungszustand zur Fehlersuche ausgeben
rcall ohex
#endif
sec
hn_e: ret
HandleNegotiation:
;Voraussetzung: PRINTER_FOUND gesetzt
;Bei leerem Puffer kann die Device-ID per Nibble-Mode abgefragt werden
;Bei nicht-leerem Puffer kann kein IEEE1284-GeräteID-String beschafft werden.
;I.d.R. ist dies keine Einschränkung.
sbrs EzBits,BUFFER_EMPTY
ret ;nicht wenn Puffer gefüllt!
in r17,PinB ;Daten lesen (Extensibility Byte)
;Steuerleitungen prüfen
in r16,PinC
andi r16,7 ;untere 3 Bits
cpi r16,0b110 ;SEL=H, INI=H, AF=L?
brne hn_e
;Jetzt (schon) String einlesen, später ist die Zeit zu knapp!
;In der Regel ist auch diese Zeit zu knapp, aber Windows versucht es ja
;zweimal. Dann ist der String beim zweiten Versuch da.
sbr EzBits,1<<BUFFER_FULL ;Idle totlegen (auch: Zeichenausgabe!)
;Information per USB beschaffen (Idle ist totgelegt mit BUFFER_FULL)
jbrs EzBits,HAVE_1284_ID,hn_s
rcall Read1284DevId
brcs hn_e
;Statusleitungen setzen
hn_s: push StatusResp ;aktuellen Zustand retten
ldi r16,0b10111111
mov StatusResp,r16
ldi r16,0b00111111
out PortC,r16 ;BSY+ACK low, alles andere high
;warten auf STB\-Interrupt (kein IDLE-Aufruf hier!) 10 ms lang
ldihl Z,CPUCLK/7/100 ;10ms {7 = Takte pro Runde}
hn_w: sbiw ZH:ZL,1 ;(2)
jbic PinC,SEL,hn_x ;(2)
sbis PortC,BSY ;(1) Interrupt gekommen?
brne hn_w ;(2)
breq hn_x ;kein Interrupt? Raus!
mov r16,DataCatch
cp r16,r17 ;Gleich geblieben?
brne hn_x ;raus!
;warten auf STB/ (Low-High-Flanke) 10 ms lang
ldihl Z,CPUCLK/7/100
hn_v: sbiw ZH:ZL,1 ;(2)
jbic PinC,SEL,hn_x ;(2)
sbis PinD,STB ;(1)
brne hn_v ;(2)
breq hn_x ;keine Rückflanke? Raus!
cbi PortC,BSY ;BUSY wieder wegnehmen
;Steuerleitungen prüfen
in r16,PinC
com r16 ;SEL=H, INI=H, AF=H?
andi r16,7 ;untere 3 Bits
breq hn_y ;Jetzt ist die Aushandlung prinzipiell OK.
;Ende der Aushandlung
hn_x: pop StatusResp
cbr EzBits,1<<BUFFER_FULL
mov r16,StatusResp
cbr r16,1<<BSY
sbr r16,1<<ACK
out PortC,r16
ret
hn_y:
;Art der Aushandlung prüfen und entsprechend rückmelden
ldi r16,0b01010111
cpi r17,4 ;Using Nibble Mode?
breq hn_1
cbr r16,1<<ONL ;Nicht unterstützter Modus
;Statusleitungen setzen
hn_1: out PortC,r16
ldi r16,0b01000111 ;bei (irrtümlichem) STB ONL=L setzen lassen,
mov StatusResp,r16 ; BSY low lassen, ACK high lassen
#ifdef DEBUG
rcall ocrlf
rcall SendStrAfter
.db "IEEE1284:GetDevId[",0,0
mov r16,r17
rcall ohex
ldi r16,']'
rcall putchar
jbis PortC,ONL,hn_k
rcall SendStrAfter
.db " - nicht untersttzt!",0
hn_k:
#endif
jbic PortC,ONL,hn_r ;Nicht unterstützter Modus
;Daten nibbleweise ausgeben
ldihl X,Sektor+2
ld WL,-X
ld WH,-X ;mindestens 3, höchstens 512
hn_l: ld r16,X+
rcall TwoNibbles
brcs hn_f ;Zeitüberschreitungsfehler
sbiw WH:WL,1
brne hn_l
#ifdef DEBUG
rcall SendStrAfter
.db " Abholung OK.",0
#endif
rjmp hn_r ;kein Fehler anzeigen
hn_f:
cbi PortC,ONL ;Fehlersituation anzeigen
;etwas warten, bis Host SEL zurücknimmt
hn_r: ldihl Z,0 ;16000000/5/65536 -> 20 ms
hn_u: sbiw WH:WL,1 ;(2)
sbic PinC,SEL ;(1)
brne hn_u ;(2)
#ifdef DEBUG
rcall ocrlf
#endif
rjmp hn_x ;zum Ende
;===============
;== Sonstiges ==
;===============
#if 0
ToggleC:
in r17,PortC
eor r17,r16
out PortC,r17
ret
#endif
SpannungMessen:
;Einfache Betriebsspannungsmessung
;PA: R17:R16 = Betriebsspannung in 10-mV-Schritten
cbi DDRA, 0 ;Eingang
cbi PortA,0 ;kein Pull-Up
outi ADMUX,0b11000000;Uref einschalten, Kanal 0
outi ADCSRA,0b11000110;ADC EIN, Teiler 64
sm_1: jbis ADCSRA,6,sm_1 ;1x wandeln
sbi ADCSRA,6
sm_2: jbis ADCSRA,6,sm_2 ;noch einmal wandeln
inhl r17,r16,ADC ;1023->5,115V
lsrw r17,r16 ; 511->5,11V (halbieren)
out ADCSRA,ZERO
out ADMUX,ZERO
sbi DDRA, 0 ;wieder Ausgang
ret
Idle:
;Drucker-Byte zum Puffer schaffen (nicht zum USB)
;VR: -
sbrs EzBits,PRINTER_FOUND
ret
sbis PortC,BSY ;BUSY high?
ret
pushhl X
push r16
in r16,SREG
rcall HandleLptByte
out SREG,r16
pop r16
pophl X
ret
;============================
;== Serielle Schnittstelle ==
;============================
ospac: ;Leerzeichen
ldi r16,' '
putchar:
;Zeichen auf serielle Schnittstelle ausgeben
;PE: R16=Zeichen
;Verändert Flags nicht!
pch1: rcall Idle
jbic UCSRA,UDRE,pch1 ;Warte bis Sendehalteregister leer
out UDR,r16
ret
#if 0
peekchar:
;"Tastenabfrage" von Terminalemulation - diese Routine blockiert nicht!
;PE: -
;PA: Z=0: UART_InChar = Zeichen (wird abgeholt)
; Z=1: kein Zeichen da
sez
sbis UCSRA,RXC
ret
in UART_InChar,UDR
clz
ret
#endif
getchar: ;Blockierende Tastenabfrage
rcall Idle
jbic UCSRA,RXC,getchar
in UART_InChar,UDR
ret
ocrlf: ;Wagenrücklauf+Zeilenvorschub
ldi r16,0x0D
rcall putchar
ldi r16,0x0A
rjmp putchar
ohex: ;Hex-Byte-Ausgabe
push r16
swap r16
rcall onib
pop r16
onib: ;Low-Nibble-Ausgabe
andi r16,0x0F
addi r16,'0'
cpi r16,'0'+10
brcs putchar
addi r16,7
rjmp putchar
.include "vt100.i90"
.include "avrstr.i90"
.include "itoa.i90"
LineDel:
;PE: R16=Zeilenummer, R17=Farbe
push r16
rcall SendColor
pop r17
clr r16
rcall GotoXY
SendLineDel:
ldi r17,'K'
SendCSIz: ;CSI und 1 Folgezeichen (r17) senden
rcall SendCSI
mov r16,r17
rjmp putchar
LineDel2:
;PE: R18=Zeilenummer, R19=Farbe
movw r17:r16,r19:r18
rcall LineDel
inc r18
ret
ScreenInit:
ldi r17,0x07
rcall SendColor
rcall SendStrAfter
.db 27,"[2J", 0,0
ldi r18,0
ldi r19,0x70
rcall LineDel2
rcall SendStrAfter
.db " LPT2USB - Konverter parallel -> USB-Drucker h#s",0
ldi r19,UPPERCOLOR
lo1: rcall LineDel2
cpi r18,12
brne lo1
ldi r16,0
ldi r17,2
rcall GotoXY
rcall SendStrAfter
.db " Betriebsspannung: V",13,10
.db " Deskriptor:",13,10
.db " 1284 DevID:",13,10,10,10
.db " Status: ",27,"[4mERR",27,"[24m=X ONL=X PE=X ",27,"[4mACK",27,"[24m=X BSY=",13,10
.db " Gesendete/Empfangene Bytes: / kByte/s",13,10,0
ldi r18,23
ldi r19,0x70
rcall LineDel2
rcall SendStrAfter
.db " Firmware-Erstellung: ",__DATE__," ",__TIME__,0
ldi r17,0x07
rcall SendColor
rcall SendStrAfter
.db 0x1B,"[13;23r", 0x1B,"[13;0H", 0
ret
EnterScroll:
;In Rollbereich zurückkehren: Kursorposition und Farbe(!)
ldi r17,'u'
rcall SendCSIz
ldi r17,0x07
rcall SendColor
ret
LeaveScroll:
;Rollbereich verlassen: Kursorposition retten
ldi r17,'s'
rcall SendCSIz
ret
SendNumberY:
;32-Bit-Zahl bei Y ausgeben
ld r2,Y+
ld r3,Y+
ld r4,Y+
ld r5,Y+
rjmp SendNumberDezIntMin
SendNumberX4:
;24-Bit-Zahl x4 und 3 Nachkommastellen ausgeben
ldi r18,3 ;rechtsbündig
SendNumberX4a: ;für linksbündig
clr r5
lslw r3,r2
rolw r5,r4
lslw r3,r2
rolw r5,r4
ldi r19,8 ;7 Ziffern + Komma
rjmp SendNumberDez
Actualize:
;Obere Bildhälfte zyklisch aktualisieren
;Spannung anzeigen
ldi r16,19
ldi r17,2
rcall GotoXY
rcall SpannungMessen
movw r3:r2,r17:r16
clr r4
clr r5
ldi r18,2 ;Nachkommastellen
ldi r19,4 ;Feldbreite
rcall SendNumberDez
;Status anzeigen (falls geändert, für weniger Gezappel)
in r16,PortC
lds r17,LastStatus
cp r16,r17
breq ac_1
sts LastStatus,r16
ldi r16,13 ;ERR-Status
ldi r17,7
rcall GotoXY
ldi r16,'L'
sbic PortC,3
ldi r16,'H'
sbis DDRC,3
ldi r16,'Z'
rcall putchar
ldi r16,19 ;SEL-Status
ldi r17,7
rcall GotoXY
ldi r16,'L'
sbic PortC,4
ldi r16,'H'
sbis DDRC,4
ldi r16,'Z'
rcall putchar
ldi r16,24 ;PE-Status
ldi r17,7
rcall GotoXY
ldi r16,'L'
sbic PortC,5
ldi r16,'H'
sbis DDRC,5
ldi r16,'Z'
rcall putchar
ldi r16,30 ;ACK-Status
ldi r17,7
rcall GotoXY
ldi r16,'L'
sbic PortC,6
ldi r16,'H'
sbis DDRC,6
ldi r16,'Z'
rcall putchar
ldi r16,36 ;BSY-Status
ldi r17,7
rcall GotoXY
ldi r16,'L'
sbic PortC,7
ldi r16,'H'
sbis DDRC,7
ldi r16,'Z'
rcall putchar
ac_1:
;Gesendete Bytes anzeigen (falls geändert)
lds r20,Zyklus
jbrc r20,7,ac_2
ldi r16,29
ldi r17,8
rcall GotoXY
ldi YL,LptSent
rcall SendNumberY
ldi r17,UPPERCOLOR
rcall SendColor
ldi r16,'/'
rcall putchar
ldi r17,UPPERCOLOR^0x0C
rcall SendColor
rcall SendNumberY ;LptRecv
ac_2:
;Datenrate anzeigen
ldi YL,LptSent
ldihl X,LastSent ;Differenzen bilden (24 bit)
clr r17
ld r2,Y+
ld r16,X
st X+,r2
sub r2,r16
ld r3,Y+
ld r16,X
st X+,r3
sbc r3,r16
ld r4,Y+
ld r16,X
st X+,r4
sbc r4,r16
breq ac_3
sbr r17,1<<6 ;Ergebnis R4:R3:R2 nicht Null
ac_3: inc YL
ld r6,Y+
ld r16,X
st X+,r6
sub r6,r16
ld r7,Y+
ld r16,X
st X+,r7
sbc r7,r16
ld r1,Y
ld r16,X
st X,r1
sbc r1,r16
breq ac_4
sbr r17,1<<6 ;Ergebnis R1:R7:R6 nicht Null
ac_4:
;Feststellen, ob eine Ausgabe nötig ist:
;* wenn ungleich Null
;* wenn vorher ungleich Null (und jetzt Null)
mov r16,r17
or r16,r20
andi r16,1<<6
breq ac_5 ;Keine Aktualisierung von 0/0
cbr r20,1<<6 ;Null-Bit übertragen
sbrc r17,6
sbr r20,1<<6
;Werte vervierfachen (Byte/s) weil Messung mit 4 Hz
ldi r16,48
ldi r17,8
rcall GotoXY
push r1
rcall SendNumberX4
pop r4
movw r3:r2,r7:r6
ldi r17,'C'
rcall SendCSIz ;Kursor rechts (grauen "/" übergehen)
ldi r18,0x23 ;linksbündig
rcall SendNumberX4a ;an Position 57
ac_5: cbr r20,1<<7
sts Zyklus,r20
ret
;Drehstrich rechts oben darstellen (Aktivitätsanzeige)
Drehstriche:
.db "-\|/"
Drehstrich:
ldi r17,0x74
rcall SendColor
ldi r16,79
ldi r17,0
rcall GotoXY
lds r16,Zyklus
inc r16
cbr r16,4
sts Zyklus,r16
andi r16,3
ldihl Z,Drehstriche*2 ;Byteadresse
add ZL,r16
adc ZH,ZERO
lpm r16,Z
rjmp putchar
;Initialisierung
main: outihl SP,RAMEND
clr ZERO
clr EzBits
ldi_ ONES, 0b11111111
outi OCR0, CPUCLK/64/1000 ;Zählerende: ca. 1 ms
outi TCCR0,0b00001011 ;Timer0: CTC, Vorteiler 64
out DDRA, ONES ;PortA standardmäßig Ausgabe (kein ADU)
;out PortB,ONES ;Pullups aktivieren? (Frisst Strom!)
out PortC,ONES ;Defaults und Pullups
;outi DDRC, 0b11111000 ;Statusleitungen später Ausgänge!
outi PortD,0b11100101 ;Defaults, Pullup für STB, rote LED EIN
outi DDRD, 0b11111010 ;TxD ist auch Ausgang
outi MCUCR,0b00000010 ;INT0 bei fallender Flanke
outi GICR, 0b01000000 ;INT0 freigeben
outihl OCR1A,CPUCLK/64/4-1 ;62499
outi TCCR1B,0b00001011 ;Zeitgeber1 liefert 4Hz
clr r16 ;(Aktualisierung bei HyperTerminal)
ldihl Z,SRAM_START
ldihl W,SRAM_SIZE
loeschram:
st Z+,r16 ;gesamten RAM löschen
sbiw WH:WL,1
brne loeschram
;Energie sparen - mit sleep?
sei
outihl UBRR, (CPUCLK/8/BaudRate-1)/2 ;Baudrate (12 bit) mit Rundung
outi UCSRB,0b00011000 ;Sender und Empfänger freigeben
ldi_ StatusResp,0b10010101 ;Das gesetzte BUSY-Bit ist Signal...
rcall SL811_Init
EZUSB_Delay 100 ;Ohne Delay geht Schnittstelle nicht!?
scrini: rcall ScreenInit
stsi Zyklus,0xC0
sts LastStatus,ZERO
jbrc EzBits,PRINTER_FOUND,mainloop
rcall ShowPrinterDevDat
sbrc EzBits,HAVE_1284_ID
rcall ShowDevId
;Hauptschleife (die Idle-Routine kümmert sich um parallelen Dateneingang)
mainloop:
jbrc EzBits,PRINTER_FOUND,ml1
rcall HandleUsbTransfer
rcall HandleNegotiation
rcall UpdateStatus ;Drei Statusbits vom USB holen und reflektieren
ml1:
;Testen: Statusänderung am EzHost?
sbrs EzBits,SLAVE_FOUND
rcall DoEnumSlave
sbrc EzBits,SLAVE_FOUND
rcall CheckSlaveDetach
in r16,TIFR
jbrc r16,4,ml2 ;Überlauf-Interrupt Zähler1 (4 Hz)
outi TIFR,1<<4 ;Interruptanforderung löschen
rcall LeaveScroll
rcall Drehstrich
ldi r17,UPPERCOLOR^0x0C
rcall SendColor
rcall Actualize
rcall EnterScroll
ml2: jbic UCSRA,RXC,mainloop ;svw. PeekChar
rcall GetKey
cpi UART_InChar,12 ;Bild-Neuaufbau (^L)
breq scrini
cpi UART_InChar,'R'-'@' ;^R = Reset
brne ml4
cli
rjmp main
ml4: cpi UART_InChar,'p' ;Simulation eines Bytes
brne ml3
sbi DDRD,2 ;Ausgabe
cbi PortD,2 ;LOW, müsste Interrupt auslösen
cbi DDRD,2 ;Eingabe
sbi PortD,2 ;PullUp
ml3: cpi UART_InChar,'d' ;1284-ID lesen
brne ml5
jbrc EzBits,PRINTER_FOUND,mle1 ;Nur wenn Drucker gefunden!
jbrc EzBits,BUFFER_EMPTY,mle1 ;Nur wenn Puffer leer
sbi PortC,BSY ;"Beschäftigt": PC ruhig
sbr EzBits,1<<BUFFER_FULL ;"Puffer voll": Idle ruhig
rcall Read1284DevId
cbr EzBits,1<<BUFFER_FULL
cbi PortC,BSY
brcc mainloop
mle1: ldi r16,7 ;Pieps
rcall putchar
rjmp mainloop
ml5: cpi UART_InChar,'e' ;EzBits anzeigen
brne ml6
rcall SendStrAfter
.db "EzBits=",0
mov r16,EzBits
rcall ohex
ml6: cpi UART_InChar,'i' ;Info anzeigen
brne ml7
rcall ocrlf
rcall SendStrAfter
.db "Info: <40>,<42>=NAK, <80>=STALL, <10>=Disconnect...",0
rcall ocrlf
ml7: cpi UART_InChar,'r' ;Bytezähler rücksetzen
brne mle
ldi YL,LptSent
clr r0
st Y+,r0
st Y+,r0
st Y+,r0
st Y+,r0
st Y+,r0
st Y+,r0
st Y+,r0
st Y+,r0
ldi YL,LastSent
st Y+,r0
st Y+,r0
st Y+,r0
st Y+,r0
st Y+,r0
st Y+,r0
rcall SetZyklusBit7
rcall LeaveScroll
ldi r17,UPPERCOLOR^0x0C
rcall SendColor
ldi r16,29
ldi r17,8
rcall GotoXY
ldi r17,18
mll1: rcall ospac ;Löschen vom Kursor
djnz r17,mll1
rcall EnterScroll
mle: rjmp mainloop
Detected encoding: UTF-8 | 0
|