Android实战技巧之四十九:Usb通信之USB Host


Android实战技巧之四十九:Usb通信之USB Host


零 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。

一、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的插入和拔出是以系统广播的形式发送的,只要我们注册这个广播即可。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<code class = "hljs java" >    @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" );
             }
         }
     };</code>

三、Usb插入时启动程序

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

?
1
2
3
4
<code class = " hljs xml" > <intent-filter>
     <action android:name= "android.hardware.usb.action.USB_DEVICE_ATTACHED" >
</action></intent-filter>
         <meta-data android:name= "android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource= "@xml/usbfilter" ></meta-data></code>

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

?
1
2
3
4
<code class = " hljs xml" ><!--{cke_protected}{C}%3C!%2D%2D%3Fxml%20version%3D% 221.0 % 22 %20encoding%3D%22utf- 8 % 22 %3F%2D%2D%3E-->
<resources>
     <usb-device vendor-id= "1234" product-id= "5678" >
</usb-device></resources></code>

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

四、UsbManager的初始化

?
1
<code class = "hljs fix" >mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);</code>

五、列出Usb设备

?
1
2
3
4
5
6
7
8
<code class = "hljs avrasm" >        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());
         }</usbdevice></string,usbdevice></code>

六、USB使用权限

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

?
1
2
3
4
5
6
7
8
9
10
11
<code class = "hljs scss" >            //先判断是否为自己的设备
             //注意:支持十进制和十六进制
             //比如:device.getProductId() == 0x04D2
             if (device.getProductId() == 1234 && device.getVendorId() == 5678 ) {
                 if (mUsbManager.hasPermission(device)) {
                     //do your work
                 } else {
                     mUsbManager.requestPermission(device,mPermissionIntent);
                 }
             }
</code>

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

?
1
<code class = "hljs java" > public static final String ACTION_DEVICE_PERMISSION = "com.linc.USB_PERMISSION" ;</code>

注册广播

?
1
2
3
<code class = "hljs cs" >        mPermissionIntent = PendingIntent.getBroadcast( this , 0 , new Intent(ACTION_DEVICE_PERMISSION), 0 );
         IntentFilter permissionFilter = new IntentFilter(ACTION_DEVICE_PERMISSION);
         registerReceiver(mUsbReceiver,permissionFilter);</code>

接收器的代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<code class = "hljs java" > 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!!!" );
                     }
                 }
             }
         }
     };</code>

七、通信

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<code class = "hljs cs" > 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 ;
                 }
             }
         }
     }</code>

发送数据如下:

?
1
<code class = "hljs cs" >result = mUsbDeviceConnection.bulkTransfer(mUsbEndpointOut, mData, ( int )buffSize, 1500 ); //需要在另一个线程中进行</code>

八、其他

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值