From 39a0d9019076a29a57a38a817b1830aef0708c11 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 14:26:09 +0200 Subject: Adding upstream version 20230210. Signed-off-by: Daniel Baumann --- usbdux/Makefile_dux | 18 + usbdux/fx2-include.asm | 164 +++++ usbdux/usbdux_firmware.asm | 1184 +++++++++++++++++++++++++++++++++ usbdux/usbduxfast_firmware.asm | 547 +++++++++++++++ usbdux/usbduxsigma_firmware.asm | 1394 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 3307 insertions(+) create mode 100644 usbdux/Makefile_dux create mode 100644 usbdux/fx2-include.asm create mode 100644 usbdux/usbdux_firmware.asm create mode 100644 usbdux/usbduxfast_firmware.asm create mode 100644 usbdux/usbduxsigma_firmware.asm (limited to 'usbdux') diff --git a/usbdux/Makefile_dux b/usbdux/Makefile_dux new file mode 100644 index 0000000..16c1c73 --- /dev/null +++ b/usbdux/Makefile_dux @@ -0,0 +1,18 @@ +# (c) Bernd Porr +# GNU public license +# no warranty +# + +all: usbduxfast_firmware.bin usbdux_firmware.bin usbduxsigma_firmware.bin + +usbduxfast_firmware.bin: fx2-include.asm usbduxfast_firmware.asm + as31 -Fbin usbduxfast_firmware.asm + +usbdux_firmware.bin: fx2-include.asm usbdux_firmware.asm + as31 -Fbin usbdux_firmware.asm + +usbduxsigma_firmware.bin: fx2-include.asm usbduxsigma_firmware.asm + as31 -Fbin usbduxsigma_firmware.asm + +clean: + rm -f *.bin *~ *.lst *.bin diff --git a/usbdux/fx2-include.asm b/usbdux/fx2-include.asm new file mode 100644 index 0000000..9877996 --- /dev/null +++ b/usbdux/fx2-include.asm @@ -0,0 +1,164 @@ +; rev 0.91 +; (c) Bernd Porr, BerndPorr@f2s.com +; GPL, GNU public license +; +; This program is free software; you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation; either version 2 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, write to the Free Software +; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +; +; In conjunction with the as31. +; Include-file for the FX2 by Cypress. The rest of the regs is defined +; by the as31 itself. +; +; from the TRM of the FX2: +; + ;; CPU control + .equ CPUCS,0E600H + .equ REVCTL,0E60BH + ;; interface config + .equ IFCONFIG,0E601H + .equ FIFORESET,0E604H + ;; Endpoint configs + .equ EP1OUTCFG,0E610H + .equ EP1INCFG,0E611H + .equ EP2CFG,0E612H + .equ EP4CFG,0E613H + .equ EP6CFG,0E614H + .equ EP8CFG,0E615H + ;; packets per frame, always one for USB 1.1 + .equ EP2ISOINPKTS,0E640H + .equ EP4ISOINPKTS,0E641H + .equ EP6ISOINPKTS,0E642H + .equ EP8ISOINPKTS,0E643H + ;; endpoint byte counts + .equ EP1OUTBC,0E68DH + .equ EP2BCH,0E690H + .equ EP2BCL,0E691H + .equ EP4BCH,0E694H + .equ EP4BCL,0E695H + .equ EP6BCH,0E698H + .equ EP6BCL,0E699H + .equ EP8BCH,0E69CH + .equ EP8BCL,0E69DH + ;; + .equ EP4AUTOINLENH,0E622H + .equ EP4AUTOINLENL,0E623H + .equ EP6AUTOINLENH,0E624H + .equ EP6AUTOINLENL,0E625H + .equ EP2FIFOCFG,0E618H + .equ EP4FIFOCFG,0E619H + .equ EP6FIFOCFG,0E61AH + .equ EP8FIFOCFG,0E61BH + ;; + .equ INPKTEND,0E648H + .equ OUTPKTEND,0E649H + .equ GPIFCTLCFG,0E6C3H + .equ GPIFABORT,0E6F5H + .equ GPIFIDLECTL,0E6C2H + .equ GPIFWFSELECT,0E6C0H + .equ GPIFREADYCFG,0E6F3H + .equ GPIFIDLECS,0E6C1H + .equ EP6GPIFFLGSEL,0E6E2H + .equ EP6GPIFPDFSTOP,0E6E3H + .equ EP6GPIFTRIG,0E6E4H + .equ GPIFTCB3,0E6CEH + .equ GPIFTCB2,0E6CFH + .equ GPIFTCB1,0E6D0H + .equ GPIFTCB0,0E6D1H + .equ EP4GPIFFLGSEL,0E6DAH + .equ EP4GPIFPFSTOP,0E6DBH + ;; + ;; endpoint control + .equ EP2CS,0E6A3H + .equ EP4CS,0E6A4H + .equ EP6CS,0E6A5H + .equ EP8CS,0E6A6H + ;; endpoint buffers + .equ EP2FIFOBUF,0F000H + .equ EP4FIFOBUF,0F400H + .equ EP6FIFOBUF,0F800H + .equ EP8FIFOBUF,0FC00H + ;; IRQ enable for bulk NAK + .equ IBNIE,0E658H + ;; interrupt requ for NAK + .equ IBNIRQ,0E659H + ;; USB INT enables + .equ USBIE,0E65CH + ;; USB interrupt request + .equ USBIRQ,0E65DH + ;; endpoint IRQ enable + .equ EPIE,0E65EH + ;; endpoint IRQ requests + .equ EPIRQ,0E65FH + ;; USB error IRQ requests + .equ USBERRIE,0E662H + ;; USB error IRQ request + .equ USBERRIRQ,0E663H + ;; USB interrupt 2 autovector + .equ INT2IVEC,0E666H + ;; autovector enable + .equ INTSETUP,0E668H + ;; port cfg + .equ PORTACFG,0E670H + .equ PORTCCFG,0E671H + .equ PORTECFG,0E672H + ;; I2C bus + .equ I2CS,0E678H + .equ I2DAT,0E679H + .equ I2CTL,0E67AH + ;; auto pointers, read/write is directed to the pointed address + .equ XAUTODAT1,0E67BH + .equ XAUTODAT2,0E67CH + ;; USB-control + .equ USBCS,0E680H + + .equ IOA,80H + .equ DPL0,82H + .equ DPH0,83H + .equ DPL1,84H + .equ DPH1,85H + .equ DPS,86H + .equ CKCON,8Eh + .equ IOB,90H + .equ EXIF,91h + .equ MPAGE,92h + .equ AUTOPTRH1,9AH + .equ AUTOPTRL1,9BH + .equ AUTOPTRH2,9DH + .equ AUTOPTRL2,9EH + .equ IOC,0A0H + .equ INT2CLR,0A1H + .equ INT4CLR,0A2H + .equ EP2468STAT,0AAH + .equ EP24FIFOFLGS,0ABH + .equ EP68FIFOFLGS,0ACH + .equ AUTOPTRSETUP,0AFH + .equ IOD,0B0H + .equ IOE,0B1H + .equ OEA,0B2H + .equ OEB,0B3H + .equ OEC,0B4H + .equ OED,0B5H + .equ OEE,0B6H + .equ GPIFTRIG,0BBH + .equ EIE,0E8h + .equ EIP,0F8h + .equ GPIFIE,0E660H + +;;; serial control + .equ SCON0,098h + .equ SBUF0,099h + + ;;; end of file + + diff --git a/usbdux/usbdux_firmware.asm b/usbdux/usbdux_firmware.asm new file mode 100644 index 0000000..b93a895 --- /dev/null +++ b/usbdux/usbdux_firmware.asm @@ -0,0 +1,1184 @@ +; usbdux_firmware.asm +; Copyright (C) 2004,2009 Bernd Porr, Bernd.Porr@f2s.com +; For usbdux.c +; +; This program is free software; you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation; either version 2 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, write to the Free Software +; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +; +; +; Firmware: usbdux_firmware.asm for usbdux.c +; Description: University of Stirling USB DAQ & INCITE Technology Limited +; Devices: [ITL] USB-DUX (usbdux.o) +; Author: Bernd Porr +; Updated: 17 Apr 2009 +; Status: stable +; +;;; +;;; +;;; + + .inc fx2-include.asm + + .equ CHANNELLIST,80h ; channellist in indirect memory + + .equ CMD_FLAG,90h ; flag if next IN transf is DIO + .equ SGLCHANNEL,91h ; channel for INSN + .equ PWMFLAG,92h ; PWM + + .equ DIOSTAT0,98h ; last status of the digital port + .equ DIOSTAT1,99h ; same for the second counter + + .equ CTR0,0A0H ; counter 0 + .equ CTR1,0A2H ; counter 1 + + .org 0000h ; after reset the processor starts here + ljmp main ; jump to the main loop + + .org 000bh ; timer 0 irq + ljmp timer0_isr + + .org 0043h ; the IRQ2-vector + ljmp jmptbl ; irq service-routine + + .org 0100h ; start of the jump table + +jmptbl: ljmp sudav_isr + nop + ljmp sof_isr + nop + ljmp sutok_isr + nop + ljmp suspend_isr + nop + ljmp usbreset_isr + nop + ljmp hispeed_isr + nop + ljmp ep0ack_isr + nop + ljmp spare_isr + nop + ljmp ep0in_isr + nop + ljmp ep0out_isr + nop + ljmp ep1in_isr + nop + ljmp ep1out_isr + nop + ljmp ep2_isr + nop + ljmp ep4_isr + nop + ljmp ep6_isr + nop + ljmp ep8_isr + nop + ljmp ibn_isr + nop + ljmp spare_isr + nop + ljmp ep0ping_isr + nop + ljmp ep1ping_isr + nop + ljmp ep2ping_isr + nop + ljmp ep4ping_isr + nop + ljmp ep6ping_isr + nop + ljmp ep8ping_isr + nop + ljmp errlimit_isr + nop + ljmp spare_isr + nop + ljmp spare_isr + nop + ljmp spare_isr + nop + ljmp ep2isoerr_isr + nop + ljmp ep4isoerr_isr + nop + ljmp ep6isoerr_isr + nop + ljmp ep8isoerr_isr + + + ;; dummy isr +sudav_isr: +sutok_isr: +suspend_isr: +usbreset_isr: +hispeed_isr: +ep0ack_isr: +spare_isr: +ep0in_isr: +ep0out_isr: +ep1in_isr: +ibn_isr: +ep0ping_isr: +ep1ping_isr: +ep2ping_isr: +ep4ping_isr: +ep6ping_isr: +ep8ping_isr: +errlimit_isr: +ep2isoerr_isr: +ep4isoerr_isr: +ep6isoerr_isr: +ep8isoerr_isr: +ep6_isr: +ep2_isr: +ep4_isr: + + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + push psw + + ;; clear the USB2 irq bit and return + mov a,EXIF + clr acc.4 + mov EXIF,a + + pop psw + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + + reti + + +;;; main program +;;; basically only initialises the processor and +;;; then engages in an endless loop +main: + mov DPTR,#CPUCS ; CPU control register + mov a,#00010000b ; 48Mhz + lcall syncdelaywr + + mov dptr,#REVCTL + mov a,#00000011b ; allows skip + lcall syncdelaywr + + mov IP,#0 ; all std 8051 int have low priority + mov EIP,#0FFH ; all FX2 interrupts have high priority + + mov dptr,#INTSETUP ; IRQ setup register + mov a,#08h ; enable autovector + lcall syncdelaywr + + lcall initAD ; init the ports to the converters + + lcall initeps ; init the isochronous data-transfer + + lcall init_timer + +mloop2: nop + +;;; pwm + mov r0,#PWMFLAG ; pwm on? + mov a,@r0 ; get info + jz mloop2 ; it's off + + mov a,GPIFTRIG ; GPIF status + anl a,#80h ; done bit + jz mloop2 ; GPIF still busy + + mov a,#01h ; WR,EP4, 01 = EP4 + mov GPIFTRIG,a ; restart it + + sjmp mloop2 ; loop for ever + + +;;; GPIF waveform for PWM +waveform: + ;; 0 1 2 3 4 5 6 7(not used) + ;; len (gives 50.007Hz) + .db 195, 195, 195, 195, 195, 195, 1, 1 + + ;; opcode + .db 002H, 006H, 002H, 002H, 002H, 002H, 002H, 002H + + ;; out + .db 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH + + ;; log + .db 000H, 000H, 000H, 000H, 000H, 000H, 000H, 000H + + +stopPWM: + mov r0,#PWMFLAG ; flag for PWM + mov a,#0 ; PWM (for the main loop) + mov @r0,a ; set it + + mov dptr,#IFCONFIG ; switch off GPIF + mov a,#10000000b ; gpif, 30MHz, internal IFCLK + lcall syncdelaywr + ret + + +;;; init PWM +startPWM: + mov dptr,#IFCONFIG ; switch on IFCLK signal + mov a,#10000010b ; gpif, 30MHz, internal IFCLK + lcall syncdelaywr + + mov OEB,0FFH ; output to port B + + mov DPTR,#EP4CFG + mov a,#10100000b ; valid, out, bulk + movx @DPTR,a + + ;; reset the endpoint + mov dptr,#FIFORESET + mov a,#80h ; NAK + lcall syncdelaywr + mov a,#84h ; reset EP4 + NAK + lcall syncdelaywr + mov a,#0 ; normal op + lcall syncdelaywr + + mov dptr,#EP4BCL + mov a,#0H ; discard packets + lcall syncdelaywr ; empty FIFO buffer + lcall syncdelaywr ; empty FIFO buffer + + ;; aborts all transfers by the GPIF + mov dptr,#GPIFABORT + mov a,#0ffh ; abort all transfers + lcall syncdelaywr + + ;; wait for GPIF to finish +wait_f_abort: + mov a,GPIFTRIG ; GPIF status + anl a,#80h ; done bit + jz wait_f_abort ; GPIF busy + + mov dptr,#GPIFCTLCFG + mov a,#10000000b ; tri state for CTRL + lcall syncdelaywr + + mov dptr,#GPIFIDLECTL + mov a,#11110000b ; all CTL outputs low + lcall syncdelaywr + + ;; abort if FIFO is empty + mov a,#00000001b ; abort if empty + mov dptr,#EP4GPIFFLGSEL + lcall syncdelaywr + + ;; + mov a,#00000001b ; stop if GPIF flg + mov dptr,#EP4GPIFPFSTOP + lcall syncdelaywr + + ;; transaction counter + mov a,#0ffH + mov dptr,#GPIFTCB3 + lcall syncdelaywr + + ;; transaction counter + mov a,#0ffH + mov dptr,#GPIFTCB2 + lcall syncdelaywr + + ;; transaction counter + mov a,#0ffH ; 512 bytes + mov dptr,#GPIFTCB1 + lcall syncdelaywr + + ;; transaction counter + mov a,#0ffH + mov dptr,#GPIFTCB0 + lcall syncdelaywr + + ;; RDY pins. Not used here. + mov a,#0 + mov dptr,#GPIFREADYCFG + lcall syncdelaywr + + ;; drives the output in the IDLE state + mov a,#1 + mov dptr,#GPIFIDLECS + lcall syncdelaywr + + ;; direct data transfer from the EP to the GPIF + mov dptr,#EP4FIFOCFG + mov a,#00010000b ; autoout=1, byte-wide + lcall syncdelaywr + + ;; waveform 0 is used for FIFO out + mov dptr,#GPIFWFSELECT + mov a,#00000000b + movx @dptr,a + lcall syncdelay + + ;; transfer the delay byte from the EP to the waveform + mov dptr,#0e781h ; EP1 buffer + movx a,@dptr ; get the delay + mov dptr,#waveform ; points to the waveform + mov r2,#6 ; fill 6 bytes +timloop: + movx @dptr,a ; save timing in a xxx + inc dptr + djnz r2,timloop ; fill the 6 delay bytes + + ;; load waveform + mov AUTOPTRH2,#0E4H ; XDATA0H + lcall syncdelay + mov AUTOPTRL2,#00H ; XDATA0L + lcall syncdelay + + mov dptr,#waveform ; points to the waveform + + mov AUTOPTRSETUP,#7 ; autoinc and enable + lcall syncdelay + + mov r2,#20H ; 32 bytes to transfer + +wavetr: + movx a,@dptr + inc dptr + push dpl + push dph + push dpl1 + push dph1 + mov dptr,#XAUTODAT2 + movx @dptr,a + lcall syncdelay + pop dph1 + pop dpl1 + pop dph + pop dpl + djnz r2,wavetr + + mov dptr,#OUTPKTEND + mov a,#084H + lcall syncdelaywr + lcall syncdelaywr + + mov r0,#PWMFLAG ; flag for PWM + mov a,#1 ; PWM (for the main loop) + mov @r0,a ; set it + + ret + + + +;;; initialise the ports for the AD-converter +initAD: + mov OEA,#27H ;PortA0,A1,A2,A5 Outputs + mov IOA,#22H ;/CS = 1, disable transfers to the converters + ret + + +;;; init the timer for the soft counters +init_timer: + ;; init the timer for 2ms sampling rate + mov CKCON,#00000001b; CLKOUT/12 for timer + mov TL0,#010H ; 16 + mov TH0,#0H ; 256 + mov IE,#82H ; switch on timer interrupt (80H for all IRQs) + mov TMOD,#00000000b ; 13 bit counters + setb TCON.4 ; enable timer 0 + ret + + +;;; from here it's only IRQ handling... + +;;; A/D-conversion: +;;; control-byte in a, +;;; result in r3(low) and r4(high) +;;; this routine is optimised for speed +readAD: ; mask the control byte + anl a,#01111100b ; only the channel, gain+pol are left + orl a,#10000001b ; start bit, external clock + ;; set CS to low + clr IOA.1 ; set /CS to zero + ;; send the control byte to the AD-converter + mov R2,#8 ; bit-counter +bitlp: jnb ACC.7,bitzero ; jump if Bit7 = 0? + setb IOA.2 ; set the DIN bit + sjmp clock ; continue with the clock +bitzero:clr IOA.2 ; clear the DIN bit +clock: setb IOA.0 ; SCLK = 1 + clr IOA.0 ; SCLK = 0 + rl a ; next Bit + djnz R2,bitlp + + ;; continue the aquisition (already started) + clr IOA.2 ; clear the DIN bit + mov R2,#5 ; five steps for the aquision +clockaq:setb IOA.0 ; SCLK = 1 + clr IOA.0 ; SCLK = 0 + djnz R2,clockaq ; loop + + ;; read highbyte from the A/D-converter + ;; and do the conversion + mov r4,#0 ; Highbyte goes into R4 + mov R2,#4 ; COUNTER 4 data bits in the MSB + mov r5,#08h ; create bit-mask +gethi: ; loop get the 8 highest bits from MSB downw + setb IOA.0 ; SCLK = 1 + clr IOA.0 ; SCLK = 0 + mov a,IOA ; from port A + jnb ACC.4,zerob ; the in-bit is zero + mov a,r4 ; get the byte + orl a,r5 ; or the bit to the result + mov r4,a ; save it again in r4 +zerob: mov a,r5 ; get r5 in order to shift the mask + rr a ; rotate right + mov r5,a ; back to r5 + djnz R2,gethi + ;; read the lowbyte from the A/D-converter + mov r3,#0 ; Lowbyte goes into R3 + mov r2,#8 ; COUNTER 8 data-bits in the LSB + mov r5,#80h ; create bit-mask +getlo: ; loop get the 8 highest bits from MSB downw + setb IOA.0 ; SCLK = 1 + clr IOA.0 ; SCLK = 0 + mov a,IOA ; from port A + jnb ACC.4,zerob2 ; the in-bit is zero + mov a,r3 ; get the result-byte + orl a,r5 ; or the bit to the result + mov r3,a ; save it again in r4 +zerob2: mov a,r5 ; get r5 in order to shift the mask + rr a ; rotate right + mov r5,a ; back to r5 + djnz R2,getlo + setb IOA.1 ; set /CS to one + ;; + ret + + + +;;; aquires data from A/D channels and stores them in the EP6 buffer +conv_ad: + mov AUTOPTRH1,#0F8H ; auto pointer on EP6 + mov AUTOPTRL1,#00H + mov AUTOPTRSETUP,#7 + mov r0,#CHANNELLIST ; points to the channellist + + mov a,@r0 ; number of channels + mov r1,a ; counter + + mov DPTR,#XAUTODAT1 ; auto pointer +convloop: + inc r0 + mov a,@r0 ; Channel + lcall readAD + mov a,R3 ; + movx @DPTR,A + mov a,R4 ; + movx @DPTR,A + djnz r1,convloop + + ret + + + + +;;; initilise the transfer +;;; It is assumed that the USB interface is in alternate setting 3 +initeps: + mov dptr,#FIFORESET + mov a,#80H + movx @dptr,a ; reset all fifos + mov a,#2 + movx @dptr,a ; + mov a,#4 + movx @dptr,a ; + mov a,#6 + movx @dptr,a ; + mov a,#8 + movx @dptr,a ; + mov a,#0 + movx @dptr,a ; normal operat + + mov DPTR,#EP2CFG + mov a,#10010010b ; valid, out, double buff, iso + movx @DPTR,a + + mov dptr,#EP2FIFOCFG + mov a,#00000000b ; manual + movx @dptr,a + + mov dptr,#EP2BCL ; "arm" it + mov a,#00h + movx @DPTR,a ; can receive data + lcall syncdelay ; wait to sync + movx @DPTR,a ; can receive data + lcall syncdelay ; wait to sync + movx @DPTR,a ; can receive data + lcall syncdelay ; wait to sync + + mov DPTR,#EP1OUTCFG + mov a,#10100000b ; valid + movx @dptr,a + + mov dptr,#EP1OUTBC ; "arm" it + mov a,#00h + movx @DPTR,a ; can receive data + lcall syncdelay ; wait until we can write again + movx @dptr,a ; make shure its really empty + lcall syncdelay ; wait + + mov DPTR,#EP6CFG ; ISO data from here to the host + mov a,#11010010b ; Valid + movx @DPTR,a ; ISO transfer, double buffering + + mov DPTR,#EP8CFG ; EP8 + mov a,#11100000b ; BULK data from here to the host + movx @DPTR,a ; + + mov dptr,#EPIE ; interrupt enable + mov a,#10001000b ; enable irq for ep1out,8 + movx @dptr,a ; do it + + mov dptr,#EPIRQ ; clear IRQs + mov a,#10100000b + movx @dptr,a + + ;; enable interrups + mov DPTR,#USBIE ; USB int enables register + mov a,#2 ; enables SOF (1ms/125us interrupt) + movx @DPTR,a ; + + mov EIE,#00000001b ; enable INT2 in the 8051's SFR + mov IE,#80h ; IE, enable all interrupts + + ret + + +;;; counter +;;; r0: DIOSTAT +;;; r1: counter address +;;; r2: up/down-mask +;;; r3: reset-mask +;;; r4: clock-mask +counter: + mov a,IOB ; actual IOB input state + mov r5,a ; save in r5 + anl a,r3 ; bit mask for reset + jz no_reset ; reset if one + clr a ; set counter to zero + mov @r1,a + inc r4 + mov @r1,a + sjmp ctr_end +no_reset: + mov a,@r0 ; get last state + xrl a,r5 ; has it changed? + anl a,r5 ; is it now on? + anl a,r4 ; mask out the port + jz ctr_end ; no rising edge + mov a,r5 ; get port B again + anl a,r2 ; test if up or down + jnz ctr_up ; count up + mov a,@r1 + dec a + mov @r1,a + cjne a,#0ffh,ctr_end ; underflow? + inc r1 ; high byte + mov a,@r1 + dec a + mov @r1,a + sjmp ctr_end +ctr_up: ; count up + mov a,@r1 + inc a + mov @r1,a + jnz ctr_end + inc r1 ; high byte + mov a,@r1 + inc a + mov @r1,a +ctr_end: + mov a,r5 + mov @r0,a + ret + +;;; implements two soft counters with up/down and reset +timer0_isr: + push dps + push acc + push psw + push 00h ; R0 + push 01h ; R1 + push 02h ; R2 + push 03h ; R3 + push 04h ; R4 + push 05h ; R5 + + mov r0,#DIOSTAT0 ; status of port + mov r1,#CTR0 ; address of counter0 + mov a,#00000001b ; bit 0 + mov r4,a ; clock + rl a ; bit 1 + mov r2,a ; up/down + rl a ; bit 2 + mov r3,a ; reset mask + lcall counter + inc r0 ; to DISTAT1 + inc r1 ; to CTR1 + inc r1 + mov a,r3 + rl a ; bit 3 + rl a ; bit 4 + mov r4,a ; clock + rl a ; bit 5 + mov r2,a ; up/down + rl a ; bit 6 + mov r3,a ; reset + lcall counter + + pop 05h ; R5 + pop 04h ; R4 + pop 03h ; R3 + pop 02h ; R2 + pop 01h ; R1 + pop 00h ; R0 + pop psw + pop acc + pop dps + + reti + +;;; interrupt-routine for SOF +;;; is for full speed +sof_isr: + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + push psw + push 00h ; R0 + push 01h ; R1 + push 02h ; R2 + push 03h ; R3 + push 04h ; R4 + push 05h ; R5 + push 06h ; R6 + push 07h ; R7 + + mov a,EP2468STAT + anl a,#20H ; full? + jnz epfull ; EP6-buffer is full + + lcall conv_ad ; conversion + + mov DPTR,#EP6BCH ; byte count H + mov a,#0 ; is zero + lcall syncdelaywr ; wait until we can write again + + mov DPTR,#EP6BCL ; byte count L + mov a,#10H ; is 8x word = 16 bytes + lcall syncdelaywr ; wait until we can write again + +epfull: + ;; do the D/A conversion + mov a,EP2468STAT + anl a,#01H ; empty + jnz epempty ; nothing to get + + mov dptr,#0F000H ; EP2 fifo buffer + lcall dalo ; conversion + + mov dptr,#EP2BCL ; "arm" it + mov a,#00h + lcall syncdelaywr ; wait for the rec to sync + lcall syncdelaywr ; wait for the rec to sync + +epempty: + ;; clear INT2 + mov a,EXIF ; FIRST clear the USB (INT2) interrupt request + clr acc.4 + mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable + + mov DPTR,#USBIRQ ; points to the SOF + mov a,#2 ; clear the SOF + movx @DPTR,a + +nosof: + pop 07h + pop 06h + pop 05h + pop 04h ; R4 + pop 03h ; R3 + pop 02h ; R2 + pop 01h ; R1 + pop 00h ; R0 + pop psw + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + reti + + +reset_ep8: + ;; erase all data in ep8 + mov dptr,#FIFORESET + mov a,#80H ; NAK + lcall syncdelaywr + mov dptr,#FIFORESET + mov a,#8 ; reset EP8 + lcall syncdelaywr + mov dptr,#FIFORESET + mov a,#0 ; normal operation + lcall syncdelaywr + ret + + +reset_ep6: + ;; throw out old data + mov dptr,#FIFORESET + mov a,#80H ; NAK + lcall syncdelaywr + mov dptr,#FIFORESET + mov a,#6 ; reset EP6 + lcall syncdelaywr + mov dptr,#FIFORESET + mov a,#0 ; normal operation + lcall syncdelaywr + ret + +;;; interrupt-routine for ep1out +;;; receives the channel list and other commands +ep1out_isr: + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + push psw + push 00h ; R0 + push 01h ; R1 + push 02h ; R2 + push 03h ; R3 + push 04h ; R4 + push 05h ; R5 + push 06h ; R6 + push 07h ; R7 + + mov dptr,#0E780h ; FIFO buffer of EP1OUT + movx a,@dptr ; get the first byte + mov r0,#CMD_FLAG ; pointer to the command byte + mov @r0,a ; store the command byte for ep8 + + mov dptr,#ep1out_jmp; jump table for the different functions + rl a ; multiply by 2: sizeof sjmp + jmp @a+dptr ; jump to the jump table + ;; jump table, corresponds to the command bytes defined + ;; in usbdux.c +ep1out_jmp: + sjmp storechannellist; a=0 + sjmp single_da ; a=1 + sjmp config_digital_b; a=2 + sjmp write_digital_b ; a=3 + sjmp storesglchannel ; a=4 + sjmp readcounter ; a=5 + sjmp writecounter ; a=6 + sjmp pwm_on ; a=7 + sjmp pwm_off ; a=8 + +pwm_on: + lcall startPWM + sjmp over_da + +pwm_off: + lcall stopPWM + sjmp over_da + + ;; read the counter +readcounter: + lcall reset_ep8 ; reset ep8 + lcall ep8_ops ; fill the counter data in there + sjmp over_da ; jump to the end + + ;; write zeroes to the counters +writecounter: + mov dptr,#0e781h ; buffer + mov r0,#CTR0 ; r0 points to counter 0 + movx a,@dptr ; channel number + jz wrctr0 ; first channel + mov r1,a ; counter +wrctrl: + inc r0 ; next counter + inc r0 ; next counter + djnz r1,wrctrl ; advance to the right counter +wrctr0: + inc dptr ; get to the value + movx a,@dptr ; get value + mov @r0,a ; save in ctr + inc r0 ; next byte + inc dptr + movx a,@dptr ; get value + mov @r0,a ; save in ctr + sjmp over_da ; jump to the end + +storesglchannel: + mov r0,#SGLCHANNEL ; the conversion bytes are now stored in 80h + mov dptr,#0e781h ; FIFO buffer of EP1OUT + movx a,@dptr ; + mov @r0,a + + lcall reset_ep8 ; reset FIFO + ;; Save new A/D data in EP8. This is the first byte + ;; the host will read during an INSN. If there are + ;; more to come they will be handled by the ISR of + ;; ep8. + lcall ep8_ops ; get A/D data + + sjmp over_da + + +;;; Channellist: +;;; the first byte is zero: +;;; we've just received the channel list +;;; the channel list is stored in the addresses from CHANNELLIST which +;;; are _only_ reachable by indirect addressing +storechannellist: + mov r0,#CHANNELLIST ; the conversion bytes are now stored in 80h + mov r2,#9 ; counter + mov dptr,#0e781h ; FIFO buffer of EP1OUT +chanlloop: + movx a,@dptr ; + mov @r0,a + inc dptr + inc r0 + djnz r2,chanlloop + + lcall reset_ep6 ; reset FIFO + + ;; load new A/D data into EP6 + ;; This must be done. Otherwise the ISR is never called. + ;; The ISR is only called when data has _left_ the + ;; ep buffer here it has to be refilled. + lcall ep6_arm ; fill with the first data byte + + sjmp over_da + +;;; Single DA conversion. The 2 bytes are in the FIFO buffer +single_da: + mov dptr,#0e781h ; FIFO buffer of EP1OUT + lcall dalo ; conversion + sjmp over_da + +;;; configure the port B as input or output (bitwise) +config_digital_b: + mov dptr,#0e781h ; FIFO buffer of EP1OUT + movx a,@dptr ; get the second byte + mov OEB,a ; set the output enable bits + sjmp over_da + +;;; Write one byte to the external digital port B +;;; and prepare for digital read +write_digital_b: + mov dptr,#0e781h ; FIFO buffer of EP1OUT + movx a,@dptr ; get the second byte + mov OEB,a ; output enable + inc dptr ; next byte + movx a,@dptr ; bits + mov IOB,a ; send the byte to the I/O port + + lcall reset_ep8 ; reset FIFO of ep 8 + + ;; fill ep8 with new data from port B + ;; When the host requests the data it's already there. + ;; This must be so. Otherwise the ISR is not called. + ;; The ISR is only called when a packet has been delivered + ;; to the host. Thus, we need a packet here in the + ;; first instance. + lcall ep8_ops ; get digital data + + ;; + ;; for all commands the same +over_da: + mov dptr,#EP1OUTBC + mov a,#00h + lcall syncdelaywr ; arm + lcall syncdelaywr ; arm + lcall syncdelaywr ; arm + + ;; clear INT2 + mov a,EXIF ; FIRST clear the USB (INT2) interrupt request + clr acc.4 + mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable + + mov DPTR,#EPIRQ ; + mov a,#00001000b ; clear the ep1outirq + movx @DPTR,a + + pop 07h + pop 06h + pop 05h + pop 04h ; R4 + pop 03h ; R3 + pop 02h ; R2 + pop 01h ; R1 + pop 00h ; R0 + pop psw + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + reti + + + +;;; all channels +dalo: + movx a,@dptr ; number of channels + inc dptr ; pointer to the first channel + mov r0,a ; 4 channels +nextDA: + movx a,@dptr ; get the first low byte + mov r3,a ; store in r3 (see below) + inc dptr ; point to the high byte + movx a,@dptr ; get the high byte + mov r4,a ; store in r4 (for writeDA) + inc dptr ; point to the channel number + movx a,@dptr ; get the channel number + inc dptr ; get ready for the next channel + lcall writeDA ; write value to the DAC + djnz r0,nextDA ; next channel + ret + + + +;;; D/A-conversion: +;;; control-byte in a, +;;; value in r3(low) and r4(high) +writeDA: ; mask the control byte + anl a,#11000000b ; only the channel is left + orl a,#00110000b ; internal clock, bipolar mode, +/-5V + orl a,r4 ; or the value of R4 to it + ;; set CS to low + clr IOA.5 ; set /CS to zero + ;; send the first byte to the DA-converter + mov R2,#8 ; bit-counter +DA1: jnb ACC.7,zeroda ; jump if Bit7 = 0? + setb IOA.2 ; set the DIN bit + sjmp clkda ; continue with the clock +zeroda: clr IOA.2 ; clear the DIN bit +clkda: setb IOA.0 ; SCLK = 1 + clr IOA.0 ; SCLK = 0 + rl a ; next Bit + djnz R2,DA1 + + + ;; send the second byte to the DA-converter + mov a,r3 ; low byte + mov R2,#8 ; bit-counter +DA2: jnb ACC.7,zeroda2 ; jump if Bit7 = 0? + setb IOA.2 ; set the DIN bit + sjmp clkda2 ; continue with the clock +zeroda2:clr IOA.2 ; clear the DIN bit +clkda2: setb IOA.0 ; SCLK = 1 + clr IOA.0 ; SCLK = 0 + rl a ; next Bit + djnz R2,DA2 + ;; + setb IOA.5 ; set /CS to one + ;; +noDA: ret + + + +;;; arm ep6 +ep6_arm: + lcall conv_ad + + mov DPTR,#EP6BCH ; byte count H + mov a,#0 ; is zero + lcall syncdelaywr ; wait until the length has arrived + + mov DPTR,#EP6BCL ; byte count L + mov a,#10H ; is one + lcall syncdelaywr ; wait until the length has been proc + ret + + + +;;; converts one analog/digital channel and stores it in EP8 +;;; also gets the content of the digital ports B and D depending on +;;; the COMMAND flag +ep8_ops: + mov dptr,#0fc01h ; ep8 fifo buffer + clr a ; high byte + movx @dptr,a ; set H=0 + mov dptr,#0fc00h ; low byte + mov r0,#CMD_FLAG + mov a,@r0 + movx @dptr,a ; save command byte + + mov dptr,#ep8_jmp ; jump table for the different functions + rl a ; multiply by 2: sizeof sjmp + jmp @a+dptr ; jump to the jump table + ;; jump table, corresponds to the command bytes defined + ;; in usbdux.c +ep8_jmp: + sjmp ep8_err ; a=0, err + sjmp ep8_err ; a=1, err + sjmp ep8_err ; a=2, err + sjmp ep8_dio ; a=3, digital read + sjmp ep8_sglchannel ; a=4, analog A/D + sjmp ep8_readctr ; a=5, read counter + sjmp ep8_err ; a=6, write counter + + ;; reads all counters +ep8_readctr: + mov r0,#CTR0 ; points to counter0 + mov dptr,#0fc02h ; ep8 fifo buffer + mov r1,#8 ; transfer 4 16bit counters +ep8_ctrlp: + mov a,@r0 ; get the counter + movx @dptr,a ; save in the fifo buffer + inc r0 ; inc pointer to the counters + inc dptr ; inc pointer to the fifo buffer + djnz r1,ep8_ctrlp ; loop until ready + + sjmp ep8_send ; send the data + + ;; read one A/D channel +ep8_sglchannel: + mov r0,#SGLCHANNEL ; points to the channel + mov a,@r0 ; Ch0 + + lcall readAD ; start the conversion + + mov DPTR,#0fc02h ; EP8 FIFO + mov a,R3 ; get low byte + movx @DPTR,A ; store in FIFO + inc dptr ; next fifo entry + mov a,R4 ; get high byte + movx @DPTR,A ; store in FIFO + + sjmp ep8_send ; send the data + + ;; read the digital lines +ep8_dio: + mov DPTR,#0fc02h ; store the contents of port B + mov a,IOB ; in the next + movx @dptr,a ; entry of the buffer + + inc dptr + clr a ; high byte is zero + movx @dptr,a ; next byte of the EP + +ep8_send: + mov DPTR,#EP8BCH ; byte count H + mov a,#0 ; is zero + lcall syncdelaywr + + mov DPTR,#EP8BCL ; byte count L + mov a,#10H ; 16 bytes + lcall syncdelaywr ; send the data over to the host + +ep8_err: + ret + + + +;;; EP8 interrupt: gets one measurement from the AD converter and +;;; sends it via EP8. The channel # is stored in address 80H. +;;; It also gets the state of the digital registers B and D. +ep8_isr: + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + push psw + push 00h ; R0 + push 01h ; R1 + push 02h ; R2 + push 03h ; R3 + push 04h ; R4 + push 05h ; R5 + push 06h ; R6 + push 07h ; R7 + + lcall ep8_ops + + ;; clear INT2 + mov a,EXIF ; FIRST clear the USB (INT2) interrupt request + clr acc.4 + mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable + + mov DPTR,#EPIRQ ; + mov a,#10000000b ; clear the ep8irq + movx @DPTR,a + + pop 07h + pop 06h + pop 05h + pop 04h ; R4 + pop 03h ; R3 + pop 02h ; R2 + pop 01h ; R1 + pop 00h ; R0 + pop psw + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + reti + + +;; need to delay every time the byte counters +;; for the EPs have been changed. + +syncdelay: + nop + nop + nop + nop + nop + nop + nop + nop + nop + ret + +syncdelaywr: + movx @dptr,a + lcall syncdelay + ret + + +.End + + diff --git a/usbdux/usbduxfast_firmware.asm b/usbdux/usbduxfast_firmware.asm new file mode 100644 index 0000000..0d8e7f8 --- /dev/null +++ b/usbdux/usbduxfast_firmware.asm @@ -0,0 +1,547 @@ +; usbduxfast_firmware.asm +; Copyright (C) 2004,2009 Bernd Porr, Bernd.Porr@f2s.com +; +; This program is free software; you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation; either version 2 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, write to the Free Software +; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +; +; +; Firmware: usbduxfast_firmware.asm for usbdux.c +; Description: Firmware for usbduxfast +; Devices: [ITL] USB-DUX (usbdux.o) +; Author: Bernd Porr +; Updated: 17 Apr 2009 +; Status: stable +; +;;; +;;; +;;; + + .inc fx2-include.asm + + .equ WFLOADED,70H ; waveform is loaded + + .org 0000h ; after reset the processor starts here + ljmp main ; jump to the main loop + + .org 0043h ; the IRQ2-vector + ljmp jmptbl ; irq service-routine + + .org 0100h ; start of the jump table + +jmptbl: ljmp sudav_isr + nop + ljmp sof_isr + nop + ljmp sutok_isr + nop + ljmp suspend_isr + nop + ljmp usbreset_isr + nop + ljmp hispeed_isr + nop + ljmp ep0ack_isr + nop + ljmp spare_isr + nop + ljmp ep0in_isr + nop + ljmp ep0out_isr + nop + ljmp ep1in_isr + nop + ljmp ep1out_isr + nop + ljmp ep2_isr + nop + ljmp ep4_isr + nop + ljmp ep6_isr + nop + ljmp ep8_isr + nop + ljmp ibn_isr + nop + ljmp spare_isr + nop + ljmp ep0ping_isr + nop + ljmp ep1ping_isr + nop + ljmp ep2ping_isr + nop + ljmp ep4ping_isr + nop + ljmp ep6ping_isr + nop + ljmp ep8ping_isr + nop + ljmp errlimit_isr + nop + ljmp spare_isr + nop + ljmp spare_isr + nop + ljmp spare_isr + nop + ljmp ep2isoerr_isr + nop + ljmp ep4isoerr_isr + nop + ljmp ep6isoerr_isr + nop + ljmp ep8isoerr_isr + + + ;; dummy isr +sof_isr: +sudav_isr: +sutok_isr: +suspend_isr: +usbreset_isr: +hispeed_isr: +ep0ack_isr: +spare_isr: +ep0in_isr: +ep0out_isr: +ep1out_isr: +ep1in_isr: +ibn_isr: +ep0ping_isr: +ep1ping_isr: +ep2ping_isr: +ep4ping_isr: +ep6ping_isr: +ep8ping_isr: +errlimit_isr: +ep2isoerr_isr: +ep4isoerr_isr: +ep6isoerr_isr: +ep8isoerr_isr: +ep6_isr: +ep2_isr: +ep8_isr: + + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + push psw + + ;; clear the USB2 irq bit and return + mov a,EXIF + clr acc.4 + mov EXIF,a + + pop psw + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + + reti + + +;;; main program +;;; basically only initialises the processor and +;;; then engages in an endless loop +main: + mov dptr,#REVCTL + mov a,#00000011b ; allows skip + lcall syncdelaywr + + mov DPTR,#CPUCS ; CPU control register + mov a,#00010000b ; 48Mhz + lcall syncdelaywr + + mov dptr,#IFCONFIG ; switch on IFCLK signal + mov a,#10100010b ; gpif, 30MHz + lcall syncdelaywr + + mov dptr,#FIFORESET + mov a,#80h + lcall syncdelaywr + mov a,#8 + lcall syncdelaywr + mov a,#2 + lcall syncdelaywr + mov a,#4 + lcall syncdelaywr + mov a,#6 + lcall syncdelaywr + mov a,#0 + lcall syncdelaywr + + mov dptr,#INTSETUP ; IRQ setup register + mov a,#08h ; enable autovector + lcall syncdelaywr + + lcall initeps ; init the isochronous data-transfer + + lcall initGPIF + +;;; main loop + +mloop2: + lcall gpif_run + sjmp mloop2 ; do nothing. The rest is done by the IRQs + + +gpif_run: + mov a,WFLOADED + jz no_trig ; do not trigger + mov a,GPIFTRIG ; GPIF status + anl a,#80h ; done bit + jz no_trig ; GPIF busy + +;;; gpif has stopped + mov a,#06h ; RD,EP6 + mov GPIFTRIG,a +no_trig: + ret + + + +initGPIF: + mov DPTR,#EP6CFG ; BLK data from here to the host + mov a,#11100000b ; Valid, quad buffering + lcall syncdelaywr ; write + + mov dptr,#EP6FIFOCFG + mov a,#00001001b ; autoin, wordwide + lcall syncdelaywr + + mov dptr,#EP6AUTOINLENH + mov a,#00000010b ; 512 bytes + lcall syncdelaywr ; write + + mov dptr,#EP6AUTOINLENL + mov a,#00000000b ; 0 + lcall syncdelaywr ; write + + mov dptr,#GPIFWFSELECT + mov a,#11111100b ; waveform 0 for FIFO RD + lcall syncdelaywr + + mov dptr,#GPIFCTLCFG + mov a,#10000000b ; tri state for CTRL + lcall syncdelaywr + + mov dptr,#GPIFIDLECTL + mov a,#11111111b ; all CTL outputs high + lcall syncdelaywr + mov a,#11111101b ; reset counter + lcall syncdelaywr + mov a,#11111111b ; reset to high again + lcall syncdelaywr + + mov a,#00000010b ; abort when full + mov dptr,#EP6GPIFFLGSEL + lcall syncdelaywr + + mov a,#00000001b ; stop when buffer overfl + mov dptr,#EP6GPIFPDFSTOP + lcall syncdelaywr + + mov a,#0 + mov dptr,#GPIFREADYCFG + lcall syncdelaywr + + mov a,#0 + mov dptr,#GPIFIDLECS + lcall syncdelaywr + +; waveform 1 +; this is a dummy waveform which is used +; during the upload of another waveform into +; wavefrom 0 +; it branches directly into the IDLE state + mov dptr,#0E420H + mov a,#00111111b ; branch to IDLE + lcall syncdelaywr + + mov dptr,#0E428H ; opcode + mov a,#00000001b ; deceision point + lcall syncdelaywr + + mov dptr,#0E430H + mov a,#0FFH ; output is high + lcall syncdelaywr + + mov dptr,#0E438H + mov a,#0FFH ; logic function + lcall syncdelaywr + +; signals that no waveform 0 is loaded so far + mov WFLOADED,#0 ; waveform flag + + ret + + + +;;; initilise the transfer +;;; It is assumed that the USB interface is in alternate setting 1 +initeps: + mov DPTR,#EP4CFG + mov a,#10100000b ; valid, bulk, out + lcall syncdelaywr + + mov dptr,#EP4BCL ; "arm" it + mov a,#00h + lcall syncdelaywr ; wait until we can write again + lcall syncdelaywr ; wait + lcall syncdelaywr ; wait + + mov DPTR,#EP8CFG + mov a,#0 ; disable EP8, it overlaps with EP6!! + lcall syncdelaywr + + mov dptr,#EPIE ; interrupt enable + mov a,#00100000b ; enable irq for ep4 + lcall syncdelaywr ; do it + + mov dptr,#EPIRQ ; clear IRQs + mov a,#00100100b + movx @dptr,a + + mov DPTR,#USBIE ; USB int enable register + mov a,#0 ; SOF etc + movx @DPTR,a ; + + mov DPTR,#GPIFIE ; GPIF int enable register + mov a,#0 ; done IRQ + movx @DPTR,a ; + + mov EIE,#00000001b ; enable INT2 in the 8051's SFR + mov IE,#80h ; IE, enable all interrupts + + ret + + +;;; interrupt-routine for ep4 +;;; receives the channel list and other commands +ep4_isr: + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + push psw + push 00h ; R0 + push 01h ; R1 + push 02h ; R2 + push 03h ; R3 + push 04h ; R4 + push 05h ; R5 + push 06h ; R6 + push 07h ; R7 + + mov dptr,#0f400h ; FIFO buffer of EP4 + movx a,@dptr ; get the first byte + + mov dptr,#ep4_jmp ; jump table for the different functions + rl a ; multiply by 2: sizeof sjmp + jmp @a+dptr ; jump to the jump table + +ep4_jmp: + sjmp storewaveform ; a=0 + sjmp init_ep6 ; a=1 + +init_ep6: + ; stop ep6 + ; just now do nothing + + ljmp over_wf + + +storewaveform: + mov WFLOADED,#0 ; waveform flag + + mov dptr,#EP6FIFOCFG + mov a,#00000000b ; + lcall syncdelaywr + + mov dptr,#GPIFABORT + mov a,#0ffh ; abort all transfers + lcall syncdelaywr + +wait_f_abort: + mov a,GPIFTRIG ; GPIF status + anl a,#80h ; done bit + jz wait_f_abort ; GPIF busy + + mov dptr,#GPIFWFSELECT + mov a,#11111101b ; select dummy waveform + movx @dptr,a + lcall syncdelay + + mov dptr,#FIFORESET + mov a,#80h ; NAK + lcall syncdelaywr + mov a,#6 ; reset EP6 + lcall syncdelaywr + mov a,#0 ; normal op + lcall syncdelaywr + +; change to dummy waveform 1 + mov a,#06h ; RD,EP6 + mov GPIFTRIG,a + +; wait a bit + mov r2,255 +loopx: + djnz r2,loopx + +; abort waveform if not already so + mov dptr,#GPIFABORT + mov a,#0ffh ; abort all transfers + lcall syncdelaywr + +; wait again + mov r2,255 +loopx2: + djnz r2,loopx2 + +; check for DONE +wait_f_abort2: + mov a,GPIFTRIG ; GPIF status + anl a,#80h ; done bit + jz wait_f_abort2 ; GPIF busy + +; upload the new waveform into waveform 0 + mov AUTOPTRH2,#0E4H ; XDATA0H + lcall syncdelay + mov AUTOPTRL2,#00H ; XDATA0L + lcall syncdelay + + mov AUTOPTRH1,#0F4H ; EP4 high + lcall syncdelay + mov AUTOPTRL1,#01H ; EP4 low + lcall syncdelay + + mov AUTOPTRSETUP,#7 ; autoinc and enable + lcall syncdelay + + mov r2,#20H ; 32 bytes to transfer + +wavetr: + mov dptr,#XAUTODAT1 + movx a,@dptr + lcall syncdelay + mov dptr,#XAUTODAT2 + movx @dptr,a + lcall syncdelay + djnz r2,wavetr + + mov dptr,#EP6FIFOCFG + mov a,#00001001b ; autoin, wordwide + lcall syncdelaywr + + mov dptr,#GPIFWFSELECT + mov a,#11111100b + movx @dptr,a + lcall syncdelay + + mov dptr,#FIFORESET + mov a,#80h ; NAK + lcall syncdelaywr + mov a,#6 ; reset EP6 + lcall syncdelaywr + mov a,#0 ; normal op + lcall syncdelaywr + + mov dptr,#0E400H+10H; waveform 0: first CTL byte + movx a,@dptr ; get it + orl a,#11111011b ; force all bits to one except the range bit + mov dptr,#GPIFIDLECTL + lcall syncdelaywr + + mov WFLOADED,#1 ; waveform flag + +; do the common things here +over_wf: + mov dptr,#EP4BCL + mov a,#00h + movx @DPTR,a ; arm it + lcall syncdelay ; wait + movx @DPTR,a ; arm it + lcall syncdelay ; wait + + ;; clear INT2 + mov a,EXIF ; FIRST clear the USB (INT2) interrupt request + clr acc.4 + mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable + + mov DPTR,#EPIRQ ; + mov a,#00100000b ; clear the ep4irq + movx @DPTR,a + + pop 07h + pop 06h + pop 05h + pop 04h ; R4 + pop 03h ; R3 + pop 02h ; R2 + pop 01h ; R1 + pop 00h ; R0 + pop psw + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + reti + + +;; need to delay every time the byte counters +;; for the EPs have been changed. + +syncdelay: + nop + nop + nop + nop + nop + nop + nop + nop + nop + ret + + +syncdelaywr: + lcall syncdelay + movx @dptr,a + ret + + +.End + + + + + + + + + + + + diff --git a/usbdux/usbduxsigma_firmware.asm b/usbdux/usbduxsigma_firmware.asm new file mode 100644 index 0000000..38f5f0e --- /dev/null +++ b/usbdux/usbduxsigma_firmware.asm @@ -0,0 +1,1394 @@ +; usbdux_firmware.asm +; Copyright (C) 2010,2015 Bernd Porr, mail@berndporr.me.uk +; For usbduxsigma.c 0.5+ +; +; This program is free software; you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation; either version 2 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, write to the Free Software +; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +; +; +; Firmware: usbduxsigma_firmware.asm for usbduxsigma.c +; Description: University of Stirling USB DAQ & INCITE Technology Limited +; Devices: [ITL] USB-DUX-SIGMA (usbduxsigma.ko) +; Author: Bernd Porr +; Updated: 20 Jul 2015 +; Status: testing +; +;;; +;;; +;;; + + .inc fx2-include.asm + +;;; a couple of flags in high memory + .equ CMD_FLAG,80h ; flag for the next in transfer + .equ PWMFLAG,81h ; PWM on or off? + .equ MAXSMPL,82H ; maximum number of samples, n channellist + .equ MUXSG0,83H ; content of the MUXSG0 register + .equ INTERVAL,88h ; uframe/frame interval + .equ INTCTR,89h ; interval counter + .equ DABUFFER,0F0h ; buffer with DA values + +;;; in precious low memory but accessible within one clock cycle + .equ DPTRL,70H + .equ DPTRH,71h + .equ ASYNC_ON,72h + .equ SMPLCTR,73h + +;;; actual code + .org 0000h ; after reset the processor starts here + ljmp main ; jump to the main loop + + .org 0003h + ljmp isr0 ; external interrupt 0: /DRY + + .org 0043h ; the IRQ2-vector + ljmp jmptbl ; irq service-routine + + .org 0100h ; start of the jump table + +jmptbl: ljmp sudav_isr + nop + ljmp sof_isr + nop + ljmp sutok_isr + nop + ljmp suspend_isr + nop + ljmp usbreset_isr + nop + ljmp hispeed_isr + nop + ljmp ep0ack_isr + nop + ljmp spare_isr + nop + ljmp ep0in_isr + nop + ljmp ep0out_isr + nop + ljmp ep1in_isr + nop + ljmp ep1out_isr + nop + ljmp ep2_isr + nop + ljmp ep4_isr + nop + ljmp ep6_isr + nop + ljmp ep8_isr + nop + ljmp ibn_isr + nop + ljmp spare_isr + nop + ljmp ep0ping_isr + nop + ljmp ep1ping_isr + nop + ljmp ep2ping_isr + nop + ljmp ep4ping_isr + nop + ljmp ep6ping_isr + nop + ljmp ep8ping_isr + nop + ljmp errlimit_isr + nop + ljmp spare_isr + nop + ljmp spare_isr + nop + ljmp spare_isr + nop + ljmp ep2isoerr_isr + nop + ljmp ep4isoerr_isr + nop + ljmp ep6isoerr_isr + nop + ljmp ep8isoerr_isr + + + ;; dummy isr +sudav_isr: +sutok_isr: +suspend_isr: +usbreset_isr: +hispeed_isr: +ep0ack_isr: +spare_isr: +ep0in_isr: +ep0out_isr: +ibn_isr: +ep0ping_isr: +ep1ping_isr: +ep2ping_isr: +ep4ping_isr: +ep6ping_isr: +ep8ping_isr: +errlimit_isr: +ep2isoerr_isr: +ep4isoerr_isr: +ep6isoerr_isr: +ep8isoerr_isr: +ep6_isr: +ep2_isr: +ep4_isr: + + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + push psw + + ;; clear the USB2 irq bit and return + mov a,EXIF + clr acc.4 + mov EXIF,a + + pop psw + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + + reti + + +ep1in_isr: + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + push psw + + mov dptr,#0E7C0h ; EP1in + mov a,IOB ; get DIO D + movx @dptr,a ; store it + inc dptr ; next byte + mov a,IOC ; get DIO C + movx @dptr,a ; store it + inc dptr ; next byte + mov a,IOD ; get DIO B + movx @dptr,a ; store it + inc dptr ; next byte + mov a,#0 ; just zero + movx @dptr,a ; pad it up + + ;; clear INT2 + mov a,EXIF ; FIRST clear the USB (INT2) interrupt request + clr acc.4 + mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable + + mov DPTR,#EPIRQ ; + mov a,#00000100b ; clear the ep1in + movx @DPTR,a + + pop psw + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + reti + + + +;;; this is triggered when DRY goes low +isr0: + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + push psw + push 00h ; R0 + push 01h ; R1 + push 02h ; R2 + push 03h ; R3 + push 04h ; R4 + push 05h ; R5 + push 06h ; R6 + push 07h ; R7 + + mov a,ASYNC_ON + jz noepsubmit + + mov DPS,#0 + mov dpl,DPTRL + mov dph,DPTRH + + lcall readADCch ; read one channel + + mov DPTRL,dpl + mov DPTRH,dph + + mov a,SMPLCTR + dec a + mov SMPLCTR,a + jnz noepsubmit + + mov ASYNC_ON,#0 + + clr IOA.7 ; START = 0 + + ;; arm the endpoint and send off the data + mov DPTR,#EP6BCH ; byte count H + mov a,#0 ; is zero + lcall syncdelaywr ; wait until we can write again + + mov r0,#MAXSMPL ; number of samples to transmit + mov a,@r0 ; get them + rl a ; a=a*2 + rl a ; a=a*2 + add a,#4 ; four bytes for DIO + mov DPTR,#EP6BCL ; byte count L + lcall syncdelaywr ; wait until we can write again + +noepsubmit: + pop 07h + pop 06h + pop 05h + pop 04h ; R4 + pop 03h ; R3 + pop 02h ; R2 + pop 01h ; R1 + pop 00h ; R0 + pop psw + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + + reti + + + +;;; main program +;;; basically only initialises the processor and +;;; then engages in an endless loop +main: + mov DPTR,#CPUCS ; CPU control register + mov a,#00010000b ; 48Mhz + lcall syncdelaywr + + mov dptr,#REVCTL + mov a,#00000011b ; allows skip + lcall syncdelaywr + + mov dptr,#INTSETUP ; IRQ setup register + mov a,#08h ; enable autovector + lcall syncdelaywr + + mov dptr,#PORTCCFG + mov a,#0 + lcall syncdelaywr + + mov IP,#01H ; int0 has highest interrupt priority + mov EIP,#0 ; all USB interrupts have low priority + + lcall initAD ; init the ports to the converters + + lcall initeps ; init the isochronous data-transfer + +;;; main loop, rest is done as interrupts +mloop2: nop + +;;; pwm + mov r0,#PWMFLAG ; pwm on? + mov a,@r0 ; get info + jz mloop2 ; it's off + + mov a,GPIFTRIG ; GPIF status + anl a,#80h ; done bit + jz mloop2 ; GPIF still busy + + mov a,#01h ; WR,EP4, 01 = EP4 + mov GPIFTRIG,a ; restart it + + sjmp mloop2 ; loop for ever + + +;;; initialise the ports for the AD-converter +initAD: + mov r0,#MAXSMPL ; length of channellist + mov @r0,#0 ; we don't want to accumlate samples + + mov ASYNC_ON,#0 ; async enable + + mov r0,#DABUFFER + mov @r0,#0 + + mov OEA,#11100000b ; PortA7,A6,A5 Outputs + mov IOA,#01100000b ; /CS = 1 and START = 0 + mov dptr,#IFCONFIG ; switch on clock on IFCLK pin + mov a,#10100000b ; gpif, 30MHz, internal IFCLK -> 15MHz for AD + lcall syncdelaywr + + mov SCON0,#013H ; ser rec en, TX/RX: stop, 48/12MHz=4MHz clock + + mov dptr,#PORTECFG + mov a,#00001000b ; special function for port E: RXD0OUT + lcall syncdelaywr + + ret + + +;;; send a byte via SPI +;;; content in a, dptr1 is changed +;;; the lookup is done in dptr1 so that the normal dptr is not affected +;;; important: /cs needs to be reset to 1 by the caller: IOA.5 +sendSPI: + inc DPS + + ;; bit reverse + mov dptr,#swap_lut ; lookup table + movc a,@a+dptr ; reverse bits + + ;; clear interrupt flag, is used to detect + ;; successful transmission + clr SCON0.1 ; clear interrupt flag + + ;; start transmission by writing the byte + ;; in the transmit buffer + mov SBUF0,a ; start transmission + + ;; wait for the end of the transmission +sendSPIwait: + mov a,SCON0 ; get transmission status + jnb ACC.1,sendSPIwait ; loop until transmitted + + inc DPS + + ret + + + + +;;; receive a byte via SPI +;;; content in a, dptr is changed +;;; the lookup is done in dptr1 so that the normal dptr is not affected +;;; important: the /CS needs to be set to 1 by the caller via "setb IOA.5" +recSPI: + inc DPS + + clr IOA.5 ; /cs to 0 + + ;; clearning the RI bit starts reception of data + clr SCON0.0 + +recSPIwait: + ;; RI goes back to 1 after the reception of the 8 bits + mov a,SCON0 ; get receive status + jnb ACC.0,recSPIwait; loop until all bits received + + ;; read the byte from the buffer + mov a,SBUF0 ; get byte + + ;; lookup: reverse the bits + mov dptr,#swap_lut ; lookup table + movc a,@a+dptr ; reverse the bits + + inc DPS + + ret + + + + +;;; reads a register +;;; register address in a +;;; returns value in a +registerRead: + anl a,#00001111b ; mask out the index to the register + orl a,#01000000b ; 010xxxxx indicates register read + clr IOA.5 ; ADC /cs to 0 + lcall sendSPI ; send the command over + lcall recSPI ; read the contents back + setb IOA.5 ; ADC /cs to 1 + ret + + + +;;; writes to a register +;;; register address in a +;;; value in r0 +registerWrite: + push acc + anl a,#00001111b ; mask out the index to the register + orl a,#01100000b ; 011xxxxx indicates register write + + clr IOA.5 ; ADC /cs to 0 + + lcall sendSPI ; + mov a,r0 + lcall sendSPI + + setb IOA.5 ; ADC /cs to 1 + pop acc + + lcall registerRead ; check if the data has arrived in the ADC + mov 0f0h,r0 ; register B + cjne a,0f0h,registerWrite ; something went wrong, try again + + ret + + + +;;; initilise the endpoints +initeps: + mov dptr,#FIFORESET + mov a,#80H + movx @dptr,a ; reset all fifos + mov a,#2 + movx @dptr,a ; + mov a,#4 + movx @dptr,a ; + mov a,#6 + movx @dptr,a ; + mov a,#8 + movx @dptr,a ; + mov a,#0 + movx @dptr,a ; normal operat + + mov DPTR,#EP2CFG + mov a,#10010010b ; valid, out, double buff, iso + movx @DPTR,a + + mov dptr,#EP2FIFOCFG + mov a,#00000000b ; manual + movx @dptr,a + + mov dptr,#EP2BCL ; "arm" it + mov a,#00h + movx @DPTR,a ; can receive data + lcall syncdelay ; wait to sync + movx @DPTR,a ; can receive data + lcall syncdelay ; wait to sync + movx @DPTR,a ; can receive data + lcall syncdelay ; wait to sync + + mov DPTR,#EP1OUTCFG + mov a,#10100000b ; valid + movx @dptr,a + + mov dptr,#EP1OUTBC ; "arm" it + mov a,#00h + movx @DPTR,a ; can receive data + lcall syncdelay ; wait until we can write again + movx @dptr,a ; make shure its really empty + lcall syncdelay ; wait + + mov DPTR,#EP6CFG ; ISO data from here to the host + mov a,#11010010b ; Valid + movx @DPTR,a ; ISO transfer, double buffering + + mov DPTR,#EP8CFG ; EP8 + mov a,#11100000b ; BULK data from here to the host + movx @DPTR,a ; + + mov dptr,#PORTACFG + mov a,#1 ; interrupt on pin A0 + lcall syncdelaywr + + ;; enable interrupts + mov dptr,#EPIE ; interrupt enable + mov a,#10001100b ; enable irq for ep1out,8,ep1in + movx @dptr,a ; do it + + mov dptr,#EPIRQ ; clear IRQs + mov a,#10001100b + movx @dptr,a + + mov DPTR,#USBIE ; USB int enables register + mov a,#2 ; enables SOF (1ms/125us interrupt) + movx @DPTR,a ; + + setb TCON.0 ; make INT0 edge triggered, falling edge + + mov EIE,#00000001b ; enable INT2/USBINT in the 8051's SFR + mov IE,#81h ; IE, enable all interrupts and INT0 + + ret + + +;;; Reads one ADC channel from the converter and stores +;;; the result at dptr +readADCch: + ;; reading data is done by just dropping /CS and start reading and + ;; while keeping the IN signal to the ADC inactive + clr IOA.5 ; /cs to 0 + + ;; 1st byte: STATUS + lcall recSPI ; index + movx @dptr,a ; store the byte + inc dptr ; increment pointer + + ;; 2nd byte: MSB + lcall recSPI ; data + movx @dptr,a + inc dptr + + ;; 3rd byte: MSB-1 + lcall recSPI ; data + movx @dptr,a + inc dptr + + ;; 4th byte: LSB + lcall recSPI ; data + movx @dptr,a + inc dptr + + ;; got all bytes + setb IOA.5 ; /cs to 1 + + ret + + + +;;; interrupt-routine for SOF +sof_isr: + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + push psw + push 00h ; R0 + push 01h ; R1 + push 02h ; R2 + push 03h ; R3 + push 04h ; R4 + push 05h ; R5 + push 06h ; R6 + push 07h ; R7 + + mov r0,#INTCTR ; interval counter + mov a,@r0 ; get the value + dec a ; decrement + mov @r0,a ; save it again + jz sof_adc ; we do ADC functions + ljmp epfull ; we skip all adc functions + +sof_adc: + mov r1,#INTERVAL ; get the interval + mov a,@r1 ; get it + mov @r0,a ; save it in the counter + mov a,EP2468STAT + anl a,#20H ; full? + jnz epfull ; EP6-buffer is full + + mov a,IOA ; conversion running? + jb ACC.7,epfull + + ;; make sure that we are starting with the first channel + mov r0,#MUXSG0 ; + mov a,@r0 ; get config of MUXSG0 + mov r0,a + mov a,#04H ; MUXSG0 + lcall registerWrite ; this resets the channel sequence + + setb IOA.7 ; start converter, START = 1 + + mov dptr,#0f800h ; EP6 buffer + mov a,IOD ; get DIO D + movx @dptr,a ; store it + inc dptr ; next byte + mov a,IOC ; get DIO C + movx @dptr,a ; store it + inc dptr ; next byte + mov a,IOB ; get DIO B + movx @dptr,a ; store it + inc dptr ; next byte + mov a,#0 ; just zero + movx @dptr,a ; pad it up + inc dptr ; algin along a 32 bit word + mov DPTRL,dpl + mov DPTRH,dph + + mov r0,#MAXSMPL + mov a,@r0 + mov SMPLCTR,a + + mov ASYNC_ON,#1 + +epfull: + ;; do the D/A conversion + mov a,EP2468STAT + anl a,#01H ; empty + jnz epempty ; nothing to get + + mov dptr,#0F000H ; EP2 fifo buffer + lcall dalo ; conversion + + mov dptr,#EP2BCL ; "arm" it + mov a,#00h + lcall syncdelaywr ; wait for the rec to sync + lcall syncdelaywr ; wait for the rec to sync + +epempty: + mov a,IOA ; conversion running? + jb ACC.7,sofend + + lcall DAsend + +sofend: + ;; clear INT2 + mov a,EXIF ; FIRST clear the USB (INT2) interrupt request + clr acc.4 + mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable + + mov DPTR,#USBIRQ ; points to the SOF + mov a,#2 ; clear the SOF + movx @DPTR,a + +nosof: + pop 07h + pop 06h + pop 05h + pop 04h ; R4 + pop 03h ; R3 + pop 02h ; R2 + pop 01h ; R1 + pop 00h ; R0 + pop psw + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + reti + + +reset_ep8: + ;; erase all data in ep8 + mov dptr,#FIFORESET + mov a,#80H ; NAK + lcall syncdelaywr + mov dptr,#FIFORESET + mov a,#8 ; reset EP8 + lcall syncdelaywr + mov dptr,#FIFORESET + mov a,#0 ; normal operation + lcall syncdelaywr + ret + + +reset_ep6: + ;; throw out old data + mov dptr,#FIFORESET + mov a,#80H ; NAK + lcall syncdelaywr + mov dptr,#FIFORESET + mov a,#6 ; reset EP6 + lcall syncdelaywr + mov dptr,#FIFORESET + mov a,#0 ; normal operation + lcall syncdelaywr + ret + + +;;; configure the ADC converter +;;; the dptr points to the init data: +;;; CONFIG 0,1,3,4,5,6 +;;; note that CONFIG2 is omitted +configADC: + clr IOA.7 ; stops ADC: START line of ADC = L + setb IOA.5 ; ADC /cs to 1 + + ;; just in case something has gone wrong + nop + nop + nop + + mov a,#11000000b ; reset the ADC + clr IOA.5 ; ADC /cs to 0 + lcall sendSPI + setb IOA.5 ; ADC /cs to 1 + + movx a,@dptr ; + inc dptr + mov r0,a + mov a,#00H ; CONFIG0 + lcall registerWrite + + movx a,@dptr ; + inc dptr + mov r0,a + mov a,#01H ; CONFIG1 + lcall registerWrite + + movx a,@dptr ; + inc dptr + mov r0,a + mov a,#03H ; MUXDIF + lcall registerWrite + + movx a,@dptr ; + inc dptr + mov r0,#MUXSG0 + mov @r0,a ; store it for reset purposes + mov r0,a + mov a,#04H ; MUXSG0 + lcall registerWrite + + movx a,@dptr ; + inc dptr + mov r0,a + mov a,#05H ; MUXSG1 + lcall registerWrite + + movx a,@dptr ; + inc dptr + mov r0,a + mov a,#06H ; SYSRED + lcall registerWrite + + ret + + +;;; interrupt-routine for ep1out +;;; receives the channel list and other commands +ep1out_isr: + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + push psw + push 00h ; R0 + push 01h ; R1 + push 02h ; R2 + push 03h ; R3 + push 04h ; R4 + push 05h ; R5 + push 06h ; R6 + push 07h ; R7 + + mov dptr,#0E780h ; FIFO buffer of EP1OUT + movx a,@dptr ; get the first byte + mov r0,#CMD_FLAG ; pointer to the command byte + mov @r0,a ; store the command byte for ep8 + + mov dptr,#ep1out_jmp; jump table for the different functions + rl a ; multiply by 2: sizeof sjmp + jmp @a+dptr ; jump to the jump table + ;; jump table, corresponds to the command bytes defined + ;; in usbdux.c +ep1out_jmp: + sjmp startadc ; a=0 + sjmp single_da ; a=1 + sjmp config_digital_b; a=2 + sjmp write_digital_b ; a=3 + sjmp initsgADchannel ; a=4 + sjmp nothing ; a=5 + sjmp nothing ; a=6 + sjmp pwm_on ; a=7 + sjmp pwm_off ; a=8 + sjmp startadcint ; a=9 + +nothing: + ljmp over_da + +pwm_on: + lcall startPWM + sjmp over_da + +pwm_off: + lcall stopPWM + sjmp over_da + +initsgADchannel: + mov ASYNC_ON,#0 + + mov dptr,#0e781h ; FIFO buffer of EP1OUT + lcall configADC ; configures the ADC esp sel the channel + + lcall reset_ep8 ; reset FIFO: get rid of old bytes + ;; Save new A/D data in EP8. This is the first byte + ;; the host will read during an INSN. If there are + ;; more to come they will be handled by the ISR of + ;; ep8. + lcall ep8_ops ; get A/D data + + sjmp over_da + +startadcint: + mov dptr,#0e781h ; FIFO buffer of EP1OUT from 2nd byte + + movx a,@dptr ; interval is the 1st byte + inc dptr ; data pointer + sjmp startadc2 ; the other paramters as with startadc + +;;; config AD: +;;; we write to the registers of the A/D converter +startadc: + mov dptr,#0e781h ; FIFO buffer of EP1OUT from 2nd byte + + mov a,#1 ; interval is 1 here all the time +startadc2: + mov r0,#INTERVAL ; set it + mov @r0,a + mov r0,#INTCTR ; the counter is also just one + mov @r0,a + + movx a,@dptr ; get length of channel list + inc dptr + mov r0,#MAXSMPL + mov @r0,a ; length of the channel list + mov SMPLCTR,a + + lcall configADC ; configures all registers + + mov ASYNC_ON,#1 ; async enable + + lcall reset_ep6 ; reset FIFO + + ;; load new A/D data into EP6 + ;; This must be done. Otherwise the ISR is never called. + ;; The ISR is only called when data has _left_ the + ;; ep buffer here it has to be refilled. + lcall ep6_arm ; fill with dummy data + + sjmp over_da + +;;; Single DA conversion. The 2 bytes are in the FIFO buffer +single_da: + mov dptr,#0e781h ; FIFO buffer of EP1OUT + lcall dalo ; conversion + sjmp over_da + +;;; configure the port B as input or output (bitwise) +config_digital_b: + mov dptr,#0e781h ; FIFO buffer of EP1OUT + movx a,@dptr ; get the second byte + inc dptr + mov OEB,a ; set the output enable bits + movx a,@dptr ; get the second byte + inc dptr + mov OEC,a + movx a,@dptr ; get the second byte + inc dptr + mov OED,a + sjmp over_da + +;;; Write one byte to the external digital port B +;;; and prepare for digital read +write_digital_b: + mov dptr,#0e781h ; FIFO buffer of EP1OUT + movx a,@dptr ; command[1] + inc dptr + mov OEB,a ; output enable + movx a,@dptr ; command[2] + inc dptr + mov OEC,a + movx a,@dptr ; command[3] + inc dptr + mov OED,a + movx a,@dptr ; command[4] + inc dptr + mov IOB,a ; + movx a,@dptr ; command[5] + inc dptr + mov IOC,a + movx a,@dptr ; command[6] + inc dptr + mov IOD,a + + lcall reset_ep8 ; reset FIFO of ep 8 + + ;; fill ep8 with new data from port B + ;; When the host requests the data it's already there. + ;; This must be so. Otherwise the ISR is not called. + ;; The ISR is only called when a packet has been delivered + ;; to the host. Thus, we need a packet here in the + ;; first instance. + lcall ep8_ops ; get digital data + + ;; + ;; for all commands the same +over_da: + mov dptr,#EP1OUTBC + mov a,#00h + lcall syncdelaywr ; arm + lcall syncdelaywr ; arm + lcall syncdelaywr ; arm + + ;; clear INT2 + mov a,EXIF ; FIRST clear the USB (INT2) interrupt request + clr acc.4 + mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable + + mov DPTR,#EPIRQ ; + mov a,#00001000b ; clear the ep1outirq + movx @DPTR,a + + pop 07h + pop 06h + pop 05h + pop 04h ; R4 + pop 03h ; R3 + pop 02h ; R2 + pop 01h ; R1 + pop 00h ; R0 + pop psw + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + reti + + + +;;; save all DA channels from the endpoint buffer in a local buffer +dalo: + movx a,@dptr ; number of bytes to send out + inc dptr ; pointer to the first byte + mov r1,#DABUFFER ; buffer for DA values + mov @r1,a ; save it + inc r1 ; inc pointer to local buffer + mov r0,a ; counter +nextDAlo: + movx a,@dptr ; get the byte + inc dptr ; point to the high byte + mov @r1,a ; save it in the buffer + inc r1 + movx a,@dptr ; get the channel number + inc dptr ; get ready for the next channel + mov @r1,a ; save it + inc r1 + djnz r0,nextDAlo ; next channel + ret + + +;;; write to the DA converter +DAsend: + mov r1,#DABUFFER ; buffer of the DA values + mov a,@r1 ; get the channel count + jz DAret ; nothing to do + inc r1 ; pointer to the first byte + mov r0,a ; counter +nextDA: + mov a,@r1 ; get the byte + inc r1 ; point to the high byte + mov r3,a ; store in r3 for writeDA + mov a,@r1 ; get the channel number + inc r1 ; get ready for the next channel + push 1 ; is modified in the subroutine + lcall writeDA ; write value to the DAC + pop 1 ; get the pointer back + djnz r0,nextDA ; next channel +DAret: + ret + + + +;;; D/A-conversion: +;;; channel number in a +;;; value in r3 +writeDA: + anl a,#00000011b ; 4 channels + mov r1,#6 ; the channel number needs to be shifted up +writeDA2: + rl a ; bit shift to the left + djnz r1,writeDA2 ; do it 6 times + orl a,#00010000b ; update outputs after write + mov r2,a ; backup + mov a,r3 ; get byte + anl a,#11110000b ; get the upper nibble + mov r1,#4 ; shift it up to the upper nibble +writeDA3: + rr a ; shift to the upper to the lower + djnz r1,writeDA3 + orl a,r2 ; merge with the channel info + clr IOA.6 ; /SYNC (/CS) of the DA to 0 + lcall sendSPI ; send it out to the SPI + mov a,r3 ; get data again + anl a,#00001111b ; get the lower nibble + mov r1,#4 ; shift that to the upper +writeDA4: + rl a + djnz r1,writeDA4 + anl a,#11110000b ; make sure that's empty + lcall sendSPI + setb IOA.6 ; /SYNC (/CS) of the DA to 1 +noDA: ret + + + +;;; arm ep6: this is just a dummy arm to get things going +ep6_arm: + mov DPTR,#EP6BCH ; byte count H + mov a,#0 ; is zero + lcall syncdelaywr ; wait until the length has arrived + + mov DPTR,#EP6BCL ; byte count L + mov a,#1 ; is one + lcall syncdelaywr ; wait until the length has been proc + ret + + + +;;; converts one analog/digital channel and stores it in EP8 +;;; also gets the content of the digital ports B,C and D depending on +;;; the COMMAND flag +ep8_ops: + mov dptr,#0fc01h ; ep8 fifo buffer + clr a ; high byte + movx @dptr,a ; set H=0 + mov dptr,#0fc00h ; low byte + mov r0,#CMD_FLAG + mov a,@r0 + movx @dptr,a ; save command byte + + mov dptr,#ep8_jmp ; jump table for the different functions + rl a ; multiply by 2: sizeof sjmp + jmp @a+dptr ; jump to the jump table + ;; jump table, corresponds to the command bytes defined + ;; in usbdux.c +ep8_jmp: + sjmp ep8_err ; a=0, err + sjmp ep8_err ; a=1, err + sjmp ep8_err ; a=2, err + sjmp ep8_dio ; a=3, digital read + sjmp ep8_sglchannel ; a=4, analog A/D + sjmp ep8_err ; a=5, err + sjmp ep8_err ; a=6, err + + ;; read one A/D channel +ep8_sglchannel: + setb IOA.7 ; start converter, START = 1 + ;; we do polling: we wait until DATA READY is zero +sglchwait: + mov a,IOA ; get /DRDY + jb ACC.0,sglchwait ; wait until data ready (DRDY=0) + mov DPTR,#0fc01h ; EP8 FIFO + lcall readADCch ; get one reading + clr IOA.7 ; stop the converter, START = 0 + + sjmp ep8_send ; send the data + + ;; read the digital lines +ep8_dio: + mov DPTR,#0fc01h ; store the contents of port B + mov a,IOB ; in the next + movx @dptr,a ; entry of the buffer + inc dptr + mov a,IOC ; port C + movx @dptr,a ; next byte of the EP + inc dptr + mov a,IOD + movx @dptr,a ; port D + +ep8_send: + mov DPTR,#EP8BCH ; byte count H + mov a,#0 ; is zero + lcall syncdelaywr + + mov DPTR,#EP8BCL ; byte count L + mov a,#10H ; 16 bytes, bec it's such a great number... + lcall syncdelaywr ; send the data over to the host + +ep8_err: + ret + + + +;;; EP8 interrupt is the endpoint which sends data back after a command +;;; The actual command fills the EP buffer already +;;; but for INSNs we need to deliver more data if the count > 1 +ep8_isr: + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + push psw + push 00h ; R0 + push 01h ; R1 + push 02h ; R2 + push 03h ; R3 + push 04h ; R4 + push 05h ; R5 + push 06h ; R6 + push 07h ; R7 + + lcall ep8_ops + + ;; clear INT2 + mov a,EXIF ; FIRST clear the USB (INT2) interrupt request + clr acc.4 + mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable + + mov DPTR,#EPIRQ ; + mov a,#10000000b ; clear the ep8irq + movx @DPTR,a + + pop 07h + pop 06h + pop 05h + pop 04h ; R4 + pop 03h ; R3 + pop 02h ; R2 + pop 01h ; R1 + pop 00h ; R0 + pop psw + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + reti + + + +;;; GPIF waveform for PWM +waveform: + ;; 0 1 2 3 4 5 6 7(not used) + ;; len (gives 50.007Hz) + .db 195, 195, 195, 195, 195, 195, 1, 1 + + ;; opcode + .db 002H, 006H, 002H, 002H, 002H, 002H, 002H, 002H + + ;; out + .db 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH + + ;; log + .db 000H, 000H, 000H, 000H, 000H, 000H, 000H, 000H + + +stopPWM: + mov r0,#PWMFLAG ; flag for PWM + mov a,#0 ; PWM (for the main loop) + mov @r0,a ; set it + + mov dptr,#IFCONFIG ; switch off GPIF + mov a,#10100000b ; gpif, 30MHz, internal IFCLK + lcall syncdelaywr + ret + + +;;; init PWM +startPWM: + mov dptr,#IFCONFIG ; switch on IFCLK signal + mov a,#10100010b ; gpif, 30MHz, internal IFCLK + lcall syncdelaywr + + mov OEB,0FFH ; output to port B + + mov DPTR,#EP4CFG + mov a,#10100000b ; valid, out, bulk + movx @DPTR,a + + ;; reset the endpoint + mov dptr,#FIFORESET + mov a,#80h ; NAK + lcall syncdelaywr + mov a,#84h ; reset EP4 + NAK + lcall syncdelaywr + mov a,#0 ; normal op + lcall syncdelaywr + + mov dptr,#EP4BCL + mov a,#0H ; discard packets + lcall syncdelaywr ; empty FIFO buffer + lcall syncdelaywr ; empty FIFO buffer + + ;; aborts all transfers by the GPIF + mov dptr,#GPIFABORT + mov a,#0ffh ; abort all transfers + lcall syncdelaywr + + ;; wait for GPIF to finish +wait_f_abort: + mov a,GPIFTRIG ; GPIF status + anl a,#80h ; done bit + jz wait_f_abort ; GPIF busy + + mov dptr,#GPIFCTLCFG + mov a,#10000000b ; tri state for CTRL + lcall syncdelaywr + + mov dptr,#GPIFIDLECTL + mov a,#11110000b ; all CTL outputs low + lcall syncdelaywr + + ;; abort if FIFO is empty + mov a,#00000001b ; abort if empty + mov dptr,#EP4GPIFFLGSEL + lcall syncdelaywr + + ;; + mov a,#00000001b ; stop if GPIF flg + mov dptr,#EP4GPIFPFSTOP + lcall syncdelaywr + + ;; transaction counter + mov a,#0ffH + mov dptr,#GPIFTCB3 + lcall syncdelaywr + + ;; transaction counter + mov a,#0ffH + mov dptr,#GPIFTCB2 + lcall syncdelaywr + + ;; transaction counter + mov a,#0ffH ; 512 bytes + mov dptr,#GPIFTCB1 + lcall syncdelaywr + + ;; transaction counter + mov a,#0ffH + mov dptr,#GPIFTCB0 + lcall syncdelaywr + + ;; RDY pins. Not used here. + mov a,#0 + mov dptr,#GPIFREADYCFG + lcall syncdelaywr + + ;; drives the output in the IDLE state + mov a,#1 + mov dptr,#GPIFIDLECS + lcall syncdelaywr + + ;; direct data transfer from the EP to the GPIF + mov dptr,#EP4FIFOCFG + mov a,#00010000b ; autoout=1, byte-wide + lcall syncdelaywr + + ;; waveform 0 is used for FIFO out + mov dptr,#GPIFWFSELECT + mov a,#00000000b + movx @dptr,a + lcall syncdelay + + ;; transfer the delay byte from the EP to the waveform + mov dptr,#0e781h ; EP1 buffer + movx a,@dptr ; get the delay + mov dptr,#waveform ; points to the waveform + mov r2,#6 ; fill 6 bytes +timloop: + movx @dptr,a ; save timing in a xxx + inc dptr + djnz r2,timloop ; fill the 6 delay bytes + + ;; load waveform + mov AUTOPTRH2,#0E4H ; XDATA0H + lcall syncdelay + mov AUTOPTRL2,#00H ; XDATA0L + lcall syncdelay + + mov dptr,#waveform ; points to the waveform + + mov AUTOPTRSETUP,#7 ; autoinc and enable + lcall syncdelay + + mov r2,#20H ; 32 bytes to transfer + +wavetr: + movx a,@dptr + inc dptr + push dpl + push dph + push dpl1 + push dph1 + mov dptr,#XAUTODAT2 + movx @dptr,a + lcall syncdelay + pop dph1 + pop dpl1 + pop dph + pop dpl + djnz r2,wavetr + + mov dptr,#OUTPKTEND + mov a,#084H + lcall syncdelaywr + lcall syncdelaywr + + mov r0,#PWMFLAG ; flag for PWM + mov a,#1 ; PWM (for the main loop) + mov @r0,a ; set it + + ret + + + +;; need to delay every time the byte counters +;; for the EPs have been changed. + +syncdelay: + nop + nop + nop + nop + nop + nop + nop + nop + nop + ret + +syncdelaywr: + movx @dptr,a + lcall syncdelay + ret + + + + .org 1F00h ; lookup table at the end of memory + +swap_lut: +.db 0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,240,8,136 +.db 72,200,40,168,104,232,24,152,88,216,56,184,120,248,4,132,68,196,36,164,100 +.db 228,20,148,84,212,52,180,116,244,12,140,76,204,44,172,108,236,28,156,92,220 +.db 60,188,124,252,2,130,66,194,34,162,98,226,18,146,82,210,50,178,114,242,10 +.db 138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,6,134,70,198,38,166 +.db 102,230,22,150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,30,158,94 +.db 222,62,190,126,254,1,129,65,193,33,161,97,225,17,145,81,209,49,177,113,241,9 +.db 137,73,201,41,169,105,233,25,153,89,217,57,185,121,249,5,133,69,197,37,165 +.db 101,229,21,149,85,213,53,181,117,245,13,141,77,205,45,173,109,237,29,157,93 +.db 221,61,189,125,253,3,131,67,195,35,163,99,227,19,147,83,211,51,179,115,243,11 +.db 139,75,203,43,171,107,235,27,155,91,219,59,187,123,251,7,135,71,199,39,167 +.db 103,231,23,151,87,215,55,183,119,247,15,143,79,207,47,175,111,239,31,159,95 +.db 223,63,191,127,255 + + + + +.End + + -- cgit v1.2.3