HarmonyOS APP开发中常用的USB通信小知识

举报
Jack20 发表于 2026/06/19 20:22:55 2026/06/19
【摘要】 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:
图片.png

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开发的核心要点:

核心要点回顾

  1. 设备发现:监听设备插拔,获取设备列表
  2. 权限管理:请求用户授权,检查权限状态
  3. 接口操作:选择接口、声明接口、释放接口
  4. 端点通信:批量传输、控制传输、中断传输
  5. 数据处理:分包传输、接收循环、错误处理

USB通信看似复杂,但掌握了设备结构和传输流程后,实现起来其实有章可循。希望本文能帮助你掌握USB开发的核心技能,让你的应用具备连接外设的能力!

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。