Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> Android實戰技巧之四十九:Usb通信之USB Host

Android實戰技巧之四十九:Usb通信之USB Host

編輯:關於android開發

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的插入和拔出是以系統廣播的形式發送的,只要我們注冊這個廣播即可。

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

<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的過濾,如下:

<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的初始化

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

五、列出Usb設備

        HashMap deviceHashMap = mUsbManager.getDeviceList();
        Iterator 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. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved