简介
1、CH9328 是一款串口转 HID 芯片,在电脑上识别为标准的 USB HID 类键盘设备。CH9328 用于单向数据传输,可以接收串口发送过来的数据(如 ASCII 码),并按照 HID 类键盘设备规范,将数据打包成标准的键盘码值通过 USB 口上传给计算机。通过提供的上位机软件,用户也可自行配置芯片的 VID、PID,以及各种字符串描述符。
2、默认串口通信波特率为 9600bps。
.
.
.
1、引脚介绍
部分引脚说明
(详细说明在 参考资料 中的网址下载说明文档)
.
.
.
.
2、工作模式
上传速度设置
CH9328的 IO1 引脚用于配置芯片的上传速度,上电时该引脚为高电平则速度配置为普通模式,上电时该引脚为低电平则速度配置为高速模式,高速模式的速度大概为普通模式的 1 倍。
.
.
工作模式设置:
工作模式 | IO2电平 | IO3电平 | IO4电平 | 功能说明 |
---|---|---|---|---|
模式0 | 1 | 1 | 1 | 该模式仅支持将可见ASCII码(如 a-z、0-9、@、#、$ 等)对应的字符转成标准的USB键值。特殊功能:接收到的串口数据包如果遇到 0x1B 时,则丢弃当前包中 0x1B 之后的数据,并将 0x1B 转换成回车键。 |
模式1 | 1 | 0 | 1 | 该模式仅支持将可见ASCII码(如 a-z、0-9、@、#、$ 等)对应的字符转成标准的USB键值。 |
模式2 | 1 | 1 | 0 | 该模式仅支持将可见ASCII码(如 a-z、0-9、@、#、$ 等)对应的字符转成标准的USB键值。特殊功能:接收到的串口数据包如果遇到 0x28 时,将 0x28 转换成回车键。 |
模式3 | 0 | 1 | 1 | 该模式为透传模式,不仅仅用于传输可见ASCII码字符。该模式下可以实现标准的USB全键盘功能。串口数据每8个字节组成一包,芯片每接收到8个字节后,直接打包通过USB口上传。即芯片为透传模式,不分析转换串口数据,直接按8个字节一包上传。故串口数据必须按照标准的USB键盘数据包进行发送。比如模拟“A”按下,则串口发送数据包为:0x00、0x00、0x04、0x00、0x00、0x00、0x00、0x00;比如模拟“A”释放,则串口发送数据包为:0x00、0x00、0x00、0x00、0x00、0x00、0x00、0x00;比如模拟“A+SHIFT”同时按下,则串口发送数据包为:0x02、0x00、0x04、0x00、0x00、0x00、0x00、0x00;比如模拟“A+SHIFT”同时释放,则串口发送数据包为:0x00、0x00、x00、0x00、0x00、0x00、0x00、0x00; |
.
.
.
3、电路连接
键盘上的电路连接原理图:
上图中已将 CH9328 的 IO1,IO3和IO4设置为高电平,IO2设置为低电平。
通过电平设置可将其工作模式设置为普通模式下的模式3(透传模式)。
.
数据手册中的电路原理图:
.
.
.
4、数据格式
在工作模式设置的模式3说明中已经大致说了数据格式。
以下为具体说明。
通信协议
使用的是CH9328的模式3。
在模式3,一次向CH9328发送8个字节的数据。
数据格式为:
[byte1][byte2][byte3][byte4][byte5][byte6][byte7][byte8]
其中第一个字节(byte1)用来标识特殊按键
[byte1]
---bit0:左Ctrl 按下为1,否则为0。
---bit1:左Shift 按下为1
---bit2:左Alt 按下为1
---bit3:左GUI 按下为1
---bit4:右Ctrl 按下为1
---bit5:右Shift 按下为1
---bit6:右Alt 按下为1
---bit7:右GUI 按下为1
.
按下左Shift 则第一个字节应该为 0000 0010
,即0x02。
完整发送的数据为
{0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
.
若按下左Shift和右Ctrl 则第一个字节应该是 0000 0010
|0001 0000
的值,结果为 0x12。
(注意:这里的 ‘|’ 是 ‘或’ 符号,功能是按位相或。)
完整发送的数据为
{0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
.
第二个字节为保留字节 全部为0即可,即0x00。
第三到第八个字节为普通按键的键值,所以不加上特殊按键最多可以发送六个按键。
若按下 左Shift,左Ctrl,A,1,/ 这五个键,完整的发送数据为
{0x03, 0x00, 0x04, 0x1E, 0x38, 0x00, 0x00, 0x00};
其中 左Shift 和 左Ctrl 为 0x02 | 0x01 得到 0x03,
A的键值为 0x04,1 的键值为 0x1E,/ 的键值为 0x38。
若仅有 A 按下,然后其余按键都松开,则完整的发送数据为
{0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00};
.
.
.
5、驱动程序
定义一个缓存空间
//USB发送数据 8字节大小缓存
uint8_t key_code[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
.
创建键码数组
定义键盘上每个按键对应的键码
//USB 未按下Fn时的键码
const uint8_t USB_KEY_CODE[KEYBOARD_ROW][KEYBOARD_COLUMN] = {
//` ~ 1 ! 2 @ 3 # 4 $ 5 % 6 ^ 7 & 8 * 9 ( 0 ) - _ = + backspace
{KEY_USB_SEPARATOR, KEY_USB_1, KEY_USB_2, KEY_USB_3, KEY_USB_4, KEY_USB_5, KEY_USB_6, KEY_USB_7, KEY_USB_8, KEY_USB_9, KEY_USB_0, KEY_USB_SUB, KEY_USB_EQUAL, KEY_USB_BACKSPACE},
//tab Q q W w E e R r T t Y y U u I i O o P p [ { ] } \ |
{KEY_USB_TAB, KEY_USB_Q, KEY_USB_W, KEY_USB_E, KEY_USB_R, KEY_USB_T, KEY_USB_Y, KEY_USB_U, KEY_USB_I, KEY_USB_O, KEY_USB_P, KEY_USB_LBRACKET, KEY_USB_RBRACKET, KEY_USB_BSLASH},
//caps lock A a S s D d F f G g H h J j K k L l ; : ' " -- Enter
{KEY_USB_CAPS_LOCK, KEY_USB_A, KEY_USB_S, KEY_USB_D, KEY_USB_F, KEY_USB_G, KEY_USB_H, KEY_USB_J, KEY_USB_K, KEY_USB_L, KEY_USB_SEMICOLON, KEY_USB_SQUOTES, 0, KEY_USB_RETURN},
//shift Z z X x C c V v B b N n M m , < . > / ? shift ↑ DELETE
{KEY_USB_LEFT_SHIFT, KEY_USB_Z, KEY_USB_X, KEY_USB_C, KEY_USB_V, KEY_USB_B, KEY_USB_N, KEY_USB_M, KEY_USB_COMMA, KEY_USB_FSTOP, KEY_USB_FSLASH, KEY_USB_RIGHT_SHIFT, KEY_USB_UP_ARROW, KEY_USB_DELETE},
//ctrl Win alt ---- space --------- Fn ctrl ← ↓ →
{KEY_USB_LEFT_CTRL, KEY_USB_LEFT_GUI, KEY_USB_LEFT_ALT, 0, 0, KEY_USB_SPACE, 0, 0, 0, 0, KEY_USB_RIGHT_CTRL, KEY_USB_LEFT_ARROW, KEY_USB_DOWN_ARROW, KEY_USB_RIGHT_ARROW}};
//USB 按下Fn时第一行按键的键码
uint8_t FN_USB_Code[14] = {KEY_USB_ESC, KEY_USB_F1, KEY_USB_F2, KEY_USB_F3, KEY_USB_F4, KEY_USB_F5, KEY_USB_F6, KEY_USB_F7, KEY_USB_F8, KEY_USB_F9, KEY_USB_F10, KEY_USB_F11, KEY_USB_F12, KEY_USB_F13};
键码发送
更改第一个字节
//参数为此按键的键码和按键状态(按下为1,松开为0)。
void first_bit(uint8_t key_, uint8_t state);
.
实现方法
对缓存空间内第0字节的位操作可实现发送数据的更改。
void first_bit(uint8_t key_, uint8_t state)
{
//判断键码是否正确
if (key_ != 0)
{
//如果按键为按下
if (state == 1)
{
key_code[0] = key_code[0] | key_;
}
else//否则当按键此按键松开
{
key_ = ~key_;
key_code[0] = key_code[0] & key_;
}
}
}
.
.
更改其余字节
//参数为此按键的键码和按键状态(按下为1,松开为0)。
//因为CH9328只能发送六个普通按键的状态,
//所以当按下的按键数量大于6时,将无法发送,
//此函数可返回0/1,表示是否设置成功,
//当已经按下6个普通按键,再按下一个普通按键时,将无法设置,返回0。
uint8_t other_bit(uint8_t key_, uint8_t state)
.
实现方法
uint8_t other_bit(uint8_t key_, uint8_t state)
{
//返回值,先设置为0,如果以下代码未将键码存入缓冲区,
//返回值未改变,为0,
//意味着已经按下6个按键,此按键无法设置。
uint8_t o_ = 0;
//判断键码是否正确
if (key_ != 0)
{
//如果状态为按下(1)
if (state == 1)
{
//从缓冲区的 第2字节开始查询空的位置
for (uint8_t i = 2; i < 8; i++)
{
//如果在第 i 字节找到空位置
if (key_code[i] == 0x00)
{
//将键码存入空位置中
key_code[i] = key_;
o_ = 1;//返回1 ,表示设置成功
break;
}
}
}
//如果状态为松开(0)
else if (state == 0)
{
//从缓冲区的 第2字节开始查询此键码所在的位置
for (uint8_t i = 2; i < 8; i++)
{
//如果找到此键码保存的位置
if (key_code[i] == key_)
{
//将键码删除,使此位置为空(0x00)。
key_code[i] = 0x00;
o_ = 1;//返回1 ,表示设置成功
break;
}
}
}
}
return o_;
}
.
.
发送缓存空间数据
原理图中ESP32的串口1连接 CH340C,用于代码下载和键盘调试,
串口2连接CH9328,用于发送按键数据。
先将ESP32的串口2打开。
Serial2.begin(9600);//波特率9600
.
发送缓存空间的数据。
Serial2.write(key_code, 8);
.
.
.
6、参考资料
1、沁恒CH9328数据手册
http://www.wch.cn/downloads/file/224.html?time=2021-01-02%2023:41:25&code=rYSU24iPmeAbHiFXl8Oyf3PsP0W8Pza8XGbBUDPw
.
.
资料获取
全部工程文件进ESP32工程群(483217976)自行下载,所有文件在 ESP32键盘 目录下。