Android USB HID整理

本文详细介绍了Android系统中与USB HID设备的交互过程,包括数据传输、HID报告描述符解析以及Android API的使用。重点讲解了Input和Output数据的概念,控制和中断管道的工作方式,以及如何通过控制传输和中断传输读写HID报告。同时,提到了Android驱动调试工具对于解析HID报告描述符的支持,以及在APP中解析和组装Report数据的方法。对于AndroidAccessory模式,文章指出其适用于特定设备的适配,并给出了相关参考资料。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

USB协议支持外设热插拔,这些外设分为许多不同类型,每一种设备类都具有相同的动作和类似的功能。比如显示器,鼠标,话筒等等。
HID(Human Interface Device)类提供了人机接口的界面,许多典型的HID类设备具有LED,音频反馈等,以此展现设备信息给用户。
HID设备定义的最基本目的:

  • 尽可能节省设备的数据空间
  • 允许操作系统忽略未知的信息
  • 使数据定义可扩展
  • 支持嵌套和集合
  • 拥有自身的相关信息,使之适用于一般的软件

详细资料可直接查阅 www.usb.org/hid
对开发来说最主要的定义在HID Usage Tables 1.22

数据传输

从Host到Device的数据称为Output数据,从Device到Host的数据,称为Input数据。
USB HID 拓扑图
通常HID设备会有一条控制管道和一条输入中断管道组成。Input中断管道用于设备端发起的HID事件通知并传输到Host端。而控制管道是双向的,既可以由Host发起用于获取设备的数据,也可以用于发送Host端发起的HID事件通知并传输到设备端。通常一些HID设备还会单独支持一条输出中断管道,用于Host端发送较大的数据内容给设备端。

在这里插入图片描述

Android 传输数据API
  1. Input Endpoint接收Input Data:
val bytesRead = mConnection!!.bulkTransfer(mInUsbEndpoint, buffer, size, timeout)
  1. Control Endpoint 获取描述符或者Report以及发送Host端HID报告
  /*获取描述符*/
  enum class CommonRequest(val value: Int){
      //获取描述符
      GET_DESCRIPTOR (0x06),
  }
  
  enum class HidValue(val value: Int){
    /*高字节用于描述符*/
      HID_DESCRIPTOR((0x21 shl 8) and 0xFFFF),
      HID_REPORT_DESCRIPTOR( (0x22 shl 8) and 0xFFFF),
      PHYSICS_DESCRIPTOR((0x23 shl 8) and 0xFFFF),
      //!!:reserved 其他高字节保留,可自定义用
      /*低字节为非0值时被用来选定实体描述符,即需要或上LOW_BYTE_REPORT_ID*/
      INPUT(0x01 shl 8 and 0xffff),
      OUTPUT(0x02 shl 8 and  0xffff),
      FEATURE(0x03 shl 8 and 0xffff),
      
      PROTOCOL_BOOT(0x00),
      PROTOCOL_REPORT(0x01)
      //!!:reserved 其他低字节保留,可自定义用
  }
  //!!常用: 获取HID报告描述符:
  Int value = HidValue.HID_REPORT_DESCRIPTOR.value
 
  val ret = mConnection !!. controlTransfer (
                RequestType.GET_DESCRIPTOR_FROM_DEVICE.value, CommonRequest.GET_DESCRIPTOR.value, value ,
                0x00 , outputBuffer, outputBuffer.size, timeout)
                
  //!!常用: 用于host主动获取设备Input报告:
  Int value = HidValue.INPUT.value or reportId!!.toInt()
  val ret = mConnection!!.controlTransfer(
   RequestType.HID_INTERFACE_FROM_DEVICE.value, HidRequest.GET_REPORT.value, hidValue,
   mUsbInterface!!.id , outputBuffer, outputBuffer.size, timeout)

  //!!常用: 发送Host端触发的Output报告
  Int value = HidValue.OUTPUT.value or reportId!!.toInt()
  val ret = mConnection!!.controlTransfer(
    RequestType.HID_INTERFACE_FROM_HOST.value, HidRequest.SET_REPORT.value,value,
    mUsbInterface!!.id , data, data.size, timeout)
  1. 如果存在Ouput Endpoint接口发送大数据
ret = mConnection!!.bulkTransfer(mOutUsbEndpoint, data, data.size, timeout)
HID报告描述符

HID报告分为Input,OutPut ,Feature三种主要报告。我们一般只用到Input和Output两种报告,Input是设备端触发的报告,可以由设备端主动通知host,也可以由host端主动获取该报告。Output是host端触发的报告,由host直接发给设备端。

HID报告描述符就是用于定义HID报告格式,通过解析HID报告描述符我们便能知道设备端支持的Input/Output/Feature报告的格式。之后设备端和host端都按此约定来组装和解析HID报告。整个通信就能顺利完成。

因为大多数设备支持的功能是相同的,因此USB HID协议便约定了通用类的页码(UsagePage)和通用类编号(Usage). 解析HID报告描述符就是要解析出我们感兴趣的类所在的Report的Id, 以及该类在该Report的哪几位(offset), 这个类的数据类型是怎样的.

注意:有时候无法从控制端口获取到设备的HID报告描述符,可能是设备端Bug也可能是设备端做了限制,枚举完之后不再允许获取描述符,有的时候是设备处理较久,需要加大等待时间调试。

Android 解析HID报告描述符
  1. 解析Item:
    报告描述符由一条条的Item组成.解析报告描述符的第一步就是要解析出这些Item.每一个Item都包含一个字节的前缀,这个前缀中包含了三个信息–item tag,、item type、item size。Item有两种基本类型:short items and long item。Item的数据部分的长度取决于Item的基本类型。

在这里插入图片描述
在这里插入图片描述
2. 解析出Item后需要跟进Item 的类型判断是Local, Global ,Main. 将Item转换成感兴趣的Usage 与ReportFeild的对应关系表.
相关代码参考UsbHidProxy中的HidReportDescriptorParser,此处仅列出最关键的ItemTag定义以及与Local, Global ,Main的关系. Local, Global ,Main转换成ReportFeild原理可参考USB HID Report Descriptor 报告描述符详解

  enum class ItemTag(val value: Byte, val type: ItemType){
        //二进制B1111
        LONG(0xF,ItemType.UNKNOWN),
        /*Hid other tag should be short*/
        //B1000
        INPUT(0x8,ItemType.MAIN),
        //B1001
        OUTPUT(0x9,ItemType.MAIN),
        //B1011
        FEATURE(0xB,ItemType.MAIN),
        //B1010
        COLLECTION(0xa,ItemType.MAIN),
        //B1100
        END_COLLECTION(0xc,ItemType.MAIN),
        //B0000
        USAGE_PAGE(0x0,ItemType.GLOBAL),
        //B0001
        LOGICAL_MIN(0x1,ItemType.GLOBAL),
        //B0010
        LOGICAL_MAX(0x2,ItemType.GLOBAL),
        //B0101
        UNIT_EXPONENT(0x5,ItemType.GLOBAL),
        //B0110
        UNIT(0x6,ItemType.GLOBAL),
        //B0111
        REPORT_SIZE(0x7,ItemType.GLOBAL),
        //B1000
        REPORT_ID(0x8,ItemType.GLOBAL),
        //B1001
        REPORT_COUNT(0x9,ItemType.GLOBAL),
        //B1010
        PUSH(0xa,ItemType.GLOBAL),
        //1011
        POP(0xb,ItemType.GLOBAL),
        //B0000
        USAGE(0x0,ItemType.LOCAL),
        //B0001
        USAGE_MIN(0x1,ItemType.LOCAL),
        //B0010
        USAGE_MAX(0x2,ItemType.LOCAL),
        //B0011
        DESIGNATOR_INDEX(0x3,ItemType.LOCAL),
        //B0100
        DESIGNATOR_MIN(0x4,ItemType.LOCAL),
        //B0101
        DESIGNATOR_MAX(0x5,ItemType.LOCAL),
        //B0111
        STRING_INDEX(0x7,ItemType.LOCAL),
        //B1000
        STRING_MIN(0x8,ItemType.LOCAL),
        //B1001
        STRING_MAX(0x9,ItemType.LOCAL),
        //1010
        DELIMITER(0xa,ItemType.LOCAL),
        //B1011~B1111
        RESERVED(0xf,ItemType.LOCAL);

    }

    enum class ItemType(val value: Byte){
        MAIN(0x0),
        GLOBAL(0x1),
        //B10
        LOCAL(0x2),
        UNKNOWN(0xf);
        fun isThis(itemPreValue : Byte) : Boolean{
            val temp =  itemPreValue.toInt() and 0xf shr 2

            if(this.value.compareTo(temp) == 0){
                return true
            }
            return false
        }
        companion object{
            fun find(itemPreValue : Byte) : ItemType? {
                for (temp in ItemType.values()){
                    if(temp.isThis(itemPreValue)){
                        return temp
                    }
                }
                return null
            }
        }
    }

    enum class ItemSize(val value: Byte, val realSizeValue : Int){
        ZERO(0x0,0),
        ONE(0x1,1),
        TWO(0x2,2),
        FOUR(0x3,4);
    }
  1. Android驱动调试工具中已经支持了HID报告描述符的解析. 因此在APP中进行HID报告描述符一般是该APP安装在host端且需求要支持多种类似功能设备做的. 如果单纯只是适配一种固定的USB 设备,只需要直接根据驱动调试工具解析出来的report格式来进行组装和解析report数据即可.
 #可使用以下命令,插入usb设备后即可打印出解析后的HID报告描述符:
 getevent -d
Android Accessory模式

Android 提供了Accessory模式一套完整的APIs,但这部分接口需要驱动按照官方指导文档修改定制部分代码才能支持.同时也对host端APP提出了定制要求. 因此一般适用于host单独适配某一类固定HID device且该HID device功能比较单一的情况(需要驱动协调处理多模块功能的情况,该设计也不适用)下应用. 具体可参考USB HID Report Descriptor 报告描述符详解

参考

总体参考:
www.usb.org/hid
HID设备类定义中文版

调用接口参数说明相关:
关于Android设备USBHID通信控制传输相关接口说明
Android(安卓)系统USB-OTG-HID外设通讯开发
Android usb 控制传输,关于Android设备USBHID通信控制传输相关接口说明
USB设备请求命令详解

理解解析与组装Report参考:
USB描述符解析–>枚举.
USB HID Report Descriptor 报告描述符详解

描述符具体类别值定义参考:
从零开始学USB(二十一、USB接口HID类设备(三)_报表描述符Global类)
【USB笔记】 标准设备请求Standard Device Requests

Android Accessory模式参考
Android下USB Accessory的实现分析
ANDROID: 关于USB ACCESSORY & HID

其他实践参考
Android USB Host 使用详解(U盘)(二)

Android USB Host 使用详解(U盘)(三)

AndroidUSB实现原理参考:
Android的USB系统简单分析之一

<think>好的,我现在要处理用户关于基于安卓手机开发人形机器人的核心代码和通信协议的问题。首先,我需要明确用户的需求。他们可能想要知道如何利用安卓手机来控制一个人形机器人,涉及核心代码的编写、通信协议的选择以及USB传感器的集成。 首先,核心代码部分。安卓开发通常使用Java或Kotlin,可能需要涉及硬件控制。但人形机器人通常需要实时控制,安卓系统可能不是实时的,这里可能存在挑战。所以需要考虑如何通过安卓发送指令,可能通过USB或蓝牙连接到机器人的控制器,比如Arduino或Raspberry Pi。核心代码可能包括运动控制算法,比如逆运动学计算,以及传感器数据处理。 然后是通信协议。USB是常见的选择,但需要确定机器人控制器支持的协议。常见的有USB HID或者自定义协议。安卓的USB API需要处理权限和设备连接,这部分代码可能需要使用Android SDK的UsbManager类。另外,数据格式需要考虑,比如发送控制命令的结构,可能使用二进制协议或者JSON格式,但二进制效率更高。 传感器集成部分,用户提到USB传感器,需要安卓设备能够读取这些传感器的数据。可能需要开发一个中间件来处理传感器数据,然后通过USB转发给机器人。或者,传感器直接连接到机器人控制器,安卓手机只发送控制命令。需要明确传感器类型,比如陀螺仪、加速度计,或者环境传感器,但用户没有具体说明。 接下来,我需要考虑用户可能的背景。他们可能是开发者,有一定安卓开发经验,但对机器人控制不太熟悉。因此,回答需要涵盖基本步骤,同时提供代码示例,比如连接USB设备的代码片段,以及通信协议的实现。 可能遇到的问题包括USB权限处理、实时性不足、数据同步等。需要提到这些挑战,并给出建议,比如使用异步任务处理USB通信,或者考虑蓝牙低延迟版本如BLE 5.0作为替代。 然后,需要引用相关资源。比如Android开发者文档关于USB的部分,机器人操作系统ROS的可能集成,或者现有的开源项目作为参考。但用户没有提供站内引用,所以可能需要避免假设,或者建议他们查阅特定文档。 最后,生成相关问题,帮助用户深入思考,比如实时性优化、传感器数据处理方法、替代通信协议比较等。 现在,整理这些思路,按照用户要求的格式,分步骤回答,确保使用正确的中文,LaTeX格式,代码块用Python示例(虽然实际可能用Java/Kotlin,但用户示例用了Python,可能需要调整),但用户问题中的示例是快速排序的Python代码,但当前问题涉及安卓开发,可能更适合Java或Kotlin。不过用户没有特别指定,可能需要确认,但按照用户提供的示例,可能仍用Python。不过实际安卓开发不使用Python,这可能需要说明,或者提供伪代码。 但用户的问题明确提到核心代码和通信协议,可能需要更准确的示例。比如,展示一个简单的Android USB连接的代码片段,或者控制命令发送的示例。但考虑到用户示例中的Python代码,可能他们接受其他语言的示例,但需要明确说明。 综合考虑,给出步骤,分核心代码结构、通信协议设计、传感器集成,每部分给出要点和示例代码(用Java/Kotlin),并提到可能的挑战和解决方案。最后生成相关问题。</think>### 基于安卓手机开发人形机器人的核心代码与通信协议实现步骤 #### 一、核心代码结构设计 1. **硬件抽象层(HAL)** 负责与机器人硬件(如舵机、传感器)交互,需封装底层指令。例如控制舵机角度的函数: ```java public class ServoController { public static void setAngle(int servoId, float angle) { // 将角度值转换为硬件指令,通过通信协议发送 } } ``` 2. **运动控制算法** 使用逆运动学(Inverse Kinematics)计算关节角度。对于双足机器人步态,需解算方程: $$ \theta_i = f(x,y,z) $$ 其中 $(x,y,z)$ 为足端目标坐标,$\theta_i$ 为关节角度[^1]。 3. **传感器数据处理** 通过USB读取传感器(如IMU)数据并滤波: ```java public class SensorManager { public float[] readIMU() { // 从USB接口读取原始字节并解析为加速度/角速度 return new float[]{ax, ay, az, gx, gy, gz}; } } ``` --- #### 二、通信协议设计 1. **协议选择** - **自定义二进制协议**:效率高,适合实时控制。帧结构示例: ``` [Header][Command ID][Data][Checksum] ``` - Header: `0xAA55`(固定标识) - Command ID: 1字节(如`0x01`代表舵机控制) - Data: 变长(如舵机ID+角度值) - Checksum: CRC8校验 2. **安卓USB通信实现** ```java UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); UsbDevice device = ... // 通过设备过滤找到目标设备 UsbInterface interface = device.getInterface(0); UsbEndpoint endpoint = interface.getEndpoint(0); // 假设端点0为输出 UsbDeviceConnection connection = manager.openDevice(device); connection.claimInterface(interface, true); byte[] command = buildCommand(0x01, new byte[]{0x02, 0x45}); // 控制2号舵机到45度 connection.bulkTransfer(endpoint, command, command.length, 100); ``` --- #### 三、USB传感器集成 1. **Android USB Host API配置** - 在`AndroidManifest.xml`声明USB权限: ```xml <uses-feature android:name="android.hardware.usb.host" /> <intent-filter> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> </intent-filter> ``` 2. **传感器数据解析** 以超声波测距传感器为例,数据包解析逻辑: ```java public float parseDistance(byte[] rawData) { // 示例协议:2字节无符号整数(单位毫米) int distance = ((rawData[0] & 0xFF) << 8) | (rawData[1] & 0xFF); return distance / 1000.0f; // 转换为米 } ``` --- ### 关键挑战与解决方案 1. **实时性问题** - **挑战**:Android非实时操作系统可能导致控制延迟 - **方案**:使用`HandlerThread`提升优先级,或外接实时协处理器(如STM32) 2. **多传感器同步** - **挑战**:USB带宽竞争导致数据不同步 - **方案**:采用分时复用策略,或选用集线器扩展USB接口 3. **协议可靠性** - **挑战**:数据传输错误可能导致机器人失控 - **方案**:添加重传机制与超时检测,关键指令使用双冗余校验 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值