2 ; Copyright (C) 2010,2015 Bernd Porr, mail@berndporr.me.uk
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 <mail@berndporr.me.uk>
24 ; Updated: 20 Jul 2015
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
42 .equ INTERVAL,88h ; uframe/frame interval
43 .equ INTCTR,89h ; interval counter
46 .org 0000h ; after reset the processor starts here
47 ljmp main ; jump to the main loop
50 ljmp isr0 ; external interrupt 0: /DRY
52 .org 0043h ; the IRQ2-vector
53 ljmp jmptbl ; irq service-routine
55 .org 0100h ; start of the jump table
57 jmptbl: ljmp sudav_isr
156 ;; clear the USB2 irq bit and return
181 mov dptr,#0E7C0h ; EP1in
182 mov a,IOB ; get DIO D
183 movx @dptr,a ; store it
185 mov a,IOC ; get DIO C
186 movx @dptr,a ; store it
188 mov a,IOD ; get DIO B
189 movx @dptr,a ; store it
192 movx @dptr,a ; pad it up
195 mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
197 mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
200 mov a,#00000100b ; clear the ep1in
214 ;;; this is triggered when DRY goes low
242 lcall readADCch ; read one channel
258 clr IOA.7 ; START = 0
260 ;; arm the endpoint and send off the data
261 mov DPTR,#EP6BCH ; byte count H
263 lcall syncdelaywr ; wait until we can write again
265 mov r0,#MAXSMPL ; number of samples to transmit
269 add a,#4 ; four bytes for DIO
270 mov DPTR,#EP6BCL ; byte count L
271 lcall syncdelaywr ; wait until we can write again
295 ;;; basically only initialises the processor and
296 ;;; then engages in an endless loop
298 mov DPTR,#CPUCS ; CPU control register
299 mov a,#00010000b ; 48Mhz
303 mov a,#00000011b ; allows skip
306 mov IP,#0 ; all std 8051 int have low priority
307 mov EIP,#0FFH ; all FX2 interrupts have high priority
309 mov dptr,#INTSETUP ; IRQ setup register
310 mov a,#08h ; enable autovector
317 lcall initAD ; init the ports to the converters
319 lcall initeps ; init the isochronous data-transfer
321 ;;; main loop, rest is done as interrupts
325 mov r0,#PWMFLAG ; pwm on?
329 mov a,GPIFTRIG ; GPIF status
330 anl a,#80h ; done bit
331 jz mloop2 ; GPIF still busy
333 mov a,#01h ; WR,EP4, 01 = EP4
334 mov GPIFTRIG,a ; restart it
336 sjmp mloop2 ; loop for ever
339 ;;; initialise the ports for the AD-converter
341 mov r0,#MAXSMPL ; length of channellist
342 mov @r0,#0 ; we don't want to accumlate samples
344 mov r0,#ASYNC_ON ; async enable
345 mov @r0,#0 ; we don't want to accumlate samples
347 mov OEA,#11100000b ; PortA7,A6,A5 Outputs
348 mov IOA,#01100000b ; /CS = 1 and START = 0
349 mov dptr,#IFCONFIG ; switch on clock on IFCLK pin
350 mov a,#10100000b ; gpif, 30MHz, internal IFCLK -> 15MHz for AD
353 mov SCON0,#013H ; ser rec en, TX/RX: stop, 48/12MHz=4MHz clock
356 mov a,#00001000b ; special function for port E: RXD0OUT
362 ;;; send a byte via SPI
363 ;;; content in a, dptr1 is changed
364 ;;; the lookup is done in dptr1 so that the normal dptr is not affected
365 ;;; important: /cs needs to be reset to 1 by the caller: IOA.5
370 mov dptr,#swap_lut ; lookup table
371 movc a,@a+dptr ; reverse bits
373 ;; clear interrupt flag, is used to detect
374 ;; successful transmission
375 clr SCON0.1 ; clear interrupt flag
377 ;; start transmission by writing the byte
378 ;; in the transmit buffer
379 mov SBUF0,a ; start transmission
381 ;; wait for the end of the transmission
383 mov a,SCON0 ; get transmission status
384 jnb ACC.1,sendSPIwait ; loop until transmitted
393 ;;; receive a byte via SPI
394 ;;; content in a, dptr is changed
395 ;;; the lookup is done in dptr1 so that the normal dptr is not affected
396 ;;; important: the /CS needs to be set to 1 by the caller via "setb IOA.5"
402 ;; clearning the RI bit starts reception of data
406 ;; RI goes back to 1 after the reception of the 8 bits
407 mov a,SCON0 ; get receive status
408 jnb ACC.0,recSPIwait; loop until all bits received
410 ;; read the byte from the buffer
411 mov a,SBUF0 ; get byte
413 ;; lookup: reverse the bits
414 mov dptr,#swap_lut ; lookup table
415 movc a,@a+dptr ; reverse the bits
425 ;;; register address in a
426 ;;; returns value in a
428 anl a,#00001111b ; mask out the index to the register
429 orl a,#01000000b ; 010xxxxx indicates register read
430 clr IOA.5 ; ADC /cs to 0
431 lcall sendSPI ; send the command over
432 lcall recSPI ; read the contents back
433 setb IOA.5 ; ADC /cs to 1
438 ;;; writes to a register
439 ;;; register address in a
443 anl a,#00001111b ; mask out the index to the register
444 orl a,#01100000b ; 011xxxxx indicates register write
446 clr IOA.5 ; ADC /cs to 0
452 setb IOA.5 ; ADC /cs to 1
455 lcall registerRead ; check if the data has arrived in the ADC
456 mov 0f0h,r0 ; register B
457 cjne a,0f0h,registerWrite ; something went wrong, try again
463 ;;; initilise the endpoints
467 movx @dptr,a ; reset all fifos
477 movx @dptr,a ; normal operat
480 mov a,#10010010b ; valid, out, double buff, iso
484 mov a,#00000000b ; manual
487 mov dptr,#EP2BCL ; "arm" it
489 movx @DPTR,a ; can receive data
490 lcall syncdelay ; wait to sync
491 movx @DPTR,a ; can receive data
492 lcall syncdelay ; wait to sync
493 movx @DPTR,a ; can receive data
494 lcall syncdelay ; wait to sync
497 mov a,#10100000b ; valid
500 mov dptr,#EP1OUTBC ; "arm" it
502 movx @DPTR,a ; can receive data
503 lcall syncdelay ; wait until we can write again
504 movx @dptr,a ; make shure its really empty
505 lcall syncdelay ; wait
507 mov DPTR,#EP6CFG ; ISO data from here to the host
508 mov a,#11010010b ; Valid
509 movx @DPTR,a ; ISO transfer, double buffering
511 mov DPTR,#EP8CFG ; EP8
512 mov a,#11100000b ; BULK data from here to the host
516 mov a,#1 ; interrupt on pin A0
520 mov dptr,#EPIE ; interrupt enable
521 mov a,#10001100b ; enable irq for ep1out,8,ep1in
524 mov dptr,#EPIRQ ; clear IRQs
528 mov DPTR,#USBIE ; USB int enables register
529 mov a,#2 ; enables SOF (1ms/125us interrupt)
532 setb TCON.0 ; make INT0 edge triggered, falling edge
534 mov EIE,#00000001b ; enable INT2/USBINT in the 8051's SFR
535 mov IE,#81h ; IE, enable all interrupts and INT0
540 ;;; Reads one ADC channel from the converter and stores
541 ;;; the result at dptr
543 ;; reading data is done by just dropping /CS and start reading and
544 ;; while keeping the IN signal to the ADC inactive
549 movx @dptr,a ; store the byte
550 inc dptr ; increment pointer
568 setb IOA.5 ; /cs to 1
574 ;;; interrupt-routine for SOF
592 clr IE.7 ; make sure that no other int's disturbe us
594 mov r0,#INTCTR ; interval counter
595 mov a,@r0 ; get the value
597 mov @r0,a ; save it again
598 jz sof_adc ; we do ADC functions
599 ljmp epfull ; we skip all adc functions
602 mov r1,#INTERVAL ; get the interval
604 mov @r0,a ; save it in the counter
607 jnz epfull ; EP6-buffer is full
609 mov a,IOA ; conversion running?
612 ;; make sure that we are starting with the first channel
614 mov a,@r0 ; get config of MUXSG0
617 lcall registerWrite ; this resets the channel sequence
619 setb IOA.7 ; start converter, START = 1
621 mov dptr,#0f800h ; EP6 buffer
622 mov a,IOD ; get DIO D
623 movx @dptr,a ; store it
625 mov a,IOC ; get DIO C
626 movx @dptr,a ; store it
628 mov a,IOB ; get DIO B
629 movx @dptr,a ; store it
632 movx @dptr,a ; pad it up
633 inc dptr ; algin along a 32 bit word
645 mov @r0,#1 ; enable data collection
648 ;; do the D/A conversion
651 jnz epempty ; nothing to get
653 mov dptr,#0F000H ; EP2 fifo buffer
654 lcall dalo ; conversion
656 mov dptr,#EP2BCL ; "arm" it
658 lcall syncdelaywr ; wait for the rec to sync
659 lcall syncdelaywr ; wait for the rec to sync
663 mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
665 mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
667 mov DPTR,#USBIRQ ; points to the SOF
668 mov a,#2 ; clear the SOF
672 setb IE.7 ; re-enable global interrupts
693 ;; erase all data in ep8
701 mov a,#0 ; normal operation
707 ;; throw out old data
715 mov a,#0 ; normal operation
720 ;;; configure the ADC converter
721 ;;; the dptr points to the init data:
722 ;;; CONFIG 0,1,3,4,5,6
723 ;;; note that CONFIG2 is omitted
725 clr IOA.7 ; stops ADC: START line of ADC = L
726 setb IOA.5 ; ADC /cs to 1
728 ;; just in case something has gone wrong
733 mov a,#11000000b ; reset the ADC
734 clr IOA.5 ; ADC /cs to 0
736 setb IOA.5 ; ADC /cs to 1
759 mov @r0,a ; store it for reset purposes
779 ;;; interrupt-routine for ep1out
780 ;;; receives the channel list and other commands
798 clr IE.7 ; block other interrupts
800 mov dptr,#0E780h ; FIFO buffer of EP1OUT
801 movx a,@dptr ; get the first byte
802 mov r0,#CMD_FLAG ; pointer to the command byte
803 mov @r0,a ; store the command byte for ep8
805 mov dptr,#ep1out_jmp; jump table for the different functions
806 rl a ; multiply by 2: sizeof sjmp
807 jmp @a+dptr ; jump to the jump table
808 ;; jump table, corresponds to the command bytes defined
813 sjmp config_digital_b; a=2
814 sjmp write_digital_b ; a=3
815 sjmp initsgADchannel ; a=4
820 sjmp startadcint ; a=9
835 mov @r0,#0 ; make sure that no async activity is on
837 mov dptr,#0e781h ; FIFO buffer of EP1OUT
838 lcall configADC ; configures the ADC esp sel the channel
840 lcall reset_ep8 ; reset FIFO: get rid of old bytes
841 ;; Save new A/D data in EP8. This is the first byte
842 ;; the host will read during an INSN. If there are
843 ;; more to come they will be handled by the ISR of
845 lcall ep8_ops ; get A/D data
850 mov dptr,#0e781h ; FIFO buffer of EP1OUT from 2nd byte
852 movx a,@dptr ; interval is the 1st byte
853 inc dptr ; data pointer
854 sjmp startadc2 ; the other paramters as with startadc
857 ;;; we write to the registers of the A/D converter
859 mov dptr,#0e781h ; FIFO buffer of EP1OUT from 2nd byte
861 mov a,#1 ; interval is 1 here all the time
863 mov r0,#INTERVAL ; set it
865 mov r0,#INTCTR ; the counter is also just one
868 movx a,@dptr ; get length of channel list
871 mov @r0,a ; length of the channel list
875 lcall configADC ; configures all registers
877 mov r0,#ASYNC_ON ; async enable
878 mov @r0,#1 ; enable it
880 lcall reset_ep6 ; reset FIFO
882 ;; load new A/D data into EP6
883 ;; This must be done. Otherwise the ISR is never called.
884 ;; The ISR is only called when data has _left_ the
885 ;; ep buffer here it has to be refilled.
886 lcall ep6_arm ; fill with dummy data
890 ;;; Single DA conversion. The 2 bytes are in the FIFO buffer
892 mov dptr,#0e781h ; FIFO buffer of EP1OUT
893 lcall dalo ; conversion
896 ;;; configure the port B as input or output (bitwise)
898 mov dptr,#0e781h ; FIFO buffer of EP1OUT
899 movx a,@dptr ; get the second byte
901 mov OEB,a ; set the output enable bits
902 movx a,@dptr ; get the second byte
905 movx a,@dptr ; get the second byte
910 ;;; Write one byte to the external digital port B
911 ;;; and prepare for digital read
913 mov dptr,#0e781h ; FIFO buffer of EP1OUT
914 movx a,@dptr ; command[1]
916 mov OEB,a ; output enable
917 movx a,@dptr ; command[2]
920 movx a,@dptr ; command[3]
923 movx a,@dptr ; command[4]
926 movx a,@dptr ; command[5]
929 movx a,@dptr ; command[6]
933 lcall reset_ep8 ; reset FIFO of ep 8
935 ;; fill ep8 with new data from port B
936 ;; When the host requests the data it's already there.
937 ;; This must be so. Otherwise the ISR is not called.
938 ;; The ISR is only called when a packet has been delivered
939 ;; to the host. Thus, we need a packet here in the
941 lcall ep8_ops ; get digital data
944 ;; for all commands the same
948 lcall syncdelaywr ; arm
949 lcall syncdelaywr ; arm
950 lcall syncdelaywr ; arm
953 mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
955 mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
958 mov a,#00001000b ; clear the ep1outirq
961 setb IE.7 ; re-enable interrupts
984 movx a,@dptr ; number of bytes to send out
985 inc dptr ; pointer to the first byte
988 movx a,@dptr ; get the byte
989 inc dptr ; point to the high byte
990 mov r3,a ; store in r3 for writeDA
991 movx a,@dptr ; get the channel number
992 inc dptr ; get ready for the next channel
993 lcall writeDA ; write value to the DAC
994 djnz r0,nextDA ; next channel
1000 ;;; channel number in a
1003 anl a,#00000011b ; 4 channels
1004 mov r1,#6 ; the channel number needs to be shifted up
1006 rl a ; bit shift to the left
1007 djnz r1,writeDA2 ; do it 6 times
1008 orl a,#00010000b ; update outputs after write
1011 anl a,#11110000b ; get the upper nibble
1012 mov r1,#4 ; shift it up to the upper nibble
1014 rr a ; shift to the upper to the lower
1016 orl a,r2 ; merge with the channel info
1017 clr IOA.6 ; /SYNC of the DA to 0
1018 lcall sendSPI ; send it out to the SPI
1019 mov a,r3 ; get data again
1020 anl a,#00001111b ; get the lower nibble
1021 mov r1,#4 ; shift that to the upper
1025 anl a,#11110000b ; make sure that's empty
1027 setb IOA.6 ; /SYNC of the DA to 1
1032 ;;; arm ep6: this is just a dummy arm to get things going
1034 mov DPTR,#EP6BCH ; byte count H
1036 lcall syncdelaywr ; wait until the length has arrived
1038 mov DPTR,#EP6BCL ; byte count L
1040 lcall syncdelaywr ; wait until the length has been proc
1045 ;;; converts one analog/digital channel and stores it in EP8
1046 ;;; also gets the content of the digital ports B,C and D depending on
1047 ;;; the COMMAND flag
1049 mov dptr,#0fc01h ; ep8 fifo buffer
1051 movx @dptr,a ; set H=0
1052 mov dptr,#0fc00h ; low byte
1055 movx @dptr,a ; save command byte
1057 mov dptr,#ep8_jmp ; jump table for the different functions
1058 rl a ; multiply by 2: sizeof sjmp
1059 jmp @a+dptr ; jump to the jump table
1060 ;; jump table, corresponds to the command bytes defined
1063 sjmp ep8_err ; a=0, err
1064 sjmp ep8_err ; a=1, err
1065 sjmp ep8_err ; a=2, err
1066 sjmp ep8_dio ; a=3, digital read
1067 sjmp ep8_sglchannel ; a=4, analog A/D
1068 sjmp ep8_err ; a=5, err
1069 sjmp ep8_err ; a=6, err
1071 ;; read one A/D channel
1073 setb IOA.7 ; start converter, START = 1
1074 ;; we do polling: we wait until DATA READY is zero
1076 mov a,IOA ; get /DRDY
1077 jb ACC.0,sglchwait ; wait until data ready (DRDY=0)
1078 mov DPTR,#0fc01h ; EP8 FIFO
1079 lcall readADCch ; get one reading
1080 clr IOA.7 ; stop the converter, START = 0
1082 sjmp ep8_send ; send the data
1084 ;; read the digital lines
1086 mov DPTR,#0fc01h ; store the contents of port B
1087 mov a,IOB ; in the next
1088 movx @dptr,a ; entry of the buffer
1091 movx @dptr,a ; next byte of the EP
1094 movx @dptr,a ; port D
1097 mov DPTR,#EP8BCH ; byte count H
1101 mov DPTR,#EP8BCL ; byte count L
1102 mov a,#10H ; 16 bytes, bec it's such a great number...
1103 lcall syncdelaywr ; send the data over to the host
1110 ;;; EP8 interrupt is the endpoint which sends data back after a command
1111 ;;; The actual command fills the EP buffer already
1112 ;;; but for INSNs we need to deliver more data if the count > 1
1133 mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
1135 mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
1138 mov a,#10000000b ; clear the ep8irq
1160 ;;; GPIF waveform for PWM
1162 ;; 0 1 2 3 4 5 6 7(not used)
1163 ;; len (gives 50.007Hz)
1164 .db 195, 195, 195, 195, 195, 195, 1, 1
1167 .db 002H, 006H, 002H, 002H, 002H, 002H, 002H, 002H
1170 .db 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH
1173 .db 000H, 000H, 000H, 000H, 000H, 000H, 000H, 000H
1177 mov r0,#PWMFLAG ; flag for PWM
1178 mov a,#0 ; PWM (for the main loop)
1181 mov dptr,#IFCONFIG ; switch off GPIF
1182 mov a,#10100000b ; gpif, 30MHz, internal IFCLK
1189 mov dptr,#IFCONFIG ; switch on IFCLK signal
1190 mov a,#10100010b ; gpif, 30MHz, internal IFCLK
1193 mov OEB,0FFH ; output to port B
1196 mov a,#10100000b ; valid, out, bulk
1199 ;; reset the endpoint
1203 mov a,#84h ; reset EP4 + NAK
1205 mov a,#0 ; normal op
1209 mov a,#0H ; discard packets
1210 lcall syncdelaywr ; empty FIFO buffer
1211 lcall syncdelaywr ; empty FIFO buffer
1213 ;; aborts all transfers by the GPIF
1215 mov a,#0ffh ; abort all transfers
1218 ;; wait for GPIF to finish
1220 mov a,GPIFTRIG ; GPIF status
1221 anl a,#80h ; done bit
1222 jz wait_f_abort ; GPIF busy
1224 mov dptr,#GPIFCTLCFG
1225 mov a,#10000000b ; tri state for CTRL
1228 mov dptr,#GPIFIDLECTL
1229 mov a,#11110000b ; all CTL outputs low
1232 ;; abort if FIFO is empty
1233 mov a,#00000001b ; abort if empty
1234 mov dptr,#EP4GPIFFLGSEL
1238 mov a,#00000001b ; stop if GPIF flg
1239 mov dptr,#EP4GPIFPFSTOP
1242 ;; transaction counter
1247 ;; transaction counter
1252 ;; transaction counter
1253 mov a,#0ffH ; 512 bytes
1257 ;; transaction counter
1262 ;; RDY pins. Not used here.
1264 mov dptr,#GPIFREADYCFG
1267 ;; drives the output in the IDLE state
1269 mov dptr,#GPIFIDLECS
1272 ;; direct data transfer from the EP to the GPIF
1273 mov dptr,#EP4FIFOCFG
1274 mov a,#00010000b ; autoout=1, byte-wide
1277 ;; waveform 0 is used for FIFO out
1278 mov dptr,#GPIFWFSELECT
1283 ;; transfer the delay byte from the EP to the waveform
1284 mov dptr,#0e781h ; EP1 buffer
1285 movx a,@dptr ; get the delay
1286 mov dptr,#waveform ; points to the waveform
1287 mov r2,#6 ; fill 6 bytes
1289 movx @dptr,a ; save timing in a xxx
1291 djnz r2,timloop ; fill the 6 delay bytes
1294 mov AUTOPTRH2,#0E4H ; XDATA0H
1296 mov AUTOPTRL2,#00H ; XDATA0L
1299 mov dptr,#waveform ; points to the waveform
1301 mov AUTOPTRSETUP,#7 ; autoinc and enable
1304 mov r2,#20H ; 32 bytes to transfer
1327 mov r0,#PWMFLAG ; flag for PWM
1328 mov a,#1 ; PWM (for the main loop)
1335 ;; need to delay every time the byte counters
1336 ;; for the EPs have been changed.
1357 .org 1F00h ; lookup table at the end of memory
1360 .db 0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,240,8,136
1361 .db 72,200,40,168,104,232,24,152,88,216,56,184,120,248,4,132,68,196,36,164,100
1362 .db 228,20,148,84,212,52,180,116,244,12,140,76,204,44,172,108,236,28,156,92,220
1363 .db 60,188,124,252,2,130,66,194,34,162,98,226,18,146,82,210,50,178,114,242,10
1364 .db 138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,6,134,70,198,38,166
1365 .db 102,230,22,150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,30,158,94
1366 .db 222,62,190,126,254,1,129,65,193,33,161,97,225,17,145,81,209,49,177,113,241,9
1367 .db 137,73,201,41,169,105,233,25,153,89,217,57,185,121,249,5,133,69,197,37,165
1368 .db 101,229,21,149,85,213,53,181,117,245,13,141,77,205,45,173,109,237,29,157,93
1369 .db 221,61,189,125,253,3,131,67,195,35,163,99,227,19,147,83,211,51,179,115,243,11
1370 .db 139,75,203,43,171,107,235,27,155,91,219,59,187,123,251,7,135,71,199,39,167
1371 .db 103,231,23,151,87,215,55,183,119,247,15,143,79,207,47,175,111,239,31,159,95
1372 .db 223,63,191,127,255