2 ; Copyright (C) 2004,2009 Bernd Porr, Bernd.Porr@f2s.com
5 ; This program is free software; you can redistribute it and/or modify
6 ; it under the terms of the GNU General Public License as published by
7 ; the Free Software Foundation; either version 2 of the License, or
8 ; (at your option) any later version.
10 ; This program is distributed in the hope that it will be useful,
11 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ; GNU General Public License for more details.
15 ; You should have received a copy of the GNU General Public License
16 ; along with this program; if not, write to the Free Software
17 ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 ; Firmware: usbdux_firmware.asm for usbdux.c
21 ; Description: University of Stirling USB DAQ & INCITE Technology Limited
22 ; Devices: [ITL] USB-DUX (usbdux.o)
23 ; Author: Bernd Porr <Bernd.Porr@f2s.com>
24 ; Updated: 17 Apr 2009
33 .equ CHANNELLIST,80h ; channellist in indirect memory
35 .equ CMD_FLAG,90h ; flag if next IN transf is DIO
36 .equ SGLCHANNEL,91h ; channel for INSN
37 .equ PWMFLAG,92h ; PWM
39 .equ DIOSTAT0,98h ; last status of the digital port
40 .equ DIOSTAT1,99h ; same for the second counter
42 .equ CTR0,0A0H ; counter 0
43 .equ CTR1,0A2H ; counter 1
45 .org 0000h ; after reset the processor starts here
46 ljmp main ; jump to the main loop
48 .org 000bh ; timer 0 irq
51 .org 0043h ; the IRQ2-vector
52 ljmp jmptbl ; irq service-routine
54 .org 0100h ; start of the jump table
56 jmptbl: ljmp sudav_isr
156 ;; clear the USB2 irq bit and return
173 ;;; basically only initialises the processor and
174 ;;; then engages in an endless loop
176 mov DPTR,#CPUCS ; CPU control register
177 mov a,#00010000b ; 48Mhz
181 mov a,#00000011b ; allows skip
184 mov IP,#0 ; all std 8051 int have low priority
185 mov EIP,#0FFH ; all FX2 interrupts have high priority
187 mov dptr,#INTSETUP ; IRQ setup register
188 mov a,#08h ; enable autovector
191 lcall initAD ; init the ports to the converters
193 lcall initeps ; init the isochronous data-transfer
200 mov r0,#PWMFLAG ; pwm on?
204 mov a,GPIFTRIG ; GPIF status
205 anl a,#80h ; done bit
206 jz mloop2 ; GPIF still busy
208 mov a,#01h ; WR,EP4, 01 = EP4
209 mov GPIFTRIG,a ; restart it
211 sjmp mloop2 ; loop for ever
214 ;;; GPIF waveform for PWM
216 ;; 0 1 2 3 4 5 6 7(not used)
217 ;; len (gives 50.007Hz)
218 .db 195, 195, 195, 195, 195, 195, 1, 1
221 .db 002H, 006H, 002H, 002H, 002H, 002H, 002H, 002H
224 .db 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH
227 .db 000H, 000H, 000H, 000H, 000H, 000H, 000H, 000H
231 mov r0,#PWMFLAG ; flag for PWM
232 mov a,#0 ; PWM (for the main loop)
235 mov dptr,#IFCONFIG ; switch off GPIF
236 mov a,#10000000b ; gpif, 30MHz, internal IFCLK
243 mov dptr,#IFCONFIG ; switch on IFCLK signal
244 mov a,#10000010b ; gpif, 30MHz, internal IFCLK
247 mov OEB,0FFH ; output to port B
250 mov a,#10100000b ; valid, out, bulk
253 ;; reset the endpoint
257 mov a,#84h ; reset EP4 + NAK
263 mov a,#0H ; discard packets
264 lcall syncdelaywr ; empty FIFO buffer
265 lcall syncdelaywr ; empty FIFO buffer
267 ;; aborts all transfers by the GPIF
269 mov a,#0ffh ; abort all transfers
272 ;; wait for GPIF to finish
274 mov a,GPIFTRIG ; GPIF status
275 anl a,#80h ; done bit
276 jz wait_f_abort ; GPIF busy
279 mov a,#10000000b ; tri state for CTRL
282 mov dptr,#GPIFIDLECTL
283 mov a,#11110000b ; all CTL outputs low
286 ;; abort if FIFO is empty
287 mov a,#00000001b ; abort if empty
288 mov dptr,#EP4GPIFFLGSEL
292 mov a,#00000001b ; stop if GPIF flg
293 mov dptr,#EP4GPIFPFSTOP
296 ;; transaction counter
301 ;; transaction counter
306 ;; transaction counter
307 mov a,#0ffH ; 512 bytes
311 ;; transaction counter
316 ;; RDY pins. Not used here.
318 mov dptr,#GPIFREADYCFG
321 ;; drives the output in the IDLE state
326 ;; direct data transfer from the EP to the GPIF
328 mov a,#00010000b ; autoout=1, byte-wide
331 ;; waveform 0 is used for FIFO out
332 mov dptr,#GPIFWFSELECT
337 ;; transfer the delay byte from the EP to the waveform
338 mov dptr,#0e781h ; EP1 buffer
339 movx a,@dptr ; get the delay
340 mov dptr,#waveform ; points to the waveform
341 mov r2,#6 ; fill 6 bytes
343 movx @dptr,a ; save timing in a xxx
345 djnz r2,timloop ; fill the 6 delay bytes
348 mov AUTOPTRH2,#0E4H ; XDATA0H
350 mov AUTOPTRL2,#00H ; XDATA0L
353 mov dptr,#waveform ; points to the waveform
355 mov AUTOPTRSETUP,#7 ; autoinc and enable
358 mov r2,#20H ; 32 bytes to transfer
381 mov r0,#PWMFLAG ; flag for PWM
382 mov a,#1 ; PWM (for the main loop)
389 ;;; initialise the ports for the AD-converter
391 mov OEA,#27H ;PortA0,A1,A2,A5 Outputs
392 mov IOA,#22H ;/CS = 1, disable transfers to the converters
396 ;;; init the timer for the soft counters
398 ;; init the timer for 2ms sampling rate
399 mov CKCON,#00000001b; CLKOUT/12 for timer
402 mov IE,#82H ; switch on timer interrupt (80H for all IRQs)
403 mov TMOD,#00000000b ; 13 bit counters
404 setb TCON.4 ; enable timer 0
408 ;;; from here it's only IRQ handling...
411 ;;; control-byte in a,
412 ;;; result in r3(low) and r4(high)
413 ;;; this routine is optimised for speed
414 readAD: ; mask the control byte
415 anl a,#01111100b ; only the channel, gain+pol are left
416 orl a,#10000001b ; start bit, external clock
418 clr IOA.1 ; set /CS to zero
419 ;; send the control byte to the AD-converter
420 mov R2,#8 ; bit-counter
421 bitlp: jnb ACC.7,bitzero ; jump if Bit7 = 0?
422 setb IOA.2 ; set the DIN bit
423 sjmp clock ; continue with the clock
424 bitzero:clr IOA.2 ; clear the DIN bit
425 clock: setb IOA.0 ; SCLK = 1
430 ;; continue the aquisition (already started)
431 clr IOA.2 ; clear the DIN bit
432 mov R2,#5 ; five steps for the aquision
433 clockaq:setb IOA.0 ; SCLK = 1
435 djnz R2,clockaq ; loop
437 ;; read highbyte from the A/D-converter
438 ;; and do the conversion
439 mov r4,#0 ; Highbyte goes into R4
440 mov R2,#4 ; COUNTER 4 data bits in the MSB
441 mov r5,#08h ; create bit-mask
442 gethi: ; loop get the 8 highest bits from MSB downw
443 setb IOA.0 ; SCLK = 1
445 mov a,IOA ; from port A
446 jnb ACC.4,zerob ; the in-bit is zero
447 mov a,r4 ; get the byte
448 orl a,r5 ; or the bit to the result
449 mov r4,a ; save it again in r4
450 zerob: mov a,r5 ; get r5 in order to shift the mask
452 mov r5,a ; back to r5
454 ;; read the lowbyte from the A/D-converter
455 mov r3,#0 ; Lowbyte goes into R3
456 mov r2,#8 ; COUNTER 8 data-bits in the LSB
457 mov r5,#80h ; create bit-mask
458 getlo: ; loop get the 8 highest bits from MSB downw
459 setb IOA.0 ; SCLK = 1
461 mov a,IOA ; from port A
462 jnb ACC.4,zerob2 ; the in-bit is zero
463 mov a,r3 ; get the result-byte
464 orl a,r5 ; or the bit to the result
465 mov r3,a ; save it again in r4
466 zerob2: mov a,r5 ; get r5 in order to shift the mask
468 mov r5,a ; back to r5
470 setb IOA.1 ; set /CS to one
476 ;;; aquires data from A/D channels and stores them in the EP6 buffer
478 mov AUTOPTRH1,#0F8H ; auto pointer on EP6
481 mov r0,#CHANNELLIST ; points to the channellist
483 mov a,@r0 ; number of channels
486 mov DPTR,#XAUTODAT1 ; auto pointer
502 ;;; initilise the transfer
503 ;;; It is assumed that the USB interface is in alternate setting 3
507 movx @dptr,a ; reset all fifos
517 movx @dptr,a ; normal operat
520 mov a,#10010010b ; valid, out, double buff, iso
524 mov a,#00000000b ; manual
527 mov dptr,#EP2BCL ; "arm" it
529 movx @DPTR,a ; can receive data
530 lcall syncdelay ; wait to sync
531 movx @DPTR,a ; can receive data
532 lcall syncdelay ; wait to sync
533 movx @DPTR,a ; can receive data
534 lcall syncdelay ; wait to sync
537 mov a,#10100000b ; valid
540 mov dptr,#EP1OUTBC ; "arm" it
542 movx @DPTR,a ; can receive data
543 lcall syncdelay ; wait until we can write again
544 movx @dptr,a ; make shure its really empty
545 lcall syncdelay ; wait
547 mov DPTR,#EP6CFG ; ISO data from here to the host
548 mov a,#11010010b ; Valid
549 movx @DPTR,a ; ISO transfer, double buffering
551 mov DPTR,#EP8CFG ; EP8
552 mov a,#11100000b ; BULK data from here to the host
555 mov dptr,#EPIE ; interrupt enable
556 mov a,#10001000b ; enable irq for ep1out,8
559 mov dptr,#EPIRQ ; clear IRQs
564 mov DPTR,#USBIE ; USB int enables register
565 mov a,#2 ; enables SOF (1ms/125us interrupt)
568 mov EIE,#00000001b ; enable INT2 in the 8051's SFR
569 mov IE,#80h ; IE, enable all interrupts
576 ;;; r1: counter address
581 mov a,IOB ; actual IOB input state
582 mov r5,a ; save in r5
583 anl a,r3 ; bit mask for reset
584 jz no_reset ; reset if one
585 clr a ; set counter to zero
591 mov a,@r0 ; get last state
592 xrl a,r5 ; has it changed?
593 anl a,r5 ; is it now on?
594 anl a,r4 ; mask out the port
595 jz ctr_end ; no rising edge
596 mov a,r5 ; get port B again
597 anl a,r2 ; test if up or down
598 jnz ctr_up ; count up
602 cjne a,#0ffh,ctr_end ; underflow?
622 ;;; implements two soft counters with up/down and reset
634 mov r0,#DIOSTAT0 ; status of port
635 mov r1,#CTR0 ; address of counter0
636 mov a,#00000001b ; bit 0
641 mov r3,a ; reset mask
668 ;;; interrupt-routine for SOF
669 ;;; is for full speed
689 jnz epfull ; EP6-buffer is full
691 lcall conv_ad ; conversion
693 mov DPTR,#EP6BCH ; byte count H
695 lcall syncdelaywr ; wait until we can write again
697 mov DPTR,#EP6BCL ; byte count L
698 mov a,#10H ; is 8x word = 16 bytes
699 lcall syncdelaywr ; wait until we can write again
702 ;; do the D/A conversion
705 jnz epempty ; nothing to get
707 mov dptr,#0F000H ; EP2 fifo buffer
708 lcall dalo ; conversion
710 mov dptr,#EP2BCL ; "arm" it
712 lcall syncdelaywr ; wait for the rec to sync
713 lcall syncdelaywr ; wait for the rec to sync
717 mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
719 mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
721 mov DPTR,#USBIRQ ; points to the SOF
722 mov a,#2 ; clear the SOF
745 ;; erase all data in ep8
753 mov a,#0 ; normal operation
759 ;; throw out old data
767 mov a,#0 ; normal operation
771 ;;; interrupt-routine for ep1out
772 ;;; receives the channel list and other commands
790 mov dptr,#0E780h ; FIFO buffer of EP1OUT
791 movx a,@dptr ; get the first byte
792 mov r0,#CMD_FLAG ; pointer to the command byte
793 mov @r0,a ; store the command byte for ep8
795 mov dptr,#ep1out_jmp; jump table for the different functions
796 rl a ; multiply by 2: sizeof sjmp
797 jmp @a+dptr ; jump to the jump table
798 ;; jump table, corresponds to the command bytes defined
801 sjmp storechannellist; a=0
803 sjmp config_digital_b; a=2
804 sjmp write_digital_b ; a=3
805 sjmp storesglchannel ; a=4
806 sjmp readcounter ; a=5
807 sjmp writecounter ; a=6
821 lcall reset_ep8 ; reset ep8
822 lcall ep8_ops ; fill the counter data in there
823 sjmp over_da ; jump to the end
825 ;; write zeroes to the counters
827 mov dptr,#0e781h ; buffer
828 mov r0,#CTR0 ; r0 points to counter 0
829 movx a,@dptr ; channel number
830 jz wrctr0 ; first channel
833 inc r0 ; next counter
834 inc r0 ; next counter
835 djnz r1,wrctrl ; advance to the right counter
837 inc dptr ; get to the value
838 movx a,@dptr ; get value
839 mov @r0,a ; save in ctr
842 movx a,@dptr ; get value
843 mov @r0,a ; save in ctr
844 sjmp over_da ; jump to the end
847 mov r0,#SGLCHANNEL ; the conversion bytes are now stored in 80h
848 mov dptr,#0e781h ; FIFO buffer of EP1OUT
852 lcall reset_ep8 ; reset FIFO
853 ;; Save new A/D data in EP8. This is the first byte
854 ;; the host will read during an INSN. If there are
855 ;; more to come they will be handled by the ISR of
857 lcall ep8_ops ; get A/D data
863 ;;; the first byte is zero:
864 ;;; we've just received the channel list
865 ;;; the channel list is stored in the addresses from CHANNELLIST which
866 ;;; are _only_ reachable by indirect addressing
868 mov r0,#CHANNELLIST ; the conversion bytes are now stored in 80h
870 mov dptr,#0e781h ; FIFO buffer of EP1OUT
878 lcall reset_ep6 ; reset FIFO
880 ;; load new A/D data into EP6
881 ;; This must be done. Otherwise the ISR is never called.
882 ;; The ISR is only called when data has _left_ the
883 ;; ep buffer here it has to be refilled.
884 lcall ep6_arm ; fill with the first data byte
888 ;;; Single DA conversion. The 2 bytes are in the FIFO buffer
890 mov dptr,#0e781h ; FIFO buffer of EP1OUT
891 lcall dalo ; conversion
894 ;;; configure the port B as input or output (bitwise)
896 mov dptr,#0e781h ; FIFO buffer of EP1OUT
897 movx a,@dptr ; get the second byte
898 mov OEB,a ; set the output enable bits
901 ;;; Write one byte to the external digital port B
902 ;;; and prepare for digital read
904 mov dptr,#0e781h ; FIFO buffer of EP1OUT
905 movx a,@dptr ; get the second byte
906 mov OEB,a ; output enable
909 mov IOB,a ; send the byte to the I/O port
911 lcall reset_ep8 ; reset FIFO of ep 8
913 ;; fill ep8 with new data from port B
914 ;; When the host requests the data it's already there.
915 ;; This must be so. Otherwise the ISR is not called.
916 ;; The ISR is only called when a packet has been delivered
917 ;; to the host. Thus, we need a packet here in the
919 lcall ep8_ops ; get digital data
922 ;; for all commands the same
926 lcall syncdelaywr ; arm
927 lcall syncdelaywr ; arm
928 lcall syncdelaywr ; arm
931 mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
933 mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
936 mov a,#00001000b ; clear the ep1outirq
960 movx a,@dptr ; number of channels
961 inc dptr ; pointer to the first channel
962 mov r0,a ; 4 channels
964 movx a,@dptr ; get the first low byte
965 mov r3,a ; store in r3 (see below)
966 inc dptr ; point to the high byte
967 movx a,@dptr ; get the high byte
968 mov r4,a ; store in r4 (for writeDA)
969 inc dptr ; point to the channel number
970 movx a,@dptr ; get the channel number
971 inc dptr ; get ready for the next channel
972 lcall writeDA ; write value to the DAC
973 djnz r0,nextDA ; next channel
979 ;;; control-byte in a,
980 ;;; value in r3(low) and r4(high)
981 writeDA: ; mask the control byte
982 anl a,#11000000b ; only the channel is left
983 orl a,#00110000b ; internal clock, bipolar mode, +/-5V
984 orl a,r4 ; or the value of R4 to it
986 clr IOA.5 ; set /CS to zero
987 ;; send the first byte to the DA-converter
988 mov R2,#8 ; bit-counter
989 DA1: jnb ACC.7,zeroda ; jump if Bit7 = 0?
990 setb IOA.2 ; set the DIN bit
991 sjmp clkda ; continue with the clock
992 zeroda: clr IOA.2 ; clear the DIN bit
993 clkda: setb IOA.0 ; SCLK = 1
999 ;; send the second byte to the DA-converter
1001 mov R2,#8 ; bit-counter
1002 DA2: jnb ACC.7,zeroda2 ; jump if Bit7 = 0?
1003 setb IOA.2 ; set the DIN bit
1004 sjmp clkda2 ; continue with the clock
1005 zeroda2:clr IOA.2 ; clear the DIN bit
1006 clkda2: setb IOA.0 ; SCLK = 1
1007 clr IOA.0 ; SCLK = 0
1011 setb IOA.5 ; set /CS to one
1021 mov DPTR,#EP6BCH ; byte count H
1023 lcall syncdelaywr ; wait until the length has arrived
1025 mov DPTR,#EP6BCL ; byte count L
1027 lcall syncdelaywr ; wait until the length has been proc
1032 ;;; converts one analog/digital channel and stores it in EP8
1033 ;;; also gets the content of the digital ports B and D depending on
1034 ;;; the COMMAND flag
1036 mov dptr,#0fc01h ; ep8 fifo buffer
1038 movx @dptr,a ; set H=0
1039 mov dptr,#0fc00h ; low byte
1042 movx @dptr,a ; save command byte
1044 mov dptr,#ep8_jmp ; jump table for the different functions
1045 rl a ; multiply by 2: sizeof sjmp
1046 jmp @a+dptr ; jump to the jump table
1047 ;; jump table, corresponds to the command bytes defined
1050 sjmp ep8_err ; a=0, err
1051 sjmp ep8_err ; a=1, err
1052 sjmp ep8_err ; a=2, err
1053 sjmp ep8_dio ; a=3, digital read
1054 sjmp ep8_sglchannel ; a=4, analog A/D
1055 sjmp ep8_readctr ; a=5, read counter
1056 sjmp ep8_err ; a=6, write counter
1058 ;; reads all counters
1060 mov r0,#CTR0 ; points to counter0
1061 mov dptr,#0fc02h ; ep8 fifo buffer
1062 mov r1,#8 ; transfer 4 16bit counters
1064 mov a,@r0 ; get the counter
1065 movx @dptr,a ; save in the fifo buffer
1066 inc r0 ; inc pointer to the counters
1067 inc dptr ; inc pointer to the fifo buffer
1068 djnz r1,ep8_ctrlp ; loop until ready
1070 sjmp ep8_send ; send the data
1072 ;; read one A/D channel
1074 mov r0,#SGLCHANNEL ; points to the channel
1077 lcall readAD ; start the conversion
1079 mov DPTR,#0fc02h ; EP8 FIFO
1080 mov a,R3 ; get low byte
1081 movx @DPTR,A ; store in FIFO
1082 inc dptr ; next fifo entry
1083 mov a,R4 ; get high byte
1084 movx @DPTR,A ; store in FIFO
1086 sjmp ep8_send ; send the data
1088 ;; read the digital lines
1090 mov DPTR,#0fc02h ; store the contents of port B
1091 mov a,IOB ; in the next
1092 movx @dptr,a ; entry of the buffer
1095 clr a ; high byte is zero
1096 movx @dptr,a ; next byte of the EP
1099 mov DPTR,#EP8BCH ; byte count H
1103 mov DPTR,#EP8BCL ; byte count L
1104 mov a,#10H ; 16 bytes
1105 lcall syncdelaywr ; send the data over to the host
1112 ;;; EP8 interrupt: gets one measurement from the AD converter and
1113 ;;; sends it via EP8. The channel # is stored in address 80H.
1114 ;;; It also gets the state of the digital registers B and D.
1135 mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
1137 mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
1140 mov a,#10000000b ; clear the ep8irq
1161 ;; need to delay every time the byte counters
1162 ;; for the EPs have been changed.