2 ; Copyright (C) 2004 Bernd Porr, Bernd.Porr@cn.stir.ac.uk
4 ; This program is free software; you can redistribute it and/or modify
5 ; it under the terms of the GNU General Public License as published by
6 ; the Free Software Foundation; either version 2 of the License, or
7 ; (at your option) any later version.
9 ; This program is distributed in the hope that it will be useful,
10 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 ; GNU General Public License for more details.
14 ; You should have received a copy of the GNU General Public License
15 ; along with this program; if not, write to the Free Software
16 ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 ; Firmware: usbdux_firmware.asm for usbdux.c
20 ; Description: University of Stirling USB DAQ & INCITE Technology Limited
21 ; Devices: [ITL] USB-DUX (usbdux.o)
22 ; Author: Bernd Porr <Bernd.Porr@cn.stir.ac.uk>
23 ; Updated: 25 Jan 2004
32 .equ CHANNELLIST,80h ; channellist in indirect memory
34 .equ DIOFLAG,90h ; flag if next IN transf is DIO
36 .org 0000h ; after reset the processor starts here
37 ljmp main ; jump to the main loop
39 .org 0043h ; the IRQ2-vector
40 ljmp jmptbl ; irq service-routine
42 .org 0100h ; start of the jump table
44 jmptbl: ljmp sudav_isr
145 ;; clear the USB2 irq bit and return
162 ;;; basically only initialises the processor and
163 ;;; then engages in an endless loop
165 mov DPTR,#CPUCS ; CPU control register
166 mov a,#00010000b ; 48Mhz
170 mov dptr,#INTSETUP ; IRQ setup register
171 mov a,#08h ; enable autovector
175 lcall initAD ; init the ports to the converters
177 lcall initeps ; init the isochronous data-transfer
187 sjmp mloop2 ; do nothing. The rest is done by the IRQs
190 ;;; initialise the ports for the AD-converter
192 mov OEA,#27H ;PortA0,A1,A2,A5 Outputs
193 mov IOA,#22H ;/CS = 1, disable transfers to the converters
199 ;;; from here it's only IRQ handling...
202 ;;; control-byte in a,
203 ;;; result in r3(low) and r4(high)
204 ;;; this routine is optimised for speed
205 readAD: ; mask the control byte
206 anl a,#01111100b ; only the channel, gain+pol are left
207 orl a,#10000001b ; start bit, external clock
209 clr IOA.1 ; set /CS to zero
210 ;; send the control byte to the AD-converter
211 mov R2,#8 ; bit-counter
212 bitlp: jnb ACC.7,bitzero ; jump if Bit7 = 0?
213 setb IOA.2 ; set the DIN bit
214 sjmp clock ; continue with the clock
215 bitzero:clr IOA.2 ; clear the DIN bit
216 clock: setb IOA.0 ; SCLK = 1
221 ;; continue the aquisition (already started)
222 clr IOA.2 ; clear the DIN bit
223 mov R2,#5 ; five steps for the aquision
224 clockaq:setb IOA.0 ; SCLK = 1
226 djnz R2,clockaq ; loop
228 ;; read highbyte from the A/D-converter
229 ;; and do the conversion
230 mov r4,#0 ; Highbyte goes into R4
231 mov R2,#4 ; COUNTER 4 data bits in the MSB
232 mov r5,#08h ; create bit-mask
233 gethi: ; loop get the 8 highest bits from MSB downw
234 setb IOA.0 ; SCLK = 1
236 mov a,IOA ; from port A
237 jnb ACC.4,zerob ; the in-bit is zero
238 mov a,r4 ; get the byte
239 orl a,r5 ; or the bit to the result
240 mov r4,a ; save it again in r4
241 zerob: mov a,r5 ; get r5 in order to shift the mask
243 mov r5,a ; back to r5
245 ;; read the lowbyte from the A/D-converter
246 mov r3,#0 ; Lowbyte goes into R3
247 mov r2,#8 ; COUNTER 8 data-bits in the LSB
248 mov r5,#80h ; create bit-mask
249 getlo: ; loop get the 8 highest bits from MSB downw
250 setb IOA.0 ; SCLK = 1
252 mov a,IOA ; from port A
253 jnb ACC.4,zerob2 ; the in-bit is zero
254 mov a,r3 ; get the result-byte
255 orl a,r5 ; or the bit to the result
256 mov r3,a ; save it again in r4
257 zerob2: mov a,r5 ; get r5 in order to shift the mask
259 mov r5,a ; back to r5
261 setb IOA.1 ; set /CS to one
267 ;;; aquires data from all 8 channels and stores it in the EP6 buffer
269 mov AUTOPTRH1,#0F8H ; EP6
272 mov r0,#CHANNELLIST ; points to the channellist
281 inc r0 ; next channel
348 ;;; initilise the transfer
349 ;;; It is assumed that the USB interface is in alternate setting 3
353 movx @dptr,a ; reset all fifos
355 movx @dptr,a ; normal operat
358 mov a,#10010010b ; valid, out, double buff, iso
362 mov a,#00000000b ; manual
365 mov dptr,#EP2BCL ; "arm" it
367 movx @DPTR,a ; can receive data
368 lcall syncdelay ; wait to sync
369 movx @DPTR,a ; can receive data
370 lcall syncdelay ; wait to sync
371 movx @DPTR,a ; can receive data
372 lcall syncdelay ; wait to sync
375 mov a,#10100000b ; valid
379 mov a,#00000000b ; manual
382 mov dptr,#EP4BCL ; "arm" it
384 movx @DPTR,a ; can receive data
385 lcall syncdelay ; wait until we can write again
386 movx @dptr,a ; make shure its really empty
387 lcall syncdelay ; wait
389 mov DPTR,#EP6CFG ; ISO data from here to the host
390 mov a,#11010010b ; Valid
391 movx @DPTR,a ; ISO transfer, double buffering
393 mov DPTR,#EP8CFG ; EP8
394 mov a,#11100000b ; BULK data from here to the host
397 mov dptr,#EPIE ; interrupt enable
398 mov a,#10100000b ; enable irq for ep4,8
401 mov dptr,#EPIRQ ; clear IRQs
406 mov DPTR,#USBIE ; USB int enables register
407 mov a,#2 ; enables SOF (1ms/125us interrupt)
410 mov EIE,#00000001b ; enable INT2 in the 8051's SFR
411 mov IE,#80h ; IE, enable all interrupts
418 ;;; interrupt-routine for SOF
419 ;;; is for full speed
439 jnz epfull ; EP6-buffer is full
441 lcall convlo ; conversion
443 mov DPTR,#EP6BCH ; byte count H
446 lcall syncdelay ; wait until we can write again
448 mov DPTR,#EP6BCL ; byte count L
449 mov a,#10H ; is 8x word = 16 bytes
451 lcall syncdelay ; wait until we can write again
454 ;; do the D/A conversion
457 jnz epempty ; nothing to get
459 mov dptr,#0F000H ; EP2 fifo buffer
460 lcall dalo ; conversion
462 mov dptr,#EP2BCL ; "arm" it
464 movx @DPTR,a ; can receive data
465 lcall syncdelay ; wait for the rec to sync
466 movx @dptr,a ; just to make sure that it's empty
467 lcall syncdelay ; wait for the rec to sync
471 mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
473 mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
475 mov DPTR,#USBIRQ ; points to the SOF
476 mov a,#2 ; clear the SOF
501 ;;; interrupt-routine for ep4
502 ;;; receives the channel list and other commands
520 mov dptr,#0f400h ; FIFO buffer of EP4
521 movx a,@dptr ; get the first byte
523 mov dptr,#ep4_jmp ; jump table for the different functions
524 rl a ; multiply by 2: sizeof sjmp
525 jmp @a+dptr ; jump to the jump table
527 sjmp storechannellist; a=0
529 sjmp config_digital_b; a=2
530 sjmp write_digital_b ; a=3
534 ;;; the first byte is zero:
535 ;;; we've just received the channel list
536 ;;; the channel list is stored in the addresses from 80H which
537 ;;; are _only_ reachable by indirect addressing
539 mov r0,#CHANNELLIST ; the conversion bytes are now stored in 80h
541 mov dptr,#0f401h ; FIFO buffer of EP4
548 clr a ; announce analogue transaction
549 mov r0,#DIOFLAG ; pointer to the command byte
550 mov @r0,a ; set the command byte
553 ;;; Single DA conversion. The 2 bytes are in the FIFO buffer
555 mov dptr,#0f401h ; FIFO buffer of EP4
556 lcall dalo ; conversion
559 ;;; configure the port B
561 mov dptr,#0f401h ; FIFO buffer of EP4
562 movx a,@dptr ; get the second byte
563 mov OEB,a ; set the output enable bits
566 ;;; Write one byte to the external digital port B
567 ;;; and prepare for digital read
569 mov dptr,#0f401h ; FIFO buffer of EP4
570 movx a,@dptr ; get the second byte
571 mov OEB,a ; output enable
574 mov IOB,a ; send the byte to the I/O port
575 mov a,#0ffh ; announce DIO transaction
576 mov r0,#DIOFLAG ; pointer to the command byte
577 mov @r0,a ; set the command byte
580 ;;; more things here to come...
585 movx @DPTR,a ; arm it
586 lcall syncdelay ; wait
587 movx @DPTR,a ; arm it
588 lcall syncdelay ; wait
589 movx @DPTR,a ; arm it
590 lcall syncdelay ; wait
593 mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
595 mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
598 mov a,#00100000b ; clear the ep4irq
622 movx a,@dptr ; number of channels
623 inc dptr ; pointer to the first channel
624 mov r0,a ; 4 channels
626 movx a,@dptr ; get the first low byte
627 mov r3,a ; store in r3 (see below)
628 inc dptr ; point to the high byte
629 movx a,@dptr ; get the high byte
630 mov r4,a ; store in r4 (for writeDA)
631 inc dptr ; point to the channel number
632 movx a,@dptr ; get the channel number
633 inc dptr ; get ready for the next channel
634 lcall writeDA ; write value to the DAC
635 djnz r0,nextDA ; next channel
641 ;;; control-byte in a,
642 ;;; value in r3(low) and r4(high)
643 writeDA: ; mask the control byte
644 anl a,#11000000b ; only the channel is left
645 orl a,#00110000b ; internal clock, bipolar mode, +/-5V
646 orl a,r4 ; or the value of R4 to it
648 clr IOA.5 ; set /CS to zero
649 ;; send the first byte to the DA-converter
650 mov R2,#8 ; bit-counter
651 DA1: jnb ACC.7,zeroda ; jump if Bit7 = 0?
652 setb IOA.2 ; set the DIN bit
653 sjmp clkda ; continue with the clock
654 zeroda: clr IOA.2 ; clear the DIN bit
655 clkda: setb IOA.0 ; SCLK = 1
661 ;; send the second byte to the DA-converter
663 mov R2,#8 ; bit-counter
664 DA2: jnb ACC.7,zeroda2 ; jump if Bit7 = 0?
665 setb IOA.2 ; set the DIN bit
666 sjmp clkda2 ; continue with the clock
667 zeroda2:clr IOA.2 ; clear the DIN bit
668 clkda2: setb IOA.0 ; SCLK = 1
673 setb IOA.5 ; set /CS to one
683 mov DPTR,#EP6BCH ; byte count H
686 lcall syncdelay ; wait until the length has arrived
688 mov DPTR,#EP6BCL ; byte count L
691 lcall syncdelay ; wait until the length has been proc
696 ;;; converts one analog/digital channel and stores it in EP8
697 ;;; also gets the content of the digital ports B and D
699 mov r0,#DIOFLAG ; pointer to the DIO flag
700 mov a,@r0 ; get the flag
701 jnz ep8_dio ; nonzero means DIO
703 mov r0,#CHANNELLIST ; points to the channellist
706 lcall readAD ; start the conversion
708 mov DPTR,#0fc00h ; EP8 FIFO
709 mov a,R3 ; get low byte
710 movx @DPTR,A ; store in FIFO
711 inc dptr ; next fifo entry
712 mov a,R4 ; get high byte
713 movx @DPTR,A ; store in FIFO
715 sjmp ep8_send ; send the data
718 mov DPTR,#0fc00h ; store the contents of port B
719 mov a,IOB ; in the next
720 movx @dptr,a ; entry of the buffer
723 clr a ; high byte is zero
724 movx @dptr,a ; next byte of the EP
727 mov DPTR,#EP8BCH ; byte count H
731 mov DPTR,#EP8BCL ; byte count L
732 mov a,#10H ; 16 bytes
733 movx @DPTR,a ; send the data over to the host
738 ;;; arms EP8 with one byte. This signals the Linux driver that
739 ;;; the EP has been armed only with a dummy byte to make the
740 ;;; IRQ work. The byte is not processed by the driver.
742 mov DPTR,#EP8BCH ; byte count H
746 mov DPTR,#EP8BCL ; byte count L
753 ;;; EP8 interrupt: gets one measurement from the AD converter and
754 ;;; sends it via EP8. The channel # is stored in address 80H.
755 ;;; It also gets the state of the digital registers B and D.
776 mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
778 mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
781 mov a,#10000000b ; clear the ep8irq
802 ;; need to delay every time the byte counters
803 ;; for the EPs have been changed.