- 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的插入和拔出是以系統廣播的形式發送的,只要我們註冊這個廣播即可。
123456789101112131415161718192021222324252627<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。1234<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的過濾,如下:
1234<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設備
12345678<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是否已經獲得權限,如果沒有就要主動申請權限:1234567891011<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>
註冊廣播
123<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>
接收器的代碼:
123456789101112131415161718<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線程中進行。
得到授權後,將做一些通信前的準備工作,如下:1234567891011121314151617181920212223242526272829303132333435363738<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與其他類型轉換,以及十六進制的問題。
具體問題具體分析吧。這篇文章磕磕絆絆,就先到這裏了
。