HarmonyOS APP开发中常用的USB通信小知识
【摘要】 HarmonyOS APP开发中常用的USB通信小知识当你需要连接打印机、扫描仪、摄像头、或者各种工业设备时,USB通信是最直接的方式。鸿蒙系统提供了完整的USB API,让应用可以像桌面软件一样访问USB设备。今天咱们就来聊聊鸿蒙系统中的USB通信开发,看看如何实现设备发现、连接和数据传输。 一、背景与动机 1.1 USB通信的应用场景USB(Universal Serial Bus)是...
HarmonyOS APP开发中常用的USB通信小知识
当你需要连接打印机、扫描仪、摄像头、或者各种工业设备时,USB通信是最直接的方式。鸿蒙系统提供了完整的USB API,让应用可以像桌面软件一样访问USB设备。今天咱们就来聊聊鸿蒙系统中的USB通信开发,看看如何实现设备发现、连接和数据传输。
一、背景与动机
1.1 USB通信的应用场景
USB(Universal Serial Bus)是最广泛使用的外设接口,在鸿蒙应用开发中有众多场景:
| 应用场景 | 设备类型 | 通信方式 |
|---|---|---|
| 打印输出 | 打印机 | USB打印类 |
| 图像采集 | 摄像头、扫描仪 | 大批量传输 |
| 数据存储 | U盘、移动硬盘 | 大容量存储类 |
| 调试烧录 | 开发板、调试器 | 串口通信 |
| 工业控制 | PLC、传感器 | 自定义协议 |
| 音频设备 | 声卡、MIDI | 音频类 |
1.2 USB通信架构
鸿蒙系统提供了完整的USB访问API:

1.3 USB设备结构
USB设备的层次结构是理解USB通信的基础:
// USB设备结构
interface UsbDevice {
// 设备ID
deviceId: number;
// 厂商ID
vendorId: number;
// 产品ID
productId: number;
// 设备类
deviceClass: number;
// 设备子类
deviceSubclass: number;
// 协议
protocol: number;
// 接口列表
interfaces: UsbInterface[];
// 厂商名称
manufacturer?: string;
// 产品名称
product?: string;
// 序列号
serial?: string;
}
// USB接口
interface UsbInterface {
// 接口ID
interfaceId: number;
// 接口类
interfaceClass: number;
// 接口子类
interfaceSubclass: number;
// 协议
protocol: number;
// 端点列表
endpoints: UsbEndpoint[];
}
// USB端点
interface UsbEndpoint {
// 端点地址
endpointAddress: number;
// 方向:IN(0x80) 或 OUT(0x00)
direction: number;
// 类型:控制(0)、批量(2)、中断(3)、等时(1)
type: number;
// 最大包大小
maxPacketSize: number;
}
二、核心原理
2.1 USB通信流程
完整的USB通信包含多个步骤:
flowchart TD
A[开始] --> B[监听USB设备]
B --> C[设备插入事件]
C --> D[获取设备列表]
D --> E[选择目标设备]
E --> F[请求设备权限]
F --> G{权限是否授予?}
G -->|否| H[等待用户授权]
H --> G
G -->|是| I[打开设备]
I --> J[选择接口]
J --> K[声明接口]
K --> L[获取端点]
L --> M[开始通信]
M --> N[数据传输]
N --> O{是否继续?}
O -->|是| N
O -->|否| P[释放接口]
P --> Q[关闭设备]
Q --> R[完成]
classDef start fill:#4CAF50,stroke:#388E3C,color:#fff
classDef process fill:#4A90E2,stroke:#2E5B8C,color:#fff
classDef decision fill:#F5A623,stroke:#C17A00,color:#fff
classDef end fill:#E74C3C,stroke:#C0392B,color:#fff
class A,R start
class B,C,D,E,F,I,J,K,L,M,N process
class G,H,O decision
class P,Q end
2.2 端点类型与用途
USB有四种端点类型,各有用途:
| 端点类型 | 数值 | 特点 | 典型用途 |
|---|---|---|---|
| 控制端点 | 0 | 可靠传输,用于配置 | 设备枚举、配置 |
| 批量端点 | 2 | 可靠传输,大数据量 | 打印机、存储 |
| 中断端点 | 3 | 可靠传输,小数据量 | 鼠标、键盘 |
| 等时端点 | 1 | 不可靠,固定速率 | 音频、视频 |
2.3 传输方向
// USB方向常量
const USB_DIRECTION = {
OUT: 0x00, // 主机到设备
IN: 0x80 // 设备到主机
};
// 判断端点方向
function isEndpointIn(endpoint: UsbEndpoint): boolean {
return (endpoint.endpointAddress & 0x80) === 0x80;
}
function isEndpointOut(endpoint: UsbEndpoint): boolean {
return (endpoint.endpointAddress & 0x80) === 0x00;
}
三、代码实战
3.1 USB管理核心类封装
import usb from '@ohos.usb';
import { BusinessError } from '@ohos.base';
/**
* USB设备信息(扩展)
*/
export interface UsbDeviceInfo extends usb.USBDevice {
// 是否已授权
isAuthorized: boolean;
// 设备描述
description: string;
}
/**
* USB管理器
* 封装USB设备发现、连接、通信等功能
*/
export class UsbManager {
private static instance: UsbManager;
// USB管理实例
private usbManager: usb.UsbManager | null = null;
// 当前连接的设备
private connectedDevice: usb.USBDevice | null = null;
// 当前打开的接口
private openedInterface: usb.USBInterface | null = null;
// 设备连接回调
private onDeviceAttached: ((device: UsbDeviceInfo) => void) | null = null;
private onDeviceDetached: ((deviceId: number) => void) | null = null;
private constructor() {
this.initUsbManager();
}
/**
* 获取单例实例
*/
public static getInstance(): UsbManager {
if (!UsbManager.instance) {
UsbManager.instance = new UsbManager();
}
return UsbManager.instance;
}
/**
* 初始化USB管理
*/
private initUsbManager(): void {
try {
this.usbManager = usb.getUsbManager();
// 监听设备插入
usb.on('deviceAttached', (device: usb.USBDevice) => {
console.info(`[USB] 设备插入: ${device.vendorId}:${device.productId}`);
if (this.onDeviceAttached) {
const deviceInfo: UsbDeviceInfo = {
...device,
isAuthorized: false,
description: this.getDeviceDescription(device)
};
this.onDeviceAttached(deviceInfo);
}
});
// 监听设备拔出
usb.on('deviceDetached', (deviceId: number) => {
console.info(`[USB] 设备拔出: ${deviceId}`);
if (this.onDeviceDetached) {
this.onDeviceDetached(deviceId);
}
});
console.info('[USB] 初始化成功');
} catch (error) {
console.error('[USB] 初始化失败:', error);
}
}
/**
* 获取设备描述
*/
private getDeviceDescription(device: usb.USBDevice): string {
const vendorId = device.vendorId.toString(16).padStart(4, '0');
const productId = device.productId.toString(16).padStart(4, '0');
return `VID:${vendorId} PID:${productId}`;
}
/**
* 获取设备列表
*/
public async getDeviceList(): Promise<UsbDeviceInfo[]> {
if (!this.usbManager) {
return [];
}
try {
const devices = await this.usbManager.getDevices();
return devices.map((device) => ({
...device,
isAuthorized: this.hasDevicePermission(device),
description: this.getDeviceDescription(device)
}));
} catch (error) {
console.error('[USB] 获取设备列表失败:', error);
return [];
}
}
/**
* 检查设备权限
*/
public hasDevicePermission(device: usb.USBDevice): boolean {
if (!this.usbManager) return false;
try {
return this.usbManager.hasRight(device.vendorId, device.productId);
} catch (error) {
return false;
}
}
/**
* 请求设备权限
*/
public async requestDevicePermission(device: usb.USBDevice): Promise<boolean> {
if (!this.usbManager) return false;
try {
const hasPermission = this.hasDevicePermission(device);
if (hasPermission) {
return true;
}
// 请求权限
await this.usbManager.requestRight(device.vendorId, device.productId);
console.info('[USB] 权限请求已发送');
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[USB] 请求权限失败: ${err.message}`);
return false;
}
}
/**
* 打开设备
*/
public async openDevice(device: usb.USBDevice): Promise<usb.USBDevicePipe | null> {
if (!this.usbManager) return null;
try {
// 检查权限
const hasPermission = this.hasDevicePermission(device);
if (!hasPermission) {
const requested = await this.requestDevicePermission(device);
if (!requested) {
console.error('[USB] 无设备权限');
return null;
}
}
// 打开设备
const pipe = await this.usbManager.openDevice(device);
this.connectedDevice = device;
console.info(`[USB] 设备已打开: ${device.vendorId}:${device.productId}`);
return pipe;
} catch (error) {
console.error('[USB] 打开设备失败:', error);
return null;
}
}
/**
* 关闭设备
*/
public async closeDevice(pipe: usb.USBDevicePipe): Promise<boolean> {
if (!this.usbManager) return false;
try {
await this.usbManager.closePipe(pipe);
this.connectedDevice = null;
this.openedInterface = null;
console.info('[USB] 设备已关闭');
return true;
} catch (error) {
console.error('[USB] 关闭设备失败:', error);
return false;
}
}
/**
* 选择接口
*/
public selectInterface(
device: usb.USBDevice,
interfaceId: number
): usb.USBInterface | null {
const iface = device.interfaces.find(i => i.interfaceId === interfaceId);
if (!iface) {
console.error(`[USB] 接口 ${interfaceId} 不存在`);
return null;
}
return iface;
}
/**
* 声明接口
*/
public async claimInterface(
pipe: usb.USBDevicePipe,
iface: usb.USBInterface
): Promise<boolean> {
if (!this.usbManager) return false;
try {
await this.usbManager.claimInterface(pipe, iface);
this.openedInterface = iface;
console.info(`[USB] 接口已声明: ${iface.interfaceId}`);
return true;
} catch (error) {
console.error('[USB] 声明接口失败:', error);
return false;
}
}
/**
* 释放接口
*/
public async releaseInterface(
pipe: usb.USBDevicePipe,
iface: usb.USBInterface
): Promise<boolean> {
if (!this.usbManager) return false;
try {
await this.usbManager.releaseInterface(pipe, iface);
this.openedInterface = null;
console.info('[USB] 接口已释放');
return true;
} catch (error) {
console.error('[USB] 释放接口失败:', error);
return false;
}
}
/**
* 获取端点
*/
public getEndpoint(
iface: usb.USBInterface,
direction: number,
type: number
): usb.USBEndpoint | null {
const endpoint = iface.endpoints.find(
e => (e.endpointAddress & 0x80) === direction && e.type === type
);
if (!endpoint) {
console.error('[USB] 未找到匹配的端点');
return null;
}
return endpoint;
}
/**
* 批量传输(IN)
*/
public async bulkTransferIn(
pipe: usb.USBDevicePipe,
endpoint: usb.USBEndpoint,
length: number,
timeout: number = 5000
): Promise<ArrayBuffer | null> {
if (!this.usbManager) return null;
try {
const data = await this.usbManager.bulkTransferRead(
pipe,
endpoint,
length,
timeout
);
return data;
} catch (error) {
console.error('[USB] 批量读取失败:', error);
return null;
}
}
/**
* 批量传输(OUT)
*/
public async bulkTransferOut(
pipe: usb.USBDevicePipe,
endpoint: usb.USBEndpoint,
data: ArrayBuffer,
timeout: number = 5000
): Promise<number> {
if (!this.usbManager) return -1;
try {
const transferred = await this.usbManager.bulkTransferWrite(
pipe,
endpoint,
data,
timeout
);
return transferred;
} catch (error) {
console.error('[USB] 批量写入失败:', error);
return -1;
}
}
/**
* 控制传输
*/
public async controlTransfer(
pipe: usb.USBDevicePipe,
setup: usb.USBControlParams,
data?: ArrayBuffer,
timeout: number = 5000
): Promise<number> {
if (!this.usbManager) return -1;
try {
const result = await this.usbManager.controlTransfer(
pipe,
setup,
data,
timeout
);
return result;
} catch (error) {
console.error('[USB] 控制传输失败:', error);
return -1;
}
}
/**
* 中断传输(IN)
*/
public async interruptTransferIn(
pipe: usb.USBDevicePipe,
endpoint: usb.USBEndpoint,
length: number,
timeout: number = 5000
): Promise<ArrayBuffer | null> {
if (!this.usbManager) return null;
try {
const data = await this.usbManager.interruptTransferRead(
pipe,
endpoint,
length,
timeout
);
return data;
} catch (error) {
console.error('[USB] 中断读取失败:', error);
return null;
}
}
/**
* 中断传输(OUT)
*/
public async interruptTransferOut(
pipe: usb.USBDevicePipe,
endpoint: usb.USBEndpoint,
data: ArrayBuffer,
timeout: number = 5000
): Promise<number> {
if (!this.usbManager) return -1;
try {
const transferred = await this.usbManager.interruptTransferWrite(
pipe,
endpoint,
data,
timeout
);
return transferred;
} catch (error) {
console.error('[USB] 中断写入失败:', error);
return -1;
}
}
/**
* 设置回调
*/
public setOnDeviceAttached(callback: (device: UsbDeviceInfo) => void): void {
this.onDeviceAttached = callback;
}
public setOnDeviceDetached(callback: (deviceId: number) => void): void {
this.onDeviceDetached = callback;
}
/**
* 移除监听
*/
public removeAllListeners(): void {
usb.off('deviceAttached');
usb.off('deviceDetached');
console.info('[USB] 已移除所有监听');
}
}
3.2 USB串口通信示例
import { UsbManager, UsbDeviceInfo } from './UsbManager';
import usb from '@ohos.usb';
/**
* USB串口通信
* 用于USB转串口设备(如CH340、CP2102等)
*/
export class UsbSerialPort {
private usbManager: UsbManager = UsbManager.getInstance();
// USB管道
private pipe: usb.USBDevicePipe | null = null;
// 接口
private iface: usb.USBInterface | null = null;
// 端点
private inEndpoint: usb.USBEndpoint | null = null;
private outEndpoint: usb.USBEndpoint | null = null;
// 接收回调
private onDataReceived: ((data: ArrayBuffer) => void) | null = null;
// 接收线程
private receiving: boolean = false;
/**
* 打开串口
*/
public async open(device: usb.USBDevice, baudRate: number = 115200): Promise<boolean> {
try {
// 打开设备
this.pipe = await this.usbManager.openDevice(device);
if (!this.pipe) return false;
// 选择接口(通常使用第一个接口)
this.iface = this.usbManager.selectInterface(device, 0);
if (!this.iface) return false;
// 声明接口
const claimed = await this.usbManager.claimInterface(this.pipe, this.iface);
if (!claimed) return false;
// 获取端点
this.inEndpoint = this.usbManager.getEndpoint(
this.iface,
0x80, // IN
0x02 // BULK
);
this.outEndpoint = this.usbManager.getEndpoint(
this.iface,
0x00, // OUT
0x02 // BULK
);
if (!this.inEndpoint || !this.outEndpoint) {
console.error('[串口] 未找到端点');
return false;
}
// 配置波特率(设备特定)
await this.setBaudRate(baudRate);
console.info(`[串口] 已打开,波特率: ${baudRate}`);
return true;
} catch (error) {
console.error('[串口] 打开失败:', error);
return false;
}
}
/**
* 关闭串口
*/
public async close(): Promise<void> {
this.receiving = false;
if (this.pipe && this.iface) {
await this.usbManager.releaseInterface(this.pipe, this.iface);
await this.usbManager.closeDevice(this.pipe);
}
this.pipe = null;
this.iface = null;
this.inEndpoint = null;
this.outEndpoint = null;
console.info('[串口] 已关闭');
}
/**
* 设置波特率
* 注意:不同芯片的实现不同
*/
private async setBaudRate(baudRate: number): Promise<boolean> {
if (!this.pipe) return false;
// CH340的波特率设置(示例)
const setup: usb.USBControlParams = {
requestType: 0x40,
recipient: 0x00,
direction: 0x00,
request: 0x03, // 设置波特率请求
value: 0,
index: 0
};
// 构建波特率数据
const data = new ArrayBuffer(4);
const view = new DataView(data);
view.setUint32(0, baudRate, true);
const result = await this.usbManager.controlTransfer(this.pipe, setup, data);
return result >= 0;
}
/**
* 发送数据
*/
public async write(data: ArrayBuffer): Promise<boolean> {
if (!this.pipe || !this.outEndpoint) {
console.error('[串口] 未连接');
return false;
}
const transferred = await this.usbManager.bulkTransferOut(
this.pipe,
this.outEndpoint,
data
);
return transferred >= 0;
}
/**
* 发送字符串
*/
public async writeString(text: string): Promise<boolean> {
const encoder = new TextEncoder();
const data = encoder.encode(text).buffer;
return await this.write(data);
}
/**
* 开始接收数据
*/
public async startReceiving(): Promise<void> {
if (!this.pipe || !this.inEndpoint) {
console.error('[串口] 未连接');
return;
}
this.receiving = true;
// 启动接收循环
while (this.receiving) {
try {
const data = await this.usbManager.bulkTransferIn(
this.pipe,
this.inEndpoint,
64, // 读取长度
1000 // 超时
);
if (data && data.byteLength > 0) {
if (this.onDataReceived) {
this.onDataReceived(data);
}
}
} catch (error) {
// 超时继续
if (!this.receiving) break;
}
}
}
/**
* 停止接收
*/
public stopReceiving(): void {
this.receiving = false;
}
/**
* 设置数据接收回调
*/
public setOnDataReceived(callback: (data: ArrayBuffer) => void): void {
this.onDataReceived = callback;
}
}
3.3 USB设备管理UI
import { UsbManager, UsbDeviceInfo } from './UsbManager';
import usb from '@ohos.usb';
@Entry
@Component
struct UsbDevicePage {
private usbManager: UsbManager = UsbManager.getInstance();
@State devices: UsbDeviceInfo[] = [];
@State selectedDevice: UsbDeviceInfo | null = null;
@State isConnected: boolean = false;
@State deviceInfo: string = '';
@State interfaceInfo: string = '';
aboutToAppear(): void {
this.refreshDevices();
this.registerCallbacks();
}
aboutToDisappear(): void {
this.usbManager.removeAllListeners();
}
/**
* 刷新设备列表
*/
async refreshDevices(): Promise<void> {
this.devices = await this.usbManager.getDeviceList();
}
/**
* 注册回调
*/
registerCallbacks(): void {
this.usbManager.setOnDeviceAttached((device: UsbDeviceInfo) => {
this.refreshDevices();
});
this.usbManager.setOnDeviceDetached((deviceId: number) => {
this.refreshDevices();
if (this.selectedDevice && this.selectedDevice.deviceId === deviceId) {
this.selectedDevice = null;
this.isConnected = false;
}
});
}
/**
* 连接设备
*/
async connectDevice(device: UsbDeviceInfo): Promise<void> {
// 请求权限
const hasPermission = await this.usbManager.requestDevicePermission(device);
if (!hasPermission) {
console.error('[USB] 无权限');
return;
}
this.selectedDevice = device;
this.isConnected = true;
// 显示设备信息
this.deviceInfo = `厂商ID: 0x${device.vendorId.toString(16)}\n` +
`产品ID: 0x${device.productId.toString(16)}\n` +
`设备类: ${device.deviceClass}\n` +
`接口数: ${device.interfaces.length}`;
// 显示接口信息
if (device.interfaces.length > 0) {
const iface = device.interfaces[0];
this.interfaceInfo = `接口ID: ${iface.interfaceId}\n` +
`接口类: ${iface.interfaceClass}\n` +
`端点数: ${iface.endpoints.length}`;
}
}
/**
* 断开设备
*/
disconnectDevice(): void {
this.selectedDevice = null;
this.isConnected = false;
this.deviceInfo = '';
this.interfaceInfo = '';
}
build() {
Column() {
// 标题栏
Row() {
Text('USB设备')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
Blank()
Button('刷新')
.fontSize(14)
.height(35)
.backgroundColor('#4A90E2')
.onClick(() => {
this.refreshDevices();
})
}
.width('100%')
.padding(20)
.backgroundColor('#1A1A2E')
// 设备列表
if (this.devices.length > 0) {
Column() {
Text(`发现 ${this.devices.length} 个设备`)
.fontSize(16)
.fontColor('#FFFFFF')
.width('100%')
.margin({ bottom: 15 })
List() {
ForEach(this.devices, (device: UsbDeviceInfo) => {
ListItem() {
Row() {
Column() {
Text(device.product || device.description)
.fontSize(16)
.fontColor('#FFFFFF')
Text(device.manufacturer || 'Unknown')
.fontSize(12)
.fontColor('#AAAAAA')
.margin({ top: 5 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
// 权限状态
if (device.isAuthorized) {
Text('已授权')
.fontSize(12)
.fontColor('#4CAF50')
.margin({ right: 10 })
} else {
Text('未授权')
.fontSize(12)
.fontColor('#FF5722')
.margin({ right: 10 })
}
Button(this.isConnected && this.selectedDevice?.deviceId === device.deviceId ? '已连接' : '连接')
.width(70)
.height(35)
.backgroundColor(
this.isConnected && this.selectedDevice?.deviceId === device.deviceId
? '#4CAF50' : '#4A90E2'
)
.enabled(!this.isConnected || this.selectedDevice?.deviceId !== device.deviceId)
.onClick(() => {
if (this.isConnected) {
this.disconnectDevice();
} else {
this.connectDevice(device);
}
})
}
.width('100%')
.padding(15)
.backgroundColor('#16213E')
.borderRadius(8)
.margin({ bottom: 10 })
}
}, (device: UsbDeviceInfo) => device.deviceId.toString())
}
.width('100%')
.height('40%')
}
.width('90%')
.padding(20)
.backgroundColor('#16213E')
.borderRadius(12)
.margin({ top: 20 })
} else {
Column() {
Text('未发现USB设备')
.fontSize(18)
.fontColor('#AAAAAA')
Text('请插入USB设备')
.fontSize(14)
.fontColor('#666666')
.margin({ top: 10 })
}
.margin({ top: 50 })
}
// 设备详情
if (this.isConnected && this.selectedDevice) {
Column() {
Text('设备详情')
.fontSize(16)
.fontColor('#FFFFFF')
.width('100%')
.margin({ bottom: 15 })
Text(this.deviceInfo)
.fontSize(14)
.fontColor('#AAAAAA')
.width('100%')
.margin({ bottom: 15 })
if (this.interfaceInfo) {
Text('接口信息')
.fontSize(14)
.fontColor('#FFFFFF')
.width('100%')
.margin({ bottom: 10 })
Text(this.interfaceInfo)
.fontSize(14)
.fontColor('#AAAAAA')
.width('100%')
}
Button('断开连接')
.width('100%')
.height(40)
.backgroundColor('#FF5722')
.margin({ top: 20 })
.onClick(() => {
this.disconnectDevice();
})
}
.width('90%')
.padding(20)
.backgroundColor('#16213E')
.borderRadius(12)
.margin({ top: 20 })
}
}
.width('100%')
.height('100%')
.backgroundColor('#0F0F1A')
}
}
3.4 USB打印机示例
import { UsbManager } from './UsbManager';
import usb from '@ohos.usb';
/**
* USB打印机控制器
*/
export class UsbPrinter {
private usbManager: UsbManager = UsbManager.getInstance();
private pipe: usb.USBDevicePipe | null = null;
private outEndpoint: usb.USBEndpoint | null = null;
/**
* 连接打印机
*/
public async connect(device: usb.USBDevice): Promise<boolean> {
try {
// 打开设备
this.pipe = await this.usbManager.openDevice(device);
if (!this.pipe) return false;
// 查找打印接口
let printInterface: usb.USBInterface | null = null;
for (const iface of device.interfaces) {
// 打印机类为7
if (iface.interfaceClass === 0x07) {
printInterface = iface;
break;
}
}
if (!printInterface) {
console.error('[打印机] 未找到打印接口');
return false;
}
// 声明接口
await this.usbManager.claimInterface(this.pipe, printInterface);
// 获取输出端点
this.outEndpoint = this.usbManager.getEndpoint(
printInterface,
0x00, // OUT
0x02 // BULK
);
if (!this.outEndpoint) {
console.error('[打印机] 未找到输出端点');
return false;
}
console.info('[打印机] 已连接');
return true;
} catch (error) {
console.error('[打印机] 连接失败:', error);
return false;
}
}
/**
* 断开连接
*/
public async disconnect(): Promise<void> {
if (this.pipe) {
await this.usbManager.closeDevice(this.pipe);
}
this.pipe = null;
this.outEndpoint = null;
}
/**
* 发送打印数据
*/
public async print(data: ArrayBuffer): Promise<boolean> {
if (!this.pipe || !this.outEndpoint) {
console.error('[打印机] 未连接');
return false;
}
const transferred = await this.usbManager.bulkTransferOut(
this.pipe,
this.outEndpoint,
data
);
return transferred >= 0;
}
/**
* 打印文本
*/
public async printText(text: string): Promise<boolean> {
// ESC/POS指令
const commands: number[] = [];
// 初始化
commands.push(0x1B, 0x40);
// 文本内容
const encoder = new TextEncoder();
const textBytes = encoder.encode(text);
commands.push(...textBytes);
// 换行
commands.push(0x0A);
// 切纸
commands.push(0x1D, 0x56, 0x00);
const data = new Uint8Array(commands).buffer;
return await this.print(data);
}
}
四、踩坑与注意事项
4.1 权限配置
// module.json5
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.USE_USB",
"reason": "使用USB设备"
},
{
"name": "ohos.permission.MANAGE_USB",
"reason": "管理USB设备"
}
]
}
}
4.2 设备权限请求
坑点:USB设备需要用户显式授权。
/**
* 处理权限请求
*/
async function handlePermission(device: usb.USBDevice): Promise<boolean> {
const hasPermission = usbManager.hasRight(device.vendorId, device.productId);
if (hasPermission) {
return true;
}
// 显示提示
console.info('[USB] 请在弹窗中授权USB设备访问');
// 请求权限(会弹出系统对话框)
await usbManager.requestRight(device.vendorId, device.productId);
// 再次检查
return usbManager.hasRight(device.vendorId, device.productId);
}
4.3 端点选择
坑点:需要正确选择IN/OUT端点。
/**
* 查找批量端点
*/
function findBulkEndpoints(iface: usb.USBInterface): {
in: usb.USBEndpoint | null;
out: usb.USBEndpoint | null;
} {
let inEndpoint: usb.USBEndpoint | null = null;
let outEndpoint: usb.USBEndpoint | null = null;
for (const endpoint of iface.endpoints) {
// 批量端点
if (endpoint.type === 0x02) {
if ((endpoint.endpointAddress & 0x80) === 0x80) {
inEndpoint = endpoint;
} else {
outEndpoint = endpoint;
}
}
}
return { in: inEndpoint, out: outEndpoint };
}
4.4 大数据传输
坑点:大数据需要分包传输。
/**
* 分包发送大数据
*/
async function sendInChunks(
pipe: usb.USBDevicePipe,
endpoint: usb.USBEndpoint,
data: ArrayBuffer,
chunkSize: number = 4096
): Promise<boolean> {
const totalLength = data.byteLength;
const chunkCount = Math.ceil(totalLength / chunkSize);
for (let i = 0; i < chunkCount; i++) {
const start = i * chunkSize;
const end = Math.min(start + chunkSize, totalLength);
const chunk = data.slice(start, end);
const transferred = await usbManager.bulkTransferWrite(pipe, endpoint, chunk);
if (transferred < 0) {
console.error(`[USB] 发送第 ${i + 1} 包失败`);
return false;
}
// 等待设备处理
await delay(10);
}
console.info(`[USB] 发送完成,共 ${chunkCount} 包`);
return true;
}
function delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
4.5 设备热插拔处理
坑点:设备拔出时需要清理资源。
/**
* 设备拔出处理
*/
usb.on('deviceDetached', async (deviceId: number) => {
console.info(`[USB] 设备拔出: ${deviceId}`);
// 检查是否为当前连接的设备
if (currentDevice && currentDevice.deviceId === deviceId) {
// 清理资源
if (currentPipe) {
try {
await usbManager.closePipe(currentPipe);
} catch (error) {
// 设备已拔出,忽略错误
}
}
currentDevice = null;
currentPipe = null;
// 通知UI
console.info('[USB] 连接已断开');
}
});
五、HarmonyOS 6适配
5.1 API变更
| API | HarmonyOS 5 | HarmonyOS 6 | 说明 |
|---|---|---|---|
| getDevices() | 同步返回 | 异步Promise | 改为异步 |
| bulkTransferRead() | 基础功能 | 支持零拷贝 | 性能优化 |
| 新增 | - | getDeviceSpeed() | 获取USB速度 |
适配代码:
/**
* HarmonyOS 6 USB适配
*/
async function getDevicesAdaptive(): Promise<usb.USBDevice[]> {
const apiVersion = getApiVersion();
try {
if (apiVersion >= 6) {
// HarmonyOS 6
return await usbManager.getDevices();
} else {
// HarmonyOS 5
return usbManager.getDevices();
}
} catch (error) {
console.error('[USB] 获取设备失败:', error);
return [];
}
}
5.2 新增功能
HarmonyOS 6增强了USB能力:
// 1. 获取USB速度
const speed = await usbManager.getDeviceSpeed(device);
console.info(`USB速度: ${speed} Mbps`);
// 2. USB描述符解析
const descriptors = await usbManager.getDeviceDescriptors(device);
console.info(`厂商: ${descriptors.manufacturer}`);
console.info(`产品: ${descriptors.product}`);
// 3. 等时传输
const isoData = await usbManager.isochronousTransferRead(
pipe,
endpoint,
packetSize,
packetCount
);
// 4. 异步通知
usbManager.on('transferComplete', (result: usb.TransferResult) => {
console.info(`传输完成: ${result.bytesTransferred} 字节`);
});
5.3 性能优化
// 1. 零拷贝传输
const buffer = await usbManager.allocateBuffer(65536);
await usbManager.bulkTransferReadZeroCopy(pipe, endpoint, buffer);
// 2. 批量传输优化
await usbManager.setTransferBatchSize(16384);
// 3. 异步传输
const transferId = await usbManager.asyncBulkTransferWrite(
pipe,
endpoint,
data,
(result) => {
console.info(`异步传输完成: ${result.bytesTransferred}`);
}
);
// 取消传输
await usbManager.cancelTransfer(transferId);
六、总结
USB通信为鸿蒙应用提供了与外设交互的能力,从打印机到工业设备,应用场景广泛。本文全面讲解了鸿蒙系统中USB开发的核心要点:
核心要点回顾:
- 设备发现:监听设备插拔,获取设备列表
- 权限管理:请求用户授权,检查权限状态
- 接口操作:选择接口、声明接口、释放接口
- 端点通信:批量传输、控制传输、中断传输
- 数据处理:分包传输、接收循环、错误处理
USB通信看似复杂,但掌握了设备结构和传输流程后,实现起来其实有章可循。希望本文能帮助你掌握USB开发的核心技能,让你的应用具备连接外设的能力!
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)