Source file: /~heha/basteln/PC/oszi/adc2usb/adc2usb.zip/ADC2USB.A51

;FX2-Firmware für ADC2USB, haftmann#software 11/04
;Für zwei 16-bit-A/D-Wandler, die am GPIF angeschlossen sind
;Im Hauptprogramm wird nur ein dptr benutzt (also kein DPS), nur Registerbank 0
;Zu übersetzen mit ASEM51; ggf. EEPROM-Datei erstellen mit HEX2BIX -i -f 0xC2
;Nur für EZ-USB FX2, 480 Mbit/s! Läuft eingeschränkt auch mit 12 Mbit/s.
;Außerdem wird ein serieller D/A-Wandler TDA1543 angesteuert, der eine
;präzise Sinusschwingung mit einstellbaren ca. 400 Hz erzeugt.
;Die Kurvenform kommt von der hinten stehenden Tabelle...
	$nopaging
	$nosymbols
	$nomod51
	$nolist
	$include(ezusbfx2.mcu)
	$include(makros.i51)
	$list
	$genonly		;das "Innere" von Makros
	$condonly		;das "Innere" von IFxx
always_renum	equ	0
;###############
;## Schaltung ##
;###############
;PortA (Pinzuordnung endgültig seit 17.12.04)
ADC_RESET	BIT	IOA.0	;ADU-Rücksetzen (Firmware-Start)
ADC_CS		BIT	IOA.1	;Schaltkreisauswahl für die A/D-Wandler
Led3		BIT	IOA.2	;Weitere LED (Debug), zz. freies Pin
Led		BIT	IOA.3	;Gelbe USB-Leuchtdiode
Led2		BIT	IOA.4	;Grüne LED, High-Speed- und USB-Fehler-Anzeige?
Dau_BCK		BIT	IOA.5	;Dieser serielle DAU wäre viel besser am
Dau_WS		BIT	IOA.6	;synchron-seriellen Port angeschlossen,
Dau_Data	BIT	IOA.7	;aber per Ping-Pong geht es hier auch
;PortB ist Datenbus LOW-Teil
;PortC existiert am 56pol. Gehäuse nicht
;PortD ist Datenbus HIGH-Teil
;PortE existiert am 56pol. Gehäuse nicht
;CTL0 = A/D-Umsetzer CONVST (Start der Umsetzung; beide ADUs)
;CTL1 = A/D-Umsetzer RD0 (Lese-Leitung erster ADU)
;CTL2 = A/D-Umsetzer RD1 (Lese-Leitung zweiter ADU)
;RDY0, RDY1 sind ungenutzt (offene Eingänge bspw. mit Ucc oder BUSY verbinden!)

;-----------
;## DATEN ##	Datenbereich wird beim Einschalten mit Nullen gelöscht
;-----------
DSEG AT 20h
IntReq:		ds	1	;für USB-Interruptrequests, bitadressierbar
bits:		ds	1
LedBlink	BIT	bits.0
Led2Blink	BIT	bits.1
HighSpeed	BIT	bits.5
Configuration:	ds	1	;hier: Null (adressiert) oder Eins (konfig.)
AltSetting:	ds	1	;hier: Null (Bulk), Eins (ISO) oder Zwei (INT)
DSEG AT 30h
LedTime:	ds	1	;"Nachblinkzeit" der LED in ms
BlinkF:		ds	1	;"Blinkfrequenz" in ms (halbe Periode)
Blink:		ds	1	;Blink-Zähler
FrameCnt:	ds	1	;zum Mitzählen "ganzer" Rahmen
Led2Time:	ds	1	;"Nachleuchtzeit" der HS-LED
WirklichVoll:	ds	1	;Um ADU-ChipAuswahl nicht sporadisch zu steuern
;Für den DAU: dptr1 zeigt permanent in die Kurventabelle ab 1000h bis 2000h
a0:		ds	1	;3-Byte-Addierwert pro DAC-Ausgabe
a1:		ds	1	;auf DPH0:DPL0:dpd1:dpd0
a2:		ds	1	;= Round(Wunschfrequenz[Hz]*2684,35)
dpd0:		ds	1	;16 "Nachkomma-Bits" für dptr1
dpd1:		ds	1

;--------------
;## PROGRAMM ##
;--------------
;--> Dieses Programm kommt beinahe ohne Interrupts aus! <--
;Nach dieser Fallstudie frage ich mich, wieso Anchor/Cypress dem Anwender
;einen Bären mit 'zig Interrupts aufbinden will, wobei die Beispielquellen
;nichts anderes machen als in der ISR ein Bit zu setzen und im Hauptprogramm
;abzufragen (Polling). Dazu sind doch Interrupt-Request-Bits da!
;Nur für's Resume [Fortsetzen] schien eine Mini-ISR unumgänglich zu sein.
;(Naja, Schlafmodus und Resume sind hier ganz nettes Beiwerk...)

CSEG AT 0
	ajmp	main
ORG TIMER0
	ajmp	T0ISR
WaveDat:	;Nur eine Wellenform wird benötigt, 1 MSa/s, 48 IFCLK-Takte
;Alternative Zeiten sind zweckmäßig für geringere Abtastrate bei Full-Speed
;maximal 256 kSa/s, jitterfrei 250 kSa/s, 192 IFCLK-Takte
;Für HighSpeed wird WAVEDATA+1 auf 04h (statt 94h) modifiziert!
	db	28,144+4,3,3,3,3,3,7	;LenBr für FullSpeed (Zeit in Takten)
	db	0,16,2,0,0,2,0,0	;Opcode (2=Abtasten, 16=Interrupt)
	db	0FEh,0FFh,0FDh,0FDh,0FFh,0FBh,0FBh,0FFh	;Output
	db	0,0,0,0,0,0,0,3Fh	;LFun (ungenutzt)
;Normalerweise würden 500 Bytes pro Mikrorahmen (FS: 1000 Bytes pro Rahmen)
;zur Übertragung ausreichen, aber am Oszibild machte sich Jitter bemerkbar,
;daher sind die FIFOs leicht größer als notwendig dimensioniert.

ORG 33h		;RESUME-ISR (erforderlich, sonst geht das Wecken nicht)
	clr	EICON.4
	reti

;================================
;== Zeichenketten-Umwurstelung ==
;================================
ReadUTF8:
;UTF8/8859-1 -> Unicode-Konvertierung (Unicode-16 für USB)
;Um das EEPROM-File kleiner und den Quelltext leichter pflegbar zu machen...
;PE: dptr=Quell-UTF8; jedes "falsche" UTF8 wird als ISO-8859-1 interpretiert!
;    (Damit kann man in Westeuropa immer mit ISO-8859-1 arbeiten.)
;    DPS.0=0!
;PA: r7:r6=Unicode-16
;    dptr vorgerückt
;VR: A,R4,R5,R6,R7,dptr,Flags
;LZ: Stark vom Zeichensatz abhängig, bei reinem ASCII 14 Takte
	movx	a,@dptr
	inc	dptr
	mov	r6,a		;R6 enthält fortan erstes Byte für Rückfall
	jnb	ACC.7,utf1	;normales ASCII
	jnb	ACC.6,utf1	;kein gültiges UTF8-Führungsbyte
	jnb	ACC.5,utf2	;2-Byte-UTF8
	jb	ACC.4,utf1	;kein 3-Byte-UTF8
utf2:	anl	a,#3Fh
	jz	utf1
	dec	a
	jz	utf1
	inc	a
	rr	a
	rr	a
	mov	r5,a		;geANDetes und geschobenes Führungsbyte
	movx	a,@dptr
	jnb	ACC.7,utf1	;kein gültiges Trail-Byte
	jb	ACC.6,utf1	;ebenso
	clr	ACC.7
	xch	a,r5		;R5=geANDetes Folgebyte, A=höheres Byte
	mov	r4,a		;R4=höheres Byte (Bits 7:6 oben, 10:8 unten)
	anl	a,#0C0h
	orl	a,r5
	xch	a,r4		;R4=niederwertiges Byte, A=höheres Byte
	anl	a,#0Fh		;3 Datenbits 10:8 und 1 "Testbit"
	mov	r7,a		;R7 setzen (höheres Byte)
	jnb	ACC.3,okay2	;war 2-Byte-Kode
	push	DPL0
	push	DPH0		;retten zur Restaurierung (fehlt "dec dptr")
	 inc	dptr
	 movx	a,@dptr		;drittes Byte
	 jb	ACC.6,utf1r	;kein Folgebyte, ganze Kette verwerfen
	 jbc	ACC.7,utf3	;und #0:R6 (»Windows-Zeichensatz«) annehmen
utf1r:
	pop	DPH0		;dptr zurückstellen
	pop	DPL0
utf1:	mov	r7,#0
	ret
utf3:
	 mov	r5,a
	 mov	a,r4		;R5=geANDetes drittes Byte, A="mittleres" Byte
	 rr	a
	 rr	a
	 mov	r4,a		;R4=rechtsgeschobenes "mittleres" Byte
	 anl	a,#0C0h		;Bit 7:6
	 orl	a,r5		;A=Bit 7:0
	 xch	a,r4		;Low-Teil fertig, in R4, A="mittleres" Byte
	 anl	a,#3Fh		;Bit 13:8
	 xch	a,r7		;nach R7, A=höchstes Byte (nur 2 Bits 15:14)
	 rr	a
	 rr	a
	 anl	a,#0C0h
	 orl	a,r7		;High-Teil fertig
	 mov	r7,a		;in R7
	 add	a,#-8
	 jnc	utf1r		;zu klein, ungültig!
	pop	ACC
	pop	ACC
okay2:	mov	a,r4
	mov	r6,a		;R6:=R4 (Low-Teil) setzen
	inc	dptr
	ret

MakeString:	;USB-String-Deskriptor (erst) bei Anforderung zusammenbauen
;PE: R2=String-Nummer (in nullterminierter Kette)
;PA: StringPuffer gefüllt mit USB-String-Deskriptor
;VR: R0=0,R2=0,R4,R5,R6,R7,dptr
;LZ: Etwa linear mit R2 zunehmend!
	mov     dptr,#StringDescriptors
	inc	r2
	sjmp	ms2
ms1:	movx	a,@dptr
	inc	dptr
	jnz	ms1
ms2:	djnz	r2,ms1
	push	MPAGE
	 mov	MPAGE,#HIGH(StringPuffer)
	 mov	r0,#0
	 mov	r7,#3		;"String-Deskriptor"
ms3:	 mov	a,r6
	 ST			;(zunächst wird eine falsche Länge gesetzt)
	 inc	r0
	 mov	a,r7
	 ST			;"Zeichen", High-Byte
	 inc	r0
	 call	ReadUTF8
	 mov	a,r6
	 orl	a,r7
	 jnz	ms3
	 mov	a,r0
	 ST	0		;Länge in Bytes nachtragen
	pop	MPAGE
	ret

SetGpif:
;Setzt EP2 und GPIF in Abhängigkeit von <Configuration> und <AltSetting>:
;GpifAbort, FifoReset, Ep2Cfg, AutoInLen, Ep2Cfg, WAVEDATA[1], GpifTc, GpifTrig
	ST	GpifAbort	;GPIF abwürgen für Registerzugriff
	SyncDelay
	ST	FifoReset,80h	;IN-Ende frei machen, FIFO leeren (?)
	SyncDelay
	ST	,2
	SyncDelay
	ST	,0
	SyncDelay
	mov	r2,a		;Ungültiger EP2 (a=0)
	jnb	Configuration.0,Ep2Tot	;wenn Null
	mov	dptr,#64	;Full Speed: 64-Byte-Stückelung
	mov	a,#11110000b	;INT IN, 4 x 512 Byte
	jb	AltSetting.1,Ep2Set	;wenn 2 (gibts nur bei High Speed)
	mov	a,#11100000b	;BULK IN, 4 x 512 Byte
	jnb	AltSetting.0,Ep2Set	;wenn Null
	mov	a,#11011000b	;ISO IN, 4 x 1024 Byte
	mov	dptr,#1020	;Full Speed: 1020 (1023 wäre krumm) Bytes
Ep2Set:	mov	r2,#144+4	;lange warten (3 µs extra) für 250 kSa/s
	jnb	HighSpeed,Ep2FS
	clr	ACC.3		;es reicht ein 512-Byte-Endpoint
	mov	r2,#4		;kurz warten für 1 MSa/s
Ep2Tot:	mov	dptr,#512	;High Speed: stets 512 Bytes pro µRahmen
Ep2FS:	ST	EP2Cfg		;(sowohl BULK als auch ISO als auch INT)
	mov	a,DPH0
	ST	EP2AutoInLenH	;das "Quantum" setzen, 64, 512 oder 1020
	mov	a,DPL0
	ST	NEXT
	mov	a,r2
	jz	Ep2Ok		;unkonfiguriert gibt's kein GPIF
	STORE	WAVEDATA+1	;Zeit setzen
	SyncDelay
	ST	EP2FifoCfg,00001001b	;AutoIn, kein ZeroLenIn
	ST	GpifTcB3,0FFh	;Langen Transfer aktivieren
	mov	GpifTrig,#4+2-2	;GPIF starten
Ep2Ok:	ret

;=========================
;== Routinen für Ende 0 ==	;PE: R0=SETUPDAT, R4=SDAT[0], R5=SDAT[2]
;=========================	     c=0          R6=SDAT[4], R7=SDAT[5]

GetStatus:
	cjne	r4,#80h,gs_no_dev	;GS_DEVICE
nullin:	clr	a		;kein WakeUp(Bit1), kein SelfPower(Bit0)
wordin:	inc	MPAGE
	ST	EP0BUF
	ST	NEXT,0
	dec	MPAGE
	ST	EP0BCL,2
	ret
gs_no_dev:
	cjne	r4,#81h,gs_no_if	;GS_INTERFACE
	jmp	nullin		;auch zwei Nullen melden
gs_no_if:
	cjne	r4,#82h,err	;GS_ENDPOINT
	cjne	r6,#82h,err	;EP2IN?
	LD	EP2CS		;STALL-Bit angeln
	anl	a,#01h
	jmp	wordin

ClearFeature:
	cjne	r4,#02h,err	;ENDPOINT OUT
	cjne	r5,#00h,err	;einziges Feature: STALL
	cjne	r6,#82h,err	;nur EP2IN
	ST	EP2CS,0		;STALL entfernen
ResTog2:
	ST	TOGCTL,12h	;Toggle-Bit adressieren
	setb	ACC.5		;WRITEDELAY
	ST			;Togglebit zurücksetzen
	ret

SetFeature:
	cjne	r4,#2,err	;FT_ENDPOINT
	cjne	r5,#0,err	;einziges Feature: STALL
	ST	EP2CS,1		;STALL setzen
	ret
	
err:	setb	c
	ret

GetDescriptor:
	cjne	r4,#80h,err	;nur DEVICE_IN
	LD	SETUPDAT+3
	cjne	a,#01h,gd_no_dev	;GD_DEVICE
	mov	dptr,#DeviceDescriptor
gd_sto:	mov	a,DPH0
	ST	SUDPTRH
	mov	a,DPL0
	ST	NEXT
	ret
gd_no_dev:
	cjne	a,#02h,gd_no_conf	;GD_CONFIGURATION
	mov	dptr,#ConfigDescFS
	jnb	HighSpeed,gd_sto
	mov	dptr,#ConfigDescHS
	jmp	gd_sto
gd_no_conf:
	cjne	a,#03h,gd_no_str	;GD_STRING
	mov	a,r5
	mov	r2,a
	add	a,#-5			;Nur 0..4 zulassen
	jc	err
	call	MakeString
	mov	dptr,#StringPuffer
	jmp	gd_sto
gd_no_str:
	cjne	a,#06h,gd_no_qual	;GD_QUALIFIER
	mov	dptr,#DeviceQualifier
	jmp	gd_sto
gd_no_qual:
	cjne	a,#07h,err		;GD_OTHERSPEED
	mov	dptr,#ConfigDescHS
	jnb	HighSpeed,gd_sto
	mov	dptr,#ConfigDescFS
	jmp	gd_sto

GetConfiguration:
	cjne	r4,#80h,err	;nur DEVICE_IN
	mov	a,Configuration	 ;Entweder konfiguriert oder unkonfiguriert
gi:	inc	MPAGE
	ST	EP0BUF
	dec	MPAGE
	ST	EP0BCL,1
retu:	ret

SetConfiguration:
	cjne	r4,#0,err	;nur DEVICE_OUT
	mov	a,r5
	add	a,#-2
	jc	err		;nur Konfiguration 0 oder 1
	mov	Configuration,r5
	jmp	SetGpif		;GPIF ein/auschhalten usw.

GetAltSet:
	cjne	r4,#81h,err	;nur INTERFACE IN
	mov	a,AltSetting
	jmp	gi

SetAltSet:
	cjne	r4,#1,err	;nur INTERFACE OUT
	mov	a,#-2
	jnb	HighSpeed,sa1	;-2 bei Full Speed
	dec	a		;-3 bei High Speed
sa1:	add	a,r5
	jc	err		;maximal 1 (FS) bzw. 2 (HS) zulässig
	mov	AltSetting,r5
	jmp	SetGpif		;Endpoint konfigurieren usw.
	
SetFreq:	;hersteller-spezifischer Request F0h zum Setzen/Abfragen der
		;fein einstellbaren Frequenz; Datenphase: 4 Bytes (3 genutzt)
	LD	SETUPDAT+6	;Länge Low
	cjne	a,#4,err2
	LD	SETUPDAT+7	;Länge High
	jnz	err2	
	mov	AutoPtrH1,#HIGH(EP0BUF)
	mov	AutoPtrL1,#LOW(EP0BUF)
	mov	dptr,#XAutoDat1
	mov	a,r4
	jb	ACC.7,GetFreq
	ST	EP0BCL		;EP0Out-Puffer aktivieren
wd:	mov	a,EP01STAT
	jb	ACC.0,wd	;Warten auf EP0-Daten (Endlosschleife!)
	clr	EA	;Daten = Round(Wunschfrequenz[Hz]*2684,35)
	LOAD
	mov	a0,a
	LOAD
	mov	a1,a
	LOAD
	mov	a2,a
	setb	EA
	ret
GetFreq:		;Wunschfrequenz[Hz] = Daten/2684,35
	mov	a,a0
	STORE
	mov	a,a1
	STORE
	mov	a,a2
	STORE
	STORE	,0
markok:	ST	EP0BCL,4
	ret

SUD_Tab:
	ajmp	GetStatus	;0
	ajmp	ClearFeature	;1
err2:	ajmp	err		;2
	ajmp	SetFeature	;3
	ajmp	err		;4
	ajmp	err		;5, sollte nicht vorkommen
	ajmp	GetDescriptor	;6
	ajmp	err		;7 SetDescriptor
	ajmp	GetConfiguration;8
	ajmp	SetConfiguration;9
	ajmp	GetAltSet	;10
	ajmp	SetAltSet	;11
	;ajmp	err		;12 SyncFrame

HandleSUD:
;Unterprogramm zur Behandlung von SetUpData
;liefert CY=1 für EP0STALL
	LD	SETUPDAT	;SETUPDAT	-> R4
	mov	r4,a
	LD	NEXT		;SETUPDAT+1	-> Programmverteiler
	mov	r7,a
	CJE	a,#0F0h,SetFreq
	add	a,#-12
	jc	err2
	LD	NEXT		;SETUPDAT+2	-> R5
	mov	r5,a
	LD	SETUPDAT+4	;SETUPDAT+4	-> R6
	mov	r6,a
	LD	NEXT		;SETUPDAT+5	-> R7
	xch	a,r7
	JMPTBL	SUD_Tab		;mit c=0 Routinen anspringen

;=====================
;== DAU-Ansteuerung ==
;=====================
;Hätten wir ein 100+poliges Gehäuse, könnte man wunderschön das serielle
;Port im Modus 0 (synchron) benutzen.
;Beim 56poligen bleibt nur der Ausweg des Ping-Pong-Verfahrens.
;Es reicht geradeso für 1 Kanal mit satten 48 kSa/s und 16 bit
T0ISR:
;Für diese ISR belässt das Hauptprogramm folgende Register unberührt:
;dptr1, r1			;Aufruf+Ansprung 7
	push	PSW		;2
	push	ACC		;2
	 inc	DPS		;2 dptr1 verwenden
	 inc	DPH1		;2
	 movx	a,@dptr		;2 Sinustabelle, High-Byte
REPT 8				;Zwischensumme 10
	 rlc	a		;1
	 mov	Dau_Data,C	;2
	 clr	Dau_BCK		;2 Low-Zeit = 160 ns (mindestens 22 ns)
	 setb	Dau_BCK		;2
ENDM				;Zwischensumme 56
	 dec	DPH1		;2
	 movx	a,@dptr		;2 Low-Byte
	 dec	DPS		;2
REPT 7				;Zwischensumme 6
	 rlc	a		;1
	 mov	Dau_Data,C	;2
	 clr	Dau_BCK		;2
	 setb	Dau_BCK		;2
ENDM				;Zwischensumme 49
	 setb	Dau_WS		;2
	 rlc	a		;1
	 mov	Dau_Data,C	;2
	 clr	Dau_BCK		;2
	 setb	Dau_BCK		;2
	 anl	IOA,#1Fh	;3 !!Anpassen bei Änderung der Bit-Zuweisung!!
	 ;clr	Dau_Data	;2
	 ;clr	Dau_WS		;2
	 ;clr	Dau_BCK		;2
	 setb	Dau_BCK		;2
				;Zwischensumme 17 (14)
	 mov	a,dpd0		;2
	 add	a,a0		;2
	 mov	dpd0,a		;2
	 mov	a,dpd1		;2
	 addc	a,a1		;2
	 mov	dpd1,a		;2
	 mov	a,DPL1		;2
	 addc	a,a2		;2
	 mov	DPL1,a		;2
	 jnc	skh		;3
	 mov	a,DPH1		;2
	 swap	a		;1 Die "1" ins Low-Nibble retten
	 add	a,#20h		;2
	 swap	a		;1
	 mov	DPH1,a		;2 High-Nibble unverändert (=1) lassen
	 			;Zwischensumme 29
skh:
	pop	ACC		;2
	pop	PSW		;2
	reti			;4
				;Zwischensumme 8
				;Gesamtsumme 182 = 15,2 µs
;=== Warte-Hilfsbefehl, Argument R4 = Wartezeit in 1 µs ===
;wait:	inc	r4		;Wir brauchen 4x6=24 Takte pro Durchlauf
w1:	inc	dptr		;kurzer Befehl mit langer Dauer!
	inc	dptr		;3 Takte, 12 MHz / 3 => 0,25 µs/Befehl
	inc	dptr
	djnz	r4,w1		;3 Takte
	ret

;=== LED-Spielerei ===

StartLed:
;Start des LED-Blinkens mit a=Blinkperiodendauer, 0=Maximum
;Blinkt die LED bereits, wird nur die künftige Blinkperiode gesetzt
;VR: A,R0=LOW(OUTA)
	mov	LedTime,a	;Zeitzähler (Monoflop) neu starten
	mov	BlinkF,a	;Blinkfrequenz setzen
	jb	LedBlink,nost
	mov	Blink,a
	setb	LedBlink
	clr	Led		;LED ausschalten (Zugriff beginnt)
nost:	ret

SetWaveData:
;Setzt Bytes der Zustandsmaschine
;PE: r2=Anzahl Bytes, dptr=Byte-Zeiger
	mov	AUTOPTRH2,#HIGH(WAVEDATA)
	mov	AUTOPTRL2,#LOW(WAVEDATA)
	mov	AUTOPTRH1,DPH0
	mov	AUTOPTRL1,DPL0
fill:	mov	dptr,#XAUTODAT1
	movx	a,@dptr
	inc	dptr		;#XAUTODAT2
	movx	@dptr,a
	djnz	r2,fill
	ret

;=====================
;== Initialisierung ==
;=====================
main:
	mov	SP,#7Fh
	mov	CKCON,#00000000b	;Handbremse für "movx" lösen
	mov	MPAGE,#HIGH(CPUCS)	;7Eh
	ST	GpifAbort
	ST	CPUCS,00010000b		;CPU auf 48 MHz, kein CLK48
	mov	r0,#8
	mov	r3,#78h
	clr	a
clr_ram:mov	@r0,a
	inc	r0
	djnz	r3,clr_ram
	mov	IOA,a
	mov	DPS,a			;sicherheitshalber
	dec	a
	mov	OEA,a
	mov	OEC,a			;100+Pin: Strom sparen
	mov	OEE,a
	setb	EICON.5			;Resume-Interrupt (auch bei EA=0)
	mov	a2,#12			;Vorgabe: f = 50000/2048*12 = 293 Hz
;ndone:	;mov	a,GpifTrig
	;jnb	ACC.7,ndone

	ST	IfConfig,11001110b	;48 MHz intern, ASYNC, Master-Modus
	mov	AutoPtrSetup,#7		;Autoptr aktivieren
	mov	dptr,#WaveDat
	mov	r2,#32
	call	SetWaveData

;alle Endpoints bis zur Konfiguration totlegen
	ST	EP1OutCfg,0
	ST	NEXT		;EP1INCFG
	ST	NEXT		;EP2CFG
	ST	NEXT		;EP4CFG
	ST	NEXT		;EP6CFG
	ST	NEXT		;EP8CFG
;ReNummerieren, um eigene bandbreiten-angepasste Beschreiber zu liefern
;Wieder-Aufzählung ist am vorhandenen String-Deskriptor detektierbar
	LD	USBCS
IF not always_renum
	jnb	ACC.7,nohs	;HSM-Bit (wenn schon mit High-Speed gestartet)
	setb	HighSpeed
	setb	Led2
	mov	r2,a
	STORE	WAVEDATA+1,04h	;Zeit ändern
	mov	a,r2
nohs:	jb	ACC.1,noren	;ReNum überspringen, falls vom EEPROM geladen
ENDIF
	setb	ACC.3		;DisCon
	ST
	setb	ACC.1		;ReNum
	ST
	mov	r5,#24		;1,5 Sekunden warten
	mov	r3,#0
w2:	call	w1		;verputzt R4=0
	djnz	r3,w2		;braucht 65 ms
	djnz	r5,w2
	xch	a,r4		;A=0
	dec	a		;A=0FFh
	ST	USBIRQ		;alle alten Interruptanforderungen löschen
	;ST	IBNIRQ
	;ST	NAKIRQ
	;ST	EPIRQ
	ST	GPIFIRQ
	ST	USBERRIRQ
	xch	a,r4
	clr	ACC.3		;DisCon
	ST	USBCS
noren:
	setb	Led		;LED einschalten
	setb	ADC_RESET	;ADU aktivieren
	;ST	GpifTcB3,0FFh	;GPIF "unendlich" arbeiten lassen
	mov	DPL1,#LOW(Sinustabelle)
	mov	DPH1,#HIGH(Sinustabelle)
	;Sinustabelle berechnen(?)
	;Timer0 aktivieren
	mov	TH0,#-80	;ergibt 4 MHz / 80 = 50 kHz (20 µs)
	mov	TMOD,#00000010b	;8-bit-Zähler mit Auto-Reload
	mov	IE,#10000010b	;globale und Timer0-Interrupt ein
	;setb	PT0		;Kommentar entfernen bei weiteren Interrupts!
	setb	TR0		;Timer starten
	
;===================
;== Hauptschleife ==
;===================
mainloop:
	LD	USBIRQ		;USB-Interrupts prüfen
	jz	nu4		;Kurzschluss-Sprung
	ST			;alle Anforderungen löschen
	mov	IntReq,a
	jnb	IntReq.0,nu0	;SUDAV-Anforderung?
	clr	a		;lang (Blinken mit 2 Hz)
	call	StartLed
	call	HandleSUD
	LD	EP0CS		;BUSY-Bit belassen
	mov	ACC.0,c		;Ep0Stall-Bit einsetzen
	setb	ACC.7		;HSNAK setzen
	ST
nu0:
	jnb	IntReq.1,nu1	;SOF-Anforderung?
	LD	USBFRAMEL
	CJE	a,FrameCnt,nu1	;Nur "ganze" Rahmen interessieren hier!
	mov	FrameCnt,a
	ST	GpifTcB3,0FFh	;GPIF "unendlich" arbeiten lassen
				;Zugriff auf dieses Reg. unzulässig?
	jnb	Led2Blink,n2
	djnz	Led2Time,n2
	cpl	Led2		;grüne LED umschalten
	clr	Led2Blink
n2:
	jnb	LedBlink,nu1	;LED blinkt?
	djnz	Blink,nbl
	cpl	Led
	mov	Blink,BlinkF
nbl:	djnz	LedTime,nu1
	setb	Led		;Nach paar Millisekunden bleibt das Licht an
	clr	LedBlink
nu1:
	jnb	IntReq.3,nu3	;Einschlaf-Anforderung?
	;Irgendwie kommt es beim Busaufzählen zu 2 Einschlaf-Aufforderungen,
	;die zum "Defekt" des USB-Gerätes führen.
	;Dazu muss SET CONFIURATION abgewartet werden!
	mov	a,Configuration
	jz	nu3
	clr	HighSpeed	;muss zu Full Speed zurückfallen
	clr	Led2
	mov	OEA,#0		;alles hochohmig und Energie sparend
	mov	PCON,#31h	;Tiefschlaf hier
	mov	OEA,#0FFh	;Tiefschlaf beendet
nu3:
	jnb	IntReq.4,nu4	;USB-Reset?
	;ohne Behandlung folgt Absturz beim Booten mit angestecktem Gerät!
	mov	Configuration,#0
	call	ResTog2		;etwa wie SetInterface (=SetAltSetting)
nu4:
	jnb	IntReq.5,nu5	;HSGrant
	setb	HighSpeed
	setb	Led2
	call	SetGpif
nu5:
	;LD	EPIRQ		geht nicht mit ISO!
	SyncDelay
	LD	GpifIrq
	SyncDelay
	ST			;alle Anforderungen löschen
	jnb	ACC.1,nu6	;GPIF: Aktivität?
	mov	a,#128
	call	StartLed	;LED schnell blinken lassen
nu6:
	LD	UsbErrIrq
	ST			;alle Anforderungen löschen
	jnb	ACC.0,nu7	;USB-Fehler am Limit?
	ST	ClrErrCnt	;Fehler-Zähler rücksetzen
	mov	Led2Time,#64	;Zeit verlängern
	jnb	Led2Blink,nu7	;wenn gesetzt, LED belassen
	setb	Led2Blink
	cpl	Led2		;Grüne LED zeigt Fehler kurz an
nu7:
	mov	a,EP24FifoFlgs
	rrc	a		;Voll-Flag
	jc	voll
	mov	WirklichVoll,#1	;umgehend ChipSelect aktivieren
voll:	djnz	WirklichVoll,nu8
	mov	ADC_CS,c	;A/D-Wandler deaktivieren wenn's voll bleibt
nu8:
	; mov	Led3,c		
	jmp	mainloop
	
	
;USB-Deskriptoren
ALIGN 2
;********************************************
;** Antwort auf Get Descriptor: Device (1) **
;********************************************
;Geräte-Beschreiber (gleich für beide Geschwindigkeiten)
DeviceDescriptor:
	db	18	;bLength
	db	1	;bDescriptorType, 1=DEVICE
	DWI	200h	;bcdUSB 2.0
	db	0	;bDeviceClass		Multifunktion
	db	0	;bDeviceSubClass
	db	0	;bDeviceProtocol
	db	64	;bMaxPacketSize0
	DWI	04B4h	;idVendor		Cypress
	DWI	8613h	;idProduct		FX2
	DWI	0004h	;bcdDevice		Rev. D
	db	1	;iManufacturer		(1)
	db	2	;iProduct		(2)
	db	0	;iSerialNumber		keine Seriennummer!
	db	1	;bNumConfigurations

;Geräte-Qualifizierer (gleich für beide Geschwindigkeiten)
DeviceQualifier:
	db	10	;bLength
	db	6	;bDescriptorType, 6=QUALIFIER
	DWI	200h	;bcdUSB 2.0
	db	0	;bDeviceClass		Multifunktion
	db	0	;bDeviceSubClass
	db	0	;bDeviceProtocol
	db	64	;bMaxPacketSize0
	db	1	;bNumConfigurations
	db	0	;bReserved

;********************************************
;** Antwort auf Get Descriptor: Config (2) **
;********************************************
ALIGN 2
ConfigDescFS:	;FullSpeed-Version
;Konfigurations-Beschreiber 0
	db	9	;bLength
	db	2	;bDescriptorType	2=CONFIG
	DWI	CfgEFS-ConfigDescFS	;wTotalLength
	db	1	;bNumInterfaces
	db	1	;bConfigurationValue (willkürliche Nummer dieser K.)
	db	3	;iConfiguration		(3)
	db	80h	;bmAttributes (Busversorgt, kein Aufwecken)
	db	150/2	;MaxPower (in 2 Milliampere), Problem am einfachen Hub
 ;Interface-Beschreiber 0, Alternative 0:	;ÄNDERUNG bei FX2-LP zweckmäßig
	db	9	;bLength
	db	4	;bDescriptorType	4=INTERFACE
	db	0	;bInterfaceNumber
	db	0	;bAlternateSetting
	db	1	;bNumEndpoints
	db	-1	;bInterfaceClass	hersteller-spezifisch
	db	-1	;bInterfaceSubClass
	db	-1	;bInterfaceProtocol
	db	4	;iInterface		(4)
  ;Enden-Beschreiber C0I0A0:Bulk EP2IN (schlechte FIFO-Nutzung)
	db	7	;bLength
	db	5	;bDescriptorType	5=ENDPOINT
	db	82h	;bEndpointAddress	EP2IN
	db	2	;bmAttributes		2=Bulk
	DWI	64	;wMaxPacketSize
	db	0	;bInterval		bedeutungslos bei Bulk IN
 ;Interface-Beschreiber 0, Alternative 1:
	db	9	;bLength
	db	4	;bDescriptorType	4=INTERFACE
	db	0	;bInterfaceNumber
	db	1	;bAlternateSetting
	db	1	;bNumEndpoints
	db	-1	;bInterfaceClass	hersteller-spezifisch
	db	-1	;bInterfaceSubClass
	db	-1	;bInterfaceProtocol
	db	5	;iInterface		(5)
  ;Enden-Beschreiber C0I0A1:ISO EP2IN
	db	7	;bLength
	db	5	;bDescriptorType	5=ENDPOINT
	db	82h	;bEndpointAddress	EP2IN
	db	1	;bmAttributes		1=ISO, ohne Sync, Daten
	DWI	1020	;wMaxPacketSize
	db	1	;bInterval		jeden Rahmen [2^(1-1)] = 1/ms
CfgEFS:

ALIGN 2
ConfigDescHS:	;HighSpeed-Version
;Konfigurations-Beschreiber 0
	db	9	;bLength
	db	2	;bDescriptorType	2=CONFIG
	DWI	CfgEHS-ConfigDescHS	;wTotalLength
	db	1	;bNumInterfaces
	db	1	;bConfigurationValue (willkürliche Nummer dieser K.)
	db	3	;iConfiguration		(3)
	db	80h	;bmAttributes (Busversorgt, kein Aufwecken)
	db	260/2	;MaxPower (in 2 Milliampere) 100 mA mehr als in FS
 ;Interface-Beschreiber 0, Alternative 0:	;ÄNDERUNG bei FX2-LP zweckmäßig
	db	9	;bLength
	db	4	;bDescriptorType	4=INTERFACE
	db	0	;bInterfaceNumber
	db	0	;bAlternateSetting
	db	1	;bNumEndpoints
	db	-1	;bInterfaceClass	hersteller-spezifisch
	db	-1	;bInterfaceSubClass
	db	-1	;bInterfaceProtocol
	db	4	;iInterface		(4)
  ;Enden-Beschreiber C0I0A0:Bulk EP2IN
	db	7	;bLength
	db	5	;bDescriptorType	5=ENDPOINT
	db	82h	;bEndpointAddress	EP2IN
	db	2	;bmAttributes		2=Bulk
	DWI	512	;wMaxPacketSize
	db	0	;bInterval		bedeutungslos bei Bulk IN
 ;Interface-Beschreiber 0, Alternative 1:
	db	9	;bLength
	db	4	;bDescriptorType	4=INTERFACE
	db	0	;bInterfaceNumber
	db	1	;bAlternateSetting
	db	1	;bNumEndpoints
	db	-1	;bInterfaceClass	hersteller-spezifisch
	db	-1	;bInterfaceSubClass
	db	-1	;bInterfaceProtocol
	db	5	;iInterface		(5)
  ;Enden-Beschreiber C0I0A1:ISO EP2IN
	db	7	;bLength
	db	5	;bDescriptorType	5=ENDPOINT
	db	82h	;bEndpointAddress	EP2IN
	db	1	;bmAttributes		1=ISO, ohne Sync, Daten
	DWI	512	;wMaxPacketSize
	db	1	;bInterval		jeden Mikrorahmen [2^(1-1)]
 ;Interface-Beschreiber 0, Alternative 2:
	db	9	;bLength
	db	4	;bDescriptorType	4=INTERFACE
	db	0	;bInterfaceNumber
	db	2	;bAlternateSetting
	db	1	;bNumEndpoints
	db	-1	;bInterfaceClass	hersteller-spezifisch
	db	-1	;bInterfaceSubClass
	db	-1	;bInterfaceProtocol
	db	6	;iInterface		(6)
  ;Enden-Beschreiber C0I0A2:INT EP2IN
	db	7	;bLength
	db	5	;bDescriptorType	5=ENDPOINT
	db	82h	;bEndpointAddress	EP2IN
	db	3	;bmAttributes		3=INT
	DWI	512	;wMaxPacketSize
	db	1	;bInterval		jeden Mikrorahmen [2^(1-1)]
CfgEHS:

;***=======******************************************
;** Etwaige Antwort auf Get Descriptor: String (3) **
;***=======******************************************
StringDescriptors:
;Wer Umlaute oder Chinesisch mag, muss für die folgenden Strings einen Editor
;benutzen, welcher entweder ISO-Latin1 oder UTF-8 speichert!
;(ASEM51 frisst aber keine Umlaute! Bug!)
	db	0D0h,087h,0		;Deutsch (0407h in UTF8)
	db	"Thomas Koch @ cypress.com",0
	db	"ADC2USB - Schnelle A/D-Wandler am USB",0
	db	"Busversorgt, kein Aufwecken",0
	db	"Interface ohne Bandbreite, nur Bulk",0
	db	"Interface mit ISO-Bandbreite",0
	db	"Interface mit Interrupt-Transfer, nur USB 2.0",0

ALIGN 256

StringPuffer: ds 100h	;Dieser Puffer darf Seitengrenze nicht überlappen!

ORG 1000h

SinusTabelle:
	$include(sintab.i51)
END
Detected encoding: UTF-80