Menu

Adding new hardware interfaces

Luca Longinotti

Adding new hardware interfaces

The basic interface to hardware is the HardwareInterface. It is turn has sub-interfaces to various kinds of hardware. You need to implement the appropriate interfaces (e.g. AEMonitorInterface) and if you want to be able to use multiple instances, you also need to make a HardwareInterfaceFactory to build the devices.

Hint: You will need to use Device Manager to see which devices are connected. See http://www.annoyances.org/exec/show/article01-420 for how to make a shortcut to Windows Device Manager.

AE monitoring devices based on CypressFX2 under Windows

Devices like the silicon retina use the CypressFX2LP high speed USB interface chip. Under windows, they communicate with jAER via the Thesycon USBIO library. The base class for these interfaces is CypressFX2. Particular devices subclass CypressFX2 to customize the method translateEvents, which takes the raw data bytes and fills the AEPacketRaw objects that hold the raw timestamped events. See for instance the class CypressFX2DVS128HardwareInterface.

Specific steps to make your own CypressFX2 subclass are to
1. Subclass CypressFX2
1. Override the subclass AEReader of CypressFX2 to override, in particular, the method translateEvents
1. Override the method startAEReader to start the subclassed AEReader
The method translate events gets called every time a new packet arrives from hardware. It gets a synchronized referenced to the current AEPacketRaw to write events to (needs to be synchronized because the rendering thread is rendering a different packet). Then it appends the new data to this AEPacketRaw.

Here is an example

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package ch.unizh.ini.caviar.hardwareinterface.usb;

import ch.unizh.ini.caviar.aemonitor.AEPacketRaw;
import ch.unizh.ini.caviar.hardwareinterface.HardwareInterfaceException;
import ch.unizh.ini.caviar.hardwareinterface.HasUpdatableFirmware;
import de.thesycon.usbio.UsbIoBuf;
import de.thesycon.usbio.UsbIoInterface;
import de.thesycon.usbio.structs.USBIO_CLASS_OR_VENDOR_REQUEST;
import de.thesycon.usbio.structs.USBIO_DATA_BUFFER;
import java.io.IOException;

/**
 * The hardware interface for the DVS128 (second Tmpdiff128 board, with CPLD) retina boards.
 *
 * @author tobi/rapha
 */
public class CypressFX2DVS128HardwareInterface extends CypressFX2Biasgen implements HasUpdatableFirmware {

    public final static String FIRMWARE_FILENAME_DVS128_XSVF="/ch/unizh/ini/caviar/hardwareinterface/usb/dvs128CPLD.xsvf";

    /** Creates a new instance of CypressFX2Biasgen */
    protected CypressFX2DVS128HardwareInterface(int devNumber) {
        super(devNumber);
    }

    /**
     * Starts reader buffer pool thread and enables in endpoints for AEs. This method is overridden to construct
     our own reader with its translateEvents method
     */
    @Override
    public void startAEReader() throws HardwareInterfaceException {  // raphael: changed from private to protected, because i need to access this method
        setAeReader(new RetinaAEReader(this));
        allocateAEBuffers();
        getAeReader().startThread(3); // arg is number of errors before giving up
        HardwareInterfaceException.clearException();
    }

    /** This reader understands the format of raw USB data and translates to the AEPacketRaw */
    public class RetinaAEReader extends CypressFX2.AEReader{
        public RetinaAEReader(CypressFX2 cypress) throws HardwareInterfaceException{
            super(cypress);
        }

        /** Does the translation, timestamp unwrapping and reset
         * @param b the raw buffer
         */
        @Override
        protected void translateEvents(UsbIoBuf b){

//            System.out.println("buf has "+b.BytesTransferred+" bytes");
            synchronized(aePacketRawPool){
                AEPacketRaw buffer=aePacketRawPool.writeBuffer();
            //    if(buffer.overrunOccuredFlag) return;  // don't bother if there's already an overrun, consumer must get the events to clear this flag before there is more room for new events
                int shortts;
                int NumberOfWrapEvents;
                NumberOfWrapEvents=0;

                byte[] aeBuffer=b.BufferMem;
                //            byte lsb,msb;
                int bytesSent=b.BytesTransferred;
                if(bytesSent%4!=0){
//                System.out.println("CypressFX2.AEReader.translateEvents(): warning: "+bytesSent+" bytes sent, which is not multiple of 4");
                    bytesSent=(bytesSent/4)*4; // truncate off any extra part-event
                }

                int[] addresses=buffer.getAddresses();
                int[] timestamps=buffer.getTimestamps();

                // write the start of the packet
                buffer.lastCaptureIndex=eventCounter;

                for(int i=0;i<bytesSent;i+=4){

                    if((aeBuffer[i+3]&0x80)==0x80){ // timestamp bit 16 is one -> wrap
                        // now we need to increment the wrapAdd

                        wrapAdd+=0x4000L; //uses only 14 bit timestamps

                        //System.out.println("received wrap event, index:" + eventCounter + " wrapAdd: "+ wrapAdd);
                        NumberOfWrapEvents++;
                    } else if  ((aeBuffer[i+3]&0x40)==0x40  ) { // timestamp bit 15 is one -> wrapAdd reset
                        // this firmware version uses reset events to reset timestamps
                        this.resetTimestamps();
                        // log.info("got reset event, timestamp " + (0xffff&((short)aeBuffer[i]&0xff | ((short)aeBuffer[i+1]&0xff)<<8)));
                    } else if ((eventCounter>aeBufferSize-1) || (buffer.overrunOccuredFlag)) { // just do nothing, throw away events
                        buffer.overrunOccuredFlag=true;
                    } else {
                        // address is LSB MSB
                        addresses[eventCounter]=(int)((aeBuffer[i]&0xFF) | ((aeBuffer[i+1]&0xFF)<<8));

                        // same for timestamp, LSB MSB
                        shortts=(aeBuffer[i+2]&0xff | ((aeBuffer[i+3]&0xff)<<8)); // this is 15 bit value of timestamp in TICK_US tick

                        timestamps[eventCounter]=(int)(TICK_US*(shortts+wrapAdd)); //*TICK_US; //add in the wrap offset and convert to 1us tick
                        // this is USB2AERmini2 or StereoRetina board which have 1us timestamp tick
                        eventCounter++;
                        buffer.setNumEvents(eventCounter);
                    }
                } // end for

                // write capture size
                buffer.lastCaptureLength=eventCounter-buffer.lastCaptureIndex;

                // if (NumberOfWrapEvents!=0) {
                //System.out.println("Number of wrap events received: "+ NumberOfWrapEvents);
                //}
                //System.out.println("wrapAdd : "+ wrapAdd);
            } // sync on aePacketRawPool

        }
    }

    /** set the pixel array reset
     * @param value true to reset the pixels, false to let them run normally
     */
    synchronized public void setArrayReset(boolean value) {
        arrayResetEnabled=value;
        // send vendor request for device to reset array
        if(gUsbIo==null){
            throw new RuntimeException("device must be opened before sending this vendor request");
        }

        // make vendor request structure and populate it
        USBIO_CLASS_OR_VENDOR_REQUEST VendorRequest=new USBIO_CLASS_OR_VENDOR_REQUEST();

        VendorRequest.Flags=UsbIoInterface.USBIO_SHORT_TRANSFER_OK;
        VendorRequest.Type=UsbIoInterface.RequestTypeVendor;
        VendorRequest.Recipient=UsbIoInterface.RecipientDevice;
        VendorRequest.RequestTypeReservedBits=0;
        VendorRequest.Request=VENDOR_REQUEST_SET_ARRAY_RESET;
        VendorRequest.Index=0;

        VendorRequest.Value=(short)(value?1:0);  // this is the request bit, if value true, send value 1, false send value 0

        USBIO_DATA_BUFFER dataBuffer=new USBIO_DATA_BUFFER(0); // no data, value is in request value
        dataBuffer.setNumberOfBytesToTransfer(dataBuffer.Buffer().length);

        status=gUsbIo.classOrVendorOutRequest(dataBuffer,VendorRequest);
        if(status!=USBIO_ERR_SUCCESS){
            System.err.println("CypressFX2.resetPixelArray: couldn't send vendor request to reset array");
        }
    }

    public boolean isArrayReset(){
        return arrayResetEnabled;
    }

    /** Updates the firmware by downloading to the board's EEPROM */
    public void updateFirmware() throws HardwareInterfaceException {

        this.writeCPLDfirmware(FIRMWARE_FILENAME_DVS128_XSVF);
        log.info("New firmware written to CPLD");
        byte[] fw;
        try {
            fw = this.loadBinaryFirmwareFile(CypressFX2.FIRMWARE_FILENAME_DVS128_IIC);
        } catch (java.io.IOException e) {
            e.printStackTrace();
            throw new HardwareInterfaceException("Could not load firmware file ");
        }
        this.writeEEPROM(0, fw);
        log.info("New firmware written to EEPROM");
    }

}

Customizing the CypressFX2Factory

After you have created your own subclass of CypressFX2, you still need the factory to manufacture it for you. You can do this by adding your factory to the method getInterface(int n) in the CypressFX2Factory, which is shown below. The switch constructs the appropriate HardwareInterface based on the product ID (PID) of the device.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
   /** returns the n-th interface in the list, either Tmpdiff128Retina, USBAERmini2 or USB2AERmapper, TCVS320, or MonitorSequencer depending on PID
     *@param n the number to instance (0 based)
     */
    public USBInterface getInterface(int n) {
        int numAvailable = getNumInterfacesAvailable();
        if (n > numAvailable - 1) {
            log.warning("Only " + numAvailable + " interfaces available but you asked for number " + n);
            return null;
        }

        UsbIo dev = new UsbIo();

        gDevList = UsbIo.createDeviceList(GUID);

        int status = dev.open(n, gDevList, GUID);

        if (status != this.USBIO_ERR_SUCCESS) {
            log.warning(UsbIo.errorText(status));
            dev.close();
            UsbIo.destroyDeviceList(gDevList);
            return null;
        }

        USB_DEVICE_DESCRIPTOR deviceDescriptor = new USB_DEVICE_DESCRIPTOR();
        status = dev.getDeviceDescriptor(deviceDescriptor);
        if (status != USBIO_ERR_SUCCESS) {
            UsbIo.destroyDeviceList(gDevList);
            log.warning(UsbIo.errorText(status));
            dev.close();
            return null;
        }

        dev.close();
        UsbIo.destroyDeviceList(gDevList);
        short pid = (short) (0xffff & deviceDescriptor.idProduct); // for some reason returns 0xffff8613 from blank cypress fx2

        switch (pid) {
            case CypressFX2.PID_USB2AERmapper:
                return new CypressFX2Mapper(n);
            case CypressFX2.PID_DVS128_REV0:
        //    case CypressFX2.PID_TMPDIFF128_FX2_SMALL_BOARD:  // VID/PID replaced with the ones from thesycon
                return new CypressFX2DVS128HardwareInterface(n);
            case CypressFX2.PID_TMPDIFF128_RETINA:
                return new CypressFX2TmpdiffRetinaHardwareInterface(n);
            case CypressFX2.PID_TCVS320_RETINA:
                return new CypressFX2TCVS320RetinaHardwareInterface(n);
            case CypressFX2.PID_USBAERmini2:
                return new CypressFX2MonitorSequencer(n);
            default:
                log.warning("PID=" + HexString.toString(pid) + " doesn't match any device, returning CypressFX2MonitorSequencer");
                return new CypressFX2MonitorSequencer(n);
        }
    }

Related

Wiki: Home