MCB2140评估板USB HID多字节传输实现指南
1. 修改MCB2140评估板USB HID示例的消息长度
最近在调试Keil MCB2140评估板的USB HID示例时,遇到了一个典型需求:原示例只能传输单字节数据,而实际项目中需要传输16字节的数据包。经过一番研究和实践,我找到了完整的解决方案,在此分享给遇到类似问题的开发者。
MCB2140评估板采用Philips LPC2148 ARM处理器,其USB HID示例虽然功能完整,但默认配置仅支持单字节传输。实际上,USB HID协议本身支持最大64字节的报告(Report),只需对代码进行适当修改即可实现多字节传输。这个修改过程涉及三个关键部分:报告描述符修改、数据收发函数调整以及端点配置检查。
2. 核心修改步骤详解
2.1 修改报告描述符(USBDesc.c)
报告描述符是USB HID设备最重要的配置之一,它定义了设备与主机之间的数据格式和通信规则。原示例中的描述符只定义了1字节的输入和输出报告:
const BYTE HID_ReportDescriptor[] = { HID_UsagePageVendor(0x00), HID_Usage(0x01), HID_Collection(HID_Application), HID_LogicalMin(0), HID_LogicalMax(255), HID_ReportSize(8), HID_ReportCount(1), HID_Usage(0x01), HID_Input(HID_Data | HID_Variable | HID_Absolute), HID_Usage(0x01), HID_Output(HID_Data | HID_Variable | HID_Absolute), HID_EndCollection };要支持16字节传输,需要修改HID_ReportCount的值为16:
const BYTE HID_ReportDescriptor[] = { HID_UsagePageVendor(0x00), HID_Usage(0x01), HID_Collection(HID_Application), HID_LogicalMin(0), HID_LogicalMax(255), HID_ReportSize(8), // 每个字段8位(1字节) HID_ReportCount(16), // 16个字段,共16字节 HID_Usage(0x01), HID_Input(HID_Data | HID_Variable | HID_Absolute), HID_Usage(0x01), HID_Output(HID_Data | HID_Variable | HID_Absolute), HID_EndCollection };注意:报告描述符修改后,主机端也需要相应调整。Windows系统会缓存HID设备的描述符,修改后可能需要重新插拔设备或使用设备管理器卸载设备,否则主机可能继续使用旧的描述符。
2.2 调整数据收发函数(HIDUser.c)
原示例中的HID_GetReport和HID_SetReport函数只处理单字节数据,需要扩展为处理多字节:
BYTE *HID_GetReport (BYTE rtype, BYTE rid, BYTE len) { if (len > 16) len = 16; // 限制最大长度为16字节 for (BYTE i = 0; i < len; i++) { EP0buf[i] = HID_ReportIn[i]; // 将数据复制到端点0缓冲区 } return (EP0buf); } void HID_SetReport (BYTE rtype, BYTE rid, BYTE len, BYTE *buf) { if (len > 16) len = 16; // 限制最大长度为16字节 for (BYTE i = 0; i < len; i++) { HID_ReportOut[i] = buf[i]; // 从端点0缓冲区复制数据 } }同时,需要定义足够大的缓冲区来存储报告数据:
BYTE HID_ReportIn[16]; // 输入报告缓冲区(设备到主机) BYTE HID_ReportOut[16]; // 输出报告缓冲区(主机到设备)2.3 检查端点配置(usbcfg.c)
确保USB端点配置支持足够大的数据包。在MCB2140示例中,端点配置通常在usbcfg.c文件中定义:
#define HID_EP_IN 0x81 #define HID_EP_OUT 0x01 #define HID_EP_SIZE 16 // 修改为16字节同时检查USB_EndPoint数组中的相关配置:
USB_EndPointDef USB_EndPoint[] = { // 端点0(控制端点)已由USB内核自动配置 {HID_EP_OUT, USB_ENDPOINT_TYPE_INTERRUPT, HID_EP_SIZE, 0}, {HID_EP_IN, USB_ENDPOINT_TYPE_INTERRUPT, HID_EP_SIZE, 0}, {0,0,0,0} // 结束标记 };3. 完整实现流程
3.1 修改步骤总结
- 备份原项目:在进行任何修改前,先备份整个项目目录。
- 修改报告描述符:在USBDesc.c中更新HID_ReportDescriptor,将ReportCount改为16。
- 扩展缓冲区:在HIDUser.c中定义16字节的HID_ReportIn和HID_ReportOut数组。
- 更新数据处理函数:修改HID_GetReport和HID_SetReport以支持16字节数据。
- 调整端点配置:在usbcfg.c中确保HID_EP_SIZE设置为16。
- 重新编译下载:编译项目并下载到MCB2140评估板。
- 主机端测试:使用HID客户端工具测试数据传输。
3.2 测试验证方法
修改完成后,可以使用以下方法验证修改是否成功:
设备管理器检查:
- 在Windows设备管理器中查看HID设备属性
- 检查"详细信息"选项卡中的报告描述符是否正确显示16字节长度
Bus Hound抓包:
- 使用Bus Hound等USB协议分析工具
- 监控设备枚举过程,确认报告描述符已更新
- 观察数据传输时是否能够发送/接收16字节
自定义测试程序:
- 编写简单的HID主机应用程序
- 尝试发送和接收16字节数据包
- 验证数据完整性和正确性
4. 常见问题与解决方案
4.1 设备无法识别或枚举失败
可能原因:
- 报告描述符格式错误
- 端点配置不匹配
- 缓冲区溢出
解决方案:
- 使用USB协议分析工具检查枚举过程
- 逐步还原修改,确认哪一步导致问题
- 检查所有相关位置的长度定义是否一致
4.2 主机接收数据不完整
可能原因:
- 主机应用程序仍按单字节处理
- Windows缓存了旧的报告描述符
- 端点中断处理不当
解决方案:
- 重新插拔USB设备,清除主机缓存
- 更新主机应用程序以处理16字节报告
- 检查端点中断服务程序是否正确处理了多字节数据
4.3 数据传输速度慢
可能原因:
- 未充分利用USB带宽
- 轮询间隔设置过长
解决方案:
- 在报告描述符中调整轮询间隔:
HID_ReportDescriptor[] = { // ... HID_ReportInterval(1), // 1ms轮询间隔 // ... }; - 考虑使用批量传输端点替代中断传输(需修改设备类)
5. 性能优化建议
实现基本功能后,可以考虑以下优化措施:
双缓冲技术:
BYTE HID_ReportIn[2][16]; // 双缓冲 BYTE currentBuffer = 0; void PrepareNextReport() { currentBuffer ^= 1; // 切换缓冲区 // 填充HID_ReportIn[currentBuffer] }DMA传输:
- 配置USB端点使用DMA传输数据
- 减少CPU开销,提高系统响应速度
动态报告长度:
- 在报告描述符中使用可变长度报告
- 根据实际需求动态调整报告大小
错误检测与恢复:
void HID_SetReport(BYTE rtype, BYTE rid, BYTE len, BYTE *buf) { if (len > sizeof(HID_ReportOut)) { // 处理错误情况 return; } // ...正常处理... }
6. 进阶扩展思路
对于需要更复杂HID功能的应用,可以考虑:
多报告支持:
- 在报告描述符中定义多个报告ID
- 实现不同类型和长度的数据报告
HID设备组合:
- 将多个HID设备功能组合到一个物理设备中
- 例如同时实现键盘、鼠标和自定义HID设备
HID over I2C/SPI:
- 对于非USB接口的应用
- 实现基于其他总线的HID设备
免驱动签名:
- 为自定义HID设备获取微软WHQL签名
- 避免Windows显示"未知设备"警告
在实际项目中,我们成功应用这些技术将MCB2140的USB HID示例改造成了16字节数据传输的设备,稳定运行在工业数据采集系统中。关键是要确保所有相关配置的一致性,并在修改后彻底测试各种边界条件。
