• 【转】Android Service创建USB HOST通信


    之前做了一个关于Android USB通信的Case,通过Android的USB总线给Zigbee供电,和板载的Zigbee(基于Zigbee的自组网)进行通信。要使用Android的USB Host功能,首先你需要确定你的平板(手机)设备是否支持USB Host的功能,你可以从手机开发商的简介里面看到,这个功能是由CPU直接关联的,和软件没有关系,所以,你可以把你的外设模块插进去你的Android设备看看有没有反应(比如:指示灯亮),假如没有反应,那肯定是不支持的。

    废话说了那么多了,进入正题。

    我的案子是把USB的通信写在Service里面的,有不了解Service的,先看Service的相关内容。下面列出了Android对USB的支持。

    点开USB,你可以看到Android USB支持的两种模式:

    第一种就是HOST模式:Android设备为USB总线和外设供电,数据传输是双向的。

    第二种是Accessory模式:即附件模式,Android作为附件,手机和电脑连接,通常是这种模式,由USB Device端向总线供电,数据传输方向是双向的。这就是为什么手机插到电脑上可以充电的原因。

    我们讨论的是第一种模式——HOST。

    在讨论具体代码之前,我需要先讲一下再host模式下面的调试办法,因为数据线的端口被外设使用了,那么,传统的连接数据线调试的方法已经不行了。官网给出了解决办法:

    1、请把你的Android设备用数据线连接到电脑,当然,你也要把Android设备的wifi打开。

    2、在windows命令行下,进入SDK platform-tools/ 目录(具体看你把SDK安装在哪个目录了),执行 adb tcpip 5555 回车。这里其实是打开了adb调试的无线端口(Android设备在电脑上的端口映射),其实后面的数字你可以随便来,只要端口没有被占用。

    3、adb connect <device-ip-address>:5555键入回车,这里的device-ip-address是你Android端的IP地址。

    4、最后adb usb回车,假如没有问题,现在你已经可以在eclipse上看到logcat的输出了。

    其实还有个简单的办法,笔者是使用这个办法,你可以在应用商店上下一个无线ADB工具,随便哪个都可以,这类工具就是在Android端做了上面的那些工作,而且还不用连数据线,但是第三步的那个操作还是要你在windows的命令行窗口手动输入的。

    通过上面的操作,你已经可以用无线下载你写的程序、调试了。

    在写代码之前,我们必须在我们的manifest文件里面声明我们要使用USB Host的功能。

    方法:添加权限标签

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
     
    1. <uses-feature  
    2.         android:name="android.hardware.usb.host"  
    3.         android:required="true" />  
    4.   
    5.     <uses-permission  
    6.         android:name="android.hardware.usb.host"  
    7.         android:required="false" />  

    上面的代码,会表明本应用程序是要使用host的功能的。

    除开声明外,你还要添加一个关于设备信息的xml文件,这个文件放在res目录下的xml目录下:

    里面写好了我们要发现的具体设备的PID、VID,我只写了这两个,这是我在测试阶段用的一个设备的:

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
     
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <resources>  
    3.   
    4.     <usb-device  
    5.         product-id="5800"  
    6.         vendor-id="1105" />  
    7.   
    8. </resources>  

    里面的两个id你可以通过后面的枚举设备中打印出来。这样是方便设备一插入 ,系统就会提醒用户打开对设备进行声明的程序。

    当然,你还要manifest中的某个activity下声明,你使用了这个文件,那么,一旦你打开应用,就会自动跳转到你声明的那个activity:

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
     
    1. <activity  
    2.             android:name="cyc.gzpl.gzplv3.WelcomeActivity"  
    3.             android:launchMode="standard"  
    4.             android:screenOrientation="landscape"  
    5.             android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >  
    6.             <intent-filter>  
    7.                 <action android:name="android.intent.action.MAIN" />  
    8.   
    9.                 <category android:name="android.intent.category.LAUNCHER" />  
    10.             </intent-filter>  
    11.             <intent-filter>  
    12.                 <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />  
    13.             </intent-filter>  
    14.   
    15.             <meta-data  
    16.                 android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"  
    17.                 android:resource="@xml/device_filter" />  

    我放在了MainActivity。

    建立了一个Service类:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
     
    1. public class ZigbeeService extends Service {  
    2. //这里定义一些必要的变量  
    3. <span style="white-space:pre">  </span>@Override  
    4.     public void onStart(Intent intent, int startId) {  
    5.         // TODO Auto-generated method stub  
    6.         super.onStart(intent, startId); // 每次startService(intent)时都回调该方法  
    7.         System.out.println("进入service的onStart函数");  
    8.         myUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE); // 获取UsbManager  
    9.           
    10.         // 枚举设备  
    11.         enumerateDevice(myUsbManager);  
    12.         // 查找设备接口  
    13.         getDeviceInterface();  
    14.         // 获取设备endpoint  
    15.         assignEndpoint(Interface2);  
    16.         // 打开conn连接通道  
    17.         openDevice(Interface2);  
    18. }  

    这一段代码,基本上已经完成了和USB建立连接了。我们一个个来分析:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
     
    1. myUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE); // 获取UsbManager  


    这条代码的作用是获取一个UsbManager的实例,通过它我们可以获取USB的状态,并且和USB设备进行通信。总的来说,它管理了USB设备。

    你把你的外设插入USB接口后,紧接着就是调用美剧USB总线上的usb设备了,具体函数实现如下:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
     
    1. // 枚举设备函数  
    2.     private void enumerateDevice(UsbManager mUsbManager) {  
    3.         System.out.println("开始进行枚举设备!");  
    4.         if (mUsbManager == null) {  
    5.             System.out.println("创建UsbManager失败,请重新启动应用!");  
    6.             info.setText("创建UsbManager失败,请重新启动应用!");  
    7.             return;  
    8.         } else {  
    9.             HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();  
    10.             if (!(deviceList.isEmpty())) {  
    11.                 // deviceList不为空  
    12.                 System.out.println("deviceList is not null!");  
    13.                 Iterator<UsbDevice> deviceIterator = deviceList.values()  
    14.                         .iterator();  
    15.                 while (deviceIterator.hasNext()) {  
    16.                     UsbDevice device = deviceIterator.next();  
    17.                     // 输出设备信息  
    18.                     Log.i(TAG, "DeviceInfo: " + device.getVendorId() + " , "  
    19.                             + device.getProductId());  
    20.                     System.out.println("DeviceInfo:" + device.getVendorId()  
    21.                             + " , " + device.getProductId());  
    22.                     // 保存设备VID和PID  
    23.                     VendorID = device.getVendorId();  
    24.                     ProductID = device.getProductId();  
    25.                     // 保存匹配到的设备  
    26.                     if (VendorID == 1105 && ProductID == 5800) {  
    27.                         myUsbDevice = device; // 获取USBDevice  
    28.                         System.out.println("发现待匹配设备:" + device.getVendorId()  
    29.                                 + "," + device.getProductId());  
    30.                         Context context = getApplicationContext();  
    31.                         Toast.makeText(context, "发现待匹配设备", Toast.LENGTH_SHORT)  
    32.                                 .show();  
    33.                     }  
    34.                 }  
    35.             } else {  
    36.                 info.setText("请连接USB设备至PAD!");  
    37.                 Context context = getApplicationContext();  
    38.                 Toast.makeText(context, "请连接USB设备至PAD!", Toast.LENGTH_SHORT)  
    39.                         .show();  
    40.             }  
    41.         }  
    42.     }  

    通过上面的枚举函数,总线上面的设备都会被列出来,你可以选择符合你要求的设备,把它放到一个UsbDevice的实例里面。接下来还不能对USB进行直接通信。。。

    接下来的工作:查找外设的usb接口,我的zigbee上面有两个接口,一个0接口,还有一个2接口,我在这里吃了很大的亏,我一直忽视了2接口,一直死在0接口那里,0接口是用来进行配置的,作为用户层,是用不到的,2接口作为数据传输口。具体情况可能是要根据你的外设的情况来定的,推荐你接口的信息都要打印出来,并且都试一下。

    下面是查询接口的具体实现:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
     
    1. // 寻找设备接口  
    2.     private void getDeviceInterface() {  
    3.         if (myUsbDevice != null) {  
    4.             Log.d(TAG, "interfaceCounts : " + myUsbDevice.getInterfaceCount());  
    5.             for (int i = 0; i < myUsbDevice.getInterfaceCount(); i++) {  
    6.                 UsbInterface intf = myUsbDevice.getInterface(i);  
    7.   
    8.                 if (i == 0) {  
    9.                     Interface1 = intf; // 保存设备接口  
    10.                     System.out.println("成功获得设备接口:" + Interface1.getId());  
    11.                 }  
    12.                 if (i == 1) {  
    13.                     Interface2 = intf;  
    14.                     System.out.println("成功获得设备接口:" + Interface2.getId());  
    15.                 }  
    16.             }  
    17.         } else {  
    18.             System.out.println("设备为空!");  
    19.         }  
    20.   
    21.     }  

    把获取到的接口,存放在一个UsbInterface实例里面。接下来还是不能直接通信的。。。。。

    接下来,我们要分配接口的端点:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
     
    1. // 分配端点,IN | OUT,即输入输出;可以通过判断  
    2.     private UsbEndpoint assignEndpoint(UsbInterface mInterface) {  
    3.   
    4.         for (int i = 0; i < mInterface.getEndpointCount(); i++) {  
    5.             UsbEndpoint ep = mInterface.getEndpoint(i);  
    6.             // look for bulk endpoint  
    7.             if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {  
    8.                 if (ep.getDirection() == UsbConstants.USB_DIR_OUT) {  
    9.                     epBulkOut = ep;  
    10.                     System.out.println("Find the BulkEndpointOut," + "index:"  
    11.                             + i + "," + "使用端点号:"  
    12.                             + epBulkOut.getEndpointNumber());  
    13.                 } else {  
    14.                     epBulkIn = ep;  
    15.                     System.out  
    16.                             .println("Find the BulkEndpointIn:" + "index:" + i  
    17.                                     + "," + "使用端点号:"  
    18.                                     + epBulkIn.getEndpointNumber());  
    19.                 }  
    20.             }  
    21.             // look for contorl endpoint  
    22.             if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_CONTROL) {  
    23.                 epControl = ep;  
    24.                 System.out.println("find the ControlEndPoint:" + "index:" + i  
    25.                         + "," + epControl.getEndpointNumber());  
    26.             }  
    27.             // look for interrupte endpoint  
    28.             if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_INT) {  
    29.                 if (ep.getDirection() == UsbConstants.USB_DIR_OUT) {  
    30.                     epIntEndpointOut = ep;  
    31.                     System.out.println("find the InterruptEndpointOut:"  
    32.                             + "index:" + i + ","  
    33.                             + epIntEndpointOut.getEndpointNumber());  
    34.                 }  
    35.                 if (ep.getDirection() == UsbConstants.USB_DIR_IN) {  
    36.                     epIntEndpointIn = ep;  
    37.                     System.out.println("find the InterruptEndpointIn:"  
    38.                             + "index:" + i + ","  
    39.                             + epIntEndpointIn.getEndpointNumber());  
    40.                 }  
    41.             }  
    42.         }  
    43.         if (epBulkOut == null && epBulkIn == null && epControl == null  
    44.                 && epIntEndpointOut == null && epIntEndpointIn == null) {  
    45.             throw new IllegalArgumentException("not endpoint is founded!");  
    46.         }  
    47.         return epIntEndpointIn;  
    48.     }  

    usb的端点不是随便分配的,有IN/OUT之分,前者是输入,后者是输出。

    分配到的端点,用UsbEndpoint的实例存起来。Endpoint是用来传输数据的,但是,接下来,还是不能进行通信的。。。。。

    我们要建立外设和Android设备的连接才行,也就是打开设备的连接,不然怎么通信呢?代码如下:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
     
    1. // 打开设备  
    2.     public void openDevice(UsbInterface mInterface) {  
    3.         if (mInterface != null) {  
    4.             UsbDeviceConnection conn = null;  
    5.             // 在open前判断是否有连接权限;对于连接权限可以静态分配,也可以动态分配权限  
    6.             if (myUsbManager.hasPermission(myUsbDevice)) {  
    7.                 conn = myUsbManager.openDevice(myUsbDevice);  
    8.             }  
    9.   
    10.             if (conn == null) {  
    11.                 return;  
    12.             }  
    13.   
    14.             if (conn.claimInterface(mInterface, true)) {  
    15.                 myDeviceConnection = conn;  
    16.                 if (myDeviceConnection != null)// 到此你的android设备已经连上zigbee设备  
    17.                     System.out.println("open设备成功!");  
    18.                 final String mySerial = myDeviceConnection.getSerial();  
    19.                 System.out.println("设备serial number:" + mySerial);  
    20.             } else {  
    21.                 System.out.println("无法打开连接通道。");  
    22.                 conn.close();  
    23.             }  
    24.         }  
    25.     }  

    打开连接后,我们会得到一个UsbDeviceConnection的实例,我们的通信都是建立在一个可用的USB的连接上。

    终于可以通信了。。。。。。

    下面讲一下USB传输在Android里面的几个函数。。。。。

    通常有两种传输方式,一种叫控制传输,具体函数解释如下:

    这个传输函数有个特点,它是用于端点0的传输,具体,我也没用过,参考文档上说,0端点是用来配置用的,所以我也没有深究。

    另外一个函数bulk传输,它用于usb大流量数据的一个传输,我使用的是这个。

    下面是我用这个函数实现读写USB的代码:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
     
    1. // 发送数据  
    2.     private void sendMessageToPoint(byte[] buffer) {  
    3.         // bulkOut传输  
    4.         if (myDeviceConnection  
    5.                 .bulkTransfer(epBulkOut, buffer, buffer.length, 0) < 0)  
    6.             System.out.println("bulkOut返回输出为  负数");  
    7.         else {  
    8.             System.out.println("Send Message Succese!");  
    9.         }  
    10.     }  

    当发送数据的时候,我们使用的是out端点。

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
     
    1. // 从设备接收数据bulkIn  
    2.     private byte[] receiveMessageFromPoint() {  
    3.         byte[] buffer = new byte[15];  
    4.         if (myDeviceConnection.bulkTransfer(epBulkIn, buffer, buffer.length,  
    5.                 2000) < 0)  
    6.             System.out.println("bulkIn返回输出为  负数");  
    7.         else {  
    8.             System.out.println("Receive Message Succese!"  
    9.             // + "数据返回"  
    10.             // + myDeviceConnection.bulkTransfer(epBulkIn, buffer,  
    11.             // buffer.length, 3000)  
    12.                     );  
    13.         }  
    14.         return buffer;  
    15.     }  

    接收数据的时候,使用的IN端点。

    上面打开设备的时候得到的UsbDeviceConnection的实例和端点分配分配的端点,将在这里被使用到。

    OK!~~~

    到这里,大部分的任务都完成了,剩下的工作就是写一些逻辑代码去调用bulk传输函数就可以了,这里我就不多说了。

    另外,在Service是需要在manifest里面注册的,程序跑起来以后,Service是完全和程序的其他部分并行的,不影响程序的正常运行,也不会阻塞界面,实现了,后台与Zigbee的通信,这里主要讲的是usb host在Android里面的几个主要的点,关于Zigbee就没有多说了,无非就是一些看用户手册的活。

    笔者了解有限,有不对的地方,请指出来,大家一起交流、探讨。。。THKS

    from:http://blog.csdn.net/tianruxishui/article/details/38338087

  • 相关阅读:
    Android总结篇系列:Android Service
    Android 6.0 扫描不到 Ble 设备需开启位置权限
    玩转Android之Drawable的使用
    玩转Android之二维码生成与识别
    玩转Android之数据库框架ActiveAndroid的使用
    玩转Android之加速度传感器的使用,模仿微信摇一摇
    玩转Android之在线视频播放控件Vitamio的使用
    Android5.0之TextInputLayout、FloatingActionButton的使用
    Android自定义View之ProgressBar出场记
    Android5.0之Activity的转场动画
  • 原文地址:https://www.cnblogs.com/xuan52rock/p/6702835.html
Copyright © 2020-2023  润新知