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

2019-07-14 02:35发布

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!                     

零 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 ");           if(UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {                tvInfo.append("ACTION_USB_DEVICE_ATTACHED ");            } else if(UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {                tvInfo.append("ACTION_USB_DEVICE_DETACHED ");            }        }    };
  • 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

三、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" />
  • 1
  • 2
  • 3
  • 4
  • 5
在usbfilter中加入厂商id和产品id的过滤,如下:<resources>    <usb-device vendor-id="1234" product-id="5678" />resources>
  • 1
  • 2
  • 3
  • 4
结果就是,当此型号设备通过Usb连接到系统时,对应的Activity就会启动。

四、UsbManager的初始化

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

五、列出Usb设备

        HashMap deviceHashMap = mUsbManager.getDeviceList();        Iterator iterator = deviceHashMap.values().iterator();        while (iterator.hasNext()) {            UsbDevice device = iterator.next();            tvInfo.append(" device name: "+device.getDeviceName()+" device product name:"                    +device.getProductName()+" vendor id:"+device.getVendorId()+                    " device serial: "+device.getSerialNumber());        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

六、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);                }            }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
我们仍然使用广播来获得权限赋予情况。public static final String ACTION_DEVICE_PERMISSION = "com.linc.USB_PERMISSION";
  • 1
注册广播        mPermissionIntent = PendingIntent.getBroadcast(this,0,new Intent(ACTION_DEVICE_PERMISSION),0);        IntentFilter permissionFilter = new IntentFilter(ACTION_DEVICE_PERMISSION);        registerReceiver(mUsbReceiver,permissionFilter);
  • 1
  • 2
  • 3
接收器的代码: private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {        public void onReceive(Context context, Intent intent) {            String action = intent.getAction();            tvInfo.append("BroadcastReceiver in ");            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!!!");                    }                }            }         }    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

七、通信

UsbDevice有了权限,下面就可以进行通信了。
这里要用到:UsbInterface、UsbEndpoint(一进一出两个endpoint,双向通信)、UsbDeviceConnection。
注意:通信的过程不能在UI线程中进行。
得到授权后,将做一些通信前的准备工作,如下:private void initCommunication(UsbDevice device) {        tvInfo.append("initCommunication in ");        if(1234 == device.getVendorId() && 5678 == device.getProductId()) {            tvInfo.append("initCommunication in right device ");            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 ");                    mUsbEndpointIn = null;                    mUsbEndpointOut = null;                    mUsbInterface = null;                } else {                    tvInfo.append(" endpoint out: " + mUsbEndpointOut + ",endpoint in: " +                            mUsbEndpointIn.getAddress()+" ");                    mUsbInterface = usbInterface;                    mUsbDeviceConnection = mUsbManager.openDevice(device);                    break;                }            }        }    }
  • 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
发送数据如下:result = mUsbDeviceConnection.bulkTransfer(mUsbEndpointOut, mData, (int)buffSize, 1500);//需要在另一个线程中进行
  • 1

八、其他

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

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

这里写图片描述