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
44 .org 0000h ; after reset the processor starts here
45 ljmp main ; jump to the main loop
48 ljmp isr0 ; external interrupt 0: /DRY
50 .org 0043h ; the IRQ2-vector
51 ljmp jmptbl ; irq service-routine
53 .org 0100h ; start of the jump table
55 jmptbl: ljmp sudav_isr
155 ;; clear the USB2 irq bit and return
170 ;;; this is triggered when DRY goes low
198 lcall readADCch ; read one channel
214 clr IOA.7 ; START = 0
216 ;; arm the endpoint and send off the data
217 mov DPTR,#EP6BCH ; byte count H
219 lcall syncdelaywr ; wait until we can write again
221 mov r0,#MAXSMPL ; number of samples to transmit
225 add a,#4 ; four bytes for DIO
226 mov DPTR,#EP6BCL ; byte count L
227 lcall syncdelaywr ; wait until we can write again
251 ;;; basically only initialises the processor and
252 ;;; then engages in an endless loop
254 mov DPTR,#CPUCS ; CPU control register
255 mov a,#00010000b ; 48Mhz
259 mov a,#00000011b ; allows skip
262 mov IP,#0 ; all std 8051 int have low priority
263 mov EIP,#0FFH ; all FX2 interrupts have high priority
265 mov dptr,#INTSETUP ; IRQ setup register
266 mov a,#08h ; enable autovector
273 lcall initAD ; init the ports to the converters
275 lcall initeps ; init the isochronous data-transfer
277 ;;; main loop, rest is done as interrupts
281 mov r0,#PWMFLAG ; pwm on?
285 mov a,GPIFTRIG ; GPIF status
286 anl a,#80h ; done bit
287 jz mloop2 ; GPIF still busy
289 mov a,#01h ; WR,EP4, 01 = EP4
290 mov GPIFTRIG,a ; restart it
292 sjmp mloop2 ; loop for ever
295 ;;; initialise the ports for the AD-converter
297 mov r0,#MAXSMPL ; length of channellist
298 mov @r0,#0 ; we don't want to accumlate samples
300 mov r0,#ASYNC_ON ; async enable
301 mov @r0,#0 ; we don't want to accumlate samples
303 mov OEA,#11100000b ; PortA7,A6,A5 Outputs
304 mov IOA,#01100000b ; /CS = 1 and START = 0
305 mov dptr,#IFCONFIG ; switch on clock on IFCLK pin
306 mov a,#10100000b ; gpif, 30MHz, internal IFCLK -> 15MHz for AD
309 mov SCON0,#013H ; ser rec en, TX/RX: stop, 48/12MHz=4MHz clock
312 mov a,#00001000b ; special function for port E: RXD0OUT
318 ;;; send a byte via SPI
319 ;;; content in a, dptr1 is changed
320 ;;; the lookup is done in dptr1 so that the normal dptr is not affected
321 ;;; important: /cs needs to be reset to 1 by the caller: IOA.5
326 mov dptr,#swap_lut ; lookup table
327 movc a,@a+dptr ; reverse bits
329 ;; clear interrupt flag, is used to detect
330 ;; successful transmission
331 clr SCON0.1 ; clear interrupt flag
333 ;; start transmission by writing the byte
334 ;; in the transmit buffer
335 mov SBUF0,a ; start transmission
337 ;; wait for the end of the transmission
339 mov a,SCON0 ; get transmission status
340 jnb ACC.1,sendSPIwait ; loop until transmitted
349 ;;; receive a byte via SPI
350 ;;; content in a, dptr is changed
351 ;;; the lookup is done in dptr1 so that the normal dptr is not affected
352 ;;; important: the /CS needs to be set to 1 by the caller via "setb IOA.5"
358 ;; clearning the RI bit starts reception of data
362 ;; RI goes back to 1 after the reception of the 8 bits
363 mov a,SCON0 ; get receive status
364 jnb ACC.0,recSPIwait; loop until all bits received
366 ;; read the byte from the buffer
367 mov a,SBUF0 ; get byte
369 ;; lookup: reverse the bits
370 mov dptr,#swap_lut ; lookup table
371 movc a,@a+dptr ; reverse the bits
381 ;;; register address in a
382 ;;; returns value in a
384 anl a,#00001111b ; mask out the index to the register
385 orl a,#01000000b ; 010xxxxx indicates register read
386 clr IOA.5 ; ADC /cs to 0
387 lcall sendSPI ; send the command over
388 lcall recSPI ; read the contents back
389 setb IOA.5 ; ADC /cs to 1
394 ;;; writes to a register
395 ;;; register address in a
399 anl a,#00001111b ; mask out the index to the register
400 orl a,#01100000b ; 011xxxxx indicates register write
402 clr IOA.5 ; ADC /cs to 0
408 setb IOA.5 ; ADC /cs to 1
411 lcall registerRead ; check if the data has arrived in the ADC
412 mov 0f0h,r0 ; register B
413 cjne a,0f0h,registerWrite ; something went wrong, try again
419 ;;; initilise the endpoints
423 movx @dptr,a ; reset all fifos
433 movx @dptr,a ; normal operat
436 mov a,#10010010b ; valid, out, double buff, iso
440 mov a,#00000000b ; manual
443 mov dptr,#EP2BCL ; "arm" it
445 movx @DPTR,a ; can receive data
446 lcall syncdelay ; wait to sync
447 movx @DPTR,a ; can receive data
448 lcall syncdelay ; wait to sync
449 movx @DPTR,a ; can receive data
450 lcall syncdelay ; wait to sync
453 mov a,#10100000b ; valid
456 mov dptr,#EP1OUTBC ; "arm" it
458 movx @DPTR,a ; can receive data
459 lcall syncdelay ; wait until we can write again
460 movx @dptr,a ; make shure its really empty
461 lcall syncdelay ; wait
463 mov DPTR,#EP6CFG ; ISO data from here to the host
464 mov a,#11010010b ; Valid
465 movx @DPTR,a ; ISO transfer, double buffering
467 mov DPTR,#EP8CFG ; EP8
468 mov a,#11100000b ; BULK data from here to the host
472 mov a,#1 ; interrupt on pin A0
476 mov dptr,#EPIE ; interrupt enable
477 mov a,#10001000b ; enable irq for ep1out,8
480 mov dptr,#EPIRQ ; clear IRQs
484 mov DPTR,#USBIE ; USB int enables register
485 mov a,#2 ; enables SOF (1ms/125us interrupt)
488 setb TCON.0 ; make INT0 edge triggered, falling edge
490 mov EIE,#00000001b ; enable INT2/USBINT in the 8051's SFR
491 mov IE,#81h ; IE, enable all interrupts and INT0
496 ;;; Reads one ADC channel from the converter and stores
497 ;;; the result at dptr
499 ;; reading data is done by just dropping /CS and start reading and
500 ;; while keeping the IN signal to the ADC inactive
505 movx @dptr,a ; store the byte
506 inc dptr ; increment pointer
524 setb IOA.5 ; /cs to 1
530 ;;; interrupt-routine for SOF
548 clr IE.7 ; make sure that no other int's disturbe us
552 jnz epfull ; EP6-buffer is full
554 mov a,IOA ; conversion running?
557 ;; make sure that we are starting with the first channel
559 mov a,@r0 ; get config of MUXSG0
562 lcall registerWrite ; this resets the channel sequence
564 setb IOA.7 ; start converter, START = 1
566 mov dptr,#0f800h ; EP6 buffer
567 mov a,IOD ; get DIO D
568 movx @dptr,a ; store it
570 mov a,IOC ; get DIO C
571 movx @dptr,a ; store it
573 mov a,IOB ; get DIO B
574 movx @dptr,a ; store it
577 movx @dptr,a ; pad it up
578 inc dptr ; algin along a 32 bit word
590 mov @r0,#1 ; enable data collection
593 ;; do the D/A conversion
596 jnz epempty ; nothing to get
598 mov dptr,#0F000H ; EP2 fifo buffer
599 lcall dalo ; conversion
601 mov dptr,#EP2BCL ; "arm" it
603 lcall syncdelaywr ; wait for the rec to sync
604 lcall syncdelaywr ; wait for the rec to sync
608 mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
610 mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
612 mov DPTR,#USBIRQ ; points to the SOF
613 mov a,#2 ; clear the SOF
617 setb IE.7 ; re-enable global interrupts
638 ;; erase all data in ep8
646 mov a,#0 ; normal operation
652 ;; throw out old data
660 mov a,#0 ; normal operation
665 ;;; configure the ADC converter
666 ;;; the dptr points to the init data:
667 ;;; CONFIG 0,1,3,4,5,6
668 ;;; note that CONFIG2 is omitted
670 clr IOA.7 ; stops ADC: START line of ADC = L
671 setb IOA.5 ; ADC /cs to 1
673 ;; just in case something has gone wrong
678 mov a,#11000000b ; reset the ADC
679 clr IOA.5 ; ADC /cs to 0
681 setb IOA.5 ; ADC /cs to 1
704 mov @r0,a ; store it for reset purposes
724 ;;; interrupt-routine for ep1out
725 ;;; receives the channel list and other commands
743 clr IE.7 ; block other interrupts
745 mov dptr,#0E780h ; FIFO buffer of EP1OUT
746 movx a,@dptr ; get the first byte
747 mov r0,#CMD_FLAG ; pointer to the command byte
748 mov @r0,a ; store the command byte for ep8
750 mov dptr,#ep1out_jmp; jump table for the different functions
751 rl a ; multiply by 2: sizeof sjmp
752 jmp @a+dptr ; jump to the jump table
753 ;; jump table, corresponds to the command bytes defined
758 sjmp config_digital_b; a=2
759 sjmp write_digital_b ; a=3
760 sjmp initsgADchannel ; a=4
779 mov @r0,#0 ; make sure that no async activity is on
781 mov dptr,#0e781h ; FIFO buffer of EP1OUT
782 lcall configADC ; configures the ADC esp sel the channel
784 lcall reset_ep8 ; reset FIFO: get rid of old bytes
785 ;; Save new A/D data in EP8. This is the first byte
786 ;; the host will read during an INSN. If there are
787 ;; more to come they will be handled by the ISR of
789 lcall ep8_ops ; get A/D data
795 ;;; we write to the registers of the A/D converter
797 mov dptr,#0e781h ; FIFO buffer of EP1OUT from 2nd byte
799 movx a,@dptr ; get length of channel list
802 mov @r0,a ; length of the channel list
806 lcall configADC ; configures all registers
808 mov r0,#ASYNC_ON ; async enable
809 mov @r0,#1 ; enable it
811 lcall reset_ep6 ; reset FIFO
813 ;; load new A/D data into EP6
814 ;; This must be done. Otherwise the ISR is never called.
815 ;; The ISR is only called when data has _left_ the
816 ;; ep buffer here it has to be refilled.
817 lcall ep6_arm ; fill with dummy data
821 ;;; Single DA conversion. The 2 bytes are in the FIFO buffer
823 mov dptr,#0e781h ; FIFO buffer of EP1OUT
824 lcall dalo ; conversion
827 ;;; configure the port B as input or output (bitwise)
829 mov dptr,#0e781h ; FIFO buffer of EP1OUT
830 movx a,@dptr ; get the second byte
832 mov OEB,a ; set the output enable bits
833 movx a,@dptr ; get the second byte
836 movx a,@dptr ; get the second byte
841 ;;; Write one byte to the external digital port B
842 ;;; and prepare for digital read
844 mov dptr,#0e781h ; FIFO buffer of EP1OUT
845 movx a,@dptr ; command[1]
847 mov OEB,a ; output enable
848 movx a,@dptr ; command[2]
851 movx a,@dptr ; command[3]
854 movx a,@dptr ; command[4]
857 movx a,@dptr ; command[5]
860 movx a,@dptr ; command[6]
864 lcall reset_ep8 ; reset FIFO of ep 8
866 ;; fill ep8 with new data from port B
867 ;; When the host requests the data it's already there.
868 ;; This must be so. Otherwise the ISR is not called.
869 ;; The ISR is only called when a packet has been delivered
870 ;; to the host. Thus, we need a packet here in the
872 lcall ep8_ops ; get digital data
875 ;; for all commands the same
879 lcall syncdelaywr ; arm
880 lcall syncdelaywr ; arm
881 lcall syncdelaywr ; arm
884 mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
886 mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
889 mov a,#00001000b ; clear the ep1outirq
892 setb IE.7 ; re-enable interrupts
915 movx a,@dptr ; number of bytes to send out
916 inc dptr ; pointer to the first byte
919 movx a,@dptr ; get the byte
920 inc dptr ; point to the high byte
921 mov r3,a ; store in r3 for writeDA
922 movx a,@dptr ; get the channel number
923 inc dptr ; get ready for the next channel
924 lcall writeDA ; write value to the DAC
925 djnz r0,nextDA ; next channel
931 ;;; channel number in a
934 anl a,#00000011b ; 4 channels
935 mov r1,#6 ; the channel number needs to be shifted up
937 rl a ; bit shift to the left
938 djnz r1,writeDA2 ; do it 6 times
939 orl a,#00010000b ; update outputs after write
942 anl a,#11110000b ; get the upper nibble
943 mov r1,#4 ; shift it up to the upper nibble
945 rr a ; shift to the upper to the lower
947 orl a,r2 ; merge with the channel info
948 clr IOA.6 ; /SYNC of the DA to 0
949 lcall sendSPI ; send it out to the SPI
950 mov a,r3 ; get data again
951 anl a,#00001111b ; get the lower nibble
952 mov r1,#4 ; shift that to the upper
956 anl a,#11110000b ; make sure that's empty
958 setb IOA.6 ; /SYNC of the DA to 1
963 ;;; arm ep6: this is just a dummy arm to get things going
965 mov DPTR,#EP6BCH ; byte count H
967 lcall syncdelaywr ; wait until the length has arrived
969 mov DPTR,#EP6BCL ; byte count L
971 lcall syncdelaywr ; wait until the length has been proc
976 ;;; converts one analog/digital channel and stores it in EP8
977 ;;; also gets the content of the digital ports B,C and D depending on
980 mov dptr,#0fc01h ; ep8 fifo buffer
982 movx @dptr,a ; set H=0
983 mov dptr,#0fc00h ; low byte
986 movx @dptr,a ; save command byte
988 mov dptr,#ep8_jmp ; jump table for the different functions
989 rl a ; multiply by 2: sizeof sjmp
990 jmp @a+dptr ; jump to the jump table
991 ;; jump table, corresponds to the command bytes defined
994 sjmp ep8_err ; a=0, err
995 sjmp ep8_err ; a=1, err
996 sjmp ep8_err ; a=2, err
997 sjmp ep8_dio ; a=3, digital read
998 sjmp ep8_sglchannel ; a=4, analog A/D
999 sjmp ep8_err ; a=5, err
1000 sjmp ep8_err ; a=6, err
1002 ;; read one A/D channel
1004 setb IOA.7 ; start converter, START = 1
1005 ;; we do polling: we wait until DATA READY is zero
1007 mov a,IOA ; get /DRDY
1008 jb ACC.0,sglchwait ; wait until data ready (DRDY=0)
1009 mov DPTR,#0fc01h ; EP8 FIFO
1010 lcall readADCch ; get one reading
1011 clr IOA.7 ; stop the converter, START = 0
1013 sjmp ep8_send ; send the data
1015 ;; read the digital lines
1017 mov DPTR,#0fc01h ; store the contents of port B
1018 mov a,IOB ; in the next
1019 movx @dptr,a ; entry of the buffer
1022 movx @dptr,a ; next byte of the EP
1025 movx @dptr,a ; port D
1028 mov DPTR,#EP8BCH ; byte count H
1032 mov DPTR,#EP8BCL ; byte count L
1033 mov a,#10H ; 16 bytes, bec it's such a great number...
1034 lcall syncdelaywr ; send the data over to the host
1041 ;;; EP8 interrupt is the endpoint which sends data back after a command
1042 ;;; The actual command fills the EP buffer already
1043 ;;; but for INSNs we need to deliver more data if the count > 1
1064 mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
1066 mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
1069 mov a,#10000000b ; clear the ep8irq
1091 ;;; GPIF waveform for PWM
1093 ;; 0 1 2 3 4 5 6 7(not used)
1094 ;; len (gives 50.007Hz)
1095 .db 195, 195, 195, 195, 195, 195, 1, 1
1098 .db 002H, 006H, 002H, 002H, 002H, 002H, 002H, 002H
1101 .db 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH
1104 .db 000H, 000H, 000H, 000H, 000H, 000H, 000H, 000H
1108 mov r0,#PWMFLAG ; flag for PWM
1109 mov a,#0 ; PWM (for the main loop)
1112 mov dptr,#IFCONFIG ; switch off GPIF
1113 mov a,#10100000b ; gpif, 30MHz, internal IFCLK
1120 mov dptr,#IFCONFIG ; switch on IFCLK signal
1121 mov a,#10100010b ; gpif, 30MHz, internal IFCLK
1124 mov OEB,0FFH ; output to port B
1127 mov a,#10100000b ; valid, out, bulk
1130 ;; reset the endpoint
1134 mov a,#84h ; reset EP4 + NAK
1136 mov a,#0 ; normal op
1140 mov a,#0H ; discard packets
1141 lcall syncdelaywr ; empty FIFO buffer
1142 lcall syncdelaywr ; empty FIFO buffer
1144 ;; aborts all transfers by the GPIF
1146 mov a,#0ffh ; abort all transfers
1149 ;; wait for GPIF to finish
1151 mov a,GPIFTRIG ; GPIF status
1152 anl a,#80h ; done bit
1153 jz wait_f_abort ; GPIF busy
1155 mov dptr,#GPIFCTLCFG
1156 mov a,#10000000b ; tri state for CTRL
1159 mov dptr,#GPIFIDLECTL
1160 mov a,#11110000b ; all CTL outputs low
1163 ;; abort if FIFO is empty
1164 mov a,#00000001b ; abort if empty
1165 mov dptr,#EP4GPIFFLGSEL
1169 mov a,#00000001b ; stop if GPIF flg
1170 mov dptr,#EP4GPIFPFSTOP
1173 ;; transaction counter
1178 ;; transaction counter
1183 ;; transaction counter
1184 mov a,#0ffH ; 512 bytes
1188 ;; transaction counter
1193 ;; RDY pins. Not used here.
1195 mov dptr,#GPIFREADYCFG
1198 ;; drives the output in the IDLE state
1200 mov dptr,#GPIFIDLECS
1203 ;; direct data transfer from the EP to the GPIF
1204 mov dptr,#EP4FIFOCFG
1205 mov a,#00010000b ; autoout=1, byte-wide
1208 ;; waveform 0 is used for FIFO out
1209 mov dptr,#GPIFWFSELECT
1214 ;; transfer the delay byte from the EP to the waveform
1215 mov dptr,#0e781h ; EP1 buffer
1216 movx a,@dptr ; get the delay
1217 mov dptr,#waveform ; points to the waveform
1218 mov r2,#6 ; fill 6 bytes
1220 movx @dptr,a ; save timing in a xxx
1222 djnz r2,timloop ; fill the 6 delay bytes
1225 mov AUTOPTRH2,#0E4H ; XDATA0H
1227 mov AUTOPTRL2,#00H ; XDATA0L
1230 mov dptr,#waveform ; points to the waveform
1232 mov AUTOPTRSETUP,#7 ; autoinc and enable
1235 mov r2,#20H ; 32 bytes to transfer
1258 mov r0,#PWMFLAG ; flag for PWM
1259 mov a,#1 ; PWM (for the main loop)
1266 ;; need to delay every time the byte counters
1267 ;; for the EPs have been changed.
1288 .org 1F00h ; lookup table at the end of memory
1291 .db 0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,240,8,136
1292 .db 72,200,40,168,104,232,24,152,88,216,56,184,120,248,4,132,68,196,36,164,100
1293 .db 228,20,148,84,212,52,180,116,244,12,140,76,204,44,172,108,236,28,156,92,220
1294 .db 60,188,124,252,2,130,66,194,34,162,98,226,18,146,82,210,50,178,114,242,10
1295 .db 138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,6,134,70,198,38,166
1296 .db 102,230,22,150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,30,158,94
1297 .db 222,62,190,126,254,1,129,65,193,33,161,97,225,17,145,81,209,49,177,113,241,9
1298 .db 137,73,201,41,169,105,233,25,153,89,217,57,185,121,249,5,133,69,197,37,165
1299 .db 101,229,21,149,85,213,53,181,117,245,13,141,77,205,45,173,109,237,29,157,93
1300 .db 221,61,189,125,253,3,131,67,195,35,163,99,227,19,147,83,211,51,179,115,243,11
1301 .db 139,75,203,43,171,107,235,27,155,91,219,59,187,123,251,7,135,71,199,39,167
1302 .db 103,231,23,151,87,215,55,183,119,247,15,143,79,207,47,175,111,239,31,159,95
1303 .db 223,63,191,127,255