Android应用--基于OTG模式下对USB设备的访问
文章摘要:
本文主要描述了基于Andorid平台OTG模式下,对USB设备的访问,适用于能枚举成功的所有USB设备,以USB原始接口进行访问;
基于从所周知的原因,官方的参考文档我们应该是打不开的,所以就把资料整理一下并进行了测试,主要目的是以后自己找起来方便,如果能帮到你们,也是很开心的一件事。
官方参考文档:
如果你能打开以下链接的话,建议你看官方的参考文档:
http://developer.android.com/guide/topics/connectivity/usb/host.html
测试平台:Android 5.1 (魅蓝Note3)
开发环境:Android Studio 2.2.2
minSDK:APK12
硬件设备:圈圈教你玩USB--自定义HID设备
新建一个项目,设置最低运行版本为API12(Andorid3.1);
获取USB管理服务
// 获取USB管理服务
manager = (UsbManager) getSystemService(Context.USB_SERVICE);
if (manager == null)
{
return;
}
获取设备
// 获取设备列表(正常情况下,数量应该大于0)
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
show_log("DeviceList:" + String.valueOf(deviceList.size()));
// 查询设备列表,找出所有设备
while (deviceIterator.hasNext()) {
UsbDevice device = deviceIterator.next();
show_log("VID:" + String.format("%04X ", device.getVendorId())
+ "PID:" + String.format("%04X", device.getProductId())
);
// 进行设备匹配
if (device.getVendorId() == 0x8888 && device.getProductId() == 0x0006) {
mUsbDevice = device; // 记录设备,后续进行操作
show_log("找到指定设备");
}
// 查找接口和端点,采用方法(函数)来实现;
findIntfAndEpt();
}
注意事项:
这里的show_log是我自己定义的一个调试输出函数,你也可以设备成你自己的调试输出,不重要,删除也不影响运行。
查找接口与端点
private UsbEndpoint epOut;
private UsbEndpoint epIn;
// 寻找接口和分配结点
private void findIntfAndEpt()
{
if (mUsbDevice == null)
{
show_log("Error: 没有找到设备");
return;
}
// 获取设备接口数(多个接口时,需在判断接口类型)
// 本设备只有一个接口,在这个接口上有两个端点,OUT 和 IN
for (int i = 0; i < mUsbDevice.getInterfaceCount(); i++)
{
UsbInterface intf = mUsbDevice.getInterface(i);
mInterface = intf;
break;
}
// 通过接口来查找端点,并检查权限
if (mInterface == null) {
show_log("Error: 没有找到接口");
return;
}
UsbDeviceConnection connection = null;
// 判断是否有权限
if (!manager.hasPermission(mUsbDevice)) {
show_log("Error: 没有访问权限");
return;
}
// 打开设备,获取UsbDeviceConnection对象
connection = manager.openDevice(mUsbDevice);
if (connection == null)
{
return;
}
if (!connection.claimInterface(mInterface, true))
{
connection.close();
return;
}
show_log("找到接口");
mDeviceConnection = connection;
show_log("端点数:" + mInterface.getEndpointCount());
// 设置端点
for(int i = 0; i < mInterface.getEndpointCount(); i++ ) {
//if (mInterface.getEndpoint(1) != null)
// 端点地址,最高位表示方向
if((mInterface.getEndpoint(i).getAddress() & 0x80) == 0x00) {
epOut = mInterface.getEndpoint(i);
show_log("EpOut = " + String.format("%02X ", epOut.getAddress()));
}
else {
epIn = mInterface.getEndpoint(i);
show_log("EpIn = " + String.format("%02X ", epIn.getAddress()));
}
}
}
注意事项:
1.扫描设备不需要权限,但打开设备就需要权限了;
2.如果设备有多个接口,则需要通过接口类型(class)来识别不同接口;
权限设置
可以直接指定权限,貌似比较麻烦,还有一种就是采用意图过滤器方式来指明设备,意图过滤器会自动注册相关权限。
修改AndroidManifest.xml文件,加入以下代码:
<manifest .....
<uses-feature android:name="android.hardware.usb.host" />
<uses-sdk android:minSdkVersion="12" />
<application ..../>
<activity ..../>
<intent-filter>
..........
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter"/>
新建xml目录并建立device_filter.xml,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<resource>
<usb-device vendor-id="0x8888" product-id="6" />
</resource>
注意事项:
vendor-id和product-id支持16进制格式;
数据发送与接收
如果之前设备查找没问题,以下就可以进入激动人心的读写操作了,USB传输一共分为4种,本次测试设备采用的中断传输(貌似一般的HID设备都是采用中断传输模式;
中断输出(发送)
byte[]init_data = {0,0x55,0,0,0,0,0,0};
//使用中断传输发送数据(不包括报告id)
ByteBuffer sendbuf = ByteBuffer.wrap(init_data);
//初始化请求,endpoint为IN中断端点
UsbRequest request = new UsbRequest();
// 通过端点类型自动识别数据方向
request.initialize(mDeviceConnection, epOut);
request.queue(sendbuf, 8);
show_log("数据发送成功.");
中断输入(接收)
int maxPacketSize = endpoint.getMaxPacketSize();
ByteBuffer bBuffer = ByteBuffer.allocate(maxPacketSize);
//使用中断传输接收数据
UsbRequest request = new UsbRequest();
request.initialize(connection, epIN); // 初始化
request.queue(bBuffer, maxPacketSize); // 请求队列
while(connection.requestWait() == request); // 等待完成
注意事项:
基于读接收操作的相关代码要放在线程中处理;
这种方式完全没有考虑HID的报告描述符,而是直接写中断端点;
USB串口工作说明
批量传输
函数原型
bulkTransfer(UsbEndpoint endpoint, // 端点号,通过端点号来决定输入或是输出
byte[] buffer, // 数据缓冲区
int length, // 数据长度
int timeout) ; // 超时时间
应用示例
ret = mDeviceConnection.bulkTransfer(epOut,
sendbuf,
8,
5000);
控制传输
函数原型:
controlTransfer(int requestType,
int request,
int value,
int index,
byte[] buffer,
int length,
int timeout) ;
requestType:请求类型,bit[7]为数据方向,bit[6:5]请求类型,bit[4:0]
应用示例:
// 设置串口参数
int buadrate = 115200;
byte[] line_coding = new byte]7];
line_coding[0] = (byte)((buadrate) & 0xFF);
line_coding[1] = (byte)((buadrate >> 8) & 0xFF);
line_coding[2] = (byte)((buadrate >> 16) & 0xFF);
line_coding[3] = (byte)((buadrate >> 24) & 0xFF);
line_coding[4] = 0; // stop bits
line_coding[5] = 0; // parity
line_coding[6] = 8; // data bits
mDeviceConnection.controlTransfer(
0x21, // bmRquestType: OUT(0),类请求(01) 专属接口(00001)
0x20, // bRequest: SET_LINE_CODE
0, // wValue,
0, // wIndex:接口索引号
line_coding,7, // Data,wLength
5000);
注意事项:
此模式仅适用于CDC类的串口,不适用于自定义类的(如CP210x等)串口的波特率设置;
后记:
根据文档描述,基于Android 3.1以及之后的版本中,已经开始对OTG支持,但是实际测试过程中发现,有的设备会出现扫描不到设备的情况,大部分情况是由于系统缺少了权限文件所导致,需要在/etc/permissions目录下新建android.hardware.usb.host.xml文件;
内容如下:
<permissions>
<feature name="android.hardware.usb.host"/>
</permissions>