资讯详情

Android USB 开发

安卓USB 整理相关内容

  • 基本概念和术语
  • 常用类
  • 两种开发模式
    • 配置AndroidManifest.xml文件
    • 配置 xml资源文件(用于过滤设备) :
  • 建立通信

基本概念和术语

USB是用于数据交换的总线(Bus),发起(initiate)数据交换的一方称为主机(host),另一方称为外设(peripheral),主机负责通过总线实现通信的供电描述符(descriptor)里.外设是一个单独的物理实体,但它可能有多个逻辑设备功能(device function),比如一个网络摄像头,除了有照相机,还可能有内置的麦克风,这种外接设备也被称为复合设备(composite device)

主机通过管道(pipe)连接到外设的端点(endpoint),端点是指逻辑实体功能的逻辑实体(logic entity),管道对应一个端点.一个外设最多可以有32个端点(16个进出),主机会初始化端点,并将相应的序号和功能分配给它,一旦分配,无论管道是否关闭,都不会改变.除设备配置0号端点外,其他端点分为不同的接口(interface)**,每个接口对应一个外设功能.

管道有两种类型:

消息(message):用于双向(bi-directional)控制与状态获取,对传输的数据有结构上的要求.

流(stream):用于单向(uni-directional)数据传输不会改变传输数据的结构.

因为通信是由主机启动的,所以input和output与主机相比,输入是将外设数据传输给主机,输出是将数据传输给外设的主机.

主机有一个关键任务叫做枚举(enumeration),所有外设的描述符通过枚举主机获得(descriptor),每个外设分配一个地址(address)**并完成其他配置,如上述端点的初始化.枚举后,可以使用外设。如果主机重启,将重置所有连接的外设并再次枚举.

USB主要有四种数据传输模式:

control:提供无损传输,用于传输配置/状态/命令等通信USB设备必须支持这种传输模式.

interrupt设备的快速反应主要用于键盘等输入设备.

bulk:用于大量数据的无损传输,但不保证带宽和延迟,如用U盘复制文件.

isochronous:确保传输速度尽可能快,但可能会有数据丢失,如实时音视频传输.

常用类

Android 3.1(API12级以上原生提供 USB 开发的 API,在android.hardware.usb包下提供开发相关类别。

UsbManager 获得 USB 与连接的管理器 USB 设备通信。

# 获取 UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); 
  • UsbDevice USB 抽象的设备,每一个UsbDevice 都代表一个 USB 设备。 这种方法主要有 getDeviceClass() 返回此USB设备的类别用整形来表示 getDeviceId() 返回唯一标记此设备的设备ID数字,也用整形来表示 getDeviceName() 返回设备名称,用字符串表示 getDeviceProtocol() 返回本设备的协议类别,用整形手术表示 getDeviceSubclass () 返回该设备的子类,用整形手术表示 getVendorId() 返回生产商ID getProductId() 返回产品ID getInterfaceCount() 返回设备接口的数量 getInterface(int index) 获得该设备的接口并返回一个接口
# 获取 USB 设备列表     public List<UsbDevice> getDeviceList() {         HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();         Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();         List<UsbDevice> usbDevices = new ArrayList<>();         while (deviceIterator.hasNext()) {             UsbDevice device = deviceIterator.next();             usbDevices.add(device);         }         return usbDevices;     } 
   #获取特定设备     /**      * mVendorId=1137,mProductId=85  佳博 3150T 标签打印机      *      * @param vendorId  厂商ID      * @param productId 产品ID      * @return  device      */     public UsbDevice getUsbDevice(int vendorId, int productId) {         HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();         Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();         while (deviceIterator.hasNext()) {             UsbDevice device = deviceIterator.next();             if (device.getVendorId() == vendorId && device.getProductId() == productId) {                 Log.e("USBUtil", "getDeviceList: "   device.getDeviceName());                 return device;             }         }         Toast.makeText(context, "没有相应的设备", Toast.LENGTH_SHORT).show();         return null;     } 

UsbInterface 表示设备定义了函数集USB设备接口。 一个 UsbDevice 可能包含一个或多个UsbInterface,每个 Interface 它们是独立的,一个接口,两个端点,分别接受和发送数据 它本身就是一个类,而不是一个接口。主要方法有: getId() 得到给接口的ID 号 getInterfaceClass() 得到该接口的类别 getInterfaceSubclass () 获取接口的子类 getInterfaceProtocol() 接口的协议类别 getEndpointCount() 该接口的节点数量 getEndpoint(int index) 对于指定的index获得此接口的节点,返回一个节点

#一个usbDevice有多个UsbInterface,我们通常需要的是第一个 usbInterface=usbDevice.getInterface(0); 

UsbEndpoint UsbEndpoint 是 interface 通信通道。 它主要提供以下方法: getAddress() 获取该节点的地址 getAttributes ( ) 获取此节点的属性 getDirection() 获取该节点的数据传输方向

UsbDeviceConnection 代表USB 一个连接类。可以用这个连接USB 通过调用该方法,设备可以发送和接收数据openDevice(UsbDevice usbDevice)得到这一类的一个例子。该类提供了以下方法:

    bulkTransfer(UsbEndpoint endpoint , byte[] buffer , int length , int timeout) 

通过给定的endpoint 大量的数据传输取决于节点的方向,buffer字节数组要发送或接收,length是字节数组的长度。失败将返回负数

  controlTransfer( int requestType, int request , int value , int index , byte[] buffer , int length , int timeout) 

如果该方法通过0节点将数据传输到该设备,则传输方向取决于请求的类别requestType 为 USB_DIR_OUT 则为写数据 , USB _DIR_IN ,则为读数 UsbRequest USB 请求包。 UsbConstants USB 常量的定义

两种开发模式

Android设备最早都是以PC的USB外设形式而存在,主要功能就是Android fastboot 或Android Debug Bridge (adb),都是基于bulk传输模式的.

USB Host Mode

顾名思义,Android设备作为主机,需要安卓设备支持OTG接头.常见应用场景诸如连接数码相机,键盘,鼠标,游戏手柄等硬件.需要注意的是,通常安卓设备的电量作为主机是捉襟见肘的.

USB Accessory Mode

这种模式下Android设备承担外设的角色.应用场景诸如连接机器人控制器,音响,医疗器材等,当然前提是这些设备支持与Android设备连接并且遵守Android accessory communication protocol.这种模式可以让不具有host能力的Android设备与其他硬件交互.

注意:在谷歌的话语体系里,Host模式里与Android设备相连的硬件被称为USB device,Accessory模式里,与Android设备相连的硬件称为USB accessory.尽管是accessory是"附件"的意思,其承担的却是逻辑上主机(host)的角色.

Android通过两种模式支持各种 USB 外设和 Android USB 附件(实现Android附件协议的硬件):USB附件和USB主机。USB开发需 Android 3.1(API级别12)以上。

配置AndroidManifest.xml文件

配置好清单文件后当用户连接与您的设备过滤器匹配的设备时,系统会向他们显示一个对话框,询问他们是否要启动您的应用程序。如果用户接受,则应用程序将自动具有访问设备的权限,直到设备断开连接。如果给了默认,那么这个 USB 设备插入后会自动启动这个 Activity

在android系统中,默认是关闭usb模式的,当我们将usb外部设备插入android设备时,android系统不回出任何响应。如果我们要打开系统usb模式,需要在android清单文件里使用标签声明响应的usb模式

    #Host模式
    <uses-feature android:name="android.hardware.usb.host" />
    #accessory模式
    <uses-permission android:name="android.hardware.usb.accessory" />

配置usb设备插入的通知和相关usb设备的过滤

若我们希望在插入usb设备时,android系统能接收到提示性通知,则需要在android组件中配置和标签配置相关信息,中配置我们要接收的相关类型的信息通知,配置我们需要检测的指定的设备,标签内容对应一个xml资源文件,里面配置我们指定的usb设备。如下:

<activity  >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <!--host模式下通知的过滤-->
    <intent-filter>
        <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
    </intent-filter>
    <!--host模式下指定过滤设备的配置文件-->
    <meta-data
        android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
        android:resource="@xml/device_filter" />
    <!--accessory模式下通知的过滤-->
    <intent-filter>
        <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
    </intent-filter>
    <!--accessory模式下指定过滤设备的配置文件-->
    <meta-data
        android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
        android:resource="@xml/device_filter" />

</activity>

配置 xml资源文件(用于过滤设备) :

在XML资源文件中,声明要过滤的USB设备的元素。以下列表描述了属性 。通常,如果要过滤特定设备并使用类,子类和协议(如果要过滤一组USB设备(如大容量存储设备或数码相机)),请使用供应商(vendor-id)和产品(product-id)ID,在开发中这些过滤ID一般可以在文档中找到,或者自己连上看也行。你可以指定部分或全部这些属性。
将资源文件保存在res/xml/目录中。资源文件名(不带.xml扩展名)必须与您在元素中指定的文件名相同 。对于XML资源文件格式的 例子如下:
<?xml version="1.0" encoding="utf-8"?>
<rescources xmlns:android="http://schemas.android.com/apk/res/android">
    <!--host模式-->
    <usb-device
        class=""
        vendor-id=""
        product-id=""
        protoclo=""
        subclass="" />

    <!--accessory模式-->
    <usb-accessory
        model="xxx"
        manufacturer="xxx" />

建立通信

无论是host模式还是accessory模式,在进行检测获取usb设备时,都需要用到UsbManager类,只是调用不同的方法而已。如下: host模式

```
 #UsbManager
 UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
 #获取设备列表 
 HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
  #遍历设备,找到指定的productId和vendorId 设备
  Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
    while (deviceIterator.hasNext()) {
        UsbDevice device = deviceIterator.next();
        if (device.getVendorId() == vendorId && device.getProductId() == productId) {
      #获取当前UsbDevice
       mUsbDevice = device;
        # 检查mUsbDevice 否有对应权限,若无则请求权限,通过广播接收权限信息获取情况。
          if (!usbManager.hasPermission(mUsbDevice)) {//检测是否有usb权限
        requestPermission(mUsbDevice);
    } else {
      #与usb设备进行通信
        connect(mUsbDevice);
    }
        }

/**
 * 请求usb权限
 * @param usbManager
 * @param parcelable
 */
 private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
 
public void requestPermission(UsbDevice usbDevice,UsbManager usbManager){       
            IntentFilter intentFilter = new IntentFilter(ACTION_USB_PERMISSION );
            registerReceiver(mMyBroadcastReceiver, intentFilter);
            PendingIntent broadcast = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION ), 0);
            usbManager.requestPermission(usbDevice, broadcast);

}

/** * 权限广播接收器 */ private BroadcastReceiver mMyBroadcastReceiver=new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ACTION_USB_PERMISSION .equals(action)) { synchronized (this) { UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (usbDevice != null) { if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { connect(usbDevice); Toast.makeText(context, “success get permission”, Toast.LENGTH_SHORT).show(); } else { Toast.makeText(context, “failure get permission”, Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(context, “the usbDevice be null”, Toast.LENGTH_SHORT).show(); } } } } }; public boolean connect(UsbDevice mUsbDevice) { if (mUsbDevice != null) { UsbInterface anInterface = mUsbDevice.getInterface(0); mUsbDeviceConnection = mUsbManager.openDevice(mUsbDevice);//打开设备,获取 UsbDeviceConnection 对象,连接设备,用于后面的通讯 if (mUsbDeviceConnection == null) { Toast.makeText(mContext, “mUsbDeviceConnection can’t be null”, Toast.LENGTH_SHORT).show(); return false; } if (mUsbDeviceConnection.claimInterface(anInterface, true)) { Toast.makeText(mContext, “找到USB接口”, Toast.LENGTH_SHORT).show(); int endpointCount = anInterface.getEndpointCount(); for (int i = 0; i < endpointCount; i++) { UsbEndpoint endpoint = anInterface.getEndpoint(i); if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) { if (UsbConstants.USB_DIR_IN == endpoint.getDirection()) { mUsbEndpoint_in = endpoint;//获取读数据通道 } else { mUsbEndpoint_out = endpoint;//获取写数据通道 } } } return true; } else { mUsbDeviceConnection.close();//关闭连接 Toast.makeText(mContext, “找不到USB接口”, Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(mContext, “mUsbDevice can’t be null”, Toast.LENGTH_SHORT).show(); } return false; }

/**
 * 从usb通信设备中读取数据
 * @return
 */
public byte[] readData() {
    int inMax = mUsbEndpoint_in.getMaxPacketSize();
    byte[] bytes = new byte[inMax];
    ByteBuffer byteBuffer = ByteBuffer.allocate(inMax);
    UsbRequest usbRequest = new UsbRequest();
    usbRequest.initialize(mUsbDeviceConnection, mUsbEndpoint_in);
    usbRequest.queue(byteBuffer, inMax);
    if (mUsbDeviceConnection.requestWait() == usbRequest) {
        bytes = byteBuffer.array();
    }
    return bytes;
}

/**
 * 将数据写入到usb设备中
 * @param bytes
 */
public void sendData(byte[] bytes) {
    if (mUsbDeviceConnection == null) {
        Toast.makeText(mContext, "mUsbDeviceConnection can't be null", Toast.LENGTH_SHORT).show();
        return;
    }
    if (mUsbEndpoint_out == null) {
        Toast.makeText(mContext, "mUsbEndpoint_out can't be null", Toast.LENGTH_SHORT).show();
        return;
    }

    int i = mUsbDeviceConnection.bulkTransfer(mUsbEndpoint_out, bytes, bytes.length, 1000);
    if (i < 0) {
        Toast.makeText(mContext, "failure to write", Toast.LENGTH_SHORT).show();
    } else {
        Toast.makeText(mContext, "success to write", Toast.LENGTH_SHORT).show();
    }
}



/**
 * 筛选出我们想要的usb设备
 * @param name
 * @param usbDevice
 * @return
 */
public boolean filterDevice(String name, UsbDevice usbDevice) {
    // TODO: 2019/3/13 对应判断设备是否是我们要连接的设备
    return false;
}
accessory模式(和host模式差不多,大致步骤一样,只是相关类不同而已如下)
1)获取UsbManager
2)通过usbManager获取连接到的usb外部设备UsbAccessory
3)获取usb设备引用,通过usbManager检测是否有对应权限,若无则请求权限,通过广播接收权限信息获取情况。
4)与UsbAccessory设备进行通信

public static final String action_usb_permission = "org.zhuhailong.myviewproject.permission";

private UsbManager usbManager;

public void hostAccessory(Context context) {

    //获取UsbManager
    usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);

    //获取usb设备列表
    UsbAccessory[] accessoryList = usbManager.getAccessoryList();

    if (accessoryList == null || accessoryList.length <= = 0) {
        Toast.makeText(context, "未查询到对应设备", Toast.LENGTH_SHORT).show();
        return;
    }

    UsbAccessory mUsbAccessory = null;
    for (UsbAccessory usbAccessory : accessoryList) {
        if (filterDevice(usbAccessory)) {//判断是否使我们要连接的usb设备
            break;
        }
    }

    if (usbManager.hasPermission(mUsbAccessory)) {//检测是否有usb权限
        requestPermission(mUsbAccessory);//请求usb权限
    } else {
        connect(mUsbAccessory);//连接usb设备
    }

}

/**
 * 请求usb权限
 *
 * @param usbManager
 * @param parcelable
 */
public void requestPermission(Parcelable parcelable, UsbManager usbManager) {
    UsbAccessory usbAccessory = (UsbAccessory) parcelable;
    if (!usbManager.hasPermission(usbAccessory)) {
        IntentFilter intentFilter = new IntentFilter(action_usb_permission);
        registerReceiver(mMyBroadcastReceiver, intentFilter);
        PendingIntent broadcast = PendingIntent.getBroadcast(this, 0, new Intent(action_usb_permission), 0);
        usbManager.requestPermission(usbAccessory, broadcast);
    } else {
        Toast.makeText(context, "already have permission", Toast.LENGTH_SHORT).show();
    }
}

private FileInputStream mFileInputStream;

private FileOutputStream mFileOutputStream;

/**
 * 连接设备
 * @param usbAccessory
 * @return
 */
public boolean connect(UsbAccessory usbAccessory) {
    ParcelFileDescriptor parcelFileDescriptor = usbManager.openAccessory(usbAccessory);
    if (parcelFileDescriptor != null) {
        FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();

        mFileInputStream = new FileInputStream(fileDescriptor);

        mFileOutputStream = new FileOutputStream(fileDescriptor);

    }
    return false;
}

/**
 * 写入数据
 * @param data
 */
public void write(byte[] data) {
    try {
        mFileOutputStream.write(data);
    } catch (IOException e) {
        e.printStackTrace();
    }

}

/**
 * 读取数据
 */
public void read() {
    int max;//字节数据根据协议来确定
    byte[] data = new byte[max];
    mFileInputStream.read(data);
}


/**
 * 权限广播接收器
 */
private BroadcastReceiver mMyBroadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action_usb_permission.equals(action)) {
            synchronized (this) {
                UsbAccessory usbAccessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
                if (usbAccessory != null) {
                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        connect(usbAccessory);
                        Toast.makeText(context, "success get permission", Toast.LENGTH_SHORT).show();
                    } else {
                        Toast.makeText(context, "failure get permission", Toast.LENGTH_SHORT).show();
                    }
                } else {
                    Toast.makeText(context, "the usbDevice be null", Toast.LENGTH_SHORT).show();
                }
            }
        }
    }
};

/**
 * 筛选出我们想要的usb设备
 *
 * @param usbAccessory
 * @return
 */
public boolean filterDevice(UsbAccessory usbAccessory) {
    // TODO: 2019/3/13 对应判断设备是否是我们要连接的设备
    return false;
}


  



二、USB 设备的连接和使用

2.USB 设备的插入

Android 系统中,USB 设备的插入和拔出是以系统广播的形式发送的,我们只要注册监听这个广播就好

public class USBReceiver extends BroadcastReceiver { public static final String ACTION_USB_PERMISSION = “com.android.example.USB_PERMISSION”;

@Override
public void onReceive(Context context, Intent intent) {
    String action = intent.getAction();
    if (ACTION_USB_PERMISSION.equals(action)) {
        // 获取权限结果的广播
        synchronized (this) {
            UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
            if (device != null) {
                //call method to set up device communication
                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    Log.e("USBReceiver", "获取权限成功:" + device.getDeviceName());
                } else {
                    Log.e("USBReceiver", "获取权限失败:" + device.getDeviceName());
                }
            }
        }
    }else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
        // 有新的设备插入了,在这里一般会判断这个设备是不是我们想要的,是的话就去请求权限
    } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
        // 有设备拔出了
    }
}

}

 
/**
 * 请求获取指定 USB 设备的权限
 */
public void requestPermission(UsbDevice device) {
    if (device != null) {
        if (usbManager.hasPermission(device)) {
            Toast.makeText(context, "已经获取到权限", Toast.LENGTH_SHORT).show();
        } else {
            if (mPermissionIntent != null) {
                usbManager.requestPermission(device, mPermissionIntent);
                Toast.makeText(context, "请求USB权限", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(context, "请注册USB广播", Toast.LENGTH_LONG).show();
            }
        }
    }
}
  

注册广播:
public void registerReceiver(Activity context) {
    mPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
    IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
    filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
    filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
    context.registerReceiver(usbReceiver, filter);
}
   
7.通信

与 USB 设备的通信可以是同步的也可以是异步的。无论哪种情况,你都应该创建一个新线程来执行所有数据传输,避免阻塞UI线程。

第一步,打开通信端口
public boolean openPort(UsbDevice device) {
    //获取设备接口,一般只有一个,多个的自己研究去
    usbInterface = device.getInterface(0);

    // 判断是否有权限
    if (hasPermission(device)) {
        // 打开设备,获取 UsbDeviceConnection 对象,连接设备,用于后面的通讯
        usbConnection = usbManager.openDevice(device);

        if (usbConnection == null) {
            return false;
        }
        if (usbConnection.claimInterface(usbInterface, true)) {
            Toast.makeText(Utils.getContext(), "找到 USB 设备接口", Toast.LENGTH_SHORT).show();
        } else {
            usbConnection.close();
            Toast.makeText(Utils.getContext(), "没有找到 USB 设备接口", Toast.LENGTH_SHORT).show();
            return false;
        }
    } else {
        Toast.makeText(Utils.getContext(), "没有 USB 权限", Toast.LENGTH_SHORT).show();
        return false;
    }

    //获取接口上的两个端点,分别对应 OUT 和 IN
    for (int i = 0; i < usbInterface.getEndpointCount(); ++i) {
        UsbEndpoint end = usbInterface.getEndpoint(i);
        if (end.getDirection() == UsbConstants.USB_DIR_IN) {
            usbEndpointIn = end;
        } else {
            usbEndpointOut = end;
        }
    }
    return true;
}
     
 

第二步,发送数据

usbConnection.bulkTransfer(usbEndpointOut, bytes, bytes.length, 500);





相关 开源库
链接: [usb-serial-for-android](https://github.com/mik3y/usb-serial-for-android).

标签: 20usb连接器2usb3连接器接头usb连接器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台