鸿蒙之usb通信

198 阅读5分钟

一,从最常见的名词说起

1.1 什么是USB

这个我就不多解释了,直译就是通用串行总线。再不明白的就百度。

1.2 PIPE

usb通信的最基本形式是通过USB设备里的endpoint,而主机和endpoit之间的数据传输就是通过pipe

1.3 endpoint

端点是有方向的,主机到从机成为out端点,从机到主机成为in端点。从这个说明看出端点是单方向的。(除了0端点)

1.4 管道通信方式

pipe中的数据通信方式有两种,一种是stream一种是message。message要求进出进出方向必须要求同一个管道,默认就使用ep0作为message管道

1.5 传输方式

USB endpiont有四种类型,分别对应了不同的数据传输方式,分别为control transfers控制传输、interrupt transfers中断传输、Bluk Data transfers批量传输、Isochronous Data Tranfers等时传输控制传输通常用于配置设备,获取设备信息,发送命令到设备

1.6 逻辑设备

逻辑设备就是一系列端点的组合,逻辑设备与主机之间的通信发生在一个主机的缓冲区和设备的一个端点之间。

1.7 interface

一个逻辑设备可能包含若干个接口,每个接口包含1个或多个端点。每个接口表示一种功能。一个接口对应一个驱动序。例如usb扬声器就包含一个键盘接口和一个音频流接口

1.8 设备类型定义

在设备描述符中使用bDeviceClass,bDeviceSubClass,bDeviceProtocol字段来标识。
在接口描述符中使用 bInterfaceClass, bInterfaceSubClass,bInterfaceProtocol字段来标识。 例如:HID设备类型的在接口描述符上,bInterfaceClass = 0x03,bInterfaceSubClass和bInterfaceProtocol字段随意,关于USB类定义,更加详尽的描述见:www.usb.org/defined-cla…

二、描述符

谈到USB描述符,不得不说USB最强悍的地方就在这里。一个口可以插很多的设备,可以插U盘、上网卡、音频设备、还有的是复合设备,一个设备插上后可以虚拟出多个设备出来,主设备之所以能区分不同的从设备,靠的就是描述符,而一个设备有各种不同的描述符有:USB规范定义了不同的描述符,如设备描述符为1,配置描述符为2,字符串描述符为3,接口描述符为4,端点描述符为5等等,

在鸿蒙USB服务的api中将各种描述符串联在一起,一个设备只有一个设备描述符,设备描述符有多个配置描述符,但是一个设备同一时刻只能有一种配置生效,配置描述符有多个接口描述符,接口描述符有多个端点描述符

三、数据传输

3.1 获取设备列表

// 导入USB接口api包。
import { usbManager } from '@kit.BasicServicesKit';
// 获取设备列表。
let deviceList : Array<usbManager.USBDevice> = usbManager.getDevices();
deviceList /*结构示例*/
[
  {
    name: "1-1",
    serial: "",
    manufacturerName: "",
    productName: "",
    version: "",
    vendorId: 7531,
    productId: 2,
    clazz: 9,
    subClass: 0,
    protocol: 1,
    devAddress: 1,
    busNum: 1,
    configs: [
      {
        id: 1,
        attributes: 224,
        isRemoteWakeup: true,
        isSelfPowered: true,
        maxPower: 0,
        name: "1-1",
        interfaces: [
          {
            id: 0,
            protocol: 0,
            clazz: 9,
            subClass: 0,
            alternateSetting: 0,
            name: "1-1",
            endpoints: [
              {
                address: 129,
                attributes: 3,
                interval: 12,
                maxPacketSize: 4,
                direction: 128,
                number: 1,
                type: 3,
                interfaceId: 0,
              }
            ]
          }
        ]
      }
    ]
  }
]

3.2 获取设备操作权限

import { usbManager } from '@kit.BasicServicesKit';
import { BusinessError } from '@kit.BasicServicesKit';

let deviceName : string = deviceList[0].name;
// 申请操作指定的device的操作权限。
usbManager.requestRight(deviceName).then((hasRight : boolean) => {
  console.info("usb device request right result: " + hasRight);
}).catch((error : BusinessError)=> {
  console.info("usb device request right failed : " + error);
});

3.3 打开Device设备

// 打开设备,获取数据传输通道。
let pipe : usbManager.USBDevicePipe = usbManager.connectDevice(deviceList[0]);
let interface1 : usbManager.USBInterface = deviceList[0].configs[0].interfaces[0];
/*
 打开对应接口,在设备信息(deviceList)中选取对应的interface。
interface1为设备配置中的一个接口。
*/
usbManager.claimInterface(pipe, interface1, true);

3.4 数据传输。当前仅支持批量传输和控制传输。

在数据传输也就是通信的时候控制传输选取的接口是通过设备类型定义的接口来通信,且上面打开Device设备中为设备配置一个接口的接口就是我们找的接口,构造控制传输参数看链接:USB 标准请求 - USB中文网

控制传输

import { usbManager } from '@kit.BasicServicesKit';
import { BusinessError } from '@kit.BasicServicesKit';

/*
  构造控制传输参数,这里是按照USB中文网中来写
*/
let param: usbManager.USBDeviceRequestParams = {
  bmRequestType: 0x80,    //0x80指一次由设备到主机的标准请求命令
  bRequest: 0x06,    //0x06指获取描述符
  wValue:0x01 << 8 | 0,    //该值为2个字节,高字节指描述符类型,此处0x01指设备描述符;低字节指描述符索引,设备描述符不涉及,填0
  wIndex: 0,    //索引值,可填0
  wLength: 18,    //描述符的长度,此处18表示设备描述符长度,最大支持1024
  data: new Uint8Array(18)
};

/*
写入数据 一般来说通过公司自己的通信协议写入在 param中的data中
*/
usbManager.usbControlTransfer(pipe, param).then((ret: number) => {
console.info("usbControlTransfer = ${ret}");//查看是否写入成功
})

/*
读取数据 一般来说读取的数据是在 param中的data中 bulkTransfer是读取的端口
*/
let DataUint8Array = new Uint8Array(1024)
 usbManager.bulkTransfer(pipe, inEndpoint, DataUint8Array, 15000).then((dataLength : number) =>{
   if (dataLength >= 0) { 
 console.info("usb writeData result write length : " + dataLength);//查看是否写入成功 console.info("usb readData :" +DataUint8Array) //读取的数据 
   } else { 
 console.info("writeData failed"); 
   } 
 }).catch((error : BusinessError) => { 
 console.info("usb writeData error : " + JSON.stringify(error)); });
})

批量传输

import { usbManager } from '@kit.BasicServicesKit';
import { BusinessError } from '@kit.BasicServicesKit';
/*
  读取数据,在device信息中选取对应数据接收的endpoint来做数据传输
(endpoint.direction == 0x80);dataUint8Array是要读取的数据,类型为Uint8Array。
*/
let inEndpoint : usbManager.USBEndpoint = interface1.endpoints[2];
let outEndpoint : usbManager.USBEndpoint = interface1.endpoints[1];
let dataUint8Array : Uint8Array = new Uint8Array(1024);
usbManager.bulkTransfer(pipe, inEndpoint, dataUint8Array, 15000).then((dataLength : number) => {
if (dataLength >= 0) {
  console.info("usb readData result Length : " + dataLength);
} else {
  console.info("usb readData failed : " + dataLength);
}
}).catch((error : BusinessError) => {
console.info("usb readData error : " + JSON.stringify(error));
});
/*
发送数据,在device信息中选取对应数据发送的endpoint来做数据传输。(endpoint.direction == 0)
按照公司自己的通信协议写入数据在dataUint8Array中
*/ 
usbManager.bulkTransfer(pipe, outEndpoint, dataUint8Array, 15000).then((dataLength : number) => {
  if (dataLength >= 0) {
    console.info("usb writeData result write length : " + dataLength);
  } else {
    console.info("writeData failed");
  }
}).catch((error : BusinessError) => {
  console.info("usb writeData error : " + JSON.stringify(error));
});

// 读取数据 
let DataUint8Array = new Uint8Array(1024)
usbManager.bulkTransfer(pipe, inEndpoint, DataUint8Array, 15000).then((dataLength : number) => {
  if (dataLength >= 0) {
    console.info("usb writeData result write length : " + dataLength);//查看是否写入成功
     /*
     读取的数据
     */
    console.info("usb readData :" +DataUint8Array)
  } else {
    console.info("writeData failed");
  }
}).catch((error : BusinessError) => {
  console.info("usb writeData error : " + JSON.stringify(error));
});