; 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