You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1394 lines
28 KiB
1394 lines
28 KiB
; usbdux_firmware.asm |
|
; Copyright (C) 2010,2015 Bernd Porr, mail@berndporr.me.uk |
|
; For usbduxsigma.c 0.5+ |
|
; |
|
; This program is free software; you can redistribute it and/or modify |
|
; it under the terms of the GNU General Public License as published by |
|
; the Free Software Foundation; either version 2 of the License, or |
|
; (at your option) any later version. |
|
; |
|
; This program is distributed in the hope that it will be useful, |
|
; but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
; GNU General Public License for more details. |
|
; |
|
; You should have received a copy of the GNU General Public License |
|
; along with this program; if not, write to the Free Software |
|
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
|
; |
|
; |
|
; Firmware: usbduxsigma_firmware.asm for usbduxsigma.c |
|
; Description: University of Stirling USB DAQ & INCITE Technology Limited |
|
; Devices: [ITL] USB-DUX-SIGMA (usbduxsigma.ko) |
|
; Author: Bernd Porr <mail@berndporr.me.uk> |
|
; Updated: 20 Jul 2015 |
|
; Status: testing |
|
; |
|
;;; |
|
;;; |
|
;;; |
|
|
|
.inc fx2-include.asm |
|
|
|
;;; a couple of flags in high memory |
|
.equ CMD_FLAG,80h ; flag for the next in transfer |
|
.equ PWMFLAG,81h ; PWM on or off? |
|
.equ MAXSMPL,82H ; maximum number of samples, n channellist |
|
.equ MUXSG0,83H ; content of the MUXSG0 register |
|
.equ INTERVAL,88h ; uframe/frame interval |
|
.equ INTCTR,89h ; interval counter |
|
.equ DABUFFER,0F0h ; buffer with DA values |
|
|
|
;;; in precious low memory but accessible within one clock cycle |
|
.equ DPTRL,70H |
|
.equ DPTRH,71h |
|
.equ ASYNC_ON,72h |
|
.equ SMPLCTR,73h |
|
|
|
;;; actual code |
|
.org 0000h ; after reset the processor starts here |
|
ljmp main ; jump to the main loop |
|
|
|
.org 0003h |
|
ljmp isr0 ; external interrupt 0: /DRY |
|
|
|
.org 0043h ; the IRQ2-vector |
|
ljmp jmptbl ; irq service-routine |
|
|
|
.org 0100h ; start of the jump table |
|
|
|
jmptbl: ljmp sudav_isr |
|
nop |
|
ljmp sof_isr |
|
nop |
|
ljmp sutok_isr |
|
nop |
|
ljmp suspend_isr |
|
nop |
|
ljmp usbreset_isr |
|
nop |
|
ljmp hispeed_isr |
|
nop |
|
ljmp ep0ack_isr |
|
nop |
|
ljmp spare_isr |
|
nop |
|
ljmp ep0in_isr |
|
nop |
|
ljmp ep0out_isr |
|
nop |
|
ljmp ep1in_isr |
|
nop |
|
ljmp ep1out_isr |
|
nop |
|
ljmp ep2_isr |
|
nop |
|
ljmp ep4_isr |
|
nop |
|
ljmp ep6_isr |
|
nop |
|
ljmp ep8_isr |
|
nop |
|
ljmp ibn_isr |
|
nop |
|
ljmp spare_isr |
|
nop |
|
ljmp ep0ping_isr |
|
nop |
|
ljmp ep1ping_isr |
|
nop |
|
ljmp ep2ping_isr |
|
nop |
|
ljmp ep4ping_isr |
|
nop |
|
ljmp ep6ping_isr |
|
nop |
|
ljmp ep8ping_isr |
|
nop |
|
ljmp errlimit_isr |
|
nop |
|
ljmp spare_isr |
|
nop |
|
ljmp spare_isr |
|
nop |
|
ljmp spare_isr |
|
nop |
|
ljmp ep2isoerr_isr |
|
nop |
|
ljmp ep4isoerr_isr |
|
nop |
|
ljmp ep6isoerr_isr |
|
nop |
|
ljmp ep8isoerr_isr |
|
|
|
|
|
;; dummy isr |
|
sudav_isr: |
|
sutok_isr: |
|
suspend_isr: |
|
usbreset_isr: |
|
hispeed_isr: |
|
ep0ack_isr: |
|
spare_isr: |
|
ep0in_isr: |
|
ep0out_isr: |
|
ibn_isr: |
|
ep0ping_isr: |
|
ep1ping_isr: |
|
ep2ping_isr: |
|
ep4ping_isr: |
|
ep6ping_isr: |
|
ep8ping_isr: |
|
errlimit_isr: |
|
ep2isoerr_isr: |
|
ep4isoerr_isr: |
|
ep6isoerr_isr: |
|
ep8isoerr_isr: |
|
ep6_isr: |
|
ep2_isr: |
|
ep4_isr: |
|
|
|
push dps |
|
push dpl |
|
push dph |
|
push dpl1 |
|
push dph1 |
|
push acc |
|
push psw |
|
|
|
;; clear the USB2 irq bit and return |
|
mov a,EXIF |
|
clr acc.4 |
|
mov EXIF,a |
|
|
|
pop psw |
|
pop acc |
|
pop dph1 |
|
pop dpl1 |
|
pop dph |
|
pop dpl |
|
pop dps |
|
|
|
reti |
|
|
|
|
|
ep1in_isr: |
|
push dps |
|
push dpl |
|
push dph |
|
push dpl1 |
|
push dph1 |
|
push acc |
|
push psw |
|
|
|
mov dptr,#0E7C0h ; EP1in |
|
mov a,IOB ; get DIO D |
|
movx @dptr,a ; store it |
|
inc dptr ; next byte |
|
mov a,IOC ; get DIO C |
|
movx @dptr,a ; store it |
|
inc dptr ; next byte |
|
mov a,IOD ; get DIO B |
|
movx @dptr,a ; store it |
|
inc dptr ; next byte |
|
mov a,#0 ; just zero |
|
movx @dptr,a ; pad it up |
|
|
|
;; clear INT2 |
|
mov a,EXIF ; FIRST clear the USB (INT2) interrupt request |
|
clr acc.4 |
|
mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable |
|
|
|
mov DPTR,#EPIRQ ; |
|
mov a,#00000100b ; clear the ep1in |
|
movx @DPTR,a |
|
|
|
pop psw |
|
pop acc |
|
pop dph1 |
|
pop dpl1 |
|
pop dph |
|
pop dpl |
|
pop dps |
|
reti |
|
|
|
|
|
|
|
;;; this is triggered when DRY goes low |
|
isr0: |
|
push dps |
|
push dpl |
|
push dph |
|
push dpl1 |
|
push dph1 |
|
push acc |
|
push psw |
|
push 00h ; R0 |
|
push 01h ; R1 |
|
push 02h ; R2 |
|
push 03h ; R3 |
|
push 04h ; R4 |
|
push 05h ; R5 |
|
push 06h ; R6 |
|
push 07h ; R7 |
|
|
|
mov a,ASYNC_ON |
|
jz noepsubmit |
|
|
|
mov DPS,#0 |
|
mov dpl,DPTRL |
|
mov dph,DPTRH |
|
|
|
lcall readADCch ; read one channel |
|
|
|
mov DPTRL,dpl |
|
mov DPTRH,dph |
|
|
|
mov a,SMPLCTR |
|
dec a |
|
mov SMPLCTR,a |
|
jnz noepsubmit |
|
|
|
mov ASYNC_ON,#0 |
|
|
|
clr IOA.7 ; START = 0 |
|
|
|
;; arm the endpoint and send off the data |
|
mov DPTR,#EP6BCH ; byte count H |
|
mov a,#0 ; is zero |
|
lcall syncdelaywr ; wait until we can write again |
|
|
|
mov r0,#MAXSMPL ; number of samples to transmit |
|
mov a,@r0 ; get them |
|
rl a ; a=a*2 |
|
rl a ; a=a*2 |
|
add a,#4 ; four bytes for DIO |
|
mov DPTR,#EP6BCL ; byte count L |
|
lcall syncdelaywr ; wait until we can write again |
|
|
|
noepsubmit: |
|
pop 07h |
|
pop 06h |
|
pop 05h |
|
pop 04h ; R4 |
|
pop 03h ; R3 |
|
pop 02h ; R2 |
|
pop 01h ; R1 |
|
pop 00h ; R0 |
|
pop psw |
|
pop acc |
|
pop dph1 |
|
pop dpl1 |
|
pop dph |
|
pop dpl |
|
pop dps |
|
|
|
reti |
|
|
|
|
|
|
|
;;; main program |
|
;;; basically only initialises the processor and |
|
;;; then engages in an endless loop |
|
main: |
|
mov DPTR,#CPUCS ; CPU control register |
|
mov a,#00010000b ; 48Mhz |
|
lcall syncdelaywr |
|
|
|
mov dptr,#REVCTL |
|
mov a,#00000011b ; allows skip |
|
lcall syncdelaywr |
|
|
|
mov dptr,#INTSETUP ; IRQ setup register |
|
mov a,#08h ; enable autovector |
|
lcall syncdelaywr |
|
|
|
mov dptr,#PORTCCFG |
|
mov a,#0 |
|
lcall syncdelaywr |
|
|
|
mov IP,#01H ; int0 has highest interrupt priority |
|
mov EIP,#0 ; all USB interrupts have low priority |
|
|
|
lcall initAD ; init the ports to the converters |
|
|
|
lcall initeps ; init the isochronous data-transfer |
|
|
|
;;; main loop, rest is done as interrupts |
|
mloop2: nop |
|
|
|
;;; pwm |
|
mov r0,#PWMFLAG ; pwm on? |
|
mov a,@r0 ; get info |
|
jz mloop2 ; it's off |
|
|
|
mov a,GPIFTRIG ; GPIF status |
|
anl a,#80h ; done bit |
|
jz mloop2 ; GPIF still busy |
|
|
|
mov a,#01h ; WR,EP4, 01 = EP4 |
|
mov GPIFTRIG,a ; restart it |
|
|
|
sjmp mloop2 ; loop for ever |
|
|
|
|
|
;;; initialise the ports for the AD-converter |
|
initAD: |
|
mov r0,#MAXSMPL ; length of channellist |
|
mov @r0,#0 ; we don't want to accumlate samples |
|
|
|
mov ASYNC_ON,#0 ; async enable |
|
|
|
mov r0,#DABUFFER |
|
mov @r0,#0 |
|
|
|
mov OEA,#11100000b ; PortA7,A6,A5 Outputs |
|
mov IOA,#01100000b ; /CS = 1 and START = 0 |
|
mov dptr,#IFCONFIG ; switch on clock on IFCLK pin |
|
mov a,#10100000b ; gpif, 30MHz, internal IFCLK -> 15MHz for AD |
|
lcall syncdelaywr |
|
|
|
mov SCON0,#013H ; ser rec en, TX/RX: stop, 48/12MHz=4MHz clock |
|
|
|
mov dptr,#PORTECFG |
|
mov a,#00001000b ; special function for port E: RXD0OUT |
|
lcall syncdelaywr |
|
|
|
ret |
|
|
|
|
|
;;; send a byte via SPI |
|
;;; content in a, dptr1 is changed |
|
;;; the lookup is done in dptr1 so that the normal dptr is not affected |
|
;;; important: /cs needs to be reset to 1 by the caller: IOA.5 |
|
sendSPI: |
|
inc DPS |
|
|
|
;; bit reverse |
|
mov dptr,#swap_lut ; lookup table |
|
movc a,@a+dptr ; reverse bits |
|
|
|
;; clear interrupt flag, is used to detect |
|
;; successful transmission |
|
clr SCON0.1 ; clear interrupt flag |
|
|
|
;; start transmission by writing the byte |
|
;; in the transmit buffer |
|
mov SBUF0,a ; start transmission |
|
|
|
;; wait for the end of the transmission |
|
sendSPIwait: |
|
mov a,SCON0 ; get transmission status |
|
jnb ACC.1,sendSPIwait ; loop until transmitted |
|
|
|
inc DPS |
|
|
|
ret |
|
|
|
|
|
|
|
|
|
;;; receive a byte via SPI |
|
;;; content in a, dptr is changed |
|
;;; the lookup is done in dptr1 so that the normal dptr is not affected |
|
;;; important: the /CS needs to be set to 1 by the caller via "setb IOA.5" |
|
recSPI: |
|
inc DPS |
|
|
|
clr IOA.5 ; /cs to 0 |
|
|
|
;; clearning the RI bit starts reception of data |
|
clr SCON0.0 |
|
|
|
recSPIwait: |
|
;; RI goes back to 1 after the reception of the 8 bits |
|
mov a,SCON0 ; get receive status |
|
jnb ACC.0,recSPIwait; loop until all bits received |
|
|
|
;; read the byte from the buffer |
|
mov a,SBUF0 ; get byte |
|
|
|
;; lookup: reverse the bits |
|
mov dptr,#swap_lut ; lookup table |
|
movc a,@a+dptr ; reverse the bits |
|
|
|
inc DPS |
|
|
|
ret |
|
|
|
|
|
|
|
|
|
;;; reads a register |
|
;;; register address in a |
|
;;; returns value in a |
|
registerRead: |
|
anl a,#00001111b ; mask out the index to the register |
|
orl a,#01000000b ; 010xxxxx indicates register read |
|
clr IOA.5 ; ADC /cs to 0 |
|
lcall sendSPI ; send the command over |
|
lcall recSPI ; read the contents back |
|
setb IOA.5 ; ADC /cs to 1 |
|
ret |
|
|
|
|
|
|
|
;;; writes to a register |
|
;;; register address in a |
|
;;; value in r0 |
|
registerWrite: |
|
push acc |
|
anl a,#00001111b ; mask out the index to the register |
|
orl a,#01100000b ; 011xxxxx indicates register write |
|
|
|
clr IOA.5 ; ADC /cs to 0 |
|
|
|
lcall sendSPI ; |
|
mov a,r0 |
|
lcall sendSPI |
|
|
|
setb IOA.5 ; ADC /cs to 1 |
|
pop acc |
|
|
|
lcall registerRead ; check if the data has arrived in the ADC |
|
mov 0f0h,r0 ; register B |
|
cjne a,0f0h,registerWrite ; something went wrong, try again |
|
|
|
ret |
|
|
|
|
|
|
|
;;; initilise the endpoints |
|
initeps: |
|
mov dptr,#FIFORESET |
|
mov a,#80H |
|
movx @dptr,a ; reset all fifos |
|
mov a,#2 |
|
movx @dptr,a ; |
|
mov a,#4 |
|
movx @dptr,a ; |
|
mov a,#6 |
|
movx @dptr,a ; |
|
mov a,#8 |
|
movx @dptr,a ; |
|
mov a,#0 |
|
movx @dptr,a ; normal operat |
|
|
|
mov DPTR,#EP2CFG |
|
mov a,#10010010b ; valid, out, double buff, iso |
|
movx @DPTR,a |
|
|
|
mov dptr,#EP2FIFOCFG |
|
mov a,#00000000b ; manual |
|
movx @dptr,a |
|
|
|
mov dptr,#EP2BCL ; "arm" it |
|
mov a,#00h |
|
movx @DPTR,a ; can receive data |
|
lcall syncdelay ; wait to sync |
|
movx @DPTR,a ; can receive data |
|
lcall syncdelay ; wait to sync |
|
movx @DPTR,a ; can receive data |
|
lcall syncdelay ; wait to sync |
|
|
|
mov DPTR,#EP1OUTCFG |
|
mov a,#10100000b ; valid |
|
movx @dptr,a |
|
|
|
mov dptr,#EP1OUTBC ; "arm" it |
|
mov a,#00h |
|
movx @DPTR,a ; can receive data |
|
lcall syncdelay ; wait until we can write again |
|
movx @dptr,a ; make shure its really empty |
|
lcall syncdelay ; wait |
|
|
|
mov DPTR,#EP6CFG ; ISO data from here to the host |
|
mov a,#11010010b ; Valid |
|
movx @DPTR,a ; ISO transfer, double buffering |
|
|
|
mov DPTR,#EP8CFG ; EP8 |
|
mov a,#11100000b ; BULK data from here to the host |
|
movx @DPTR,a ; |
|
|
|
mov dptr,#PORTACFG |
|
mov a,#1 ; interrupt on pin A0 |
|
lcall syncdelaywr |
|
|
|
;; enable interrupts |
|
mov dptr,#EPIE ; interrupt enable |
|
mov a,#10001100b ; enable irq for ep1out,8,ep1in |
|
movx @dptr,a ; do it |
|
|
|
mov dptr,#EPIRQ ; clear IRQs |
|
mov a,#10001100b |
|
movx @dptr,a |
|
|
|
mov DPTR,#USBIE ; USB int enables register |
|
mov a,#2 ; enables SOF (1ms/125us interrupt) |
|
movx @DPTR,a ; |
|
|
|
setb TCON.0 ; make INT0 edge triggered, falling edge |
|
|
|
mov EIE,#00000001b ; enable INT2/USBINT in the 8051's SFR |
|
mov IE,#81h ; IE, enable all interrupts and INT0 |
|
|
|
ret |
|
|
|
|
|
;;; Reads one ADC channel from the converter and stores |
|
;;; the result at dptr |
|
readADCch: |
|
;; reading data is done by just dropping /CS and start reading and |
|
;; while keeping the IN signal to the ADC inactive |
|
clr IOA.5 ; /cs to 0 |
|
|
|
;; 1st byte: STATUS |
|
lcall recSPI ; index |
|
movx @dptr,a ; store the byte |
|
inc dptr ; increment pointer |
|
|
|
;; 2nd byte: MSB |
|
lcall recSPI ; data |
|
movx @dptr,a |
|
inc dptr |
|
|
|
;; 3rd byte: MSB-1 |
|
lcall recSPI ; data |
|
movx @dptr,a |
|
inc dptr |
|
|
|
;; 4th byte: LSB |
|
lcall recSPI ; data |
|
movx @dptr,a |
|
inc dptr |
|
|
|
;; got all bytes |
|
setb IOA.5 ; /cs to 1 |
|
|
|
ret |
|
|
|
|
|
|
|
;;; interrupt-routine for SOF |
|
sof_isr: |
|
push dps |
|
push dpl |
|
push dph |
|
push dpl1 |
|
push dph1 |
|
push acc |
|
push psw |
|
push 00h ; R0 |
|
push 01h ; R1 |
|
push 02h ; R2 |
|
push 03h ; R3 |
|
push 04h ; R4 |
|
push 05h ; R5 |
|
push 06h ; R6 |
|
push 07h ; R7 |
|
|
|
mov r0,#INTCTR ; interval counter |
|
mov a,@r0 ; get the value |
|
dec a ; decrement |
|
mov @r0,a ; save it again |
|
jz sof_adc ; we do ADC functions |
|
ljmp epfull ; we skip all adc functions |
|
|
|
sof_adc: |
|
mov r1,#INTERVAL ; get the interval |
|
mov a,@r1 ; get it |
|
mov @r0,a ; save it in the counter |
|
mov a,EP2468STAT |
|
anl a,#20H ; full? |
|
jnz epfull ; EP6-buffer is full |
|
|
|
mov a,IOA ; conversion running? |
|
jb ACC.7,epfull |
|
|
|
;; make sure that we are starting with the first channel |
|
mov r0,#MUXSG0 ; |
|
mov a,@r0 ; get config of MUXSG0 |
|
mov r0,a |
|
mov a,#04H ; MUXSG0 |
|
lcall registerWrite ; this resets the channel sequence |
|
|
|
setb IOA.7 ; start converter, START = 1 |
|
|
|
mov dptr,#0f800h ; EP6 buffer |
|
mov a,IOD ; get DIO D |
|
movx @dptr,a ; store it |
|
inc dptr ; next byte |
|
mov a,IOC ; get DIO C |
|
movx @dptr,a ; store it |
|
inc dptr ; next byte |
|
mov a,IOB ; get DIO B |
|
movx @dptr,a ; store it |
|
inc dptr ; next byte |
|
mov a,#0 ; just zero |
|
movx @dptr,a ; pad it up |
|
inc dptr ; algin along a 32 bit word |
|
mov DPTRL,dpl |
|
mov DPTRH,dph |
|
|
|
mov r0,#MAXSMPL |
|
mov a,@r0 |
|
mov SMPLCTR,a |
|
|
|
mov ASYNC_ON,#1 |
|
|
|
epfull: |
|
;; do the D/A conversion |
|
mov a,EP2468STAT |
|
anl a,#01H ; empty |
|
jnz epempty ; nothing to get |
|
|
|
mov dptr,#0F000H ; EP2 fifo buffer |
|
lcall dalo ; conversion |
|
|
|
mov dptr,#EP2BCL ; "arm" it |
|
mov a,#00h |
|
lcall syncdelaywr ; wait for the rec to sync |
|
lcall syncdelaywr ; wait for the rec to sync |
|
|
|
epempty: |
|
mov a,IOA ; conversion running? |
|
jb ACC.7,sofend |
|
|
|
lcall DAsend |
|
|
|
sofend: |
|
;; clear INT2 |
|
mov a,EXIF ; FIRST clear the USB (INT2) interrupt request |
|
clr acc.4 |
|
mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable |
|
|
|
mov DPTR,#USBIRQ ; points to the SOF |
|
mov a,#2 ; clear the SOF |
|
movx @DPTR,a |
|
|
|
nosof: |
|
pop 07h |
|
pop 06h |
|
pop 05h |
|
pop 04h ; R4 |
|
pop 03h ; R3 |
|
pop 02h ; R2 |
|
pop 01h ; R1 |
|
pop 00h ; R0 |
|
pop psw |
|
pop acc |
|
pop dph1 |
|
pop dpl1 |
|
pop dph |
|
pop dpl |
|
pop dps |
|
reti |
|
|
|
|
|
reset_ep8: |
|
;; erase all data in ep8 |
|
mov dptr,#FIFORESET |
|
mov a,#80H ; NAK |
|
lcall syncdelaywr |
|
mov dptr,#FIFORESET |
|
mov a,#8 ; reset EP8 |
|
lcall syncdelaywr |
|
mov dptr,#FIFORESET |
|
mov a,#0 ; normal operation |
|
lcall syncdelaywr |
|
ret |
|
|
|
|
|
reset_ep6: |
|
;; throw out old data |
|
mov dptr,#FIFORESET |
|
mov a,#80H ; NAK |
|
lcall syncdelaywr |
|
mov dptr,#FIFORESET |
|
mov a,#6 ; reset EP6 |
|
lcall syncdelaywr |
|
mov dptr,#FIFORESET |
|
mov a,#0 ; normal operation |
|
lcall syncdelaywr |
|
ret |
|
|
|
|
|
;;; configure the ADC converter |
|
;;; the dptr points to the init data: |
|
;;; CONFIG 0,1,3,4,5,6 |
|
;;; note that CONFIG2 is omitted |
|
configADC: |
|
clr IOA.7 ; stops ADC: START line of ADC = L |
|
setb IOA.5 ; ADC /cs to 1 |
|
|
|
;; just in case something has gone wrong |
|
nop |
|
nop |
|
nop |
|
|
|
mov a,#11000000b ; reset the ADC |
|
clr IOA.5 ; ADC /cs to 0 |
|
lcall sendSPI |
|
setb IOA.5 ; ADC /cs to 1 |
|
|
|
movx a,@dptr ; |
|
inc dptr |
|
mov r0,a |
|
mov a,#00H ; CONFIG0 |
|
lcall registerWrite |
|
|
|
movx a,@dptr ; |
|
inc dptr |
|
mov r0,a |
|
mov a,#01H ; CONFIG1 |
|
lcall registerWrite |
|
|
|
movx a,@dptr ; |
|
inc dptr |
|
mov r0,a |
|
mov a,#03H ; MUXDIF |
|
lcall registerWrite |
|
|
|
movx a,@dptr ; |
|
inc dptr |
|
mov r0,#MUXSG0 |
|
mov @r0,a ; store it for reset purposes |
|
mov r0,a |
|
mov a,#04H ; MUXSG0 |
|
lcall registerWrite |
|
|
|
movx a,@dptr ; |
|
inc dptr |
|
mov r0,a |
|
mov a,#05H ; MUXSG1 |
|
lcall registerWrite |
|
|
|
movx a,@dptr ; |
|
inc dptr |
|
mov r0,a |
|
mov a,#06H ; SYSRED |
|
lcall registerWrite |
|
|
|
ret |
|
|
|
|
|
;;; interrupt-routine for ep1out |
|
;;; receives the channel list and other commands |
|
ep1out_isr: |
|
push dps |
|
push dpl |
|
push dph |
|
push dpl1 |
|
push dph1 |
|
push acc |
|
push psw |
|
push 00h ; R0 |
|
push 01h ; R1 |
|
push 02h ; R2 |
|
push 03h ; R3 |
|
push 04h ; R4 |
|
push 05h ; R5 |
|
push 06h ; R6 |
|
push 07h ; R7 |
|
|
|
mov dptr,#0E780h ; FIFO buffer of EP1OUT |
|
movx a,@dptr ; get the first byte |
|
mov r0,#CMD_FLAG ; pointer to the command byte |
|
mov @r0,a ; store the command byte for ep8 |
|
|
|
mov dptr,#ep1out_jmp; jump table for the different functions |
|
rl a ; multiply by 2: sizeof sjmp |
|
jmp @a+dptr ; jump to the jump table |
|
;; jump table, corresponds to the command bytes defined |
|
;; in usbdux.c |
|
ep1out_jmp: |
|
sjmp startadc ; a=0 |
|
sjmp single_da ; a=1 |
|
sjmp config_digital_b; a=2 |
|
sjmp write_digital_b ; a=3 |
|
sjmp initsgADchannel ; a=4 |
|
sjmp nothing ; a=5 |
|
sjmp nothing ; a=6 |
|
sjmp pwm_on ; a=7 |
|
sjmp pwm_off ; a=8 |
|
sjmp startadcint ; a=9 |
|
|
|
nothing: |
|
ljmp over_da |
|
|
|
pwm_on: |
|
lcall startPWM |
|
sjmp over_da |
|
|
|
pwm_off: |
|
lcall stopPWM |
|
sjmp over_da |
|
|
|
initsgADchannel: |
|
mov ASYNC_ON,#0 |
|
|
|
mov dptr,#0e781h ; FIFO buffer of EP1OUT |
|
lcall configADC ; configures the ADC esp sel the channel |
|
|
|
lcall reset_ep8 ; reset FIFO: get rid of old bytes |
|
;; Save new A/D data in EP8. This is the first byte |
|
;; the host will read during an INSN. If there are |
|
;; more to come they will be handled by the ISR of |
|
;; ep8. |
|
lcall ep8_ops ; get A/D data |
|
|
|
sjmp over_da |
|
|
|
startadcint: |
|
mov dptr,#0e781h ; FIFO buffer of EP1OUT from 2nd byte |
|
|
|
movx a,@dptr ; interval is the 1st byte |
|
inc dptr ; data pointer |
|
sjmp startadc2 ; the other paramters as with startadc |
|
|
|
;;; config AD: |
|
;;; we write to the registers of the A/D converter |
|
startadc: |
|
mov dptr,#0e781h ; FIFO buffer of EP1OUT from 2nd byte |
|
|
|
mov a,#1 ; interval is 1 here all the time |
|
startadc2: |
|
mov r0,#INTERVAL ; set it |
|
mov @r0,a |
|
mov r0,#INTCTR ; the counter is also just one |
|
mov @r0,a |
|
|
|
movx a,@dptr ; get length of channel list |
|
inc dptr |
|
mov r0,#MAXSMPL |
|
mov @r0,a ; length of the channel list |
|
mov SMPLCTR,a |
|
|
|
lcall configADC ; configures all registers |
|
|
|
mov ASYNC_ON,#1 ; async enable |
|
|
|
lcall reset_ep6 ; reset FIFO |
|
|
|
;; load new A/D data into EP6 |
|
;; This must be done. Otherwise the ISR is never called. |
|
;; The ISR is only called when data has _left_ the |
|
;; ep buffer here it has to be refilled. |
|
lcall ep6_arm ; fill with dummy data |
|
|
|
sjmp over_da |
|
|
|
;;; Single DA conversion. The 2 bytes are in the FIFO buffer |
|
single_da: |
|
mov dptr,#0e781h ; FIFO buffer of EP1OUT |
|
lcall dalo ; conversion |
|
sjmp over_da |
|
|
|
;;; configure the port B as input or output (bitwise) |
|
config_digital_b: |
|
mov dptr,#0e781h ; FIFO buffer of EP1OUT |
|
movx a,@dptr ; get the second byte |
|
inc dptr |
|
mov OEB,a ; set the output enable bits |
|
movx a,@dptr ; get the second byte |
|
inc dptr |
|
mov OEC,a |
|
movx a,@dptr ; get the second byte |
|
inc dptr |
|
mov OED,a |
|
sjmp over_da |
|
|
|
;;; Write one byte to the external digital port B |
|
;;; and prepare for digital read |
|
write_digital_b: |
|
mov dptr,#0e781h ; FIFO buffer of EP1OUT |
|
movx a,@dptr ; command[1] |
|
inc dptr |
|
mov OEB,a ; output enable |
|
movx a,@dptr ; command[2] |
|
inc dptr |
|
mov OEC,a |
|
movx a,@dptr ; command[3] |
|
inc dptr |
|
mov OED,a |
|
movx a,@dptr ; command[4] |
|
inc dptr |
|
mov IOB,a ; |
|
movx a,@dptr ; command[5] |
|
inc dptr |
|
mov IOC,a |
|
movx a,@dptr ; command[6] |
|
inc dptr |
|
mov IOD,a |
|
|
|
lcall reset_ep8 ; reset FIFO of ep 8 |
|
|
|
;; fill ep8 with new data from port B |
|
;; When the host requests the data it's already there. |
|
;; This must be so. Otherwise the ISR is not called. |
|
;; The ISR is only called when a packet has been delivered |
|
;; to the host. Thus, we need a packet here in the |
|
;; first instance. |
|
lcall ep8_ops ; get digital data |
|
|
|
;; |
|
;; for all commands the same |
|
over_da: |
|
mov dptr,#EP1OUTBC |
|
mov a,#00h |
|
lcall syncdelaywr ; arm |
|
lcall syncdelaywr ; arm |
|
lcall syncdelaywr ; arm |
|
|
|
;; clear INT2 |
|
mov a,EXIF ; FIRST clear the USB (INT2) interrupt request |
|
clr acc.4 |
|
mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable |
|
|
|
mov DPTR,#EPIRQ ; |
|
mov a,#00001000b ; clear the ep1outirq |
|
movx @DPTR,a |
|
|
|
pop 07h |
|
pop 06h |
|
pop 05h |
|
pop 04h ; R4 |
|
pop 03h ; R3 |
|
pop 02h ; R2 |
|
pop 01h ; R1 |
|
pop 00h ; R0 |
|
pop psw |
|
pop acc |
|
pop dph1 |
|
pop dpl1 |
|
pop dph |
|
pop dpl |
|
pop dps |
|
reti |
|
|
|
|
|
|
|
;;; save all DA channels from the endpoint buffer in a local buffer |
|
dalo: |
|
movx a,@dptr ; number of bytes to send out |
|
inc dptr ; pointer to the first byte |
|
mov r1,#DABUFFER ; buffer for DA values |
|
mov @r1,a ; save it |
|
inc r1 ; inc pointer to local buffer |
|
mov r0,a ; counter |
|
nextDAlo: |
|
movx a,@dptr ; get the byte |
|
inc dptr ; point to the high byte |
|
mov @r1,a ; save it in the buffer |
|
inc r1 |
|
movx a,@dptr ; get the channel number |
|
inc dptr ; get ready for the next channel |
|
mov @r1,a ; save it |
|
inc r1 |
|
djnz r0,nextDAlo ; next channel |
|
ret |
|
|
|
|
|
;;; write to the DA converter |
|
DAsend: |
|
mov r1,#DABUFFER ; buffer of the DA values |
|
mov a,@r1 ; get the channel count |
|
jz DAret ; nothing to do |
|
inc r1 ; pointer to the first byte |
|
mov r0,a ; counter |
|
nextDA: |
|
mov a,@r1 ; get the byte |
|
inc r1 ; point to the high byte |
|
mov r3,a ; store in r3 for writeDA |
|
mov a,@r1 ; get the channel number |
|
inc r1 ; get ready for the next channel |
|
push 1 ; is modified in the subroutine |
|
lcall writeDA ; write value to the DAC |
|
pop 1 ; get the pointer back |
|
djnz r0,nextDA ; next channel |
|
DAret: |
|
ret |
|
|
|
|
|
|
|
;;; D/A-conversion: |
|
;;; channel number in a |
|
;;; value in r3 |
|
writeDA: |
|
anl a,#00000011b ; 4 channels |
|
mov r1,#6 ; the channel number needs to be shifted up |
|
writeDA2: |
|
rl a ; bit shift to the left |
|
djnz r1,writeDA2 ; do it 6 times |
|
orl a,#00010000b ; update outputs after write |
|
mov r2,a ; backup |
|
mov a,r3 ; get byte |
|
anl a,#11110000b ; get the upper nibble |
|
mov r1,#4 ; shift it up to the upper nibble |
|
writeDA3: |
|
rr a ; shift to the upper to the lower |
|
djnz r1,writeDA3 |
|
orl a,r2 ; merge with the channel info |
|
clr IOA.6 ; /SYNC (/CS) of the DA to 0 |
|
lcall sendSPI ; send it out to the SPI |
|
mov a,r3 ; get data again |
|
anl a,#00001111b ; get the lower nibble |
|
mov r1,#4 ; shift that to the upper |
|
writeDA4: |
|
rl a |
|
djnz r1,writeDA4 |
|
anl a,#11110000b ; make sure that's empty |
|
lcall sendSPI |
|
setb IOA.6 ; /SYNC (/CS) of the DA to 1 |
|
noDA: ret |
|
|
|
|
|
|
|
;;; arm ep6: this is just a dummy arm to get things going |
|
ep6_arm: |
|
mov DPTR,#EP6BCH ; byte count H |
|
mov a,#0 ; is zero |
|
lcall syncdelaywr ; wait until the length has arrived |
|
|
|
mov DPTR,#EP6BCL ; byte count L |
|
mov a,#1 ; is one |
|
lcall syncdelaywr ; wait until the length has been proc |
|
ret |
|
|
|
|
|
|
|
;;; converts one analog/digital channel and stores it in EP8 |
|
;;; also gets the content of the digital ports B,C and D depending on |
|
;;; the COMMAND flag |
|
ep8_ops: |
|
mov dptr,#0fc01h ; ep8 fifo buffer |
|
clr a ; high byte |
|
movx @dptr,a ; set H=0 |
|
mov dptr,#0fc00h ; low byte |
|
mov r0,#CMD_FLAG |
|
mov a,@r0 |
|
movx @dptr,a ; save command byte |
|
|
|
mov dptr,#ep8_jmp ; jump table for the different functions |
|
rl a ; multiply by 2: sizeof sjmp |
|
jmp @a+dptr ; jump to the jump table |
|
;; jump table, corresponds to the command bytes defined |
|
;; in usbdux.c |
|
ep8_jmp: |
|
sjmp ep8_err ; a=0, err |
|
sjmp ep8_err ; a=1, err |
|
sjmp ep8_err ; a=2, err |
|
sjmp ep8_dio ; a=3, digital read |
|
sjmp ep8_sglchannel ; a=4, analog A/D |
|
sjmp ep8_err ; a=5, err |
|
sjmp ep8_err ; a=6, err |
|
|
|
;; read one A/D channel |
|
ep8_sglchannel: |
|
setb IOA.7 ; start converter, START = 1 |
|
;; we do polling: we wait until DATA READY is zero |
|
sglchwait: |
|
mov a,IOA ; get /DRDY |
|
jb ACC.0,sglchwait ; wait until data ready (DRDY=0) |
|
mov DPTR,#0fc01h ; EP8 FIFO |
|
lcall readADCch ; get one reading |
|
clr IOA.7 ; stop the converter, START = 0 |
|
|
|
sjmp ep8_send ; send the data |
|
|
|
;; read the digital lines |
|
ep8_dio: |
|
mov DPTR,#0fc01h ; store the contents of port B |
|
mov a,IOB ; in the next |
|
movx @dptr,a ; entry of the buffer |
|
inc dptr |
|
mov a,IOC ; port C |
|
movx @dptr,a ; next byte of the EP |
|
inc dptr |
|
mov a,IOD |
|
movx @dptr,a ; port D |
|
|
|
ep8_send: |
|
mov DPTR,#EP8BCH ; byte count H |
|
mov a,#0 ; is zero |
|
lcall syncdelaywr |
|
|
|
mov DPTR,#EP8BCL ; byte count L |
|
mov a,#10H ; 16 bytes, bec it's such a great number... |
|
lcall syncdelaywr ; send the data over to the host |
|
|
|
ep8_err: |
|
ret |
|
|
|
|
|
|
|
;;; EP8 interrupt is the endpoint which sends data back after a command |
|
;;; The actual command fills the EP buffer already |
|
;;; but for INSNs we need to deliver more data if the count > 1 |
|
ep8_isr: |
|
push dps |
|
push dpl |
|
push dph |
|
push dpl1 |
|
push dph1 |
|
push acc |
|
push psw |
|
push 00h ; R0 |
|
push 01h ; R1 |
|
push 02h ; R2 |
|
push 03h ; R3 |
|
push 04h ; R4 |
|
push 05h ; R5 |
|
push 06h ; R6 |
|
push 07h ; R7 |
|
|
|
lcall ep8_ops |
|
|
|
;; clear INT2 |
|
mov a,EXIF ; FIRST clear the USB (INT2) interrupt request |
|
clr acc.4 |
|
mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable |
|
|
|
mov DPTR,#EPIRQ ; |
|
mov a,#10000000b ; clear the ep8irq |
|
movx @DPTR,a |
|
|
|
pop 07h |
|
pop 06h |
|
pop 05h |
|
pop 04h ; R4 |
|
pop 03h ; R3 |
|
pop 02h ; R2 |
|
pop 01h ; R1 |
|
pop 00h ; R0 |
|
pop psw |
|
pop acc |
|
pop dph1 |
|
pop dpl1 |
|
pop dph |
|
pop dpl |
|
pop dps |
|
reti |
|
|
|
|
|
|
|
;;; GPIF waveform for PWM |
|
waveform: |
|
;; 0 1 2 3 4 5 6 7(not used) |
|
;; len (gives 50.007Hz) |
|
.db 195, 195, 195, 195, 195, 195, 1, 1 |
|
|
|
;; opcode |
|
.db 002H, 006H, 002H, 002H, 002H, 002H, 002H, 002H |
|
|
|
;; out |
|
.db 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH |
|
|
|
;; log |
|
.db 000H, 000H, 000H, 000H, 000H, 000H, 000H, 000H |
|
|
|
|
|
stopPWM: |
|
mov r0,#PWMFLAG ; flag for PWM |
|
mov a,#0 ; PWM (for the main loop) |
|
mov @r0,a ; set it |
|
|
|
mov dptr,#IFCONFIG ; switch off GPIF |
|
mov a,#10100000b ; gpif, 30MHz, internal IFCLK |
|
lcall syncdelaywr |
|
ret |
|
|
|
|
|
;;; init PWM |
|
startPWM: |
|
mov dptr,#IFCONFIG ; switch on IFCLK signal |
|
mov a,#10100010b ; gpif, 30MHz, internal IFCLK |
|
lcall syncdelaywr |
|
|
|
mov OEB,0FFH ; output to port B |
|
|
|
mov DPTR,#EP4CFG |
|
mov a,#10100000b ; valid, out, bulk |
|
movx @DPTR,a |
|
|
|
;; reset the endpoint |
|
mov dptr,#FIFORESET |
|
mov a,#80h ; NAK |
|
lcall syncdelaywr |
|
mov a,#84h ; reset EP4 + NAK |
|
lcall syncdelaywr |
|
mov a,#0 ; normal op |
|
lcall syncdelaywr |
|
|
|
mov dptr,#EP4BCL |
|
mov a,#0H ; discard packets |
|
lcall syncdelaywr ; empty FIFO buffer |
|
lcall syncdelaywr ; empty FIFO buffer |
|
|
|
;; aborts all transfers by the GPIF |
|
mov dptr,#GPIFABORT |
|
mov a,#0ffh ; abort all transfers |
|
lcall syncdelaywr |
|
|
|
;; wait for GPIF to finish |
|
wait_f_abort: |
|
mov a,GPIFTRIG ; GPIF status |
|
anl a,#80h ; done bit |
|
jz wait_f_abort ; GPIF busy |
|
|
|
mov dptr,#GPIFCTLCFG |
|
mov a,#10000000b ; tri state for CTRL |
|
lcall syncdelaywr |
|
|
|
mov dptr,#GPIFIDLECTL |
|
mov a,#11110000b ; all CTL outputs low |
|
lcall syncdelaywr |
|
|
|
;; abort if FIFO is empty |
|
mov a,#00000001b ; abort if empty |
|
mov dptr,#EP4GPIFFLGSEL |
|
lcall syncdelaywr |
|
|
|
;; |
|
mov a,#00000001b ; stop if GPIF flg |
|
mov dptr,#EP4GPIFPFSTOP |
|
lcall syncdelaywr |
|
|
|
;; transaction counter |
|
mov a,#0ffH |
|
mov dptr,#GPIFTCB3 |
|
lcall syncdelaywr |
|
|
|
;; transaction counter |
|
mov a,#0ffH |
|
mov dptr,#GPIFTCB2 |
|
lcall syncdelaywr |
|
|
|
;; transaction counter |
|
mov a,#0ffH ; 512 bytes |
|
mov dptr,#GPIFTCB1 |
|
lcall syncdelaywr |
|
|
|
;; transaction counter |
|
mov a,#0ffH |
|
mov dptr,#GPIFTCB0 |
|
lcall syncdelaywr |
|
|
|
;; RDY pins. Not used here. |
|
mov a,#0 |
|
mov dptr,#GPIFREADYCFG |
|
lcall syncdelaywr |
|
|
|
;; drives the output in the IDLE state |
|
mov a,#1 |
|
mov dptr,#GPIFIDLECS |
|
lcall syncdelaywr |
|
|
|
;; direct data transfer from the EP to the GPIF |
|
mov dptr,#EP4FIFOCFG |
|
mov a,#00010000b ; autoout=1, byte-wide |
|
lcall syncdelaywr |
|
|
|
;; waveform 0 is used for FIFO out |
|
mov dptr,#GPIFWFSELECT |
|
mov a,#00000000b |
|
movx @dptr,a |
|
lcall syncdelay |
|
|
|
;; transfer the delay byte from the EP to the waveform |
|
mov dptr,#0e781h ; EP1 buffer |
|
movx a,@dptr ; get the delay |
|
mov dptr,#waveform ; points to the waveform |
|
mov r2,#6 ; fill 6 bytes |
|
timloop: |
|
movx @dptr,a ; save timing in a xxx |
|
inc dptr |
|
djnz r2,timloop ; fill the 6 delay bytes |
|
|
|
;; load waveform |
|
mov AUTOPTRH2,#0E4H ; XDATA0H |
|
lcall syncdelay |
|
mov AUTOPTRL2,#00H ; XDATA0L |
|
lcall syncdelay |
|
|
|
mov dptr,#waveform ; points to the waveform |
|
|
|
mov AUTOPTRSETUP,#7 ; autoinc and enable |
|
lcall syncdelay |
|
|
|
mov r2,#20H ; 32 bytes to transfer |
|
|
|
wavetr: |
|
movx a,@dptr |
|
inc dptr |
|
push dpl |
|
push dph |
|
push dpl1 |
|
push dph1 |
|
mov dptr,#XAUTODAT2 |
|
movx @dptr,a |
|
lcall syncdelay |
|
pop dph1 |
|
pop dpl1 |
|
pop dph |
|
pop dpl |
|
djnz r2,wavetr |
|
|
|
mov dptr,#OUTPKTEND |
|
mov a,#084H |
|
lcall syncdelaywr |
|
lcall syncdelaywr |
|
|
|
mov r0,#PWMFLAG ; flag for PWM |
|
mov a,#1 ; PWM (for the main loop) |
|
mov @r0,a ; set it |
|
|
|
ret |
|
|
|
|
|
|
|
;; need to delay every time the byte counters |
|
;; for the EPs have been changed. |
|
|
|
syncdelay: |
|
nop |
|
nop |
|
nop |
|
nop |
|
nop |
|
nop |
|
nop |
|
nop |
|
nop |
|
ret |
|
|
|
syncdelaywr: |
|
movx @dptr,a |
|
lcall syncdelay |
|
ret |
|
|
|
|
|
|
|
.org 1F00h ; lookup table at the end of memory |
|
|
|
swap_lut: |
|
.db 0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,240,8,136 |
|
.db 72,200,40,168,104,232,24,152,88,216,56,184,120,248,4,132,68,196,36,164,100 |
|
.db 228,20,148,84,212,52,180,116,244,12,140,76,204,44,172,108,236,28,156,92,220 |
|
.db 60,188,124,252,2,130,66,194,34,162,98,226,18,146,82,210,50,178,114,242,10 |
|
.db 138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,6,134,70,198,38,166 |
|
.db 102,230,22,150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,30,158,94 |
|
.db 222,62,190,126,254,1,129,65,193,33,161,97,225,17,145,81,209,49,177,113,241,9 |
|
.db 137,73,201,41,169,105,233,25,153,89,217,57,185,121,249,5,133,69,197,37,165 |
|
.db 101,229,21,149,85,213,53,181,117,245,13,141,77,205,45,173,109,237,29,157,93 |
|
.db 221,61,189,125,253,3,131,67,195,35,163,99,227,19,147,83,211,51,179,115,243,11 |
|
.db 139,75,203,43,171,107,235,27,155,91,219,59,187,123,251,7,135,71,199,39,167 |
|
.db 103,231,23,151,87,215,55,183,119,247,15,143,79,207,47,175,111,239,31,159,95 |
|
.db 223,63,191,127,255 |
|
|
|
|
|
|
|
|
|
.End |
|
|
|
|
|
|