From: Benjamin Larsson Date: Fri, 18 Mar 2016 21:12:56 +0000 (+0100) Subject: Initial Sainsmart DDS120 firmware X-Git-Tag: sigrok-firmware-fx2lafw-0.1.4~33 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=ce1d0a869e4761d30b698b6bb1ea015a0470f09e;p=sigrok-firmware-fx2lafw.git Initial Sainsmart DDS120 firmware --- diff --git a/Makefile.am b/Makefile.am index 59ed5f24..e2109642 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,6 +25,7 @@ SUFFIXES = .a51 .c .fw .ihx .rel # Flags for firmware hex file generation SDCC_LINK_FLAGS = --code-size 0x1c00 --xram-size 0x0200 --xram-loc 0x1c00 -Wl"-b DSCR_AREA=0x1e00" -Wl"-b INT2JT=0x1f00" SDCC_LINK_FLAGS_HANTEK_6022BE = --code-size 0x3c00 --xram-size 0x0100 --xram-loc 0x3c00 -Wl"-b DSCR_AREA=0x3d00" -Wl"-b INT2JT=0x3f00" +SDCC_LINK_FLAGS_SAINSMART_DDS120 = --code-size 0x3c00 --xram-size 0x0100 --xram-loc 0x3c00 -Wl"-b DSCR_AREA=0x3d00" -Wl"-b INT2JT=0x3f00" # Include paths as_includes = -I$(srcdir)/include @@ -47,7 +48,8 @@ dirstamps = \ hw/saleae-logic/$(dirstamp) \ hw/sigrok-fx2-8ch/$(dirstamp) \ hw/sigrok-fx2-16ch/$(dirstamp) \ - hw/hantek-6022be/$(dirstamp) + hw/hantek-6022be/$(dirstamp) \ + hw/sainsmart-dds120/$(dirstamp) # Final firmware files firmware_binaries = \ @@ -59,7 +61,8 @@ firmware_binaries = \ hw/saleae-logic/fx2lafw-saleae-logic.fw \ hw/sigrok-fx2-8ch/fx2lafw-sigrok-fx2-8ch.fw \ hw/sigrok-fx2-16ch/fx2lafw-sigrok-fx2-16ch.fw \ - hw/hantek-6022be/hantek-6022be.fw + hw/hantek-6022be/hantek-6022be.fw \ + hw/sainsmart-dds120/sainsmart-dds120.fw fx2lafw_headers = \ include/command.h \ @@ -81,6 +84,12 @@ hantek_6022be_sources = \ hantek_6022be_objects = \ hantek_6022be.rel +sainsmart_dds120_sources = \ + sainsmart_dds120.c + +sainsmart_dds120_objects = \ + sainsmart_dds120.rel + fx2lib_headers = \ fx2lib/include/autovector.h \ fx2lib/include/delay.h \ @@ -218,12 +227,13 @@ hw_sources = \ hw/saleae-logic/dscr.a51 \ hw/sigrok-fx2-8ch/dscr.a51 \ hw/sigrok-fx2-16ch/dscr.a51 \ - hw/hantek-6022be/dscr.a51 + hw/hantek-6022be/dscr.a51 \ + hw/sainsmart-dds120/dscr.a51 firmwaredir = $(datadir)/sigrok-firmware firmware_DATA = $(firmware_binaries) -dist_noinst_DATA = $(fx2lafw_headers) $(fx2lafw_sources) $(hantek_6022be_headers) $(hantek_6022be_sources) $(fx2lib_headers) $(fx2lib_sources) $(fx2lib_ints_sources) $(hw_sources) +dist_noinst_DATA = $(fx2lafw_headers) $(fx2lafw_sources) $(hantek_6022be_headers) $(hantek_6022be_sources) $(sainsmart_dds120_sources) $(sainsmart_dds120_headers) $(fx2lib_headers) $(fx2lib_sources) $(fx2lib_ints_sources) $(hw_sources) dist_noinst_SCRIPTS = autogen.sh MOSTLYCLEANFILES = *.asm fx2lib/lib/*.asm fx2lib/lib/*.lib fx2lib/lib/interrupts/*.asm fx2lib/lib/interrupts/*.lib @@ -288,6 +298,8 @@ hw/saleae-logic/dscr.rel: hw/saleae-logic/$(dirstamp) include/dscr.inc hw/sigrok-fx2-8ch/dscr.rel: hw/sigrok-fx2-8ch/$(dirstamp) include/dscr.inc hw/sigrok-fx2-16ch/dscr.rel: hw/sigrok-fx2-16ch/$(dirstamp) include/dscr.inc hw/hantek-6022be/dscr.rel: hw/hantek-6022be/$(dirstamp) include/dscr.inc +hw/sainsmart-dds120/dscr.rel: hw/sainsmart-dds120/$(dirstamp) include/dscr.inc + # Create build subdirectories $(dirstamps): @@ -329,6 +341,9 @@ hw/sigrok-fx2-16ch/fx2lafw-sigrok-fx2-16ch.ihx: hw/sigrok-fx2-16ch/dscr.rel $(fx hw/hantek-6022be/hantek-6022be.ihx: hw/hantek-6022be/dscr.rel $(hantek_6022be_objects) $(fx2lib_libs) $(AM_V_GEN)$(SDCC) -mmcs51 $(SDCC_LINK_FLAGS_HANTEK_6022BE) -o $@ hw/hantek-6022be/dscr.rel $(hantek_6022be_objects) $(fx2lib_libs) +hw/sainsmart-dds120/sainsmart-dds120.ihx: hw/sainsmart-dds120/dscr.rel $(sainsmart_dds120_objects) $(fx2lib_libs) + $(AM_V_GEN)$(SDCC) -mmcs51 $(SDCC_LINK_FLAGS_SAINSMART_DDS120) -o $@ hw/sainsmart-dds120/dscr.rel $(sainsmart_dds120_objects) $(fx2lib_libs) + .a51.rel: $(SF_V_SDAS)$(SDAS8051) -glos $(as_includes) $@ $< diff --git a/hw/sainsmart-dds120/dscr.a51 b/hw/sainsmart-dds120/dscr.a51 new file mode 100644 index 00000000..d83e2fe1 --- /dev/null +++ b/hw/sainsmart-dds120/dscr.a51 @@ -0,0 +1,362 @@ +;; +;; This file is part of the sigrok-firmware-fx2lafw project. +;; +;; Copyright (C) 2009 Ubixum, Inc. +;; +;; This library is free software; you can redistribute it and/or +;; modify it under the terms of the GNU Lesser General Public +;; License as published by the Free Software Foundation; either +;; version 2.1 of the License, or (at your option) any later version. +;; +;; This library 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 +;; Lesser General Public License for more details. +;; +;; You should have received a copy of the GNU Lesser General Public +;; License along with this library; if not, write to the Free Software +;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +;; + +; this is a the default +; full speed and high speed +; descriptors found in the TRM +; change however you want but leave +; the descriptor pointers so the setupdat.c file works right + + +.module DEV_DSCR + +; descriptor types +; same as setupdat.h +DSCR_DEVICE_TYPE=1 +DSCR_CONFIG_TYPE=2 +DSCR_STRING_TYPE=3 +DSCR_INTERFACE_TYPE=4 +DSCR_ENDPOINT_TYPE=5 +DSCR_DEVQUAL_TYPE=6 + +; for the repeating interfaces +DSCR_INTERFACE_LEN=9 +DSCR_ENDPOINT_LEN=7 + +; endpoint types +ENDPOINT_TYPE_CONTROL=0 +ENDPOINT_TYPE_ISO=1 +ENDPOINT_TYPE_BULK=2 +ENDPOINT_TYPE_INT=3 + + .globl _dev_dscr, _dev_qual_dscr, _highspd_dscr, _fullspd_dscr, _dev_strings, _dev_strings_end +; These need to be in code memory. If +; they aren't you'll have to manully copy them somewhere +; in code memory otherwise SUDPTRH:L don't work right + .area DSCR_AREA (CODE) + +_dev_dscr: + .db dev_dscr_end-_dev_dscr ; len + .db DSCR_DEVICE_TYPE ; type + .dw 0x0002 ; usb 2.0 + .db 0xff ; class (vendor specific) + .db 0xff ; subclass (vendor specific) + .db 0xff ; protocol (vendor specific) + .db 64 ; packet size (ep0) + .dw 0xB504 ; vendor id + .dw 0x2260 ; product id + .dw 0x0000 ; version id + .db 1 ; manufacturure str idx + .db 2 ; product str idx + .db 0 ; serial str idx + .db 1 ; n configurations +dev_dscr_end: + +_dev_qual_dscr: + .db dev_qualdscr_end-_dev_qual_dscr + .db DSCR_DEVQUAL_TYPE + .dw 0x0002 ; usb 2.0 + .db 0 + .db 0 + .db 0 + .db 64 ; max packet + .db 1 ; n configs + .db 0 ; extra reserved byte +dev_qualdscr_end: + +_highspd_dscr: + .db highspd_dscr_end-_highspd_dscr ; dscr len ;; Descriptor length + .db DSCR_CONFIG_TYPE + ; can't use .dw because byte order is different + .db (highspd_dscr_realend-_highspd_dscr) % 256 ; total length of config lsb + .db (highspd_dscr_realend-_highspd_dscr) / 256 ; total length of config msb + .db 1 ; n interfaces + .db 1 ; config number + .db 0 ; config string + .db 0x80 ; attrs = bus powered, no wakeup + .db 55 ; max power = 110mA +highspd_dscr_end: + +; all the interfaces next +; BULK interface + .db DSCR_INTERFACE_LEN + .db DSCR_INTERFACE_TYPE + .db 0 ; index + .db 0 ; alt setting idx + .db 1 ; n endpoints + .db 0xff ; class + .db 0 + .db 0 + .db 0 ; string index + +; endpoint 6 in + .db DSCR_ENDPOINT_LEN + .db DSCR_ENDPOINT_TYPE + .db 0x86 ; ep1 dir=in and address + .db ENDPOINT_TYPE_BULK ; type + .db 0x00 ; max packet LSB + .db 0x02 ; max packet size=512 bytes + .db 0x00 ; polling interval + +; ISOCHRONOUS interface + .db DSCR_INTERFACE_LEN + .db DSCR_INTERFACE_TYPE + .db 0 ; index + .db 1 ; alt setting idx + .db 1 ; n endpoints + .db 0xff ; class + .db 0 + .db 1 + .db 0 ; string index + +; endpoint 2 in + .db DSCR_ENDPOINT_LEN + .db DSCR_ENDPOINT_TYPE + .db 0x82 ; ep1 dir=in and address + .db ENDPOINT_TYPE_ISO ; type + .db 0x00 ; max packet LSB + .db 0x14 ; max packet size=3*1024 bytes + .db 0x01 ; polling interval + +; ISOCHRONOUS interface 16MB/s + .db DSCR_INTERFACE_LEN + .db DSCR_INTERFACE_TYPE + .db 0 ; index + .db 2 ; alt setting idx + .db 1 ; n endpoints + .db 0xff ; class + .db 0 + .db 1 + .db 0 ; string index + +; endpoint 2 in + .db DSCR_ENDPOINT_LEN + .db DSCR_ENDPOINT_TYPE + .db 0x82 ; ep1 dir=in and address + .db ENDPOINT_TYPE_ISO ; type + .db 0x00 ; max packet LSB + .db 0x0c ; max packet size=2*1024 bytes + .db 0x01 ; polling interval + +; ISOCHRONOUS interface 8MB/s + .db DSCR_INTERFACE_LEN + .db DSCR_INTERFACE_TYPE + .db 0 ; index + .db 3 ; alt setting idx + .db 1 ; n endpoints + .db 0xff ; class + .db 0 + .db 1 + .db 0 ; string index + +; endpoint 2 in + .db DSCR_ENDPOINT_LEN + .db DSCR_ENDPOINT_TYPE + .db 0x82 ; ep1 dir=in and address + .db ENDPOINT_TYPE_ISO ; type + .db 0x00 ; max packet LSB + .db 0x04 ; max packet size=1024 bytes + .db 0x01 ; polling interval + +; ISOCHRONOUS interface 4MB/s + .db DSCR_INTERFACE_LEN + .db DSCR_INTERFACE_TYPE + .db 0 ; index + .db 4 ; alt setting idx + .db 1 ; n endpoints + .db 0xff ; class + .db 0 + .db 1 + .db 0 ; string index + +; endpoint 2 in + .db DSCR_ENDPOINT_LEN + .db DSCR_ENDPOINT_TYPE + .db 0x82 ; ep1 dir=in and address + .db ENDPOINT_TYPE_ISO ; type + .db 0x00 ; max packet LSB + .db 0x04 ; max packet size=1024 bytes + .db 0x02 ; polling interval + + +; ISOCHRONOUS interface 2MB/s + .db DSCR_INTERFACE_LEN + .db DSCR_INTERFACE_TYPE + .db 0 ; index + .db 5 ; alt setting idx + .db 1 ; n endpoints + .db 0xff ; class + .db 0 + .db 1 + .db 0 ; string index + +; endpoint 2 in + .db DSCR_ENDPOINT_LEN + .db DSCR_ENDPOINT_TYPE + .db 0x82 ; ep1 dir=in and address + .db ENDPOINT_TYPE_ISO ; type + .db 0x00 ; max packet LSB + .db 0x04 ; max packet size=1024 bytes + .db 0x03 ; polling interval + +; ISOCHRONOUS interface 1MB/s + .db DSCR_INTERFACE_LEN + .db DSCR_INTERFACE_TYPE + .db 0 ; index + .db 6 ; alt setting idx + .db 1 ; n endpoints + .db 0xff ; class + .db 0 + .db 1 + .db 0 ; string index + +; endpoint 2 in + .db DSCR_ENDPOINT_LEN + .db DSCR_ENDPOINT_TYPE + .db 0x82 ; ep1 dir=in and address + .db ENDPOINT_TYPE_ISO ; type + .db 0x00 ; max packet LSB + .db 0x04 ; max packet size=1024 bytes + .db 0x04 ; polling interval + +; ISOCHRONOUS interface 500 kB/s + .db DSCR_INTERFACE_LEN + .db DSCR_INTERFACE_TYPE + .db 0 ; index + .db 7 ; alt setting idx + .db 1 ; n endpoints + .db 0xff ; class + .db 0 + .db 1 + .db 0 ; string index + +; endpoint 2 in + .db DSCR_ENDPOINT_LEN + .db DSCR_ENDPOINT_TYPE + .db 0x82 ; ep1 dir=in and address + .db ENDPOINT_TYPE_ISO ; type + .db 0x00 ; max packet LSB + .db 0x02 ; max packet size=512 bytes + .db 0x04 ; polling interval + + +highspd_dscr_realend: + +.even +_fullspd_dscr: + .db fullspd_dscr_end-_fullspd_dscr ; dscr len + .db DSCR_CONFIG_TYPE + ; can't use .dw because byte order is different + .db (fullspd_dscr_realend-_fullspd_dscr) % 256 ; total length of config lsb + .db (fullspd_dscr_realend-_fullspd_dscr) / 256 ; total length of config msb + .db 2 ; n interfaces + .db 1 ; config number + .db 0 ; config string + .db 0x80 ; attrs = bus powered, no wakeup + .db 55 ; max power = 110mA +fullspd_dscr_end: + + +; all the interfaces next +; BULK interface + .db DSCR_INTERFACE_LEN + .db DSCR_INTERFACE_TYPE + .db 0 ; index + .db 0 ; alt setting idx + .db 1 ; n endpoints + .db 0xff ; class + .db 0 + .db 0 + .db 0 ; string index + +; endpoint 6 in + .db DSCR_ENDPOINT_LEN + .db DSCR_ENDPOINT_TYPE + .db 0x86 ; ep1 dir=in and address + .db ENDPOINT_TYPE_BULK ; type + .db 0x40 ; max packet LSB + .db 0x00 ; max packet size=512 bytes + .db 0x00 ; polling interval + +; ISOCHRONOUS interface 1 MB/s + .db DSCR_INTERFACE_LEN + .db DSCR_INTERFACE_TYPE + .db 0 ; index + .db 1 ; alt setting idx + .db 1 ; n endpoints + .db 0xff ; class + .db 0 + .db 1 + .db 0 ; string index + +; endpoint 2 in + .db DSCR_ENDPOINT_LEN + .db DSCR_ENDPOINT_TYPE + .db 0x82 ; ep1 dir=in and address + .db ENDPOINT_TYPE_ISO ; type + .db 0xff ; max packet LSB + .db 0x03 ; max packet size=1023 bytes + .db 0x01 ; polling interval + +; ISOCHRONOUS interface 500 kB/s + .db DSCR_INTERFACE_LEN + .db DSCR_INTERFACE_TYPE + .db 0 ; index + .db 2 ; alt setting idx + .db 1 ; n endpoints + .db 0xff ; class + .db 0 + .db 1 + .db 0 ; string index + +; endpoint 2 in + .db DSCR_ENDPOINT_LEN + .db DSCR_ENDPOINT_TYPE + .db 0x82 ; ep1 dir=in and address + .db ENDPOINT_TYPE_ISO ; type + .db 0x00 ; max packet LSB + .db 0x02 ; max packet size=512 bytes + .db 0x01 ; polling interval + +fullspd_dscr_realend: + +.even +_dev_strings: +; sample string +_string0: + .db string0end-_string0 ; len + .db DSCR_STRING_TYPE + .db 0x09, 0x04 ; 0x0409 is the language code for English. Possible to add more codes after this. +string0end: +; add more strings here +_string1: + .db string1end-_string1 ; len + .db DSCR_STRING_TYPE + .ascii 'O\0D\0M\0' +string1end: +_string2: + .db string2end-_string2 ; len + .db DSCR_STRING_TYPE + .ascii 'H\0a\0n\0t\0e\0k\0D\0S\0O\0006\0000\0002\0002\0B\0E\0' +string2end: + + +_dev_strings_end: + .dw 0x0000 ; in case you wanted to look at memory between _dev_strings and _dev_strings_end diff --git a/sainsmart_dds120.c b/sainsmart_dds120.c new file mode 100644 index 00000000..aa1190ae --- /dev/null +++ b/sainsmart_dds120.c @@ -0,0 +1,484 @@ +/* + * This file is part of the sigrok-firmware-fx2lafw project. + * + * Copyright (C) 2009 Ubixum, Inc. + * Copyright (C) 2015 Jochen Hoenicke + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +/* Change to support as many interfaces as you need. */ +static BYTE altiface = 0; + +static volatile __bit dosud = FALSE; +static volatile __bit dosuspend = FALSE; + +extern __code BYTE highspd_dscr; +extern __code BYTE fullspd_dscr; + +void resume_isr(void) __interrupt RESUME_ISR +{ + CLEAR_RESUME(); +} + +void sudav_isr(void) __interrupt SUDAV_ISR +{ + dosud = TRUE; + CLEAR_SUDAV(); +} + +void usbreset_isr(void) __interrupt USBRESET_ISR +{ + handle_hispeed(FALSE); + CLEAR_USBRESET(); +} + +void hispeed_isr(void) __interrupt HISPEED_ISR +{ + handle_hispeed(TRUE); + CLEAR_HISPEED(); +} + +void suspend_isr(void) __interrupt SUSPEND_ISR +{ + dosuspend = TRUE; + CLEAR_SUSPEND(); +} + +void timer2_isr(void) __interrupt TF2_ISR +{ + /* Toggle the 1kHz pin, only accurate up to ca 8MHz */ + IOE = IOE^0x04; + TF2 = 0; +} + +/** + * The gain stage is 2 stage approach. -6dB and -20dB on the first stage (attentuator). The second stage is then doing the gain by 3 different resistor values switched into the feedback loop. + * #Channel 0: + * PC1=1; PC2=0; PC3= 0 -> Gain x0.1 = -20dB + * PC1=1; PC2=0; PC3= 1 -> Gain x0.2 = -14dB + * PC1=1; PC2=1; PC3= 0 -> Gain x0.4 = -8dB + * PC1=0; PC2=0; PC3= 0 -> Gain x0.5 = -6dB + * PC1=0; PC2=0; PC3= 1 -> Gain x1 = 0dB + * PC1=0; PC2=1; PC3= 0 -> Gain x2 = +6dB + * #Channel 1: + * PE1=1; PC4=0; PC5= 0 -> Gain x0.1 = -20dB + * PE1=1; PC4=0; PC5= 1 -> Gain x0.2 = -14dB + * PE1=1; PC4=1; PC5= 0 -> Gain x0.4 = -8dB + * PE1=0; PC4=0; PC5= 0 -> Gain x0.5 = -6dB + * PE1=0; PC4=0; PC5= 1 -> Gain x1 = 0dB + * PE1=0; PC4=1; PC5= 0 -> Gain x2 = +6dB + */ +static BOOL set_voltage(BYTE channel, BYTE val) +{ + BYTE bits_C, bit_E, mask_C, mask_E; + + if (channel == 0) { + mask_C = 0x0E; + mask_E = 0x00; + bit_E = 0; + switch (val) { + case 1: + bits_C = 0x02; + break; + case 2: + bits_C = 0x06; + break; + case 5: + bits_C = 0x00; + break; + case 10: + bits_C = 0x04; + break; + case 20: + bits_C = 0x08; + break; + default: + return FALSE; + } + } else if (channel == 1) { + mask_C = 0x30; + mask_E = 0x02; + switch (val) { + case 1: + bits_C = 0x00; + bit_E = 0x02; + break; + case 2: + bits_C = 0x10; + bit_E = 0x02; + break; + case 5: + bits_C = 0x00; + bit_E = 0x00; + break; + case 10: + bits_C = 0x10; + bit_E = 0x00; + break; + case 20: + bits_C = 0x20; + bit_E = 0x00; + break; + default: + return FALSE; + } + } else { + return FALSE; + } + IOC = (IOC & ~mask_C) | (bits_C & mask_C); + IOE = (IOE & ~mask_E) | (bit_E & mask_E); + + return TRUE; +} + +static BOOL set_numchannels(BYTE numchannels) +{ + if (numchannels == 1 || numchannels == 2) { + BYTE fifocfg = 7 + numchannels; + EP2FIFOCFG = fifocfg; + EP6FIFOCFG = fifocfg; + return TRUE; + } + + return FALSE; +} + +static void clear_fifo(void) +{ + GPIFABORT = 0xff; + SYNCDELAY3; + FIFORESET = 0x80; + SYNCDELAY3; + FIFORESET = 0x82; + SYNCDELAY3; + FIFORESET = 0x86; + SYNCDELAY3; + FIFORESET = 0; +} + +static void stop_sampling(void) +{ + GPIFABORT = 0xff; + SYNCDELAY3; + INPKTEND = (altiface == 0) ? 6 : 2; +} + +static void start_sampling(void) +{ + int i; + + clear_fifo(); + + for (i = 0; i < 1000; i++); + + while (!(GPIFTRIG & 0x80)) + ; + + SYNCDELAY3; + GPIFTCB1 = 0x28; + SYNCDELAY3; + GPIFTCB0 = 0; + GPIFTRIG = (altiface == 0) ? 6 : 4; + +} + +static void select_interface(BYTE alt) +{ + const BYTE *pPacketSize = \ + ((USBCS & bmHSM) ? &highspd_dscr : &fullspd_dscr) + + (9 + (16 * alt) + 9 + 4); + + altiface = alt; + + if (alt == 0) { + /* Bulk on EP6. */ + EP2CFG = 0x00; + EP6CFG = 0xe0; + EP6GPIFFLGSEL = 1; + EP6AUTOINLENL = pPacketSize[0]; + EP6AUTOINLENH = pPacketSize[1]; + } else { + /* Iso on EP2. */ + EP2CFG = 0xd8; + EP6CFG = 0x00; + EP2GPIFFLGSEL = 1; + EP2AUTOINLENL = pPacketSize[0]; + EP2AUTOINLENH = pPacketSize[1] & 0x7; + EP2ISOINPKTS = (pPacketSize[1] >> 3) + 1; + } +} + +static const struct samplerate_info { + BYTE rate; + BYTE wait0; + BYTE wait1; + BYTE opc0; + BYTE opc1; + BYTE out0; + BYTE ifcfg; +} samplerates[] = { + { 48, 0x80, 0, 3, 0, 0x00, 0xea }, + { 30, 0x80, 0, 3, 0, 0x00, 0xaa }, + { 24, 1, 0, 2, 1, 0x40, 0xea }, + { 16, 1, 1, 2, 0, 0x40, 0xea }, + { 12, 2, 1, 2, 0, 0x40, 0xea }, + { 8, 3, 2, 2, 0, 0x40, 0xea }, + { 4, 6, 5, 2, 0, 0x40, 0xea }, + { 2, 12, 11, 2, 0, 0x40, 0xea }, + { 1, 24, 23, 2, 0, 0x40, 0xea }, + { 50, 48, 47, 2, 0, 0x40, 0xea }, + { 20, 120, 119, 2, 0, 0x40, 0xea }, + { 10, 240, 239, 2, 0, 0x40, 0xea }, +}; + +static BOOL set_samplerate(BYTE rate) +{ + BYTE i = 0; + + while (samplerates[i].rate != rate) { + i++; + if (i == sizeof(samplerates) / sizeof(samplerates[0])) + return FALSE; + } + + IFCONFIG = samplerates[i].ifcfg; + + AUTOPTRSETUP = 7; + AUTOPTRH2 = 0xE4; + AUTOPTRL2 = 0x00; + + /* + * The program for low-speed, e.g. 1 MHz, is: + * wait 24, CTL2=0, FIFO + * wait 23, CTL2=1 + * jump 0, CTL2=1 + * + * The program for 24 MHz is: + * wait 1, CTL2=0, FIFO + * jump 0, CTL2=1 + * + * The program for 30/48 MHz is: + * jump 0, CTL2=Z, FIFO, LOOP + */ + + EXTAUTODAT2 = samplerates[i].wait0; + EXTAUTODAT2 = samplerates[i].wait1; + EXTAUTODAT2 = 1; + EXTAUTODAT2 = 0; + EXTAUTODAT2 = 0; + EXTAUTODAT2 = 0; + EXTAUTODAT2 = 0; + EXTAUTODAT2 = 0; + + EXTAUTODAT2 = samplerates[i].opc0; + EXTAUTODAT2 = samplerates[i].opc1; + EXTAUTODAT2 = 1; + EXTAUTODAT2 = 0; + EXTAUTODAT2 = 0; + EXTAUTODAT2 = 0; + EXTAUTODAT2 = 0; + EXTAUTODAT2 = 0; + + EXTAUTODAT2 = samplerates[i].out0; + EXTAUTODAT2 = 0x44; + EXTAUTODAT2 = 0x44; + EXTAUTODAT2 = 0x00; + EXTAUTODAT2 = 0x00; + EXTAUTODAT2 = 0x00; + EXTAUTODAT2 = 0x00; + EXTAUTODAT2 = 0x00; + + EXTAUTODAT2 = 0; + EXTAUTODAT2 = 0; + EXTAUTODAT2 = 0; + EXTAUTODAT2 = 0; + EXTAUTODAT2 = 0; + EXTAUTODAT2 = 0; + EXTAUTODAT2 = 0; + EXTAUTODAT2 = 0; + + for (i = 0; i < 96; i++) + EXTAUTODAT2 = 0; + + return TRUE; +} + +/* Set *alt_ifc to the current alt interface for ifc. */ +BOOL handle_get_interface(BYTE ifc, BYTE *alt_ifc) +{ + (void)ifc; + + *alt_ifc = altiface; + + return TRUE; +} + +/* + * Return TRUE if you set the interface requested. + * + * Note: This function should reconfigure and reset the endpoints + * according to the interface descriptors you provided. + */ +BOOL handle_set_interface(BYTE ifc,BYTE alt_ifc) +{ + if (ifc == 0) + select_interface(alt_ifc); + + return TRUE; +} + +BYTE handle_get_configuration(void) +{ + /* We only support configuration 0. */ + return 0; +} + +BOOL handle_set_configuration(BYTE cfg) +{ + /* We only support configuration 0. */ + (void)cfg; + + return TRUE; +} + +BOOL handle_vendorcommand(BYTE cmd) +{ + stop_sampling(); + + /* Clear EP0BCH/L for each valid command. */ + if (cmd >= 0xe0 && cmd <= 0xe4) { + EP0BCH = 0; + EP0BCL = 0; + while (EP0CS & bmEPBUSY); + } + + switch (cmd) { + case 0xe0: + case 0xe1: + set_voltage(cmd - 0xe0, EP0BUF[0]); + return TRUE; + case 0xe2: + set_samplerate(EP0BUF[0]); + return TRUE; + case 0xe3: + if (EP0BUF[0] == 1) + start_sampling(); + return TRUE; + case 0xe4: + set_numchannels(EP0BUF[0]); + return TRUE; + } + + return FALSE; /* Not handled by handlers. */ +} + +static void init(void) +{ + EP4CFG = 0; + EP8CFG = 0; + + /* In idle mode tristate all outputs. */ + GPIFIDLECTL = 0x00; + GPIFCTLCFG = 0x80; + GPIFWFSELECT = 0x00; + GPIFREADYSTAT = 0x00; + + stop_sampling(); + + set_voltage(0, 1); + set_voltage(1, 1); + set_samplerate(1); + set_numchannels(2); + select_interface(0); +} + +static void main(void) +{ + /* Save energy. */ + SETCPUFREQ(CLK_12M); + + init(); + + /* Set up interrupts. */ + USE_USB_INTS(); + + ENABLE_SUDAV(); + ENABLE_USBRESET(); + ENABLE_HISPEED(); + ENABLE_SUSPEND(); + ENABLE_RESUME(); + + /* Global (8051) interrupt enable. */ + EA = 1; + + /* Init timer2. */ + RCAP2L = -1000 & 0xff; + RCAP2H = (-1000 >> 8) & 0xff; + T2CON = 0; + ET2 = 1; + TR2 = 1; + + RENUMERATE_UNCOND(); + + PORTCCFG = 0; + PORTACFG = 0; + PORTECFG = 0; + OEE = 0xFF; + OEC = 0xff; + OEA = 0x80; + + PA7 = 1; + + while (TRUE) { + if (dosud) { + dosud = FALSE; + handle_setupdata(); + } + + if (dosuspend) { + dosuspend = FALSE; + do { + /* Make sure ext wakeups are cleared. */ + WAKEUPCS |= bmWU|bmWU2; + SUSPEND = 1; + PCON |= 1; + __asm + nop + nop + nop + nop + nop + nop + nop + __endasm; + } while (!remote_wakeup_allowed && REMOTE_WAKEUP()); + + /* Resume (TRM 6.4). */ + if (REMOTE_WAKEUP()) { + delay(5); + USBCS |= bmSIGRESUME; + delay(15); + USBCS &= ~bmSIGRESUME; + } + } + } +}