
1. 项目概述为什么用CH554做USB Host转蓝牙键盘最近在折腾一个挺有意思的小项目用一块国产的8位单片机CH554把它变成一个USB Host然后读取普通USB键盘的按键数据再通过蓝牙模块发送出去最终实现一个“USB键盘转蓝牙”的转换器。听起来是不是有点像市面上卖的“蓝牙转换头”没错功能上类似但自己动手做成本能压到极低而且最关键的是你能完全掌控它。我之所以选择CH554这颗芯片核心原因就两个字性价比。它内置了USB控制器并且官方提供了相当完善的USB Host和Device例程库。这意味着你不需要外挂复杂的USB协议芯片比如常见的CH375、SL811HS等一颗几块钱的MCU就能搞定USB通信大大简化了硬件设计和BOM成本。对于想深入学习USB协议、又想做出点实用小玩意的朋友来说CH554是个绝佳的入门平台。这个项目的应用场景其实很广。比如你有一把心爱的老式机械键盘它只有USB口你想把它连到iPad、手机或者不支持USB HID的智能电视上这个转换器就能派上用场。又或者你想打造一个极简的无线桌面减少线缆缠绕。自己动手实现不仅成本可控还能根据需求定制功能比如自定义按键映射、实现宏按键、甚至加入电量显示等等。整个项目的核心逻辑链条很清晰CH554作为USB Host识别并枚举键盘 - 解析键盘发送的USB HID报告描述符和数据包 - 将解析出的按键码转换为标准的键盘扫描码或直接模拟蓝牙HID报告 - 通过串口UART将数据发送给蓝牙模块如HC-05、JDY-33等 - 蓝牙模块以蓝牙HID设备身份与电脑、手机等配对并发送按键信息。下面我就把这个过程中的技术细节、踩过的坑和实战心得毫无保留地分享出来。2. 核心思路与方案选型背后的考量做这个项目首先得把架构想明白。市面上实现类似功能大概有几种路子专用芯片方案比如一些现成的蓝牙键盘主控它们可能已经集成了USB Host和蓝牙射频。优点是省事但缺点是黑盒子可玩性低且不一定便宜。MCUUSB Host芯片蓝牙模块方案这是比较传统的做法比如用STM32CH375HC-05。功能稳定但芯片多布线复杂成本高。MCU内置USB Host蓝牙模块方案这就是我们选择的CH554方案。它把USB Host控制器集成在了MCU内部硬件上最简洁成本也最有优势。选择CH554不仅仅是看中它的内置USB。更关键的是沁恒官方提供的CH554EVT评估板资料包里面包含了完整的USB Host库函数。这对于开发者来说是莫大的福音。你不用从零开始去啃复杂的USB协议栈只需要在官方例程的基础上进行修改和适配大大降低了开发门槛。官方库已经处理了底层的USB事务传输、令牌包发送、数据包收发等繁琐工作我们只需要关注上层的应用逻辑如何识别键盘、如何获取数据。在蓝牙模块的选型上也有讲究HC-05/06经典蓝牙2.0/2.1EDR模块串口透传需要额外在MCU端实现蓝牙HID协议栈比较复杂。或者可以把它设置为从模式连接到手机后由手机端的APP来解析数据但这限制了通用性。JDY-33/CC2541等BLE模块蓝牙4.0低功耗模块。有些模块如某些特定固件的JDY-33可以直接配置为蓝牙HID设备如键盘。这是更理想的方案我们的CH554只需要通过串口按照模块规定的格式发送按键数据模块自己就会打包成标准的BLE HID报告发给主机。这样MCU端的逻辑就简化成了“USB数据解析 - 串口转发”无需关心蓝牙协议。我强烈推荐BLE HID模块方案。它让整个系统的分工非常明确CH554专心做擅长的USB通信和协议解析蓝牙模块做擅长的无线HID模拟。两者通过简单的串口指令通信稳定且高效。本次分享也将以这种方案为主线展开。注意购买蓝牙模块时一定要向卖家确认固件是否支持HID设备模式特别是键盘。很多默认的透传固件是不支持的可能需要烧录特定的固件。3. 硬件设计与核心电路解析虽然CH554内置了USB Host但外围电路的设计依然有几个关键点直接影响系统的稳定性。3.1 CH554最小系统与USB Host接口CH554的最小系统很简单电源、晶振、复位、下载口。重点在于USB Host的D和D-线连接。电源CH554工作电压是5V或3.3V。整个系统建议统一使用3.3V供电以兼容绝大多数3.3V的蓝牙模块。需要一个LDO如AMS1117-3.3将可能的5V输入降压到3.3V。必须确保3.3V电源能提供至少500mA的持续电流因为你要驱动的USB键盘本身可能需要100mA-500mA的电流根据USB规范。晶振使用12MHz晶振即可。官方USB库是基于12MHz时钟开发的不要随意更改。USB端口你需要一个USB Type-A母座来连接键盘。D和D-信号线必须串联一个27Ω的电阻典型值范围22Ω-33Ω到CH554的对应引脚P1.4和P1.5这个电阻用于阻抗匹配和信号完整性减少反射。同时D和D-线上最好各对地接一个15pF-33pF的电容用于滤除高频噪声。上拉电阻在USB Host模式下需要在D或D-线上具体哪根由设备速度决定通过一个1.5kΩ电阻上拉到3.3V。这个电阻通常集成在CH554内部可以通过软件配置使能但为了保险起见也可以在外部预留这个电阻的位置。下图是一个简化的核心电路示意图USB-A母座 | VBUS -------------------------- 5V输入 (到LDO Vin) | | GND -------------------------- GND | | D -------[27Ω]----- P1.4 (CH554 UD) --[15pF]---GND | | D- -------[27Ω]----- P1.5 (CH554 UD-) --[15pF]---GND | (内部/外部1.5k上拉)实操心得1电源隔离与滤波USB键盘是“热插拔”设备插拔瞬间会产生较大的电流冲击和电压毛刺。建议在LDO的输入和输出端都加上至少100uF的电解电容和0.1uF的瓷片电容进行滤波。特别是在CH554的VCC引脚附近一定要放置一个0.1uF的退耦电容尽可能靠近芯片引脚。我曾因为退耦电容放得远了一点导致USB枚举时不时失败折腾了好久。3.2 蓝牙模块连接与供电蓝牙模块以JDY-33为例与CH554的连接极其简单仅需三根线TXD- CH554的RXD例如P3.0RXD- CH554的TXD例如P3.1GND- 共地蓝牙模块的VCC接系统的3.3V。同样模块的电源引脚附近也需要加0.1uF的退耦电容。关键点电平匹配与流控CH554和绝大多数蓝牙模块都是3.3V电平直接连接即可无需电平转换。串口通信通常不需要硬件流控RTS/CTS但为了绝对稳定尤其是在高速或大数据量传输时可以将模块和MCU的RTS、CTS引脚互相连接并启用流控不过在这个键盘项目里数据量很小通常不需要。3.3 整体布局与抗干扰设计这是一个数字和模拟射频混合的系统。布局上建议分区布局将MCU及周边电路、USB接口电路视为“数字区”将蓝牙模块及其天线周边视为“射频区”。两者之间适当远离用地线或电源线进行隔离。天线区域净空蓝牙模块的PCB天线或陶瓷天线下方及其投影区域绝对不能走线或铺铜必须净空。这是影响蓝牙距离和稳定性的决定性因素之一。地平面尽量使用完整的铺铜作为地平面为高频信号提供最短的回流路径减少电磁干扰EMI。4. 软件框架与CH554 USB Host库解析软件是项目的灵魂。我们需要在CH554上完成两件事1. 作为USB Host与键盘通信2. 通过串口与蓝牙模块通信。4.1 开发环境与工程搭建工具链使用Keil C51进行开发。从沁恒官网下载最新的CH554EVT资料包。工程基础在资料包的EVT\EXAM\USB\USB_HOST目录下提供了USB Host的示例工程。我们以此为基础进行修改。重点关注USB_HOST.C和USB_HOST.H这两个文件它们包含了USB Host协议栈的核心函数。库函数概览HostInit(): 初始化USB Host控制器。EnumAll(): 枚举所有连接的USB设备。HID_GetReportDescr(): 获取HID设备的报告描述符用于理解键盘的数据格式。HID_GetInputReport(): 获取输入报告即键盘的按键数据。这些函数封装了底层的UH_EP0_TX/RX,UH_RECV_DATA等操作我们不需要直接调用底层函数。4.2 USB键盘枚举与数据解析流程详解这是最核心的部分。下面结合代码详细说明每一步。第一步初始化与设备检测void main() { CfgFsys(); // 配置系统时钟选择12MHz mDelaymS(5); // 短暂延时 HostInit(); // 初始化USB Host控制器 UART1Setup(); // 初始化串口1用于连接蓝牙模块 EA 1; // 开启总中断 while (1) { if (EnumAll() ERR_SUCCESS) { // 尝试枚举设备 printf(USB Device Enumerated Successfully!\r\n); // 枚举成功进入数据处理循环 ProcessKeyboard(); } else { mDelaymS(100); // 枚举失败延时后重试 } } }EnumAll()函数会执行完整的USB枚举过程复位总线、获取设备描述符、设置地址、获取配置描述符等。如果返回ERR_SUCCESS说明一个USB设备我们的键盘已经被成功识别并配置。第二步识别HID键盘设备在EnumAll()内部或之后我们需要检查枚举到的设备类型。通过解析设备描述符和配置描述符我们可以找到接口描述符。一个标准的USB键盘通常是一个“HID类Class 0x03”设备其接口子类bInterfaceSubClass通常是“引导接口Boot Interface 0x01”协议bInterfaceProtocol是“键盘1”。我们需要在代码中检查这些字段// 在枚举过程中或之后检查设备类型 if (pSetupReq-bDeviceClass 0x00 ...) { // 设备类在接口描述符中 // 找到接口描述符 if (pIntDesc-bInterfaceClass 0x03 // HID Class pIntDesc-bInterfaceSubClass 0x01 // Boot Interface pIntDesc-bInterfaceProtocol 0x01) { // Keyboard printf(Boot Protocol Keyboard Found!\r\n); bKeyboardFound 1; } }识别为键盘后我们还需要获取其报告描述符Report Descriptor。报告描述符定义了键盘上报数据的格式有多少个字节、每个字节的每一位代表哪个按键或修饰键Ctrl, Shift等。对于Boot Protocol键盘其报告格式是固定的8字节我们可以不解析报告描述符直接使用固定格式。但为了通用性解析报告描述符是更严谨的做法。官方库中的HID_GetReportDescr()函数可以帮我们获取到这份描述符。第三步周期性地获取按键数据识别成功后我们需要在一个循环里不断地向键盘请求输入报告。void ProcessKeyboard() { uint8_t HID_InputReport[8]; // Boot Keyboard输入报告通常是8字节 uint8_t keycode; while (bKeyboardFound) { // 请求输入报告 if (HID_GetInputReport(HID_InputReport, sizeof(HID_InputReport)) ERR_SUCCESS) { // 解析HID_InputReport // 字节0: 修饰键Modifier如Ctrl, Shift, Alt, GUI // 字节1: 保留 // 字节2-7: 最多6个普通按键的键码Key Code ParseKeyboardReport(HID_InputReport); // 解析并处理报告 } mDelaymS(10); // 适当延时约100Hz的轮询率足够 } }HID_GetInputReport函数内部会发起GET_REPORT或IN令牌传输取决于中断传输还是控制传输从键盘读取数据。成功读取后数据就存放在HID_InputReport数组中。第四步解析键码并转换USB HID键码Usage ID和PC端标准的扫描码Scan Code或蓝牙HID的键码HID Usage通常有直接的映射关系。我们需要一个转换表。// 简化的键码映射表示例仅映射部分键 const uint8_t HIDKeycodeToBluetooth[256] { [0x04] 0x1E, // HID Usage ID 0x04 (a/A) - Bluetooth HID a key [0x05] 0x1F, // 0x05 - b [0x06] 0x20, // 0x06 - c // ... 其他字母、数字、功能键 [0x28] 0x28, // 回车键 (Enter) [0x29] 0x29, // 退出键 (Escape) // 修饰键通常有独立的位置不需要映射直接对应 }; void ParseKeyboardReport(uint8_t *report) { uint8_t modifier report[0]; // 修饰键字节 uint8_t keycodes[6]; memcpy(keycodes, report[2], 6); // 拷贝6个普通键码 // 1. 处理修饰键如需要可转换为蓝牙报告格式的修饰键字节 uint8_t bt_modifier ConvertModifier(modifier); // 2. 处理普通键码 for (int i 0; i 6; i) { if (keycodes[i] ! 0) { // 0表示无按键 uint8_t bt_keycode HIDKeycodeToBluetooth[keycodes[i]]; if (bt_keycode ! 0) { // 将 bt_modifier 和 bt_keycode 填充到蓝牙HID报告格式中 SendToBluetooth(bt_modifier, bt_keycode); } } } // 还需要处理按键释放当键码变为0时 }ConvertModifier函数将USB HID的修饰键位映射到蓝牙HID报告的修饰键位。两者的位定义是基本一致的例如左Ctrl都是bit 0。4.3 与蓝牙HID模块的通信协议假设我们使用了一个支持HID模式的JDY-33模块。我们需要查阅其AT指令集或数据透传协议。通常这类模块在配置为HID键盘模式后会期望通过串口接收特定格式的数据包。一个常见的简化蓝牙HID键盘报告格式是8字节字节0: 修饰键 (Modifier) 字节1: 保留 (通常为0) 字节2-7: 按键键码 (Key Code)最多6个这正好和USB Boot Keyboard的报告格式对应上了所以我们的ParseKeyboardReport函数解析出修饰键和键码后几乎可以直接打包成同样的8字节数组通过串口发送给蓝牙模块。void SendToBluetooth(uint8_t modifier, uint8_t keycode) { uint8_t bt_report[8] {0}; bt_report[0] modifier; // 通常蓝牙模块要求一次发送完整的报告包括所有按下的键 // 我们需要维护一个当前按键状态的数组 UpdateCurrentKeys(keycode); // 更新内部状态数组 // 将内部状态数组拷贝到bt_report[2]-[7] memcpy(bt_report[2], current_key_array, 6); // 通过串口发送 UART1SendData(bt_report, 8); }关键点蓝牙HID是“状态报告”即每次发送的都是当前所有按下按键的状态。而USB键盘的数据报告每次发送的是变化量。因此MCU内部需要维护一个“当前按下按键”的数组。当收到USB报告时如果是新的键码非0就加入数组如果键码变为0在USB报告中该位置出现了0就从数组中移除。然后将整个数组打包进蓝牙报告发送。这确保了长按、多键组合如CtrlC的正确处理。实操心得2防抖与去重 键盘在物理按下和松开时会产生抖动USB报告可能会在短时间内上报多次相同的键码。为了避免重复触发可以在MCU端软件实现防抖逻辑例如记录每个键码上次处理的时间在短时间内如20ms忽略同一键码的重复报告。同时维护内部按键状态数组也能有效避免因USB报告轮询间隔导致的问题。5. 固件烧录、调试与功能验证写完代码后编译生成HEX文件通过WCH的ISP工具需要CH554进入Bootloader模式通常通过拉低某个引脚并上电实现烧录到芯片中。调试阶段建议分步验证先验证USB Host功能不接蓝牙模块在代码中将解析出的USB键码通过CH554的另一个串口或USB CDC虚拟串口打印到电脑的串口助手上。插上键盘按按键看打印信息是否正确。这一步确保USB部分完全正确。再验证串口通信编写一个简单的测试程序让CH554定时通过串口1发送固定的测试数据包用USB-TTL工具连接蓝牙模块的RX/TX在电脑端查看接收的数据是否正确。这一步确保MCU与蓝牙模块的硬件连接和串口配置无误。最后验证蓝牙HID功能将蓝牙模块配置为HID键盘模式通常需要发送特定的AT指令序列进行配置具体参考模块手册。然后将MCU程序中的串口发送函数真正启用发送真实的键盘报告数据。用手机或电脑搜索蓝牙设备应该能发现一个名为“XXX Keyboard”的设备配对后尝试按键看输入是否正常。常见问题与排查技巧实录问题1USB键盘插入后CH554枚举失败。排查首先用万用表测量USB口的VBUS电压是否正常5V左右以及CH554的3.3V供电是否稳定。检查D/D-线上的27Ω电阻和15pF电容是否焊接正确。使用逻辑分析仪或示波器抓取USB数据线波形看是否有正确的差分信号。降低USB总线速率在代码中配置试试。可能原因电源功率不足、信号线阻抗不匹配、晶振不起振、软件初始化时序问题。问题2枚举成功但无法获取到按键数据HID_GetInputReport总是失败。排查确认在EnumAll之后是否正确获取并设置了键盘的端点地址Endpoint Address和最大包大小Max Packet Size。检查HID_GetInputReport函数调用时传入的端点号是否正确通常是0x81表示输入端点1。在ProcessKeyboard循环中加入打印查看函数返回的错误码。可能原因端点信息获取错误、报告描述符解析错误导致请求格式不对、轮询间隔太短导致设备来不及响应。问题3蓝牙模块能被发现但配对失败或配对后无法输入。排查确认蓝牙模块是否成功进入HID模式通常有特定指示灯状态。检查MCU发送给模块的数据格式是否完全符合模块手册要求包括包头、包尾、校验等有些模块需要简单的帧格式。用串口助手监控MCU发送出的原始数据看其是否是一个有效的8字节HID报告。可能原因蓝牙模块未正确配置为HID角色、串口波特率不匹配、数据格式错误、主机手机/电脑的蓝牙驱动或系统对HID设备的支持问题。问题4按键输入有延迟或卡顿。排查检查ProcessKeyboard循环中的mDelaymS(10)是否过长可以尝试减少到5ms。检查蓝牙模块的串口波特率是否够高建议至少115200bps。检查MCU是否在处理其他中断任务导致堵塞。可能原因USB轮询周期过长、串口波特率过低、MCU主频太低或任务阻塞。6. 功能扩展与优化思路基础功能实现后这个平台还有很大的玩法和优化空间多设备管理与键鼠一体CH554的USB Host理论上可以支持多个USB设备需外接Hub。你可以扩展代码同时接入一个USB键盘和一个USB鼠标解析两者的数据然后通过蓝牙模块模拟成一个蓝牙键盘鼠标复合设备实用性大大增强。宏按键与层功能利用CH554的额外GPIO引脚连接几个物理按钮。在固件中实现“层Layer”切换功能。按下某个按钮键盘的映射层改变从而实现一套键位控制多台设备或者为游戏、设计软件定义复杂的宏命令。低功耗优化如果使用BLE模块可以充分利用其低功耗特性。当键盘长时间无操作时让CH554进入休眠模式蓝牙模块进入广播或深度睡眠模式由按键动作触发唤醒从而用电池供电做成真正的无线转换器。状态指示增加LED指示灯用于显示电源状态、蓝牙连接状态、Caps Lock状态等提升用户体验。配置界面利用CH554内置的USB Device功能当它通过Micro USB口连接到电脑时可以模拟成一个USB串口或自定义HID设备。配合电脑端的上位机软件可以实时配置按键映射、宏定义等参数并保存到CH554的EEPROM中。这个项目从硬件焊接、软件调试到功能实现完整地走通了USB Host和蓝牙HID的应用链路。过程中对USB协议、HID报告描述符、串口通信、状态机编程都有了更深刻的理解。最重要的是当你用自己亲手制作的转换器让一把老键盘在平板电脑上焕发新生时那种成就感是买成品无法比拟的。希望这份详细的拆解能帮你少走弯路顺利做出属于自己的USB to Bluetooth转换器。