之前使用 Ch9350制作过一个 USB Host Shield 【参考1】,能够读取USB键盘鼠标的输入。最近在研究 Ch554 ,使用Ch554e制作了一个同样功能的Shield,配合ESP32-C3 能够实现USB 键盘转蓝牙的功能。
使用 Ch554 的有优点如下:
- 价格较低,相对于Ch9350 10元的价格,最便宜的 Ch554e 只要不到1.5元;
- 焊接友好,对于 TSSOP-20/SOP-16或者MSOP-10普通人都能够很好的进行焊接;
- 如果你的设计对于体积敏感,可以选择MSOP-10 封装的 Ch554e;
- 外围电路简单,只需要2个电容和1个电阻
缺点:
- 需要自己使用 keil 编写程序;
- 兼容性比不上 Ch9350,可能出现无法驱动的USB设备;
这次带来就是基于 Ch554e的设计。硬件部分设计如下:
下方就是CH554e的最小系统,外部配合2个0.1uf电容,以及1个10K电阻即可工作。下载方法是:上电之前短接 DL 位置,然后再上电使用WCHISPStudio即可。不过在研发阶段建议专门准备一个开发板便于操作。同时,官方的例子都是用第一个UART作为调试输出,而Ch554只有第2个 Uart可供使用。
根据上述电路设计的PCB如下:
这是一个底板,上面直接连接 DFRobot ESP32-C3即可。焊接后的板卡如下:
直接安装在 ESP32-C3上即可使用:
接下来开始代码的设计,首先设计的是 Ch554的代码,这里直接使用官方的代码进行简单修改。
为了便于使用我们使用和Ch9350相同的输出格式:
0-1 | 57 AB | 数据头,固定数值 |
02 | 88 | 表示有效帧值 |
03 | NN | 后续数据长度,从04开始到最后的校验和 |
04 | 10 | 固定值 [7:6]:00 - 保留 [5:4]:01 - 鼠标 [3] :0 - 保留 [2:1]:00 - 未知 [0] :0 - 端口1 |
05- | AA BB CC…..MM | 键盘数据,例如:08 00 00 00 00 00 00 00 |
XX | Num | 帧序列号 |
XX | CheckSum | 校验和,从05开始的数据和 |
例如:实际发送的一个数据:
57 AB 88 0B 10 08 00 00 00 00 00 00 00 00 08
代码是基于WCH 官方修改而来的,基本原理是:比较每一次收到的数据(RxBuffer)是否和上一次(LastBuffer)相同,如果不同,那么进行上报。使用上面介绍的数据报文格式:
IsSame=TRUE;
for ( i = 0; i < len; i ++ ){
if (LastBuffer[i]!=RxBuffer[i]) {
IsSame=FALSE;
LastBuffer[i]=RxBuffer[i];
}
}
//只有与前一次不同才进行输出
if (IsSame==FALSE) {
checksum=0x00;
CH554UART1SendByte(0x57);CH554UART1SendByte(0xAB);CH554UART1SendByte(0x88);CH554UART1SendByte(len+3);CH554UART1SendByte(0x10);
for ( i = 0; i < len; i ++ ){
CH554UART1SendByte(RxBuffer[i]);
checksum=checksum+RxBuffer[i];
}
checksum=checksum+counter;
CH554UART1SendByte(counter); CH554UART1SendByte(checksum);
counter++;
}
代码使用 Keil4 编译通过。
ESP32-C3代码如下:
#include <Arduino.h>
#include <BleKeyboard.h>
BleKeyboard bleKeyboard;
#define DEBUGMODE 0
void setup() {
Serial.begin(115200);
Serial1.begin(115200, SERIAL_8N1, RX, TX);
bleKeyboard.begin();
}
void loop() {
while (Serial.available()) {
char c = Serial.read();
if (c == '1') {
Serial.println("get1");
}
if (c == '3') {
ESP.restart();
}
}
//根据 CH9350 Spec 每次最多输出 72Bytes
byte Data[72];
unsigned int CounterLast = Serial1.available();
unsigned int CounterCurrent = 0;
// 如果当前串口有数据
if (CounterLast != 0) {
// 进行简单测试,如果当前还在传输数据那么持续接收
while (CounterCurrent != CounterLast) {
CounterLast = Serial1.available();
delayMicroseconds(500);
CounterCurrent = Serial1.available();
}
}
if (CounterCurrent > 0) {
// 一次性将数据收取下来
Serial1.readBytes(Data, CounterCurrent);
unsigned int i = 0;
unsigned int Length;
while (i < CounterCurrent) {
// 识别帧头
if ((Data[i] == 0x57) && (Data[i + 1] == 0xAB)) {
// 有效键值帧
if (Data[i + 2] == 0x88) {
// 获得数据长度
Length = Data[i + 3];
if (DEBUGMODE) {
//Serial.print("Ln:");Serial.print(Length);
for (int j = 1; j < Length + 1; j++) {
if (Data[i + 3 + j] < 16) {
Serial.print("0");
}
Serial.print(Data[i + 3 + j], HEX);
Serial.print(" ");
}
Serial.println(" ");
}
//如果是键盘
if (Data[i + 4] == 0x10) {
if (DEBUGMODE) {
Serial.print("Key");
for (int j = 1; j < Length + 1; j++) {
Serial.print(Data[i + 3 + j], HEX);
Serial.print(" ");
}
Serial.println(" ");
}
//判断为Dostyle键盘
if (Data[i + 3 + 1] == 0x10) {
if (bleKeyboard.isConnected() == true) {
bleKeyboard.sendReport((KeyReport*)(&Data[i + 3 + 2]));
}
}
}
i = i + 3 + Length;
} else if (Data[i + 2] == 0x82) {
i = i + 3; // 跳过
}
}
i++;
} // while (i < Counter)
}
}
使用时,只需要给ESP32-C3供电,连接好USB键盘后就可以搜索蓝牙键盘进行连接使用了。工作的测试视频在:
https://www.bilibili.com/video/BV1zi421a7NM/?vd_source=cf6121716e06cb669a27c10276f9c920
Ch554 的代码:
ESP32 C3 代码:
参考: