STM32蓝牙HID实战:打造低功耗、高性能的客制化键盘
一、项目概述
本项目旨在使用STM32单片机打造一款功能强大的蓝牙客制化键盘,它拥有以下特点:
二、硬件设计
2.1 硬件平台
2.2 电路原理图
2.3 PCB设计
三、软件设计
3.1 开发环境
3.2 软件架构
3.3 代码实现
3.3.1 蓝牙初始化
void Bluetooth_Init(void)
{
// 设置蓝牙模块波特率为9600
USART1_Init(9600);
// 发送AT指令进入AT模式
USART1_SendString("AT\r\n");
// 设置蓝牙模块名称
USART1_SendString("AT+NAME=CustomKeyboard\r\n");
// 设置蓝牙模块配对密码
USART1_SendString("AT+PIN=1234\r\n");
// 设置蓝牙模块为从模式
USART1_SendString("AT+ROLE=0\r\n");
// 开启蓝牙模块
USART1_SendString("AT+CMODE=1\r\n");
}
代码解释:
AT
: 测试指令,确保蓝牙模块连接正常。AT+NAME=CustomKeyboard
: 设置蓝牙模块名称为 "CustomKeyboard"。AT+PIN=1234
: 设置蓝牙模块配对密码为 "1234"。AT+ROLE=0
: 将蓝牙模块设置为从模式,等待连接。AT+CMODE=1
: 允许蓝牙模块连接任何地址的设备。3.3.2 按键扫描
uint8_t KeyScan(void)
{
// 扫描按键矩阵
// ...
// 返回按键值
return key_value;
}
代码解释:
3.3.3 旋钮读取
int8_t Encoder_Read(void)
{
static uint8_t last_state = 0;
uint8_t current_state = (GPIOB->IDR & 0x03); // 读取A、B相电平
if (current_state != last_state) {
if ((current_state == 0x01 && last_state == 0x03) ||
(current_state == 0x03 && last_state == 0x02) ||
(current_state == 0x02 && last_state == 0x00) ||
(current_state == 0x00 && last_state == 0x01)) {
return 1; // 顺时针旋转
} else {
return -1; // 逆时针旋转
}
}
last_state = current_state;
return 0; // 未旋转
}
代码解释:
3.3.4 数据处理
键盘使用特定的数据格式将按键信息和旋钮信息发送给电脑:
0x01
:代表按键按下/弹起事件。0x02
:代表旋钮旋转事件。0x00
表示逆时针旋转, 0x01
表示顺时针旋转。定义按键码:
#define KEY_1 0x01
#define KEY_2 0x02
// ...
#define KEY_9 0x09
uint8_t data_buffer[2];
void Data_Process(uint8_t key_value, int8_t encoder_value) {
if (key_value != 0) {
// 处理按键事件
data_buffer[0] = 0x01; // 数据类型:按键
data_buffer[1] = key_value; // 按键码
} else if (encoder_value != 0) {
// 处理旋钮事件
data_buffer[0] = 0x02; // 数据类型:旋钮
data_buffer[1] = (encoder_value > 0) ? 0x01 : 0x00; // 旋转方向
}
}
3.3.5 蓝牙发送
void Bluetooth_Send(uint8_t *data, uint8_t len) {
// 通过蓝牙串口发送数据
for (uint8_t i = 0; i < len; i++) {
USART1_SendByte(data[i]);
}
}
代码解释:
data
和数据的长度 len
作为参数。USART1_SendByte
函数发送出去。代码实例:
// 假设 data_buffer 已经填充了要发送的数据
uint8_t data_buffer[2] = {0x01, 0x03}; // 例如:按键事件,按键码为 KEY_3
// 通过蓝牙发送数据
Bluetooth_Send(data_buffer, sizeof(data_buffer));
完整代码示例:
// ... 其他代码 ...
// 蓝牙发送函数
void Bluetooth_Send(uint8_t *data, uint8_t len) {
// 通过蓝牙串口发送数据
for (uint8_t i = 0; i < len; i++) {
USART1_SendByte(data[i]);
}
}
// 主函数
int main(void) {
// ... 初始化代码 ...
while (1) {
// 扫描按键
uint8_t key_value = KeyScan();
// 读取旋钮状态
int8_t encoder_value = Encoder_Read();
// 处理数据
Data_Process(key_value, encoder_value);
// 如果有数据需要发送
if (data_buffer[0] != 0) {
// 通过蓝牙发送数据
Bluetooth_Send(data_buffer, sizeof(data_buffer));
// 清空数据缓冲区
data_buffer[0] = 0;
}
}
}
注意:
KeyScan
, Encoder_Read
和 Data_Process
函数的具体实现。USART1_SendByte
函数替换为你实际使用的串口发送函数。四、电脑端软件
为了实现自定义快捷键功能,你需要开发一个电脑端软件,该软件需要实现以下功能:
- 连接蓝牙键盘: 搜索并连接你的蓝牙键盘设备。
- 接收数据: 持续接收来自蓝牙键盘的数据。
- 解析数据: 根据预定义的数据格式解析接收到的数据,识别按键事件和旋钮事件。
- 执行快捷键: 根据用户预先设置的快捷键映射关系,执行相应的操作。例如,用户可以将
KEY_1
映射为Ctrl+C
快捷键,将旋钮顺时针旋转映射为音量+
操作。
以下是一个使用 Python 实现的电脑端软件示例代码:
import bluetooth
import keyboard # 需要安装 keyboard 库: pip install keyboard
# 蓝牙键盘设备地址
BT_ADDR = "00:11:22:33:44:55"
# 蓝牙服务UUID
BT_UUID = "00001124-0000-1000-8000-00805F9B34FB"
def handle_data(data):
"""处理接收到的数据"""
data_type = data[0]
data_value = data[1]
if data_type == 0x01: # 按键事件
key_code = data_value
print(f"按键事件: {key_code}")
# TODO: 根据 key_code 执行相应的快捷键操作
elif data_type == 0x02: # 旋钮事件
direction = "顺时针" if data_value == 0x01 else "逆时针"
print(f"旋钮事件: {direction}")
# TODO: 根据 direction 执行相应的操作
def main():
"""主函数"""
print("正在搜索蓝牙设备...")
devices = bluetooth.discover_devices(lookup_names=True)
for addr, name in devices:
if addr == BT_ADDR:
print(f"找到设备: {name} ({addr})")
break
else:
print("未找到设备")
return
print("正在连接...")
sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
sock.connect((BT_ADDR, 1)) # 假设蓝牙服务端口号为 1
print("连接成功")
try:
while True:
data = sock.recv(1024)
if data:
handle_data(data)
except KeyboardInterrupt:
print("程序退出")
finally:
sock.close()
if __name__ == "__main__":
main()
代码说明:
- 导入库: 导入
bluetooth
库用于蓝牙通信,导入keyboard
库用于模拟键盘操作。 - 定义常量: 定义蓝牙键盘的设备地址
BT_ADDR
和服务 UUIDBT_UUID
。 handle_data()
函数: 该函数用于处理接收到的数据,根据数据类型和数据值执行相应的操作。main()
函数: 该函数是程序的入口点,负责搜索蓝牙设备、连接设备、接收数据并调用handle_data()
函数处理数据。- 模拟快捷键: 在
handle_data()
函数中,你可以使用keyboard
库提供的函数模拟键盘操作来实现快捷键功能。例如,使用keyboard.press_and_release('ctrl+c')
模拟Ctrl+C
快捷键。
注意:
BT_ADDR
替换为你的蓝牙键盘的实际地址。handle_data()
函数中的快捷键映射关系。五、总结
本文介绍了如何使用STM32制作一款蓝牙客制化键盘,并详细讲解了硬件设计、软件设计以及数据传输协议等方面的内容。通过该项目,你可以学习到蓝牙通信、按键扫描、编码器读取等知识,并锻炼嵌入式系统开发能力。
你可以根据自己的需求,进一步扩展键盘的功能,例如增加RGB背光、支持多层配置、实现宏定义等。
作者:极客小张