实现安卓与USB_HID设备通过批量传输通信

2,193 阅读5分钟

前言:翻万卷书,行千里路,会百家才。


引言

  之前讲过安卓与USB_HID设备使用控制传输进行通信,并对控制传输方法中的各个参数做了详细的讲解,如果没有看过的话可以看这篇文章:安卓实现与USB_HID设备通过控制传输通信,本次主要是想将使用批量传输的方法和在使用过程中的注意点记录下来。
  上一篇使用控制传输进行通信的文章中已经说过如何获取USB设备、如何获取安卓与HID设备的通信权限、如何获取接口、如何声明接口、如何进行控制传输等。使用批量传输在之前看,初始化和控制传输的一样,只不过在进行传输之前批量传输较控制传输而言,需要找到设备的输入输出端点,这样才能进行控制传输。我们只要知道在使用批量传输之前,需要找到设备的输入输出端点,然后调用批量传输的方法就OK了。

目录

  一、获取端点
   1.1UsbConstants
   1.2获取端点数
   1.3判断端点类型
   1.4判断端点传输方向
   1.5得到输入输出端点
  二、传输数据
   2.1 APP到HID设备
   2.2 HID设备到APP
  三、源码
  四、总结

一、获取端点

  1.1UsbConstants
  在获取端点之前,先介绍一下有关usb协议的常量:
  ①USB_DIR_IN:用于表示 UsbEndpoint数据的方向是IN(设备到主机)
  ②USB_DIR_OUT:用于表示数据的方向 UsbEndpoint是OUT(主机到设备)
  以上两个常量都是int类型的。
  1.2获取端点数
  int endpointCount = mUsbInterface.getEndpointCount();
  1.3判断端点类型
  int usbEndpointType = usbEndpoint.getType();
  这个会回复一个整型常量,常见的有以下四种:
  ① 控制传输:USB_ENDPOINT_XFER_CONTROL = 0
  ② 实时传输:USB_ENDPOINT_XFER_ISOC = 1
  ③ 批量传输:USB_ENDPOINT_XFER_BULK = 2
  ④ 中断传输:USB_ENDPOINT_XFER_INT = 3
  1.4判断端点传输方向
  usbEndpoint.getDirection()
  getDirection()这个方法就是用来判断端点方向的,通过这个方法判断获得输入输出端点
  1.5得到输入输出端点

for (int i = 0; i < endpointCount; i++) {
    //遍历所有端点,找到输入端点与输出端点
    UsbEndpoint usbEndpoint = mUsbInterface.getEndpoint(i);
    if (usbEndpoint.getDirection() == UsbConstants.USB_DIR_OUT) {
        mUsbEndpointOut = usbEndpoint;//赋值输出端点
    }else if (usbEndpoint.getDirection() == UsbConstants.USB_DIR_IN) {
        mUsbEndpointIn = usbEndpoint;//赋值输入端点
    }
}

二、传输数据

  2.1 APP到HID设备

if (manager != null) {
  if (mUsbDevice != null) {
      if (manager.hasPermission(mUsbDevice)) {
          //获取当前usb设备的通讯连接
          mUsbDeviceConnection = manager.openDevice(mUsbDevice);
          if (mUsbDeviceConnection != null) {
              mUsbInterface = mUsbDevice.getInterface(0);
              mUsbDeviceConnection.claimInterface(mUsbInterface,true);
              final byte[] sendData = {0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
              new Thread(new Runnable() {
                  @Override
                  public void run() {
                      int j = mUsbDeviceConnection.bulkTransfer(mUsbEndpointOut,sendData,sendData.length,3000);//发送数据
                      if (j > 0) {
                          //接收成功
                          showResponse("ConnectToBootloader成功");

                      }else {
                          //接收失败
                          showResponse("ConnectToBootloader失败");
                      }
                  }
              }).start();
          }
      }
  }
}

  2.2 HID设备到APP

if (manager != null) {
    if (mUsbDevice != null) {
        if (manager.hasPermission(mUsbDevice)) {
            //获取当前usb设备的通讯连接
            mUsbDeviceConnection = manager.openDevice(mUsbDevice);
            if (mUsbDeviceConnection != null) {
                mUsbInterface = mUsbDevice.getInterface(0);
                mUsbDeviceConnection.claimInterface(mUsbInterface,true);
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        int j = mUsbDeviceConnection.bulkTransfer(mUsbEndpointOut,receiveData,receiveData.length,3000);//接收数据
                        if (j > 0) {
                            //接收成功
                            showResponse("SetPageSize32Words成功");
                        }else {
                            //接收失败
                            showResponse("SetPageSize32Words失败");
                        }
                    }
                }).start();
            }
        }
    }
}

  不管是发送数据还是接收数据,都是相对主机而言的,一定要注意这一点,还有一点要注意的是端点传输的数据长度,这个是由端点描述符中wMaxPcketSize决定的,如下图:

  所以我传输的数据是16byte的,接收也是接收16byte的数组,这个得根据硬件那边的端点描述符设置的。

源码

  MainActivity.java里面的代码是根据上一篇文章中的修改的,布局也是和上一篇文章中的布局一样。上一篇写的是控制传输,这篇修改成了批量传输。

package com.example.usbhidapp;

import androidx.appcompat.app.AppCompatActivity;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
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.UsbManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.example.usbhidapp.myInterface.CommunicationListener;

import java.util.HashMap;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String TAG = "UsbHid";

    private static final String USB_ACTION = "com.wiseasy.communication.usb.hostchart";

    private final byte[] receiveData = new byte[16];

    private UsbManager manager;//USB管理器
    private UsbDevice mUsbDevice;//找到的USB设备
    private UsbInterface mUsbInterface;
    private UsbDeviceConnection mUsbDeviceConnection;
    private UsbEndpoint mUsbEndpointIn;
    private UsbEndpoint mUsbEndpointOut;
    private UsbCommunication usbCommunication;

    private TextView mTvState;
    private TextView mTvDataReceive;
    private Button mButton;
    private Button mBtnReceive;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTvState = findViewById(R.id.tv_state);
        mTvDataReceive = findViewById(R.id.tv_data_receive);
        mButton = findViewById(R.id.btn_send);
        mButton.setOnClickListener(this);

        mBtnReceive = findViewById(R.id.btn_receive);
        mBtnReceive.setOnClickListener(this);

        manager = (UsbManager) getSystemService(Context.USB_SERVICE);
        if (manager == null) {
            Log.e(TAG,"不支持OTG");
            Toast.makeText(MainActivity.this,"不支持OTG",Toast.LENGTH_SHORT).show();
            return;
        }else {
            Log.e(TAG,"usb设备:" + manager.toString());
            Toast.makeText(MainActivity.this,manager.toString(),Toast.LENGTH_SHORT).show();
            mTvState.setText(manager.toString());
        }

        usbCommunication = UsbCommunication.getInstance(this);//初始化usb连接对象
        mTvState.setText("初始化usb连接对象");
        usbCommunication.openCommunication(new CommunicationListener() {
            @Override
            public void onSuccess(int code, String msg) {

            }

            @Override
            public void onFaild(String msg) {

            }
        },0x04d9,0xf541);

        HashMap<String,UsbDevice> deviceList = manager.getDeviceList();
        if (!deviceList.isEmpty()) {
            mTvState.setText("deviceList不为空");
            for (UsbDevice device : deviceList.values()) {
                if (device.getVendorId() == 0x04d9 && device.getProductId() == 0xf541) {
                    mUsbDevice = device;
                    mTvState.setText("找到设备" + mUsbDevice);
                    mTvState.setText("hasPermission的状态" + manager.hasPermission(mUsbDevice));
                    if (manager.hasPermission(mUsbDevice)) {
                        //获取当前usb设备的通讯连接
                        mUsbDeviceConnection = manager.openDevice(mUsbDevice);
                        mTvState.setText("获取当前usb设备的通讯连接");
                        if (mUsbDeviceConnection != null) {
                            mTvState.setText("当前usb设备的通讯连接不为空");
                            //获取通讯连接端点数量
                            int endpointCount = mUsbInterface.getEndpointCount();
                            mTvState.setText("获取通讯连接端点数量:" + endpointCount);

                            for (int i = 0; i < endpointCount; i++) {
                                //遍历所有端点,找到输入端点与输出端点
                                UsbEndpoint usbEndpoint = mUsbInterface.getEndpoint(i);
                                if (usbEndpoint.getDirection() == UsbConstants.USB_DIR_OUT) {
                                    mUsbEndpointOut = usbEndpoint;//赋值输出端点
                                    mTvState.setText("赋值输出端点:" + mUsbEndpointOut);
                                }else if (usbEndpoint.getDirection() == UsbConstants.USB_DIR_IN) {
                                    mUsbEndpointIn = usbEndpoint;//赋值输入端点
                                    mTvState.setText("赋值输入端点:" + mUsbEndpointIn);
                                }
                            }

                            //当输出端点和输入端点都不为空时,表示usb连接成功,初始化完成,可以进行数据收发
                            if (mUsbEndpointOut != null && mUsbEndpointIn != null) {
                                mTvState.setText("初始化完成");
                                Toast.makeText(MainActivity.this,"初始化完成",Toast.LENGTH_SHORT).show();
                            }
                        }
                    }

                }else {
                    Toast.makeText(MainActivity.this,"NotFind VID and PID",Toast.LENGTH_SHORT).show();
                }
            }
        }else {
            Toast.makeText(MainActivity.this,"请连接USB",Toast.LENGTH_SHORT).show();
        }
    }
    
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_send://发送
                if (manager != null) {
                    if (mUsbDevice != null) {
                        if (manager.hasPermission(mUsbDevice)) {
                            //获取当前usb设备的通讯连接
                            mUsbDeviceConnection = manager.openDevice(mUsbDevice);
                            if (mUsbDeviceConnection != null) {
                                mUsbInterface = mUsbDevice.getInterface(0);
                                mUsbDeviceConnection.claimInterface(mUsbInterface,true);
                                final byte[] sendData = {0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
                                new Thread(new Runnable() {
                                    @Override
                                    public void run() {
                                        int j = mUsbDeviceConnection.bulkTransfer(mUsbEndpointOut,sendData,sendData.length,3000);//发送数据
                                        if (j > 0) {
                                            //接收成功
                                            showResponse("发送成功");

                                        }else {
                                            //接收失败
                                            showResponse("发送失败");
                                        }
                                    }
                                }).start();
                            }
                        }
                    }
                }
                break;
            case R.id.btn_receive://接收
                if (manager != null) {
                    if (mUsbDevice != null) {
                        if (manager.hasPermission(mUsbDevice)) {
                            //获取当前usb设备的通讯连接
                            mUsbDeviceConnection = manager.openDevice(mUsbDevice);
                            if (mUsbDeviceConnection != null) {
                                mUsbInterface = mUsbDevice.getInterface(0);
                                mUsbDeviceConnection.claimInterface(mUsbInterface,true);
                                new Thread(new Runnable() {
                                    @Override
                                    public void run() {
                                        int j = mUsbDeviceConnection.bulkTransfer(mUsbEndpointOut,receiveData,receiveData.length,3000);//发送数据
                                        if (j > 0) {
                                            //接收成功
                                            showResponse("接收成功");
                                        }else {
                                            //接收失败
                                            showResponse("接收失败");
                                        }
                                    }
                                }).start();
                            }
                        }
                    }
                }
                break;
            default:
                break;
        }
    }

    private void showResponse(final String response) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                switch (response) {
                    case "发送成功":
                        mTvDataReceive.setText("发送成功");
                        break;
                    case "发送失败":
                        mTvDataReceive.setText("发送失败");
                        break;
                    case "接收成功":
                        mTvDataReceive.setText("接收成功");
                        break;
                    case "接收失败":
                        mTvDataReceive.setText("接收失败");
                        break;
                    default:
                        break;
                }
            }
        });
    }
}

总结

  这就是使用批量传输数据的通信,里面也有一些注意点,主要是在自己开发的项目中有用到批量传输将代码写进到IC中去,既然控制传输都写了,干脆连批量传输也写上去吧。看到这里,有朋友发现我有写错的地方,恳请指出,虚心修改,如果对朋友你有所帮助,给个赞呗~