2 ; Copyright (C) 2010,2011 Bernd Porr, Bernd.Porr@f2s.com
3 ; For usbduxsigma.c 0.5+
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: usbduxsigma_firmware.asm for usbduxsigma.c
21 ; Description: University of Stirling USB DAQ & INCITE Technology Limited
22 ; Devices: [ITL] USB-DUX-SIGMA (usbduxsigma.ko)
23 ; Author: Bernd Porr <Bernd.Porr@f2s.com>
24 ; Updated: 24 Jul 2011
34 .equ CMD_FLAG,80h ; flag for the next in transfer
35 .equ PWMFLAG,81h ; PWM on or off?
36 .equ MAXSMPL,82H ; maximum number of samples, n channellist
37 .equ MUXSG0,83H ; content of the MUXSG0 register
40 .org 0000h ; after reset the processor starts here
41 ljmp main ; jump to the main loop
43 .org 0043h ; the IRQ2-vector
44 ljmp jmptbl ; irq service-routine
46 .org 0100h ; start of the jump table
48 jmptbl: ljmp sudav_isr
148 ;; clear the USB2 irq bit and return
165 ;;; basically only initialises the processor and
166 ;;; then engages in an endless loop
168 mov DPTR,#CPUCS ; CPU control register
169 mov a,#00010000b ; 48Mhz
173 mov a,#00000011b ; allows skip
176 mov IP,#0 ; all std 8051 int have low priority
177 mov EIP,#0FFH ; all FX2 interrupts have high priority
179 mov dptr,#INTSETUP ; IRQ setup register
180 mov a,#08h ; enable autovector
187 lcall initAD ; init the ports to the converters
189 lcall initeps ; init the isochronous data-transfer
191 ;;; main loop, rest is done as interrupts
195 mov r0,#PWMFLAG ; pwm on?
199 mov a,GPIFTRIG ; GPIF status
200 anl a,#80h ; done bit
201 jz mloop2 ; GPIF still busy
203 mov a,#01h ; WR,EP4, 01 = EP4
204 mov GPIFTRIG,a ; restart it
206 sjmp mloop2 ; loop for ever
209 ;;; initialise the ports for the AD-converter
211 mov r0,#MAXSMPL ; length of channellist
212 mov @r0,#0 ; we don't want to accumlate samples
214 mov OEA,#11100000b ; PortA7,A6,A5 Outputs
215 mov IOA,#01100000b ; /CS = 1 and START = 0
216 mov dptr,#IFCONFIG ; switch on clock on IFCLK pin
217 mov a,#10100000b ; gpif, 30MHz, internal IFCLK -> 15MHz for AD
220 mov SCON0,#013H ; ser rec en, TX/RX: stop, 48/12MHz=4MHz clock
223 mov a,#00001000b ; special function for port E: RXD0OUT
229 ;;; send a byte via SPI
230 ;;; content in a, dptr1 is changed
231 ;;; the lookup is done in dptr1 so that the normal dptr is not affected
232 ;;; important: /cs needs to be reset to 1 by the caller: IOA.5
237 mov dptr,#swap_lut ; lookup table
238 movc a,@a+dptr ; reverse bits
240 ;; clear interrupt flag, is used to detect
241 ;; successful transmission
242 clr SCON0.1 ; clear interrupt flag
244 ;; start transmission by writing the byte
245 ;; in the transmit buffer
246 mov SBUF0,a ; start transmission
248 ;; wait for the end of the transmission
250 mov a,SCON0 ; get transmission status
251 jnb ACC.1,sendSPIwait ; loop until transmitted
260 ;;; receive a byte via SPI
261 ;;; content in a, dptr is changed
262 ;;; the lookup is done in dptr1 so that the normal dptr is not affected
263 ;;; important: the /CS needs to be set to 1 by the caller via "setb IOA.5"
269 ;; clearning the RI bit starts reception of data
273 ;; RI goes back to 1 after the reception of the 8 bits
274 mov a,SCON0 ; get receive status
275 jnb ACC.0,recSPIwait; loop until all bits received
277 ;; read the byte from the buffer
278 mov a,SBUF0 ; get byte
280 ;; lookup: reverse the bits
281 mov dptr,#swap_lut ; lookup table
282 movc a,@a+dptr ; reverse the bits
292 ;;; register address in a
293 ;;; returns value in a
295 anl a,#00001111b ; mask out the index to the register
296 orl a,#01000000b ; 010xxxxx indicates register read
297 clr IOA.5 ; ADC /cs to 0
298 lcall sendSPI ; send the command over
299 lcall recSPI ; read the contents back
300 setb IOA.5 ; ADC /cs to 1
305 ;;; writes to a register
306 ;;; register address in a
310 anl a,#00001111b ; mask out the index to the register
311 orl a,#01100000b ; 011xxxxx indicates register write
313 clr IOA.5 ; ADC /cs to 0
319 setb IOA.5 ; ADC /cs to 1
322 lcall registerRead ; check if the data has arrived in the ADC
323 mov 0f0h,r0 ; register B
324 cjne a,0f0h,registerWrite ; something went wrong, try again
330 ;;; initilise the endpoints
334 movx @dptr,a ; reset all fifos
344 movx @dptr,a ; normal operat
347 mov a,#10010010b ; valid, out, double buff, iso
351 mov a,#00000000b ; manual
354 mov dptr,#EP2BCL ; "arm" it
356 movx @DPTR,a ; can receive data
357 lcall syncdelay ; wait to sync
358 movx @DPTR,a ; can receive data
359 lcall syncdelay ; wait to sync
360 movx @DPTR,a ; can receive data
361 lcall syncdelay ; wait to sync
364 mov a,#10100000b ; valid
367 mov dptr,#EP1OUTBC ; "arm" it
369 movx @DPTR,a ; can receive data
370 lcall syncdelay ; wait until we can write again
371 movx @dptr,a ; make shure its really empty
372 lcall syncdelay ; wait
374 mov DPTR,#EP6CFG ; ISO data from here to the host
375 mov a,#11010010b ; Valid
376 movx @DPTR,a ; ISO transfer, double buffering
378 mov DPTR,#EP8CFG ; EP8
379 mov a,#11100000b ; BULK data from here to the host
383 mov dptr,#EPIE ; interrupt enable
384 mov a,#10001000b ; enable irq for ep1out,8
387 mov dptr,#EPIRQ ; clear IRQs
391 mov DPTR,#USBIE ; USB int enables register
392 mov a,#2 ; enables SOF (1ms/125us interrupt)
395 mov EIE,#00000001b ; enable INT2/USBINT in the 8051's SFR
396 mov IE,#80h ; IE, enable all interrupts
401 ;;; Reads one ADC channel from the converter and stores
402 ;;; the result at dptr
404 ;; we do polling: we wait until DATA READY is zero
405 mov a,IOA ; get /DRDY
406 jb ACC.0,readADCch ; wait until data ready (DRDY=0)
408 ;; reading data is done by just dropping /CS and start reading and
409 ;; while keeping the IN signal to the ADC inactive
414 movx @dptr,a ; store the byte
415 inc dptr ; increment pointer
433 setb IOA.5 ; /cs to 1
439 ;;; interrupt-routine for SOF
457 clr IE.7 ; make sure that no other int's disturbe us
461 jnz epfull ; EP6-buffer is full
463 clr IOA.7 ; stop converter, START = 0
465 ;; make sure that we are starting with the first channel
467 mov a,@r0 ; get config of MUXSG0
470 lcall registerWrite ; this resets the channel sequence
472 setb IOA.7 ; start converter, START = 1
474 ;; get the data from the ADC as fast as possible and transfer it
476 mov dptr,#0f800h ; EP6 buffer
477 mov a,IOD ; get DIO D
478 movx @dptr,a ; store it
480 mov a,IOC ; get DIO C
481 movx @dptr,a ; store it
483 mov a,IOB ; get DIO B
484 movx @dptr,a ; store it
487 movx @dptr,a ; pad it up
488 inc dptr ; algin along a 32 bit word
490 mov r0,#MAXSMPL ; number of samples to transmit
494 ;; main loop, get all the data
496 lcall readADCch ; get one reading
497 djnz r1,eptrans ; do until we have all content transf'd
499 clr IOA.7 ; stop converter, START = 0
501 ;; arm the endpoint and send off the data
502 mov DPTR,#EP6BCH ; byte count H
504 lcall syncdelaywr ; wait until we can write again
506 mov r0,#MAXSMPL ; number of samples to transmit
510 add a,#4 ; four bytes for DIO
511 mov DPTR,#EP6BCL ; byte count L
512 lcall syncdelaywr ; wait until we can write again
515 ;; do the D/A conversion
518 jnz epempty ; nothing to get
520 mov dptr,#0F000H ; EP2 fifo buffer
521 lcall dalo ; conversion
523 mov dptr,#EP2BCL ; "arm" it
525 lcall syncdelaywr ; wait for the rec to sync
526 lcall syncdelaywr ; wait for the rec to sync
530 mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
532 mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
534 mov DPTR,#USBIRQ ; points to the SOF
535 mov a,#2 ; clear the SOF
539 setb IE.7 ; re-enable global interrupts
560 ;; erase all data in ep8
568 mov a,#0 ; normal operation
574 ;; throw out old data
582 mov a,#0 ; normal operation
587 ;;; configure the ADC converter
588 ;;; the dptr points to the init data:
589 ;;; CONFIG 0,1,3,4,5,6
590 ;;; note that CONFIG2 is omitted
592 clr IOA.7 ; stops ADC: START line of ADC = L
593 setb IOA.5 ; ADC /cs to 1
595 ;; just in case something has gone wrong
600 mov a,#11000000b ; reset the ADC
601 clr IOA.5 ; ADC /cs to 0
603 setb IOA.5 ; ADC /cs to 1
626 mov @r0,a ; store it for reset purposes
646 ;;; interrupt-routine for ep1out
647 ;;; receives the channel list and other commands
665 clr IE.7 ; block other interrupts
667 mov dptr,#0E780h ; FIFO buffer of EP1OUT
668 movx a,@dptr ; get the first byte
669 mov r0,#CMD_FLAG ; pointer to the command byte
670 mov @r0,a ; store the command byte for ep8
672 mov dptr,#ep1out_jmp; jump table for the different functions
673 rl a ; multiply by 2: sizeof sjmp
674 jmp @a+dptr ; jump to the jump table
675 ;; jump table, corresponds to the command bytes defined
680 sjmp config_digital_b; a=2
681 sjmp write_digital_b ; a=3
682 sjmp initsgADchannel ; a=4
700 mov dptr,#0e781h ; FIFO buffer of EP1OUT
701 lcall configADC ; configures the ADC esp sel the channel
703 lcall reset_ep8 ; reset FIFO: get rid of old bytes
704 ;; Save new A/D data in EP8. This is the first byte
705 ;; the host will read during an INSN. If there are
706 ;; more to come they will be handled by the ISR of
708 lcall ep8_ops ; get A/D data
714 ;;; we write to the registers of the A/D converter
716 mov dptr,#0e781h ; FIFO buffer of EP1OUT from 2nd byte
718 movx a,@dptr ; get length of channel list
721 mov @r0,a ; length of the channel list
723 lcall configADC ; configures all registers
725 lcall reset_ep6 ; reset FIFO
727 ;; load new A/D data into EP6
728 ;; This must be done. Otherwise the ISR is never called.
729 ;; The ISR is only called when data has _left_ the
730 ;; ep buffer here it has to be refilled.
731 lcall ep6_arm ; fill with dummy data
735 ;;; Single DA conversion. The 2 bytes are in the FIFO buffer
737 mov dptr,#0e781h ; FIFO buffer of EP1OUT
738 lcall dalo ; conversion
741 ;;; configure the port B as input or output (bitwise)
743 mov dptr,#0e781h ; FIFO buffer of EP1OUT
744 movx a,@dptr ; get the second byte
746 mov OEB,a ; set the output enable bits
747 movx a,@dptr ; get the second byte
750 movx a,@dptr ; get the second byte
755 ;;; Write one byte to the external digital port B
756 ;;; and prepare for digital read
758 mov dptr,#0e781h ; FIFO buffer of EP1OUT
759 movx a,@dptr ; command[1]
761 mov OEB,a ; output enable
762 movx a,@dptr ; command[2]
765 movx a,@dptr ; command[3]
768 movx a,@dptr ; command[4]
771 movx a,@dptr ; command[5]
774 movx a,@dptr ; command[6]
778 lcall reset_ep8 ; reset FIFO of ep 8
780 ;; fill ep8 with new data from port B
781 ;; When the host requests the data it's already there.
782 ;; This must be so. Otherwise the ISR is not called.
783 ;; The ISR is only called when a packet has been delivered
784 ;; to the host. Thus, we need a packet here in the
786 lcall ep8_ops ; get digital data
789 ;; for all commands the same
793 lcall syncdelaywr ; arm
794 lcall syncdelaywr ; arm
795 lcall syncdelaywr ; arm
798 mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
800 mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
803 mov a,#00001000b ; clear the ep1outirq
806 setb IE.7 ; re-enable interrupts
829 movx a,@dptr ; number of bytes to send out
830 inc dptr ; pointer to the first byte
833 movx a,@dptr ; get the byte
834 inc dptr ; point to the high byte
835 mov r3,a ; store in r3 for writeDA
836 movx a,@dptr ; get the channel number
837 inc dptr ; get ready for the next channel
838 lcall writeDA ; write value to the DAC
839 djnz r0,nextDA ; next channel
845 ;;; channel number in a
848 anl a,#00000011b ; 4 channels
849 mov r1,#6 ; the channel number needs to be shifted up
851 rl a ; bit shift to the left
852 djnz r1,writeDA2 ; do it 6 times
853 orl a,#00010000b ; update outputs after write
856 anl a,#11110000b ; get the upper nibble
857 mov r1,#4 ; shift it up to the upper nibble
859 rr a ; shift to the upper to the lower
861 orl a,r2 ; merge with the channel info
862 clr IOA.6 ; /SYNC of the DA to 0
863 lcall sendSPI ; send it out to the SPI
864 mov a,r3 ; get data again
865 anl a,#00001111b ; get the lower nibble
866 mov r1,#4 ; shift that to the upper
870 anl a,#11110000b ; make sure that's empty
872 setb IOA.6 ; /SYNC of the DA to 1
877 ;;; arm ep6: this is just a dummy arm to get things going
879 mov DPTR,#EP6BCH ; byte count H
881 lcall syncdelaywr ; wait until the length has arrived
883 mov DPTR,#EP6BCL ; byte count L
885 lcall syncdelaywr ; wait until the length has been proc
890 ;;; converts one analog/digital channel and stores it in EP8
891 ;;; also gets the content of the digital ports B,C and D depending on
894 mov dptr,#0fc01h ; ep8 fifo buffer
896 movx @dptr,a ; set H=0
897 mov dptr,#0fc00h ; low byte
900 movx @dptr,a ; save command byte
902 mov dptr,#ep8_jmp ; jump table for the different functions
903 rl a ; multiply by 2: sizeof sjmp
904 jmp @a+dptr ; jump to the jump table
905 ;; jump table, corresponds to the command bytes defined
908 sjmp ep8_err ; a=0, err
909 sjmp ep8_err ; a=1, err
910 sjmp ep8_err ; a=2, err
911 sjmp ep8_dio ; a=3, digital read
912 sjmp ep8_sglchannel ; a=4, analog A/D
913 sjmp ep8_err ; a=5, err
914 sjmp ep8_err ; a=6, err
916 ;; read one A/D channel
918 mov DPTR,#0fc01h ; EP8 FIFO
919 setb IOA.7 ; start converter, START = 1
920 lcall readADCch ; get one reading
921 clr IOA.7 ; stop the converter, START = 0
923 sjmp ep8_send ; send the data
925 ;; read the digital lines
927 mov DPTR,#0fc01h ; store the contents of port B
928 mov a,IOB ; in the next
929 movx @dptr,a ; entry of the buffer
932 movx @dptr,a ; next byte of the EP
935 movx @dptr,a ; port D
938 mov DPTR,#EP8BCH ; byte count H
942 mov DPTR,#EP8BCL ; byte count L
943 mov a,#10H ; 16 bytes, bec it's such a great number...
944 lcall syncdelaywr ; send the data over to the host
951 ;;; EP8 interrupt is the endpoint which sends data back after a command
952 ;;; The actual command fills the EP buffer already
953 ;;; but for INSNs we need to deliver more data if the count > 1
974 mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
976 mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
979 mov a,#10000000b ; clear the ep8irq
1001 ;;; GPIF waveform for PWM
1003 ;; 0 1 2 3 4 5 6 7(not used)
1004 ;; len (gives 50.007Hz)
1005 .db 195, 195, 195, 195, 195, 195, 1, 1
1008 .db 002H, 006H, 002H, 002H, 002H, 002H, 002H, 002H
1011 .db 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH
1014 .db 000H, 000H, 000H, 000H, 000H, 000H, 000H, 000H
1018 mov r0,#PWMFLAG ; flag for PWM
1019 mov a,#0 ; PWM (for the main loop)
1022 mov dptr,#IFCONFIG ; switch off GPIF
1023 mov a,#10100000b ; gpif, 30MHz, internal IFCLK
1030 mov dptr,#IFCONFIG ; switch on IFCLK signal
1031 mov a,#10100010b ; gpif, 30MHz, internal IFCLK
1034 mov OEB,0FFH ; output to port B
1037 mov a,#10100000b ; valid, out, bulk
1040 ;; reset the endpoint
1044 mov a,#84h ; reset EP4 + NAK
1046 mov a,#0 ; normal op
1050 mov a,#0H ; discard packets
1051 lcall syncdelaywr ; empty FIFO buffer
1052 lcall syncdelaywr ; empty FIFO buffer
1054 ;; aborts all transfers by the GPIF
1056 mov a,#0ffh ; abort all transfers
1059 ;; wait for GPIF to finish
1061 mov a,GPIFTRIG ; GPIF status
1062 anl a,#80h ; done bit
1063 jz wait_f_abort ; GPIF busy
1065 mov dptr,#GPIFCTLCFG
1066 mov a,#10000000b ; tri state for CTRL
1069 mov dptr,#GPIFIDLECTL
1070 mov a,#11110000b ; all CTL outputs low
1073 ;; abort if FIFO is empty
1074 mov a,#00000001b ; abort if empty
1075 mov dptr,#EP4GPIFFLGSEL
1079 mov a,#00000001b ; stop if GPIF flg
1080 mov dptr,#EP4GPIFPFSTOP
1083 ;; transaction counter
1088 ;; transaction counter
1093 ;; transaction counter
1094 mov a,#0ffH ; 512 bytes
1098 ;; transaction counter
1103 ;; RDY pins. Not used here.
1105 mov dptr,#GPIFREADYCFG
1108 ;; drives the output in the IDLE state
1110 mov dptr,#GPIFIDLECS
1113 ;; direct data transfer from the EP to the GPIF
1114 mov dptr,#EP4FIFOCFG
1115 mov a,#00010000b ; autoout=1, byte-wide
1118 ;; waveform 0 is used for FIFO out
1119 mov dptr,#GPIFWFSELECT
1124 ;; transfer the delay byte from the EP to the waveform
1125 mov dptr,#0e781h ; EP1 buffer
1126 movx a,@dptr ; get the delay
1127 mov dptr,#waveform ; points to the waveform
1128 mov r2,#6 ; fill 6 bytes
1130 movx @dptr,a ; save timing in a xxx
1132 djnz r2,timloop ; fill the 6 delay bytes
1135 mov AUTOPTRH2,#0E4H ; XDATA0H
1137 mov AUTOPTRL2,#00H ; XDATA0L
1140 mov dptr,#waveform ; points to the waveform
1142 mov AUTOPTRSETUP,#7 ; autoinc and enable
1145 mov r2,#20H ; 32 bytes to transfer
1168 mov r0,#PWMFLAG ; flag for PWM
1169 mov a,#1 ; PWM (for the main loop)
1176 ;; need to delay every time the byte counters
1177 ;; for the EPs have been changed.
1198 .org 1F00h ; lookup table at the end of memory
1201 .db 0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,240,8,136
1202 .db 72,200,40,168,104,232,24,152,88,216,56,184,120,248,4,132,68,196,36,164,100
1203 .db 228,20,148,84,212,52,180,116,244,12,140,76,204,44,172,108,236,28,156,92,220
1204 .db 60,188,124,252,2,130,66,194,34,162,98,226,18,146,82,210,50,178,114,242,10
1205 .db 138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,6,134,70,198,38,166
1206 .db 102,230,22,150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,30,158,94
1207 .db 222,62,190,126,254,1,129,65,193,33,161,97,225,17,145,81,209,49,177,113,241,9
1208 .db 137,73,201,41,169,105,233,25,153,89,217,57,185,121,249,5,133,69,197,37,165
1209 .db 101,229,21,149,85,213,53,181,117,245,13,141,77,205,45,173,109,237,29,157,93
1210 .db 221,61,189,125,253,3,131,67,195,35,163,99,227,19,147,83,211,51,179,115,243,11
1211 .db 139,75,203,43,171,107,235,27,155,91,219,59,187,123,251,7,135,71,199,39,167
1212 .db 103,231,23,151,87,215,55,183,119,247,15,143,79,207,47,175,111,239,31,159,95
1213 .db 223,63,191,127,255