All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bidib.jbidibc.usbi2c.adapter.Ch341UsbI2cAdapter Maven / Gradle / Ivy

Go to download

jBiDiB jbidibc USB POM Install the libusb drivers for using the libusb access. Under Windows OS you must install the Zadig driver as described here: https://github.com/libusb/libusb/wiki/Windows. Zadic can be downloaded from: http://zadig.akeo.ie/ In the Zadig UI you must check Options > List all devices and the select the 'USB-EPP /I2C... CH341A' device. Then select the target driver with the spinners (I used libusb-win32 (v1.2.6.0)) and click the 'Install driver' button.

There is a newer version: 2.0.26
Show newest version
/*
 * Copyright (c) 2020 Victor Antonovich 
 *
 * This work 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 work 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.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 */

package org.bidib.jbidibc.usbi2c.adapter;

import java.io.IOException;

import org.bidib.jbidibc.usbhid.UsbConstants;
import org.bidib.jbidibc.usbhid.UsbDevice;
import org.bidib.jbidibc.usbhid.UsbEndpoint;
import org.bidib.jbidibc.usbhid.UsbInterface;
import org.bidib.jbidibc.usbi2c.UsbI2cManager;
import org.bidib.jbidibc.usbi2c.UsbI2cManager.UsbDeviceIdentifier;

/**
 * CH341 is a USB bus convert chip, providing UART, printer port, parallel and synchronous serial with 2-wire or 4-wire
 * through USB bus (http://www.anok.ceti.pl/download/ch341ds1.pdf).
 */
public class Ch341UsbI2cAdapter extends BaseUsbI2cAdapter {
    private static final int CH341_I2C_LOW_SPEED = 0; // low speed - 20kHz

    private static final int CH341_I2C_STANDARD_SPEED = 1; // standard speed - 100kHz

    private static final int CH341_I2C_FAST_SPEED = 2; // fast speed - 400kHz

    private static final int CH341_I2C_HIGH_SPEED = 3; // high speed - 750kHz

    private static final int CH341_CMD_I2C_STREAM = 0xAA;

    private static final int CH341_CMD_I2C_STM_STA = 0x74;

    private static final int CH341_CMD_I2C_STM_STO = 0x75;

    private static final int CH341_CMD_I2C_STM_OUT = 0x80;

    private static final int CH341_CMD_I2C_STM_IN = 0xC0;

    private static final int CH341_CMD_I2C_STM_SET = 0x60;

    private static final int CH341_CMD_I2C_STM_END = 0x00;

    // CH341 max transfer size
    private static final int MAX_TRANSFER_SIZE = 32;

    private final byte[] writeBuffer = new byte[MAX_TRANSFER_SIZE];

    private final byte[] readBuffer = new byte[MAX_TRANSFER_SIZE];

    class Ch341UsbI2cDevice extends BaseUsbI2cDevice {
        Ch341UsbI2cDevice(int address) {
            super(address);
        }

        @Override
        protected void deviceReadReg(int reg, byte[] buffer, int length) throws IOException {
            readRegData(address, reg, buffer, length);
        }

        @Override
        protected void deviceRead(byte[] buffer, int length) throws IOException {
            readData(address, buffer, length);
        }

        @Override
        protected void deviceWrite(byte[] buffer, int length) throws IOException {
            writeData(address, buffer, length);
        }
    }

    public Ch341UsbI2cAdapter(UsbI2cManager i2cManager, UsbDevice usbDevice) {
        super(i2cManager, usbDevice);
    }

    @Override
    protected Ch341UsbI2cDevice getDeviceImpl(int address) {
        return new Ch341UsbI2cDevice(address);
    }

    @Override
    protected void init(UsbDevice usbDevice) throws IOException {
        if (usbDevice.getInterfaceCount() == 0) {
            throw new IOException("No interfaces found for device: " + usbDevice);
        }

        UsbInterface usbInterface = usbDevice.getInterface(0);
        if (usbInterface.getEndpointCount() < 2) {
            throw new IOException("No endpoints found for device: " + usbDevice);
        }

        UsbEndpoint usbReadEndpoint = null, usbWriteEndpoint = null;
        for (int i = 0; i < usbInterface.getEndpointCount(); i++) {
            UsbEndpoint usbEndpoint = usbInterface.getEndpoint(i);
            if (usbEndpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
                if (usbEndpoint.getDirection() == UsbConstants.USB_DIR_IN) {
                    usbReadEndpoint = usbEndpoint;
                }
                else {
                    usbWriteEndpoint = usbEndpoint;
                }
            }
        }

        if (usbReadEndpoint == null || usbWriteEndpoint == null) {
            throw new IOException("No read or write bulk endpoint found for device: " + usbDevice);
        }
        setBulkEndpoints(usbReadEndpoint, usbWriteEndpoint);

        configure();
    }

    protected int getClockSpeedConstant(int speed) {
        switch (speed) {
            case 20000:
                return CH341_I2C_LOW_SPEED;
            case CLOCK_SPEED_STANDARD:
                return CH341_I2C_STANDARD_SPEED;
            case CLOCK_SPEED_FAST:
                return CH341_I2C_FAST_SPEED;
            case 750000:
                return CH341_I2C_HIGH_SPEED;
        }

        return -1;
    }

    @Override
    public boolean isClockSpeedSupported(int speed) {
        return (getClockSpeedConstant(speed) >= 0);
    }

    @Override
    protected void configure() throws IOException {
        writeBuffer[0] = (byte) CH341_CMD_I2C_STREAM;
        writeBuffer[1] = (byte) (CH341_CMD_I2C_STM_SET | getClockSpeedConstant(getClockSpeed()));
        writeBuffer[2] = CH341_CMD_I2C_STM_END;
        writeBulkData(writeBuffer, 3);
    }

    private void checkDataLength(int length) {
        int maxLength = MAX_TRANSFER_SIZE - 6;
        if (length > maxLength) {
            throw new IllegalArgumentException(String.format("Invalid data length: %d (max %d)", length, maxLength));
        }
    }

    private void writeData(int address, byte[] data, int length) throws IOException {
        checkDataLength(length);
        int i = 0;
        writeBuffer[i++] = (byte) CH341_CMD_I2C_STREAM;
        writeBuffer[i++] = CH341_CMD_I2C_STM_STA; // START condition
        writeBuffer[i++] = (byte) (CH341_CMD_I2C_STM_OUT | (length + 1)); // data length + 1
        writeBuffer[i++] = getAddressByte(address, false);
        System.arraycopy(data, 0, writeBuffer, i, length);
        i += length;
        writeBuffer[i++] = CH341_CMD_I2C_STM_STO; // STOP condition
        writeBuffer[i++] = CH341_CMD_I2C_STM_END; // end of transaction
        writeBulkData(writeBuffer, i);
    }

    private void readData(int address, byte[] data, int length) throws IOException {
        checkDataLength(length);
        checkDevicePresence(address); // to avoid weird phantom devices in scan results
        int i = 0;
        writeBuffer[i++] = (byte) CH341_CMD_I2C_STREAM;
        writeBuffer[i++] = CH341_CMD_I2C_STM_STA; // START condition
        writeBuffer[i++] = (byte) (CH341_CMD_I2C_STM_OUT | 1); // zero data length + 1
        writeBuffer[i++] = getAddressByte(address, true);
        if (length > 0) {
            for (int j = 0; j < length - 1; j++) {
                writeBuffer[i++] = (byte) (CH341_CMD_I2C_STM_IN | 1);
            }
            writeBuffer[i++] = (byte) CH341_CMD_I2C_STM_IN;
        }
        writeBuffer[i++] = CH341_CMD_I2C_STM_STO; // STOP condition
        writeBuffer[i++] = CH341_CMD_I2C_STM_END; // end of transaction
        int res = transferBulkData(writeBuffer, i, data, length);
    }

    private void readRegData(int address, int reg, byte[] data, int length) throws IOException {
        checkDataLength(length);
        // Write register number
        int i = 0;
        writeBuffer[i++] = (byte) CH341_CMD_I2C_STREAM;
        writeBuffer[i++] = CH341_CMD_I2C_STM_STA; // START condition
        writeBuffer[i++] = (byte) (CH341_CMD_I2C_STM_OUT | 2); // reg data length + 1
        writeBuffer[i++] = getAddressByte(address, false);
        writeBuffer[i++] = (byte) reg;
        writeBulkData(writeBuffer, i);
        // Read register data
        i = 0;
        writeBuffer[i++] = (byte) CH341_CMD_I2C_STREAM;
        writeBuffer[i++] = CH341_CMD_I2C_STM_STA; // START condition
        writeBuffer[i++] = (byte) (CH341_CMD_I2C_STM_OUT | 1); // zero data length + 1
        writeBuffer[i++] = getAddressByte(address, true);
        if (length > 0) {
            for (int j = 0; j < length - 1; j++) {
                writeBuffer[i++] = (byte) (CH341_CMD_I2C_STM_IN | 1);
            }
            writeBuffer[i++] = (byte) CH341_CMD_I2C_STM_IN;
        }
        writeBuffer[i++] = CH341_CMD_I2C_STM_STO; // STOP condition
        writeBuffer[i++] = CH341_CMD_I2C_STM_END; // end of transaction
        transferBulkData(writeBuffer, i, data, length);
    }

    private void checkDevicePresence(int address) throws IOException {
        int i = 0;
        writeBuffer[i++] = (byte) CH341_CMD_I2C_STREAM;
        writeBuffer[i++] = CH341_CMD_I2C_STM_STA; // START condition
        writeBuffer[i++] = (byte) CH341_CMD_I2C_STM_OUT;
        writeBuffer[i++] = getAddressByte(address, true);
        writeBuffer[i++] = CH341_CMD_I2C_STM_STO; // STOP condition
        writeBuffer[i++] = CH341_CMD_I2C_STM_END; // end of transaction
        int res = transferBulkData(writeBuffer, i, writeBuffer, 1);
        if (res <= 0 || (writeBuffer[0] & 0x80) != 0) {
            throw new IOException(String.format("No device present at address 0x%02x", address));
        }
    }

    /**
     * Write request and read response, if needed.
     *
     * @param writeData
     *            data buffer to write data
     * @param writeLength
     *            data length to write
     * @param readData
     *            data buffer to read data
     * @param readLength
     *            data length to read (can be zero)
     * @return actual length of read data
     * @throws IOException
     *             in case of data read/write error or timeout
     */
    private int transferBulkData(byte[] writeData, int writeLength, byte[] readData, int readLength)
        throws IOException {
        writeBulkData(writeData, writeLength);
        if (readLength > 0) {
            readLength = readBulkData(readBuffer, MAX_TRANSFER_SIZE);
            System.arraycopy(readBuffer, 0, readData, 0, readLength);
        }
        return readLength;
    }

    /**
     * Read bulk data from USB device to data buffer.
     *
     * @param data
     *            data buffer to read data
     * @param length
     *            data length to read
     * @return actual length of read data
     * @throws IOException
     *             in case of data read error or timeout
     */
    private int readBulkData(byte[] data, int length) throws IOException {
        return bulkRead(data, 0, length, USB_TIMEOUT_MILLIS);
    }

    /**
     * Write bulk data from data buffer to USB device.
     *
     * @param data
     *            data buffer to write data
     * @param length
     *            data length to write
     * @throws IOException
     *             in case of data write error
     */
    private void writeBulkData(byte[] data, int length) throws IOException {
        bulkWrite(data, length, USB_TIMEOUT_MILLIS);
    }

    public static UsbDeviceIdentifier[] getSupportedUsbDeviceIdentifiers() {
        return new UsbDeviceIdentifier[] { new UsbDeviceIdentifier(0x1a86, 0x5512) };
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy