06.USB开发设计实践
目录介绍
- 01.基础概念介绍
- 1.1 理解USB接口
- 1.2 USB开发步骤
- 1.3 USB主机和配件
- 1.4 两种模式区别
- 1.5 USB开发相关类
- 02.常见思路和做法
- 2.1 开发场景说明
- 2.2 开发思路步骤
- 2.3 发现设备实践
- 2.4 打开设备实践
- 2.5 设备建立连接
- 2.6 数据传输实践
- 2.7 关闭设备操作
- 03.USB其他功能
- 3.1 判断请求USB权限
- 3.2 处理USB监听广播
- 3.3 UsbEndpoint设备端点
- 3.4 定期检查连接设备
- 3.5 USB断开处理逻辑
- 04.Api调用说明
- 05.原理和遇到的坑
- 06.其他问题说明
01.基础概念介绍
1.1 理解USB接口
- 如何理解USB
- USB是英文Universal Serial Bus(通用串行总线)的缩写,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。
- AOA协议是Google公司推出的用于实现Android设备与外围设备之间USB通信的协议。
- 该协议拓展了Android设备USB接口的功能,为基于Android系统的智能设备应用于数据采集和设备控制领域提供了条件。
1.2 USB开发步骤
- Android USB 配件必须遵从 Android Open Accessory(AOA)协议,该协议定义了配件如何检测和建立与 Android 设备的通信。配件应执行以下步骤:
- 等待并检测连接的设备
- 确定设备的配件模式支持
- 尝试以配件模式下启动设备(如果需要)
- 如果设备支持 AOA,与设备建立通信
- UsbManager官方文档
- https://developer.android.google.cn/reference/android/hardware/usb/
1.3 USB主机和配件
- 在 USB 配件模式下(USB accessory)
- 外部 USB 硬件充当 USB 主机。配件示例可能包括机器人控制器、扩展坞、诊断和音乐设备、自助服务终端、读卡器等等。
- 这样,不具备主机功能的 Android 设备就能够与 USB 硬件互动。Android USB 配件必须设计为与 Android 设备兼容,并且必须遵守 Android 配件通信协议。
- 在 USB 主机模式下(USB host)
- Android 设备充当主机。设备示例包括数码相机、键盘、鼠标和游戏控制器。即:安卓平板作为主机,usb外设作为从机进行数据通信。
1.4 两种模式区别
- 两种模式之间的差异。
- 当 Android 设备处于主机模式时,它会充当 USB 主机并为总线供电。
- 当 Android 设备处于 USB 配件模式时,所连接的 USB 硬件(本例中为 Android USB 配件)充当主机并为总线供电。
1.5 USB开发相关类
- android.hardware.usb包下提供了USB开发的相关类。
- 我们需要了解UsbManager、UsbDevice、UsbInterface、UsbEndpoint、UsbDeviceConnection、UsbRequest、UsbConstants。
- 1、UsbManager:获得Usb的状态,与连接的Usb设备通信。
- 2、UsbDevice:Usb设备的抽象,它包含一个或多个UsbInterface,而每个UsbInterface包含多个UsbEndpoint。Host与其通信,先打开UsbDeviceConnection,使用UsbRequest在一个端点(endpoint)发送和接收数据。
- 3、UsbInterface:定义了设备的功能集,一个UsbDevice包含多个UsbInterface,每个Interface都是独立的。
- 4、UsbEndpoint:endpoint是interface的通信通道。
- 5、UsbDeviceConnection:host与device建立的连接,并在endpoint传输数据。
- 6、UsbRequest:usb 请求包。可以在UsbDeviceConnection上异步传输数据。注意是只在异步通信时才会用到它。
- 7、UsbConstants:usb常量的定义,对应linux/usb/ch9.h
02.常见思路和做法
2.1 开发场景说明
- 开发使用的是usb主机模式,即:安卓平板作为主机,usb外设作为从机进行数据通信。
- 开发使用的是usb主机模式,即:安卓机器作为主机,usb外设作为从机进行数据通信。然后主机和联动设备需要交互。
2.2 开发思路步骤
- 大概思路如下:
- 第一步:发现设备。通过UsbManager调用getDeviceList可以获取当前连接的所有usb设备。
- 第二步:打开设备。可以将机具与usb外设之间的连接想象成一个通道,只有把通道的门打开后,两边才能进行通信。
- 第三步:数据传输。已经可以与usb外设进行数据传输
2.3 发现设备实践
- 第一步:发现设备。通过UsbManager调用getDeviceList可以获取当前连接的所有usb设备。
- 通过UsbManager这个系统提供的类,我们可以枚举出当前连接的所有usb设备,我们主要需要的是UsbDevice对象。
- UsbDevice,这个类就代表了Android所连接的usb设备。通过getDeviceList()获取已连接的USB设备列表。
UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); Map<String, UsbDevice> usbList = usbManager.getDeviceList();
- 请注意,为了使用UsbManager,你需要在AndroidManifest.xml文件中声明USB权限:
<uses-permission android:name="android.permission.USB_PERMISSION" />
2.4 打开设备实践
- 第二步:打开设备。可以将机具与usb外设之间的连接想象成一个通道,只有把通道的门打开后,两边才能进行通信。
- 需要打开刚刚搜索到的usb设备,我们可以将平板与usb外设之间的连接想象成一个通道,只有把通道的门打开后,两边才能进行通信。
- 注意:在没有定制的android设备上首次访问usb设备的时候,默认是没有访问权限的,因此首先要判断对当前要打开的usbDevice是否有访问权限。
- 需要和usb外设建立一个UsbDeviceConnection,大部分情况下还需要对usb串口进行一些配置,比如波特率,停止位,数据控制等,不然两边配置不同,收到的数据会乱码。
- 2.1 申请权限
- 2.2 获得连接口UsbInterface
- 2.3 获得连接端口UsbEndpoint
- 选择要打开的设备,并请求权限
- 使用requestPermission()方法请求USB设备的权限。你需要提供一个PendingIntent对象,用于接收权限请求的结果。
PendingIntent permissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0); usbManager.requestPermission(device, permissionIntent);
- 如何打开设备
mUsbManager.openDevice(device);
- 在广播接收器中处理权限请求结果。
private static final String ACTION_USB_PERMISSION = "com.example.myapp.USB_PERMISSION"; private final BroadcastReceiver usbReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ACTION_USB_PERMISSION.equals(action)) { synchronized (this) { UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { if (device != null) { // 权限已授予,可以打开设备 UsbDeviceConnection connection = usbManager.openDevice(device); if (connection != null) { // 打开设备成功,进行相关操作 } } } else { // 权限被拒绝 } } } } };
2.5 设备建立连接
- 使用usbManager.openDevice()方法打开USB设备,并获取一个UsbDeviceConnection对象。如果连接建立成功,使用该连接对象进行后续的USB通信操作。
- 获取连接接口,interfaceIndex是你要获取的连接接口的索引。一个USB设备可能有多个接口,每个接口都有自己的功能和设置。
- USB接口的索引从0开始,你可以使用device.getInterfaceCount()方法获取USB设备的接口数量。
- 可以使用UsbInterface对象来获取接口的相关信息,如端点(endpoint)和接口设置等。
UsbInterface usbInterface = device.getInterface(interfaceIndex);
2.6 数据传输实践
- 第三步:数据传输。已经可以与usb外设进行数据传输
- 向usb外设发送数据。使用usbDeviceConnection.bulkTransfer这个函数用于在给定的端口进行数据传输。返回值代表发送成功的字节数,如果返回-1,那就是发送失败了。
- 接受usb外设发送来的数据。找到了数据输入端口usbEndpointIn,因为数据的输入是不定时的,因此我们可以另开一个线程,来专门接受数据。
- 3.1 发送数据。调用bulkTransfer方法发送数据
- 3.2 接收数据。找到了数据输入端口usbEndpointIn,因为数据的输入是不定时的,因此我们可以另开一个线程,来专门接受数据
- 发送控制请求:
- 使用controlTransfer()方法发送控制请求到USB设备。你需要提供请求类型、请求码、值、索引、数据缓冲区、超时时间等参数。该方法将返回操作结果。
int requestType = UsbConstants.USB_TYPE_VENDOR | UsbConstants.USB_DIR_OUT; int request = 0x01; int value = 0x00; int index = 0x00; byte[] buffer = new byte[4]; int timeout = 1000; int result = connection.controlTransfer(requestType, request, value, index, buffer, buffer.length, timeout);
- 读取数据:
- 使用bulkTransfer()方法从USB设备的输入端点(IN)读取数据。你需要提供输入端点、数据缓冲区、缓冲区大小和超时时间等参数。该方法将返回读取的字节数。
byte[] buffer = new byte[64]; int timeout = 1000; int bytesRead = connection.bulkTransfer(endpointIn, buffer, buffer.length, timeout);
- 写入数据:
- 使用bulkTransfer()方法向USB设备的输出端点(OUT)写入数据。你需要提供输出端点、数据缓冲区、缓冲区大小和超时时间等参数。该方法将返回写入的字节数。
byte[] buffer = new byte[64]; int timeout = 1000; int bytesWritten = connection.bulkTransfer(endpointOut, buffer, buffer.length, timeout);
- 释放连接:
- 在不再需要与USB设备通信时,你可以使用close()方法释放UsbDeviceConnection对象。
connection.close();
2.7 关闭设备操作
- 当我们要退出App的时候,需要去关闭串口
mUsbManager.openDevice(device);
03.USB其他功能
3.1 判断请求USB权限
- 判断设备是否有权限,核心代码如下所示:
hasPermission = mUsbManager.hasPermission(device);
- 如果没有权限,需要先申请权限,这一步很主要,要不然后面肯定是读取不到串口的数据的。
- 你可以使用usbManager.requestPermission()方法来请求权限,并在回调中处理用户的响应。
mPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0); mUsbManager.requestPermission(device, mPermissionIntent);
3.2 处理USB监听广播
- 注册USB广播监听,主要是监听USB逻辑
- 创建一个广播接收器类,继承自BroadcastReceiver,并实现onReceive()方法。在onReceive()方法中处理USB相关的广播事件。
- 声明了USB权限android.permission.USB_PERMISSION,以及我们创建的广播接收器USBReceiver和相关的广播动作。
public class USBReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action != null && action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) { // 处理USB设备连接事件 UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); } else if (action != null && action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) { // 处理USB设备断开事件 } } }
3.3 UsbEndpoint设备端点
- UsbEndpoint表示USB设备的端点,用于在USB设备和主机之间进行数据传输。每个UsbInterface都包含一个或多个UsbEndpoint。
- 获取UsbEndpoint,遍历连接接口的所有端点,并选择类型为UsbConstants.USB_ENDPOINT_XFER_BULK的端点。
- 你可以根据你的需求选择不同类型的端点,如批量传输(bulk transfer)、中断传输(interrupt transfer)或等时传输(isochronous transfer)。
private UsbEndpoint epOut; private UsbEndpoint epIn; for (int i = 0; i < usbInterface.getEndpointCount(); i++) { UsbEndpoint tempEndpoint = usbInterface.getEndpoint(i); if (tempEndpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) { if (ep.getDirection() == UsbConstants.USB_DIR_OUT) { epOut = ep; } else { epIn = ep; } } }
- 可以使用UsbEndpoint对象的方法,如getAddress()、getDirection()和getMaxPacketSize()等,来获取端点的相关信息。
3.4 定期检查连接设备
- 开启一个心跳轮训,使用HandlerThread,具有独立looper的线程。在注册的时候发送检查健康消息,然后轮训。大概思路如下
mAsyncHandler.postDelayed(mDeviceCheckRunnable, 1000); private final Runnable mDeviceCheckRunnable = new Runnable() { @Override public void run() { //获取设备列表,检查是否有权限 //间隔2秒钟检查一下 mAsyncHandler.postDelayed(this, 2000); } };
3.5 USB断开处理逻辑
- 从哪里可以监听到USB断开操作,通过广播监听,如下所示:
public class USBReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action != null && action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) { // 处理USB设备断开事件 final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); } } }
- 在广播中,拿到连接的设备device时,然后释放连接,关闭设备等操作。最后回调listener接口中的onDettach方法告知开发者。
04.Api调用说明
05.原理和遇到的坑
06.其他问题说明
5.4 其他推荐
- https://github.com/felHR85/UsbSerial
5.5 博客
- USB识别开发
- https://blog.csdn.net/c19344881x/article/details/124838289
- Android USB转串口通信开发实例详解【好案例】
- https://blog.csdn.net/u011555996/article/details/86220900
- Android Usb(OTG)串口通信
- https://blog.csdn.net/MSONG93/article/details/130730467
- 安卓与串口通信-实践篇
- https://blog.csdn.net/sinat_17133389/article/details/130788942
- Android的USB通信(AOA连接)
- https://blog.csdn.net/CJohn1994/article/details/124669291
- Android-USB通信
- https://blog.csdn.net/dream_xang/article/details/124274920
- Android 沾包处理,以串口接入为例 (usb-serial-for-android)
- https://blog.csdn.net/EthanCo/article/details/129194519