好久沒有寫文章了,年前公司新開了一個專案,是和usb轉串列埠通訊相關的,需求是用安卓平板通過usb轉接後與好幾個外設進行通訊,一直忙到最近,才慢慢閒下來,趁著這個週末不忙,記錄下usb轉串列埠通訊開發的基本流程。
我們開發使用的是usb主機模式,即:安卓平板作為主機,usb外設作為從機進行資料通訊。整個開發流程可以總結為以下幾點:
1.發現裝置
UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
Map<String, UsbDevice> usbList = usbManager.getDeviceList();複製程式碼
通過UsbManager這個系統提供的類,我們可以列舉出當前連線的所有usb裝置,我們主要需要的是UsbDevice物件,關於UsbDevice這個類,官方是這樣註釋的:
This class represents a USB device attached to the android device with the android device
acting as the USB host.複製程式碼
是的,這個類就代表了android所連線的usb裝置。
2.開啟裝置
接下來,我們需要開啟剛剛搜尋到的usb裝置,我們可以將平板與usb外設之間的連線想象成一個通道,只有把通道的門開啟後,兩邊才能進行通訊。
一般來說,在沒有定製的android裝置上首次訪問usb裝置的時候,預設我們是沒有訪問許可權的,因此我們首先要判斷對當前要開啟的usbDevice是否有訪問許可權:
if (!usbManager.hasPermission(usbDevice)) {
usbPermissionReceiver = new UsbPermissionReceiver();
//申請許可權
Intent intent = new Intent(ACTION_DEVICE_PERMISSION);
PendingIntent mPermissionIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
IntentFilter permissionFilter = new IntentFilter(ACTION_DEVICE_PERMISSION);
context.registerReceiver(usbPermissionReceiver, permissionFilter);
usbManager.requestPermission(usbDevice, mPermissionIntent);
}複製程式碼
這裡我們宣告一個廣播UsbPermissionReceiver,當接受到授權成功的廣播後做一些其他處理:
private class UsbPermissionReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_DEVICE_PERMISSION.equals(action)) {
synchronized (this) {
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (device.getDeviceName().equals(usbDevice.getDeviceName()) {
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
//授權成功,在這裡進行開啟裝置操作
} else {
//授權失敗
}
}
}
}
}
}複製程式碼
接下來,我們要找到具有資料傳輸功能的介面UsbInterface,從它裡邊兒找到資料輸入和輸出埠UsbEndpoint,一般情況下,一個usbDevice有多個UsbInterface,我們需要的一般是第一個,所以:
usbInterface=usbDevice.getInterface(0);複製程式碼
同樣的,一個usbInterface有多個UsbEndpoint,有控制埠和資料埠等,因此我們需要根據型別和資料流向來找到我們需要的資料輸入和輸出兩個埠:
for (int index = 0; index < usbInterface.getEndpointCount(); index++) {
UsbEndpoint point = usbInterface.getEndpoint(index);
if (point.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (point.getDirection() == UsbConstants.USB_DIR_IN) {
usbEndpointIn = point;
} else if (point.getDirection() == UsbConstants.USB_DIR_OUT) {
usbEndpointOut = point;
}
}
}複製程式碼
最後,才是真正的開啟usb裝置,我們需要和usb外設建立一個UsbDeviceConnection,它的註釋很簡介的說明了它的用途:
This class is used for sending and receiving data and control messages to a USB device.複製程式碼
它的獲取也很簡單,就一句程式碼:
usbDeviceConnection = usbManager.openDevice(usbDevice);複製程式碼
到這裡,理論上平板和usb外設之間的連線已經建立了,也可以首發資料了,但是,我們大部分情況下還需要對usb串列埠進行一些配置,比如波特率,停止位,資料控制等,不然兩邊配置不同,收到的資料會亂碼。具體怎麼配置,就看你使用的串列埠晶片是什麼了,目前流行的有pl2303,ch340等,由於篇幅問題,需要具體配置串列埠程式碼的朋友私信我我發給你。
3.資料傳輸
到這裡,我們已經可以與usb外設進行資料傳輸了,首先來看怎麼向usb裝置傳送資料。
1.向usb外設傳送資料複製程式碼
在第二步中,我們已經獲取了資料的輸出埠usbEndpointIn,我們向外設傳送資料就是通過這個埠來實現的。來看怎麼用:
int ret = usbDeviceConnection.bulkTransfer(usbEndpointOut, data, data.length, DEFAULT_TIMEOUT);複製程式碼
bulkTransfer這個函式用於在給定的埠進行資料傳輸,第一個引數就是此次傳輸的埠,這裡我們用的輸出埠,第二個引數是要傳送的資料,型別為位元組陣列,第三個引數代表要傳送的資料長度,最後一個引數是超時,返回值代表傳送成功的位元組數,如果返回-1,那就是傳送失敗了。
2.接受usb外設傳送來的資料複製程式碼
同理,我們已經找到了資料輸入埠usbEndpointIn,因為資料的輸入是不定時的,因此我們可以另開一個執行緒,來專門接受資料,接受資料的程式碼如下:
int inMax = inEndpoint.getMaxPacketSize();
ByteBuffer byteBuffer = ByteBuffer.allocate(inMax);
UsbRequest usbRequest = new UsbRequest();
usbRequest.initialize(connection, inEndpoint);
usbRequest.queue(byteBuffer, inMax);
if(connection.requestWait() == usbRequest){
byte[] retData = byteBuffer.array();
for(Byte byte1 : retData){
System.err.println(byte1);
}
}複製程式碼
以上,就是usb轉串列埠通訊的基本流程,有些地方寫的不是很全面,比如接收usb外設資料的方法應該還有別的,不足之處歡迎指正。