零 USB背景知识

USB是一种数据通信方式,也是一种数据总线,而且是最复杂的总线之一。 
硬件上,它是用插头连接。一边是公头(plug),一边是母头(receptacle)。例如,PC上的插座就是母头,USB设备使用公头与PC连接。 
目前USB硬件接口分三种,普通PC上使用的叫Type;原来诺基亚功能机时代的接口为Mini USB;目前Android手机使用的Micro USB。

Host 
USB是由Host端控制整个总线的数据传输的。单个USB总线上,只能有一个Host。 
OTG 
On The Go,这是在USB2.0引入的一种mode,提出了一个新的概念叫主机协商协议(Host Negotiation Protocol),允许两个设备间商量谁去当Host。

预了解更多USB知识,请参考USB官网以及下面这篇文章: 
http://www.crifan.com/files/doc/docbook/usb_basic/release/html/usb_basic.html

一、Android中的USB

Android对Usb的支持是从3.1开始的,显然是加强Android平板的对外扩展能力。而对Usb使用更多的,是Android在工业中的使用。Android工业板子一般都会提供多个U口和多个串口,它们是连接外设的手段与桥梁。下面就来介绍一下Android Usb使用模式之一的USB Host。

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

二、USB插入事件

Usb的插入和拔出是以系统广播的形式发送的,只要我们注册这个广播即可。

    @Override
protected void onResume() {
super.onResume();
IntentFilter usbFilter = new IntentFilter();
usbFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
usbFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
registerReceiver(mUsbReceiver, usbFilter);
} @Override
protected void onPause() {
super.onPause();
unregisterReceiver(mUsbReceiver);
} private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
tvInfo.append("BroadcastReceiver in\n"); if(UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
tvInfo.append("ACTION_USB_DEVICE_ATTACHED\n");
} else if(UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
tvInfo.append("ACTION_USB_DEVICE_DETACHED\n");
}
}
};

三、Usb插入时启动程序

有些应用场景是,Usb插入后启动特定程序处理特定问题。 
我们的做法就是在Manifest中某个Activity加入Usb插入的action。

 <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/usbfilter" />

在usbfilter中加入厂商id和产品id的过滤,如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-device vendor-id="1234" product-id="5678" />
</resources>

结果就是,当此型号设备通过Usb连接到系统时,对应的Activity就会启动。

四、UsbManager的初始化

mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);

五、列出Usb设备

        HashMap<String,UsbDevice> deviceHashMap = mUsbManager.getDeviceList();
Iterator<UsbDevice> iterator = deviceHashMap.values().iterator();
while (iterator.hasNext()) {
UsbDevice device = iterator.next();
tvInfo.append("\ndevice name: "+device.getDeviceName()+"\ndevice product name:"
+device.getProductName()+"\nvendor id:"+device.getVendorId()+
"\ndevice serial: "+device.getSerialNumber());
}

六、USB使用权限

安卓系统对USB口的使用需要得到相应的权限,而这个权限要用户亲自给才行。 
首先我们会确认一下上一节中的device是否已经获得权限,如果没有就要主动申请权限:

            //先判断是否为自己的设备
//注意:支持十进制和十六进制
//比如:device.getProductId() == 0x04D2
if(device.getProductId() == 1234 && device.getVendorId() == 5678) {
if(mUsbManager.hasPermission(device)) {
//do your work
} else {
mUsbManager.requestPermission(device,mPermissionIntent);
}
}

我们仍然使用广播来获得权限赋予情况。

public static final String ACTION_DEVICE_PERMISSION = "com.linc.USB_PERMISSION";

注册广播

        mPermissionIntent = PendingIntent.getBroadcast(this,0,new Intent(ACTION_DEVICE_PERMISSION),0);
IntentFilter permissionFilter = new IntentFilter(ACTION_DEVICE_PERMISSION);
registerReceiver(mUsbReceiver,permissionFilter);

接收器的代码:

 private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
tvInfo.append("BroadcastReceiver in\n");
if (ACTION_DEVICE_PERMISSION.equals(action)) {
synchronized (this) {
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
if (device != null) {
tvInfo.append("usb EXTRA_PERMISSION_GRANTED");
}
} else {
tvInfo.append("usb EXTRA_PERMISSION_GRANTED null!!!");
}
}
}
}
};

七、通信

UsbDevice有了权限,下面就可以进行通信了。 
这里要用到:UsbInterface、UsbEndpoint(一进一出两个endpoint,双向通信)、UsbDeviceConnection。 
注意:通信的过程不能在UI线程中进行。 
得到授权后,将做一些通信前的准备工作,如下:

private void initCommunication(UsbDevice device) {
tvInfo.append("initCommunication in\n");
if(1234 == device.getVendorId() && 5678 == device.getProductId()) {
tvInfo.append("initCommunication in right device\n");
int interfaceCount = device.getInterfaceCount();
for (int interfaceIndex = 0; interfaceIndex < interfaceCount; interfaceIndex++) {
UsbInterface usbInterface = device.getInterface(interfaceIndex);
if ((UsbConstants.USB_CLASS_CDC_DATA != usbInterface.getInterfaceClass())
&& (UsbConstants.USB_CLASS_COMM != usbInterface.getInterfaceClass())) {
continue;
} for (int i = 0; i < usbInterface.getEndpointCount(); i++) {
UsbEndpoint ep = usbInterface.getEndpoint(i);
if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (ep.getDirection() == UsbConstants.USB_DIR_OUT) {
mUsbEndpointIn = ep;
} else {
mUsbEndpointOut = ep;
}
}
} if ((null == mUsbEndpointIn) || (null == mUsbEndpointOut)) {
tvInfo.append("endpoint is null\n");
mUsbEndpointIn = null;
mUsbEndpointOut = null;
mUsbInterface = null;
} else {
tvInfo.append("\nendpoint out: " + mUsbEndpointOut + ",endpoint in: " +
mUsbEndpointIn.getAddress()+"\n");
mUsbInterface = usbInterface;
mUsbDeviceConnection = mUsbManager.openDevice(device);
break;
}
}
}
}

发送数据如下:

result = mUsbDeviceConnection.bulkTransfer(mUsbEndpointOut, mData, (int)buffSize, 1500);//需要在另一个线程中进行

八、其他

作为一个普通的开发者,如果没有USB设备,那么调试程序是个问题。 
可以使用AdbTest程序用OTG线连接两个手机或平板试试。 
有USB设备的同事,会根据设备的通信协议规则去编码。这里面要用到byte与其他类型转换,以及十六进制的问题。 
具体问题具体分析吧。这篇文章磕磕绊绊,就先到这里了。

参考: 
1、endpoint的介绍:http://blog.chinaunix.net/uid-25314474-id-3040231.html

from:http://blog.csdn.net/lincyang/article/details/50739342

【转】Android实战技巧之四十九:Usb通信之USB Host的更多相关文章

  1. Android实战技巧之十九:android studio导出jar包(Module)并获得手机信息

    AS中并没有独立的Module 工程,可是能够在普通的Project中增加Module.所谓的Module就是我们通常所指的模块化的一个单元.并经常以jar包的形式存在.以下以一个获取手机信息的样例演 ...

  2. Android实战技巧之十二:Android Studio导入第三方类库、jar包和so库

    第三方类库源码 将一网友的XMPP代码从ADT转到AS时,发现其使用了第三方类库,源码放在了lib下,直接在AS中Import project,第三方类库并没有自动导入进来,看来需要自己动手了. 项目 ...

  3. Android实战技巧之三十八:Handler使用中可能引发的内存泄漏

    问题描写叙述 曾几何时,我们用原来的办法使用Handler时会有以下一段温馨的提示: This Handler class should be static or leaks might occur ...

  4. Android实战技巧之四十一:制作自己的Android SDK

      标签: sdkandroid定制sdk 2015-09-21 18:05 11237人阅读 评论(2) 收藏 举报  分类: Android(260)  版权声明:本文为博主原创文章,未经博主允许 ...

  5. Android实战技巧:深入解析AsyncTask

    AsyncTask的介绍及基本使用方法 关于AsyncTask的介绍和基本使用方法可以参考官方文档和Android实战技巧:多线程AsyncTask这里就不重复. AsyncTask引发的一个问题 上 ...

  6. Android项目实战(四十九):Andoird 7.0+相机适配

    解决方案类似: Android项目实战(四十):Andoird 7.0+ 安装APK适配 解决方法: 一.在AndroidManifest.xml 文件中添加 四大组件之一的 <provider ...

  7. 【转】 Pro Android学习笔记(十九):用户界面和控制(7):ListView

    目录(?)[-] 点击List的item触发 添加其他控件以及获取item数据 ListView控件以垂直布局方式显示子view.系统的android.app.ListActivity已经实现了一个只 ...

  8. Android项目实战(二十九):酒店预定日期选择

    先看需求效果图: 几个需求点: 1.显示当月以及下个月的日历 (可自行拓展更多月份) 2.首次点击选择"开始日期",再次点击选择"结束日期" (1).如果&qu ...

  9. Android简易实战教程--第二十九话《创建图片副本》

    承接第二十八话加载大图片,本篇介绍如何创建一个图片的副本. 安卓中加载的原图是无法对其修改的,因为默认权限是只读的.但是通过创建副本,就可以对其做一些修改,绘制等了. 首先创建一个简单的布局.一个放原 ...

随机推荐

  1. SSH:Hibernate框架(七种关联关系映射及配置详解)

    概念 基本映射是对一个实体进行映射,关联映射就是处理多个实体之间的关系,将关联关系映射到数据库中,所谓的关联关系在对象模型中有一个或多个引用. 分类 关联关系分为上述七种,但是由于相互之间有各种关系, ...

  2. sqlserver设计器实现约束

    一.约束类型简介 二.创建数据表 三.创建主键约束和非空约束 四.创建唯一约束 选中任一列,右键--索引/键 点击 添加 生成IX_Employees*, 类型设置为 “唯一键”, 然后选择要约束的列 ...

  3. hadoop 用户

    上篇文章说过,hadoop实际上就是就是一个liunx虚拟机,它的文件操作命令同linux,而它的用户呢?它的用户实际上就是linux主机的用户.这里我们做的例子就是:新建一个liunx用户,并为该l ...

  4. SSM(一):spring-ioc

    一.java代理模式 java代理模式是ioc的前置知识.代理模式非常简单,看代码就一目了然了. public interface role { public void makeMoney(); } ...

  5. Code Signal_练习题_firstDigit

    Find the leftmost digit that occurs in a given string. Example For inputString = "var_1__Int&qu ...

  6. thymeleaf标签使用方法总结

    https://blog.csdn.net/quuqu/article/details/52511933 常用th标签https://www.cnblogs.com/suncj/p/4030393.h ...

  7. 使用JNDI+连接池

    配置context.xml,在META-INF下新建context.xml,内容如下 <?xml version="1.0" encoding="UTF-8&quo ...

  8. 代码操作Word时,目录自动更新的两种方法

    最近的项目中有一个功能点为:根据分析数据库并生成报告.不过不是大数据.数据挖掘之类,报告的内容.组织方式都是事先固定下来的.实现的方式为,在普通word文档中插入书签制成模板,然后程序使用OpenXM ...

  9. 【Redis】Redis学习(二) master/slave、sentinel、Cluster简单总结

    项目中用到Redis,所以准备学习一下,感觉Redis的概念还是很多的,什么主从模式.sentinel模式.集群模式的,一下子都晕了,我觉得还是有必要先理清这些基本概念再说. 一.单节点实例 单节点实 ...

  10. [Linux]《鸟哥的私房菜》笔记 (缓慢更新)

    暂时不更新了..这几天一看起书来发现内容很多,这样写blog太慢,也没意义.所以现在是每天看书,在笔记本上记笔记,再配合着<操作系统>和 linux内核 加深理解.往后会以心得体会为主写一 ...