【记录】给usb-serial-for-android中的Silicon Labs的CP2102中添加RTS和DTR的支持

【背景】

新买的USB的HART猫:

【记录】为USB接口的HART猫ExSaf ESH232U安装对应的USB转RS232驱动

【记录】使用USB口的HART猫ExSaf ESH232U去检测某HART设备

其内部是Silicon Labs的CP2102的USB转串口的芯片。

现在是,希望将其用到之前:

已经支持了FTDI的FT232R的usb-serial-for-android

中的CP2102,也去添加对应的RTS和DTR,使得也可以用此HART猫去和HART设备通讯。

 

【折腾过程】

1.之前整理的一些可参考的资料:

https://android.googlesource.com/kernel/samsung/+/387df9fcfed566401f2953887ccd3ce9314f4eb9/drivers/usb/serial/cp210x.c

2.

http://124.16.139.131:24080/lxr/source/kernel/omap/drivers/usb/serial/cp210x.c?v=android-4.0.4

中有:

#define REQTYPE_HOST_TO_DEVICE  0x41

cp210x_dtr_rts

3.

http://www.silabs.com/products/mcu/Pages/USBtoUARTBridgeVCPDrivers.aspx

中的源码:

Download VCP (10.2 KB)

4.

http://code.google.com/p/usb-serial-for-android/issues/detail?id=17

或许也有参考价值。

然后发现了,对于下载到的:

http://www.firmsys.com/down/UARTtoUSB.zip

其中包含了之前就知道的:

CP2102的

Linux_3.x.x_VCP_Driver_Source.zip

其中的驱动,就是我后来看到的:

http://lxr.free-electrons.com/source/drivers/usb/serial/cp210x.c#L286

5.参考源码后,就去折腾:

D:\DevRoot\android\android_root\UsbSerialLibrary\src\com\hoho\android\usbserial\driver\Cp2102SerialDriver.java

中的内容,尤其是:

    @Override
    public void setRTS(boolean value) throws IOException {
    }

    @Override
    public void setDTR(boolean value) throws IOException {
    }

6.开始折腾了,去:

http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx

下载:

Download VCP (10.2 KB)

供参考。

7.开始是参考:

D:\DevTool\USB-To-RS232\Linux_2.6.x_VCP_Driver_Source\Linux_2.6.x_VCP_Driver_Source\cp210x.c

去写代码的。

8.但是期间发现,很多细节的定义,还是需要找对应的datasheet看,才更清楚。

所以去搜CP2102的datasheet,但是搜到:

https://www.sparkfun.com/datasheets/IC/cp2102.pdf

http://www.sinforcon.com/down/html/50.html

https://www.sparkfun.com/datasheets/PCB/CP2102%20Breakout-v01.pdf

却没有找到想要包含RTS DTR的位含义的定义的寄存器。。

9.另外,关于:

TIOCM_DTR

TIOCM_RTS

代码中找不到,也没有对应的.h头文件。

10.后来是在:

http://lxr.free-electrons.com/source/drivers/usb/serial/cp210x.c#L286

->

http://lxr.free-electrons.com/ident?i=TIOCM_DTR

->

http://lxr.free-electrons.com/source/include/uapi/asm-generic/termios.h#L33

找到的:

/* modem lines */
#define TIOCM_DTR       0x002
#define TIOCM_RTS       0x004
#define TIOCM_CTS       0x020
#define TIOCM_DSR       0x100

11.但是折腾代码期间,发现其原先有个问题:

setParameters

中,先去对于configDataBits,configParityBits,configStopBits

竟然是分别三次都单独设置一次,去调用setConfigSingle

而对应的

D:\DevTool\USB-To-RS232\Linux_2.6.x_VCP_Driver_Source\Linux_2.6.x_VCP_Driver_Source\cp210x.c

是:

分别计算对应的值,得到合并后的配置的值,然后最后只去调用一次:

cp210x_set_config

不过,后来又在:

D:\DevTool\USB-To-RS232\Silicon Labs\CP210x\UARTtoUSB\Ver.3.0\Driver\Linux_3.x.x_VCP_Driver_Source\Linux_3.x.x_VCP_Driver_Source\cp210x.c

中看到:

也是分别对于三种配置,分别去配置的。

但是很明显:

其三次调用

cp210x_set_config

去配置对应的值bits,都是分别获得最新的bits值,然后加上当前的配置,才去写入的。

而上面的代码中:

configDataBits,configParityBits,configStopBits

三个值,都只是自己独立的值:

搞得配置后面一个配置,就把之前的配置冲掉了。。。

所以,去改对应的代码为:

    @Override
    public void setParameters(int baudRate, int dataBits, int stopBits, int parity)
            throws IOException {
        setBaudRate(baudRate);

        int configBits = 0;
        
        //int configDataBits = 0;
        switch (dataBits) {
            case DATABITS_5:
                configBits |= 0x0500;
                break;
            case DATABITS_6:
                configBits |= 0x0600;
                break;
            case DATABITS_7:
                configBits |= 0x0700;
                break;
            case DATABITS_8:
                configBits |= 0x0800;
                break;
            default:
                configBits |= 0x0800;
                break;
        }
        //setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configDataBits);

        //int configParityBits = 0; // PARITY_NONE
        switch (parity) {
            case PARITY_ODD:
                configBits |= 0x0010;
                break;
            case PARITY_EVEN:
                configBits |= 0x0020;
                break;
        }
        //setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configParityBits);
        
        //int configStopBits = 0;
        switch (stopBits) {
            case STOPBITS_1:
                configBits |= 0;
                break;
            case STOPBITS_1_5:
                configBits |= 1;
                break;
            case STOPBITS_2:
                configBits |= 2;
                break;
        }
        //setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configStopBits);
        
        setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configBits);
    }

12.后面继续去调试。

然后发现:

http://code.google.com/p/usb-serial-for-android/issues/detail?id=17

中的patch:

http://usb-serial-for-android.googlecode.com/issues/attachment?aid=170001000&name=usb-serial-for-android-cp210x.patch&token=rDHcVn1fdmJbTZA-UyNWrfZBo7Y%3A1385518266246

才是,根据cp210x.c修正后的,更加完整的驱动。

然后想办法去打上此patch:

还是参考之前的:

【diff和patch的使用】+【软件开发常用方法】

结果是:

CLi@PC-CLI-1 /cygdrive/d/DevTool/Android/libs/usb serial/usb-serial-for-android
$ ls -lha
total 61K
drwx------+ 1 Administrators Domänen-Benutzer    0 Nov 27 12:48 ./
drwx------+ 1 Administrators Domänen-Benutzer    0 Nov 27 12:48 ../
-rwx------+ 1 Administrators Domänen-Benutzer  218 Nov  7 10:34 .gitignore*
drwx------+ 1 Administrators Domänen-Benutzer    0 Nov 27 12:46 arduino/
-rwx------+ 1 Administrators Domänen-Benutzer  673 Nov  7 10:34 CHANGELOG.txt*
-rwx------+ 1 Administrators Domänen-Benutzer 7.5K Nov  7 10:34 LICENSE.txt*
-rwx------+ 1 Administrators Domänen-Benutzer 4.0K Nov  7 10:34 README.md*
drwx------+ 1 Administrators Domänen-Benutzer    0 Nov 27 12:46 UsbSerialExamples/
-rwx------+ 1 Administrators Domänen-Benutzer  27K Nov 27 12:45 usb-serial-for-android-cp210x.patch*
drwx------+ 1 Administrators Domänen-Benutzer    0 Nov 27 12:46 UsbSerialLibrary/

CLi@PC-CLI-1 /cygdrive/d/DevTool/Android/libs/usb serial/usb-serial-for-android
$ patch -p1 < usb-serial-for-android-cp210x.patch
patching file UsbSerialExamples/project.properties
patching file UsbSerialExamples/res/xml/device_filter.xml
Hunk #1 succeeded at 11 with fuzz 2 (offset 3 lines).
patching file UsbSerialLibrary/project.properties
patching file UsbSerialLibrary/src/com/hoho/android/usbserial/driver/CP210xSerialDriver.java
patching file UsbSerialLibrary/src/com/hoho/android/usbserial/driver/UsbId.java
Hunk #1 FAILED at 51.
1 out of 1 hunk FAILED -- saving rejects to file UsbSerialLibrary/src/com/hoho/android/usbserial/driver/UsbId.java.rej
patching file UsbSerialLibrary/src/com/hoho/android/usbserial/driver/UsbSerialDriver.java
Hunk #1 succeeded at 79 with fuzz 2 (offset 9 lines).
patching file UsbSerialLibrary/src/com/hoho/android/usbserial/driver/UsbSerialProber.java
Hunk #1 succeeded at 112 with fuzz 2 (offset 45 lines).
patching file UsbSerialLibrary/src/com/hoho/android/usbserial/util/SerialInputOutputManager.java

很明显,部分内容没有完全打上。

估计是版本不匹配?

另外看到:

https://github.com/mik3y/usb-serial-for-android/issues/17

中说已经在2b3528b 合并了此补丁了。

所以,那就去:

https://github.com/mik3y/usb-serial-for-android

下载最新的代码,看看是否的确已经合并。

结果貌似最新的代码,和我之前就已经下载的,是一样的。

即:

最新的代码中,虽然像:

mik3y

说的:

“A similar patch was merged in 2b3528b

但是实际上,从master下载到最新的:

usb-serial-for-android-master.zip

中,对应的

CP210xSerialDriver.java

中的代码,却不是上面patch中的。

因为该patch中是:

CP210xSerialDriver.java

且包含对应的定义,都是和之前的Linux中的cp210x.c中都是一致的;

而新下载到的usb-serial-for-android-master.zip,中是:

Cp2102SerialDriver.java

且其中很多代码,就是我前面提到的,写法都是不正确的。

算了,单独从patch中,拷贝出对应的:

CP210xSerialDriver.java

然后保存后,和当前已有的

Cp2102SerialDriver.java

去比较看看。

算了,最后决定,还是手动去合并这个patch,使得不仅仅支持CP2102,而是支持CP210x。

13.后来写了代码:

/* Copyright 2011 Google 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.
 *
 * Project home page: http://code.google.com/p/usb-serial-for-android/
 */

package com.hoho.android.usbserial.driver;

import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbRequest;
import android.util.Log;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * A {@link UsbSerialDriver} implementation for a variety of CP210x devices
 * <p>
 * This driver is based on the source codes from linux kernel 3.7.
 *
 * </p>
 * <p>
 * Not all devices are tested.
 * <ul>
 * <li>Read and write of serial data (see {@link #read(byte[], int)} and
 * {@link #write(byte[], int)}.
 * <li>Setting baud rate (see {@link #setBaudRate(int)}).
 * </ul>
 * </p>
 *  *
 * @author Jongmoo Sohn (jmthegrey@gmail.com)
 * @see <a href="http://code.google.com/p/usb-serial-for-android/">USB Serial
 * for Android project page</a>
 */
public class Cp210xSerialDriver extends CommonUsbSerialDriver {

    private final String TAG = Cp210xSerialDriver.class.getSimpleName();

    /* Config request types */
    private static final int REQTYPE_HOST_TO_INTERFACE  = 0x41;
    private static final int REQTYPE_INTERFACE_TO_HOST  = 0xc1;
    private static final int REQTYPE_HOST_TO_DEVICE     = 0x40;
    private static final int REQTYPE_DEVICE_TO_HOST     = 0xc0;

    /* Config request codes */
    private static final int CP210X_IFC_ENABLE      = 0x00;
    private static final int CP210X_SET_BAUDDIV     = 0x01;
    private static final int CP210X_GET_BAUDDIV     = 0x02;
    private static final int CP210X_SET_LINE_CTL    = 0x03;
    private static final int CP210X_GET_LINE_CTL    = 0x04;
    private static final int CP210X_SET_BREAK       = 0x05;
    private static final int CP210X_IMM_CHAR        = 0x06;
    private static final int CP210X_SET_MHS         = 0x07;
    private static final int CP210X_GET_MDMSTS      = 0x08;
    private static final int CP210X_SET_XON         = 0x09;
    private static final int CP210X_SET_XOFF        = 0x0A;
    private static final int CP210X_SET_EVENTMASK   = 0x0B;
    private static final int CP210X_GET_EVENTMASK   = 0x0C;
    private static final int CP210X_SET_CHAR        = 0x0D;
    private static final int CP210X_GET_CHARS       = 0x0E;
    private static final int CP210X_GET_PROPS       = 0x0F;
    private static final int CP210X_GET_COMM_STATUS = 0x10;
    private static final int CP210X_RESET           = 0x11;
    private static final int CP210X_PURGE           = 0x12;
    private static final int CP210X_SET_FLOW        = 0x13;
    private static final int CP210X_GET_FLOW        = 0x14;
    private static final int CP210X_EMBED_EVENTS    = 0x15;
    private static final int CP210X_GET_EVENTSTATE  = 0x16;
    private static final int CP210X_SET_CHARS       = 0x19;
    private static final int CP210X_GET_BAUDRATE    = 0x1D;
    private static final int CP210X_SET_BAUDRATE    = 0x1E;

    /* CP210X_IFC_ENABLE */
    private static final int UART_ENABLE            = 0x0001;
    private static final int UART_DISABLE           = 0x0000;

    /* CP210X_(SET|GET)_BAUDDIV */
    private static final int BAUD_RATE_GEN_FREQ     = 0x384000;

    /* CP210X_(SET|GET)_LINE_CTL */
    private static final int BITS_DATA_MASK     = 0X0f00;
    private static final int BITS_DATA_5        = 0X0500;
    private static final int BITS_DATA_6        = 0X0600;
    private static final int BITS_DATA_7        = 0X0700;
    private static final int BITS_DATA_8        = 0X0800;
    private static final int BITS_DATA_9        = 0X0900;

    private static final int BITS_PARITY_MASK   = 0x00f0;
    private static final int BITS_PARITY_NONE   = 0x0000;
    private static final int BITS_PARITY_ODD    = 0x0010;
    private static final int BITS_PARITY_EVEN   = 0x0020;
    private static final int BITS_PARITY_MARK   = 0x0030;
    private static final int BITS_PARITY_SPACE  = 0x0040;

    private static final int BITS_STOP_MASK     = 0x000f;
    private static final int BITS_STOP_1        = 0x0000;
    private static final int BITS_STOP_1_5      = 0x0001;
    private static final int BITS_STOP_2        = 0x0002;

    /* CP210X_SET_BREAK */
    private static final int BREAK_ON           = 0x0001;
    private static final int BREAK_OFF          = 0x0000;

    /* CP210X_(SET_MHS|GET_MDMSTS) */
    private static final int CONTROL_DTR        = 0x0001;
    private static final int CONTROL_RTS        = 0x0002;
    private static final int CONTROL_CTS        = 0x0010;
    private static final int CONTROL_DSR        = 0x0020;
    private static final int CONTROL_RING       = 0x0040;
    private static final int CONTROL_DCD        = 0x0080;
    private static final int CONTROL_WRITE_DTR  = 0x0100;
    private static final int CONTROL_WRITE_RTS  = 0x0200;

    
    private int mBulkInSize = 256;

    private int mBaudRate = 0;
    private int mDataBits = 0;
    private int mParity = 0;
    private int mStopBits = 0;
    
    private static final int DEFAULT_BAUD_RATE = 115200;
    private static final int DEFAULT_DATA_BITS = DATABITS_8;
    private static final int DEFAULT_PARITY = PARITY_NONE;
    private static final int DEFAULT_STOP_BITS = STOPBITS_1;

    /**
     * Due to http://b.android.com/28023 , we cannot use UsbRequest async reads
     * since it gives no indication of number of bytes read. Set this to
     * {@code true} on platforms where it is fixed.
     * Safe to assume anything less than API level 17 is broken.
     */
    private static final boolean ENABLE_ASYNC_READS = true;
    
    private UsbEndpoint mEndpointOut = null;
    private UsbEndpoint mEndpointIn = null;
    
    public static final int USB_CTRL_GET_TIMEOUT_MILLIS = 5000;
    public static final int USB_CTRL_SET_TIMEOUT_MILLIS = 5000;

    private boolean mRts = false;
    private boolean mDtr = false;

    /**
     * Constructor.
     *
     * @param usbDevice the {@link UsbDevice} to use
     * @param usbConnection the {@link UsbDeviceConnection} to use
     * @throws UsbSerialRuntimeException if the given device is incompatible
     *             with this driver
     */
    public Cp210xSerialDriver(UsbDevice usbDevice, UsbDeviceConnection usbConnection) {
        super(usbDevice, usbConnection);
        
        UsbInterface intf = findInterface(usbDevice);
        if (setInterface(usbDevice, intf)) {
            UsbEndpoint epOut = null;
            UsbEndpoint epIn = null;
            // Look for bulk endpoints
            for (int i = 0; i < intf.getEndpointCount(); i++) {
                UsbEndpoint ep = intf.getEndpoint(i);
                if (UsbConstants.USB_ENDPOINT_XFER_BULK == ep.getType()) {
                    if (UsbConstants.USB_DIR_OUT == ep.getDirection()) {
                        epOut = ep;
                        Log.d(TAG, "endpointOut: " + i);
                    } else {
                        epIn = ep;
                        Log.d(TAG, "endpointIn: " + i);
                    }
                }
            }
            if (epOut == null || epIn == null) {
                throw new IllegalArgumentException("Not all endpoints found");
            }
            mEndpointOut = epOut;
            mEndpointIn = epIn;
        }
    }
    
    private UsbInterface findInterface(UsbDevice device) {
        Log.d(TAG, "findInterface " + device);
        int count = device.getInterfaceCount();
        for (int i =0 ; i < count ; i++) {
            UsbInterface intf = device.getInterface(i);
            Log.d(TAG, "Interface: " + count + " Class: " + intf.getInterfaceClass() +
                    " Subclass: " + intf.getInterfaceSubclass() +
                    " Protocol: " + intf.getInterfaceProtocol());
            if (intf.getInterfaceClass() == 255
                    && intf.getInterfaceSubclass() == 00
                    && intf.getInterfaceProtocol() == 0) {
                return intf;
            }
        }
        return null;
    }
    
    private boolean setInterface(UsbDevice device, UsbInterface intf) {
        if (mConnection != null) {
            Log.d(TAG, "Open succeeded.");
            if (mConnection.claimInterface(intf, false)) {
                Log.d(TAG, "Claim interface succeeded.");
                return true;
            } else {
                Log.d(TAG, "Claim interface failed.");
            }
        } else {
            Log.d(TAG, "No connection to set interface.");
        }
        
        return false;
    }
    
    private int cp210x_set_config(int request, int value, byte[] buffer, int length) throws IOException {
        int result = mConnection.controlTransfer(REQTYPE_HOST_TO_INTERFACE, request,
                    value, 0x0 /* index */, buffer, length, USB_CTRL_SET_TIMEOUT_MILLIS);

        if (result != length) {
            throw new IOException("cp210x_set_config failed: result=" + result);
        }
        
        return result;
    }
    
    private int cp210x_set_config_single(int request, int value) throws IOException {
        return cp210x_set_config(request, value, null, 0x0);
    }
    
    // From Sniffed Data
    private static final byte[] CP210X_SET_CHARS_DATA = {
        (byte)0x04, (byte)0x00, (byte)0x00, (byte)0x0A, (byte)0x00, (byte)0x00 };
    
    // From Sniffed Data
    private static final byte[] CP210X_SET_FLOW_DATA = {
        (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 };

    @Override
    public void open() throws IOException {
        boolean opened = false;
        try {
            for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
                if (mConnection.claimInterface(mDevice.getInterface(i), true)) {
                    Log.d(TAG, "claimInterface " + i + " SUCCESS");
                } else {
                    Log.d(TAG, "claimInterface " + i + " FAIL");
                }
            }
            // Configure Single Port
//            cp210x_set_config_single(CP210X_IFC_ENABLE, UART_ENABLE);
            
            // From Sniffed Data
            cp210x_set_config_single(CP210X_IFC_ENABLE, 0xFFFF);    // UART_ENABLE = 0x0001? 0xFFFF?
            cp210x_set_config(CP210X_SET_CHARS, 0x0000, CP210X_SET_CHARS_DATA, CP210X_SET_CHARS_DATA.length);
            cp210x_set_config(CP210X_SET_FLOW, 0x0000, CP210X_SET_FLOW_DATA, CP210X_SET_FLOW_DATA.length);
            
            // Configure the termios structure
            //  cp210x_get_termios() NOT IMPLEMENTED !
            //  Instead, following default values are set.
            if (0 == mBaudRate) mBaudRate = DEFAULT_BAUD_RATE;
            if (0 == mDataBits) mDataBits = DEFAULT_DATA_BITS;
            if (0 == mParity) mParity = DEFAULT_PARITY;
            if (0 == mStopBits) mStopBits = DEFAULT_STOP_BITS;
            
            // The baud rate must be initialised on cp2104
//            cp210x_change_speed(mBaudRate);
            setParameters(mBaudRate, mDataBits, mStopBits, mParity);
            
            // From Sniffed Data
            cp210x_set_config_single(CP210X_SET_MHS, CONTROL_WRITE_RTS);    // 0x0200
            cp210x_set_config_single(CP210X_SET_MHS, CONTROL_WRITE_DTR + CONTROL_DTR);  // 0x0101
            cp210x_set_config_single(CP210X_SET_BREAK, BREAK_OFF);
            
            opened = true;
        } finally {
            if (!opened) {
                close();
            }
        }
    }

    @Override
    public void close() {
        try {
            // From Sniffed Data
            cp210x_set_config_single(CP210X_SET_BREAK, BREAK_OFF);
            cp210x_set_config_single(CP210X_IFC_ENABLE, UART_DISABLE);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        mConnection.close();
    }

    @Override
    public int read(byte[] dest, int timeoutMillis) throws IOException {

        if (ENABLE_ASYNC_READS) {
            final int readAmt;
            synchronized (mReadBufferLock) {
                // mReadBuffer is only used for maximum read size.
                readAmt = Math.min(dest.length, mReadBuffer.length);
            }

            final UsbRequest request = new UsbRequest();
            request.initialize(mConnection, mEndpointIn);

            final ByteBuffer buf = ByteBuffer.wrap(dest);
            if (!request.queue(buf, readAmt)) {
                throw new IOException("Error queueing request.");
            }

            final UsbRequest response = mConnection.requestWait();
            if (response == null) {
                throw new IOException("Null response");
            }

            final int BytesRead = buf.position();
            if (BytesRead > 0) {
//                Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length)));
                return BytesRead;
            } else {
                return 0;
            }
        } else {
            final int totalBytesRead;

            synchronized (mReadBufferLock) {
                final int readAmt = Math.min(dest.length, mReadBuffer.length);
                totalBytesRead = mConnection.bulkTransfer(mEndpointIn, mReadBuffer,
                        readAmt, timeoutMillis);
            }

            if (totalBytesRead > 0) {
                System.arraycopy(mReadBuffer, 0, dest, 0, totalBytesRead);
            }
            return totalBytesRead;
        }
    }

    @Override
    public int write(byte[] src, int timeoutMillis) throws IOException {
        int offset = 0;

        while (offset < src.length) {
            final int writeLength;
            final int amtWritten;

            synchronized (mWriteBufferLock) {
                final byte[] writeBuffer;

                writeLength = Math.min(src.length - offset, mWriteBuffer.length);
                if (offset == 0) {
                    writeBuffer = src;
                } else {
                    // bulkTransfer does not support offsets, make a copy.
                    System.arraycopy(src, offset, mWriteBuffer, 0, writeLength);
                    writeBuffer = mWriteBuffer;
                }

                amtWritten = mConnection.bulkTransfer(mEndpointOut, writeBuffer, writeLength,
                        timeoutMillis);
            }

            if (amtWritten <= 0) {
                throw new IOException("Error writing " + writeLength
                        + " bytes at offset " + offset + " length=" + src.length);
            }

            Log.d(TAG, "Wrote amtWritten=" + amtWritten + " attempted=" + writeLength);
            offset += amtWritten;
        }
        return offset;
    }
    
    @Override
    @Deprecated
    public int setBaudRate(int baudRate) throws IOException {
        mBaudRate = cp210x_quantise_baudrate(baudRate);
        cp210x_change_speed(mBaudRate);
        return mBaudRate;
    }

    @Override
    public void setParameters(int baudRate, int dataBits, int stopBits, int parity)
            throws IOException {
        
        mBaudRate = cp210x_quantise_baudrate(baudRate);
        cp210x_change_speed(mBaudRate);

        int config = 0x0000;
        switch (dataBits) {
            case DATABITS_5:
                config |= BITS_DATA_5;
                break;
            case DATABITS_6:
                config |= BITS_DATA_6;
                break;
            case DATABITS_7:
                config |= BITS_DATA_7;
                break;
            case DATABITS_8:
                config |= BITS_DATA_8;
                break;
            default:
                throw new IllegalArgumentException("Unknown dataBits value: " + dataBits);
        }

        switch (parity) {
            case PARITY_NONE:
                config |= BITS_PARITY_NONE;
                break;
            case PARITY_ODD:
                config |= BITS_PARITY_ODD;
                break;
            case PARITY_EVEN:
                config |= BITS_PARITY_EVEN;
                break;
            case PARITY_MARK:
                config |= BITS_PARITY_MARK;
                break;
            case PARITY_SPACE:
                config |= BITS_PARITY_SPACE;
                break;
            default:
                throw new IllegalArgumentException("Unknown parity value: " + parity);
        }

        switch (stopBits) {
            case STOPBITS_1:
                config |= BITS_STOP_1;
                break;
            case STOPBITS_1_5:
                config |= BITS_STOP_1_5;
                break;
            case STOPBITS_2:
                config |= BITS_STOP_2;
                break;
            default:
                throw new IllegalArgumentException("Unknown stopBits value: " + stopBits);
        }

        int result = mConnection.controlTransfer(REQTYPE_HOST_TO_INTERFACE,
                CP210X_SET_LINE_CTL, config, 0 /* index */,
                null, 0, USB_CTRL_SET_TIMEOUT_MILLIS);
        if (result != 0) {
            throw new IOException("Setting parameters failed: result=" + result);
        }

        mParity = parity;
        mStopBits = stopBits;
        mDataBits = dataBits;
        
        verifyConfig(baudRate, config);
    }


    //convert byte array to integer type value
    //possible returned value: byte/short/int/long
    //Note: max support long -> input byte array length should not exceed 8 
    public static long byteArrToInteger(byte[] byteArr){
        long convertedInterger = 0;
        
        //follow works:
        //int readbackBaudrate= (baudrateByteArr[3]<<24)&0xff000000|(baudrateByteArr[2]<<16)&0xff0000|(baudrateByteArr[1]<<8)&0xff00|(baudrateByteArr[0]<<0)&0xff;
        
        //115200 == [0, -62, 1, 0]
        //1200==[-80, 4, 0, 0]
        for(int i = 0; i < byteArr.length; i++){
            //long curValue = byteArr[i];
            //int curValue = byteArr[i];
            byte curValue = byteArr[i];
            long shiftedValue = curValue << (i * 8);
            long mask = 0xFF << (i * 8);
            long maskedShiftedValue = shiftedValue & mask;
            //0x0, 0xC200, 0x10000, 0x0 -> 115200==0x1C200
            //0xB0, 0x400, 0x0, 0x0-> 1200==0x4B0
            convertedInterger |= maskedShiftedValue;
        }
        
       return convertedInterger;
    }
    
    //convert interger type value to byte array
    //possible input value: byte/short/int/long
    //Note: max return byte array is 8 -> max support long 
    public static byte[] integerToByteArr(long inputIntergerValue, int byteLen){
        byte[] convertedByteArr = new byte[byteLen];
        
        for (int i = 0; i < convertedByteArr.length; i++) {
            convertedByteArr[i] = (byte) ((inputIntergerValue >> (8 * i)) & 0xFF);
            //115200 == [0, -62, 1, 0, 0, 0, 0, 0]
            //1200==[-80, 4, 0, 0]
            //600==[88, 2]
            //32==[32]
        }

       return convertedByteArr;
    }
    
    private void verifyConfig(int writtenBaudrate, int writtenLineControl){
        //1. verify baudrate
        byte[] baudrateByteArr = new byte[]{0,0,0,0};
        mConnection.controlTransfer(REQTYPE_INTERFACE_TO_HOST, CP210X_GET_BAUDRATE,
                0, 0/* index */, baudrateByteArr, 4, USB_CTRL_GET_TIMEOUT_MILLIS);
        //int readbackBaudrate = (baudrateByteArr[0]) | (baudrateByteArr[1] << 8) | (baudrateByteArr[2] << 16) | (baudrateByteArr[3] << 24);
        //int readbackBaudrate= (baudrateByteArr[3]<<24)&0xff000000|(baudrateByteArr[2]<<16)&0xff0000|(baudrateByteArr[1]<<8)&0xff00|(baudrateByteArr[0]<<0)&0xff;
        int readbackBaudrate = (int)byteArrToInteger(baudrateByteArr);
        if(writtenBaudrate == readbackBaudrate){
            //ok
        }
        else{
            //false
        }
        
        //2.verify other config
        byte[] lineControlByteArr = new byte[]{0,0};
        mConnection.controlTransfer(REQTYPE_INTERFACE_TO_HOST, CP210X_GET_LINE_CTL,
                0, 0/* index */, lineControlByteArr, 2, USB_CTRL_GET_TIMEOUT_MILLIS);
        //int readbackLineControl = (lineControlByteArr[0]) | (lineControlByteArr[1] << 8);
        int readbackLineControl = (int)byteArrToInteger(lineControlByteArr);
        if(writtenLineControl == readbackLineControl){
            //ok
        }
        else{
            //false
        }
    }
    
    @Override
    public boolean getCD() throws IOException {
        return false;
    }

    @Override
    public boolean getCTS() throws IOException {
        return false;
    }

    @Override
    public boolean getDSR() throws IOException {
        return false;
    }

    @Override
    public boolean getDTR() throws IOException {
        return mDtr;
        //return true;
    }

    @Override
    public void setDTR(boolean value) throws IOException {
        mDtr = value;
        
        short control = 0;
        
        if (mDtr) {
            control |= CONTROL_DTR;
            control |= CONTROL_WRITE_DTR;
        }
        else{
            control &= ~CONTROL_DTR;
            control |= CONTROL_WRITE_DTR;
        }

        try {
            cp210x_set_config_single(CP210X_SET_MHS, control);
            
            verifyRtsDtr(control);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public boolean getRI() throws IOException {
        return false;
    }


    @Override
    public boolean getRTS() throws IOException {
        return mRts;
        //return true;
    }

    @Override
    public void setRTS(boolean value) throws IOException {
        mRts = value;
        
        short control = 0;

        if (mRts) {
            control |= CONTROL_RTS;
            control |= CONTROL_WRITE_RTS;
        }
        else {
            control &= ~CONTROL_RTS;
            control |= CONTROL_WRITE_RTS;
        }
        
        try {
            cp210x_set_config_single(CP210X_SET_MHS, control);
            
            verifyRtsDtr(control);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /*
    // set/clear RTS/CTS and DTR/DSR
    private void setDtrRts() {
        short control = 0;

        if (mRts) {
            control |= CONTROL_RTS;
            control |= CONTROL_WRITE_RTS;
        }
        else {
            control &= ~CONTROL_RTS;
            control |= CONTROL_WRITE_RTS;
        }
        
        if (mDtr) {
            control |= CONTROL_DTR;
            control |= CONTROL_WRITE_DTR;
        }
        else{
            control &= ~CONTROL_DTR;
            control |= CONTROL_WRITE_DTR;
        }

        try {
            cp210x_set_config_single(CP210X_SET_MHS, control);
            
            verifyRtsDtr(control);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    */

    private void verifyRtsDtr(int writtenMhsControl){
        //1. verify RTS DTR
        int writtenControlValue = writtenMhsControl & 0xFF; // only lowest 8 bit used for indicate RTS, DTR, ...
//        byte[] mhsControlByteArr = new byte[]{0};
        byte[] mhsControlByteArr = new byte[]{0}; // so here only need to read 1 byte
        mConnection.controlTransfer(REQTYPE_INTERFACE_TO_HOST, CP210X_GET_MDMSTS,
                0, 0/* index */, mhsControlByteArr, mhsControlByteArr.length, USB_CTRL_GET_TIMEOUT_MILLIS);
        int readbackMhsControl = (int)byteArrToInteger(mhsControlByteArr);
        //seem here can not verify written ok or not
        //for those bit not reflect whether happen RTS or DTR
        if(writtenControlValue == readbackMhsControl){
            //ok
            //written 769==0x301 -> read back: 0x01  => OK
            //higher bits auto become 0 after written
        }
        else{
            //false
        }
    }
    

    public static Map<Integer, int[]> getSupportedDevices() {
        final Map<Integer, int[]> supportedDevices = new LinkedHashMap<Integer, int[]>();
        supportedDevices.put(Integer.valueOf(UsbId.VENDOR_SILAB),
                new int[] {
                    UsbId.SILAB_CP210X_DEFAULT_0,
                    UsbId.SILAB_CP210X_DEFAULT_1,
                    UsbId.SILAB_CP210X_DEFAULT_2,
                    UsbId.SILAB_CP210X_DEFAULT_3
                });
        return supportedDevices;
    }

    /*
     * cp210x_quantise_baudrate
     * Quantises the baud rate as per AN205 Table 1
     */
    private int cp210x_quantise_baudrate(int baud) {
        if (baud <= 300)
            baud = 300;
        else if (baud <= 600)      baud = 600;
        else if (baud <= 1200)     baud = 1200;
        else if (baud <= 1800)     baud = 1800;
        else if (baud <= 2400)     baud = 2400;
        else if (baud <= 4000)     baud = 4000;
        else if (baud <= 4803)     baud = 4800;
        else if (baud <= 7207)     baud = 7200;
        else if (baud <= 9612)     baud = 9600;
        else if (baud <= 14428)    baud = 14400;
        else if (baud <= 16062)    baud = 16000;
        else if (baud <= 19250)    baud = 19200;
        else if (baud <= 28912)    baud = 28800;
        else if (baud <= 38601)    baud = 38400;
        else if (baud <= 51558)    baud = 51200;
        else if (baud <= 56280)    baud = 56000;
        else if (baud <= 58053)    baud = 57600;
        else if (baud <= 64111)    baud = 64000;
        else if (baud <= 77608)    baud = 76800;
        else if (baud <= 117028)   baud = 115200;
        else if (baud <= 129347)   baud = 128000;
        else if (baud <= 156868)   baud = 153600;
        else if (baud <= 237832)   baud = 230400;
        else if (baud <= 254234)   baud = 250000;
        else if (baud <= 273066)   baud = 256000;
        else if (baud <= 491520)   baud = 460800;
        else if (baud <= 567138)   baud = 500000;
        else if (baud <= 670254)   baud = 576000;
        else if (baud < 1000000)
            baud = 921600;
        else if (baud > 2000000)
            baud = 2000000;
        return baud;
    }
    
    /*
     * CP2101 supports the following baud rates:
     *
     *  300, 600, 1200, 1800, 2400, 4800, 7200, 9600, 14400, 19200, 28800,
     *  38400, 56000, 57600, 115200, 128000, 230400, 460800, 921600
     *
     * CP2102 and CP2103 support the following additional rates:
     *
     *  4000, 16000, 51200, 64000, 76800, 153600, 250000, 256000, 500000,
     *  576000
     *
     * The device will map a requested rate to a supported one, but the result
     * of requests for rates greater than 1053257 is undefined (see AN205).
     *
     * CP2104, CP2105 and CP2110 support most rates up to 2M, 921k and 1M baud,
     * respectively, with an error less than 1%. The actual rates are determined
     * by
     *
     *  div = round(freq / (2 x prescale x request))
     *  actual = freq / (2 x prescale x div)
     *
     * For CP2104 and CP2105 freq is 48Mhz and prescale is 4 for request <= 365bps
     * or 1 otherwise.
     * For CP2110 freq is 24Mhz and prescale is 4 for request <= 300bps or 1
     * otherwise.
     */
    private void cp210x_change_speed(int baudrate) throws IOException {
//        byte[] rawData = new byte[4];
//        
//        for (int i = 0; i < rawData.length; i++) {
//            rawData[i] = (byte) ((baudrate >> (8 * i)) & 0xFF);
//        }
//     
//        cp210x_set_config(CP210X_SET_BAUDRATE, 0x0000, rawData, rawData.length);
        byte[] baudrateByteArr = integerToByteArr(baudrate, 4);
        cp210x_set_config(CP210X_SET_BAUDRATE, 0x0000, baudrateByteArr, baudrateByteArr.length);
    }
}

调试结果是:

verifyConfig和verifyRtsDtr

都验证通过了。

但是结果最后HART猫还是无法从HART设备上读取信息,

发送对应的command 0,还是没有对应的返回信息。

14.试试去改为:

    //private static final boolean ENABLE_ASYNC_READS = true;
    private static final boolean ENABLE_ASYNC_READS = false;

看看效果,结果还是不行。

暂时就这样了。

等以后再说吧。

 

【总结】

目前结果是:

根据已有资料,基本确定了:

配置baudrate和其他config,以及设置RTS,DTR等动作,都是成功配置的。

但是目前还是不知道:

为何USB HART猫,此处最终没有成功的和HART设备通信。

 

可能的原因:

1.android的app中的代码有问题。

2.本身此处的USB HART猫,就是不能很好的支持此处通过代码配置RTS,DTR的形式去和物理上的HART设备通信,而无法获得对应的command 0的返回值的。

3.其他未知原因。


【后记】

1.后来看到:

Writing with CP2102

提到了:

CP2102的write有问题。

所以,此处我这里HART不工作,估计就是和此write不工作有关,导致我的command 0的数据,实际没有真正写下去,没写到HART设备中。

注:

不过,对于ExSaf的ESH232U的TXD的指示灯,当我发送write时,是正常可以闪的,说明是:

代码的write真正执行到了,且也通过HART猫去发送数据了,但是实际上由于上面说的write底层有问题,导致无法反正正确的数据。

另外:

另外一个,正常工作的HART猫,是可以的。

其物理连接是:USB转串口:FTDI的FT232R + RS232接口的HART猫:Exsaf ESH232R

2.所以,目前基本确定:

应该是由于

UsbSerialDriver.write() or SerialInputOutputManager.writeAsync()

不工作,导致此处HART猫不工作的。

所以:

此处,没有足够的设备和方法去调试,暂时无法通过个人解决此问题。

希望作者有空帮忙解决。


【后记 2013-11-28】

1.忘了记录于此了:

之前调试android的apk期间,记录的,该usb hart猫的vid和pid,记录与此:

UsbDevice[mName=/dev/bus/usb/001/018,mVendorId=4292,mProductId=60000,mClass=0,mSubclass=0,mProtocol=0,mInterfaces=[Landroid.os.Parcelable;@414efa70]

4292=0x10c4

60000=0xEA60

供以后需要时参考。



7 Thoughts on “【记录】给usb-serial-for-android中的Silicon Labs的CP2102中添加RTS和DTR的支持

  1. Pingback: Notepad++的正则表达式替换和替换 | UM智能科技

  2. 您好~最近也像作者一样按照这个驱动写了HART猫通讯程序,但是也遇到了跟作者一样的问题。command0已经发送出去,但是就是没有返回数据。用示波器观察HART频率,说明HART猫确实把数据发送了出去。不知道作者有没有后续的研究,望能交流~

  3. Pingback: 轻量级文本编辑器,记事本Notepad最佳替代品:Notepad++ | 快乐随风飘 · 刘旗个人博客_个人主页_独立博客网站

  4. 看得我头发晕。难道milky不会Merge?是否要去看原来的Fork? Silabs也是的,关键的EP资料不公开,找起来真费劲。

  5. 我有一块小开发板GAPmini/LPC812Mini,用的就是CP2102。正在找。多谢您的方法。我可以转交给客户了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

无觅相关文章插件,快速提升流量