转自:http://blog.csdn.net/leo_wonty/article/details/6721214
HID是一种USB通信协议,无需安装驱动就能进行交互,在学习HID之前,先来复习一下USB协议的相关内容。
USB设备描述符-概述
当插入USB设备后,主机会向设备请求各种描述符来识别设备。那什么是设备描述符呢?
Descriptor即描述符,是一个完整的数据结构,可以通过C语言等编程实现,并存储在USB设备中,用于描述一个USB设备的所有属性,USB主机是通过一系列命令来要求设备发送这些信息的。
描述符的作用就是通过命令操作作来给主机传递信息,从而让主机知道设备具有什么功能、属于哪一类设备、要占用多少带宽、使用哪类传输方式及数据量的大小,只有主机确定了这些信息之后,设备才能真正开始工作。
USB有那些标准描述符?
USB有5种标准描述符:设备描述符 、配置描述符、字符描述符、接口描述符、端点描述符 。
以下转自:http://blog.csdn.net/duckmoving/article/details/9713269
我必须声明一下,本文章是我自己的X度空间最近迁移过来的,不是抄袭。
【废话一段】
前段时间,我的小组开发一个Android主机的系统,这个系统需要外接USB的指纹机、读卡器、U盘,硬件已经有了,主机是一个开发板接口丰富,并且支持Android USB Host模式,外设自然不用说。
但是碰到了一个问题,驱动!本来这个项目是源于Windows的,外设全部是针对Windows而开发的,如果要用专门的驱动,那么开发Android本身就需要复杂的过程。后来经过硬件工程师的改造,我们将USB换成了HID模式,减轻开发难度。
经过一段时间搜索网上资料,关于Android USB Host的资料可以说非常少,不是少数,而是几乎雷同。我是百度+google,更换无数中英文关键字,最后我如愿完成自己的项目,和HID设备正常通讯了,并且识别了U盘。对于网络上中文资料的少而单一的现状,我决定自己写一篇文章,让同行们少走弯路。
我的代码参考了来自“开源中国”部分资料,如果有解决不了的,可以在那里查询。
【基础功能】
注意:本文的步骤,可能需要你具备Root的权限,否则有些操作可能会无法完成。强烈建议你先root设备。
步骤一:你必须确定你的Android设备支持USB Host,具体支持与否请参考自己的说明书。确定了才有必要看本文章。
步骤二:确认Android是否已经开放了USB访问权限,这一步非常重要。操作是:进入系统,找到目录“/system/etc/permissions”,可以用ES或者RE文件管理器进行操作。查看该目录下,是否有一个文件"android.hardware.usb.host.xml",如果没有,则自己创建一个同名文件,内容如下:
- <permissions>
- <feature name="android.hardware.usb.host"/>
- </permissions>
然后,拷贝到“/system/etc/permissions”目录下。(可以用Eclipse的DDMS帮忙,push进去)
步骤三:其实呢,有了步骤三基本也就可以了,但是我自己也不是很确定,于是有了步骤四。继续检查目录“/system/etc/permissions”下,将其中的“handheld_core_hardware.xml (手机)或者 tablet_core_hardware.xml(平板)”拖出来,打开文件,看看<permissions>结点下面有没有下面这个结点:
- <feature name="android.hardware.usb.host" />
如果没有,就自己补上一行,保存,并push进去替换原来的文件。比如我的文件内容是:
- <?xml version="1.0" encoding="utf-8"?>
- <permissions>
- <feature name="android.hardware.camera" />
- <feature name="android.hardware.location" />
- <feature name="android.hardware.location.network" />
- <feature name="android.hardware.sensor.compass" />
- <feature name="android.hardware.sensor.accelerometer" />
- <feature name="android.hardware.bluetooth" />
- <feature name="android.hardware.touchscreen" />
- <feature name="android.hardware.microphone" />
- <feature name="android.hardware.screen.portrait" />
- <feature name="android.hardware.screen.landscape" />
- <feature name="android.hardware.usb.host" />
- </permissions>
步骤四:非常重要,就是重启你的Android设备。
【详细代码】
事实上,做完上面的步骤,剩下的代码就非常地简单了,我之前搜索到的基本就是这些内容了。
强烈建议大家参考SDK文档下面的google示例:ADBTest,他写的类非常有用,我下面的内容可以忽略过去。我后来就是参考了ADBTest之后,改装成一个通用类,可以让我的主机接多个USB-HID外设,实现同时异步收发。当然,你通过我下面的代码应该也能实现举一反三。
先看看AndroidManifest.xml文件,为了写这篇文章,我特意做了大量注释:
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.usbmanager"
- android:versionCode="1"
- android:versionName="1.0" >
-
- <uses-permission android:name="android.permission.HARDWARE_TEST" />
-
- <uses-feature android:name="android.hardware.usb.host" android:required="true"/>
-
- <uses-sdk
- android:minSdkVersion="12"
- android:targetSdkVersion="17" />
-
- <application
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- <activity
- android:name=".MainActivity"
- android:label="@string/app_name"
- >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
-
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
-
- <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"/>
- <!--
- <intent-filter>
- <action android:name="android.intent.action.VIEW"></action>
- <category android:name="android.intent.category.DEFAULT"></category>
- <data android:mimeType=""></data>
- </intent-filter>
- -->
-
- </activity>
- </application>
-
- </manifest>
注意看:上面的文件提到了一个文件“
device_filter
”,他也是你能否成功的一个重要文件”
device_filter.xml
“,这个文件必须自己创建:
在项目工程中的res结点创建一个新的文件夹叫”xml“(不会操作????右键啊!),然后再在xml文件夹下创建一个xml,文件名就叫做“device_filter",内容如下:
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
-
-
-
- <usb-device vendor-id="2316" product-id="4096"/>
-
- <usb-device vendor-id="1155" product-id="22352"/>
-
- <usb-device vendor-id="1507" product-id="1544" />
- </resources>
我必须讲清楚,上面的设备是我测试用的,和大家手上的设备根本不一样,请自行查看USB设备VID\PID然后转换
。
我们再来看看布局文件activity_main.xml,
这个布局是测试用的,非常随意,请大家根据自己的项目布局测试,不一定要用我这个,也不要问我为什么要这么设计布局,哥我只是玩玩而已,不要那么认真嘛~~~~~
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- tools:context=".MainActivity" >
-
- <TextView
- android:id="@+id/tvtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/title" />
-
- <EditText
- android:id="@+id/etxsend"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignLeft="@+id/tvtitle"
- android:layout_below="@+id/tvtitle"
- android:ems="10"
- android:hint="@string/sendhint"
- android:visibility="invisible" />
-
- <EditText
- android:id="@+id/etxreceive"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignLeft="@+id/btsend"
- android:layout_below="@+id/btsend"
- android:layout_marginTop="37dp"
- android:ems="10"
- android:hint="@string/receivehint"
- android:visibility="invisible" />
-
- <Button
- android:id="@+id/btreceive"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignLeft="@+id/etxreceive"
- android:layout_below="@+id/etxreceive"
- android:text="@string/btreceive" />
-
- <Button
- android:id="@+id/btsend"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignLeft="@+id/etxsend"
- android:layout_below="@+id/etxsend"
- android:layout_marginTop="16dp"
- android:text="@string/btsend" />
-
- <ListView
- android:id="@+id/lsv1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignLeft="@+id/btreceive"
- android:layout_below="@+id/btreceive" >
- </ListView>
-
- </RelativeLayout>
特别是上面提到的一些按钮上的text,大家发挥自己想象力,我不想再贴上values下面的那些文件了。其中有些按钮根本没有用到。我是调试用的。
这回,我们进入了直接的代码模块:MainActivity.java
- package com.example.usbmanager;
-
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
-
- import android.os.Bundle;
- import android.R.string;
- import android.app.Activity;
- import android.app.PendingIntent;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.DialogInterface;
- import android.content.Intent;
- import android.content.IntentFilter;
- import android.database.DataSetObserver;
- 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.util.Log;
- import android.view.View.OnClickListener;
- import android.view.Gravity;
- import android.view.Menu;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.ArrayAdapter;
- import android.widget.Button;
- import android.widget.EditText;
- import android.widget.ListAdapter;
- import android.widget.ListView;
- import android.widget.Toast;
-
- public class MainActivity extends Activity {
- private static final String TAG = "MainActivity";
- private Button btsend;
- private UsbManager manager;
- private UsbDevice mUsbDevice;
- private ListView lsv1;
- private UsbInterface mInterface;
- private UsbDeviceConnection mDeviceConnection;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- btsend = (Button) findViewById(R.id.btsend);
-
- btsend.setOnClickListener(btsendListener);
-
- lsv1 = (ListView) findViewById(R.id.lsv1);
-
- manager = (UsbManager) getSystemService(Context.USB_SERVICE);
- if (manager == null) {
- return;
- } else {
- Log.i(TAG, "usb设备:" + String.valueOf(manager.toString()));
- }
- HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
- Log.i(TAG, "usb设备:" + String.valueOf(deviceList.size()));
- Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
- ArrayList<String> USBDeviceList = new ArrayList<String>();
- while (deviceIterator.hasNext()) {
- UsbDevice device = deviceIterator.next();
-
- USBDeviceList.add(String.valueOf(device.getVendorId()));
- USBDeviceList.add(String.valueOf(device.getProductId()));
-
-
- if (device.getVendorId() == 1155 && device.getProductId() == 22352) {
- mUsbDevice = device;
- Log.i(TAG, "找到设备");
- }
- }
-
- lsv1.setAdapter(new ArrayAdapter<String>(this,
- android.R.layout.simple_list_item_1, USBDeviceList));
- findIntfAndEpt();
-
- }
-
- private byte[] Sendbytes;
- private byte[] Receiveytes;
- private OnClickListener btsendListener = new OnClickListener() {
- int ret = -100;
- @Override
- public void onClick(View v) {
-
-
-
-
-
-
- String testString = "90000CB20301F401F401F401F407D447FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
- Sendbytes = clsPublic.HexString2Bytes(testString);
-
-
- ret = mDeviceConnection.bulkTransfer(epOut, Sendbytes, Sendbytes.length, 5000);
- Log.i(TAG,"已经发送!");
-
-
- Receiveytes=new byte[64];
- ret = mDeviceConnection.bulkTransfer(epIn, Receiveytes, Receiveytes.length, 10000);
- Log.i(TAG,"接收返回值:" + String.valueOf(ret));
- if(ret != 64) {
- DisplayToast("接收返回值"+String.valueOf(ret));
- return;
- }
- else {
-
- DisplayToast(clsPublic.Bytes2HexString(Receiveytes));
- Log.i(TAG,clsPublic.Bytes2HexString(Receiveytes));
- }
- }
- };
-
-
- public void DisplayToast(CharSequence str) {
- Toast toast = Toast.makeText(this, str, Toast.LENGTH_LONG);
-
- toast.setGravity(Gravity.TOP, 0, 200);
-
- toast.show();
- }
-
-
- private void findIntfAndEpt() {
- if (mUsbDevice == null) {
- Log.i(TAG,"没有找到设备");
- return;
- }
- for (int i = 0; i < mUsbDevice.getInterfaceCount();) {
-
-
- UsbInterface intf = mUsbDevice.getInterface(i);
- Log.d(TAG, i + " " + intf);
- mInterface = intf;
- break;
- }
-
- if (mInterface != null) {
- UsbDeviceConnection connection = null;
-
- if(manager.hasPermission(mUsbDevice)) {
-
- connection = manager.openDevice(mUsbDevice);
- if (connection == null) {
- return;
- }
- if (connection.claimInterface(mInterface, true)) {
- Log.i(TAG,"找到接口");
- mDeviceConnection = connection;
-
- getEndpoint(mDeviceConnection,mInterface);
- } else {
- connection.close();
- }
- } else {
- Log.i(TAG,"没有权限");
- }
- }
- else {
- Log.i(TAG,"没有找到接口");
- }
- }
-
-
- private UsbEndpoint epOut;
- private UsbEndpoint epIn;
-
- private void getEndpoint(UsbDeviceConnection connection, UsbInterface intf) {
- if (intf.getEndpoint(1) != null) {
- epOut = intf.getEndpoint(1);
- }
- if (intf.getEndpoint(0) != null) {
- epIn = intf.getEndpoint(0);
- }
- }
-
-
- }
还有个类clsPublic.java,用来做些类型转换的和其他用途。
- package com.example.usbmanager;
-
- public class clsPublic {
-
- public static byte[] int2bytes(int n) {
- byte[] ab = new byte[4];
- ab[0] = (byte) (0xff & n);
- ab[1] = (byte) ((0xff00 & n) >> 8);
- ab[2] = (byte) ((0xff0000 & n) >> 16);
- ab[3] = (byte) ((0xff000000 & n) >> 24);
- return ab;
- }
-
-
- public static int bytes2int(byte b[]) {
- int s = 0;
- s = ((((b[0] & 0xff) << 8 | (b[1] & 0xff)) << 8) | (b[2] & 0xff)) << 8
- | (b[3] & 0xff);
- return s;
- }
-
-
- public static char byte2char(byte b) {
- return (char) b;
- }
-
- private final static byte[] hex = "0123456789ABCDEF".getBytes();
-
- private static int parse(char c) {
- if (c >= 'a')
- return (c - 'a' + 10) & 0x0f;
- if (c >= 'A')
- return (c - 'A' + 10) & 0x0f;
- return (c - '0') & 0x0f;
- }
-
-
- public static String Bytes2HexString(byte[] b) {
- byte[] buff = new byte[2 * b.length];
- for (int i = 0; i < b.length; i++) {
- buff[2 * i] = hex[(b[i] >> 4) & 0x0f];
- buff[2 * i + 1] = hex[b[i] & 0x0f];
- }
- return new String(buff);
- }
-
-
- public static byte[] HexString2Bytes(String hexstr) {
- byte[] b = new byte[hexstr.length() / 2];
- int j = 0;
- for (int i = 0; i < b.length; i++) {
- char c0 = hexstr.charAt(j++);
- char c1 = hexstr.charAt(j++);
- b[i] = (byte) ((parse(c0) << 4) | parse(c1));
- }
- return b;
- }
- }
【调试】
如果一切顺利的话,你接上你的USB设备后,android会弹出一个访问硬件的权限提示框,你要点击“确定“。
调试的内容应该我不需要讲了,Eclipse会帮你搞定的。
好吧!已经把该说的代码都说了,说句实话,CSDN的网络稳定性还是很差,目前我正在物色新的博客平台,X园好像挺可以的。