STM32蓝牙HID实战:打造低功耗、高性能的客制化键盘

一、项目概述

本项目旨在使用STM32单片机打造一款功能强大的蓝牙客制化键盘,它拥有以下特点:

  • 九键布局,小巧便携: 满足日常使用需求,方便携带。
  • 全键可编程: 所有按键和旋钮均可通过电脑软件自定义快捷键,实现个性化功能。
  • 蓝牙无线连接: 摆脱线缆束缚,提供更自由的使用体验。
  • 二、硬件设计

    2.1 硬件平台
  • 主控芯片: STM32F103C8T6
  • 蓝牙模块: HC-05
  • 按键: 机械轴*9
  • 旋钮编码器: EC11
  • 其他: 电焊、杜邦线、PCB板等
  • 2.2 电路原理图

    2.3 PCB设计
  • 使用KiCad等EDA软件进行PCB设计,确保电路稳定可靠。
  • 采用合理的布局,优化空间利用率,打造紧凑的外观。
  • 三、软件设计

    3.1 开发环境
  • IDE: Keil MDK
  • 编译器: ARMCC
  • 调试器: ST-Link
  • 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");
    }
    

    代码解释:

  • 这部分代码首先初始化了STM32的USART1,用于与HC-05蓝牙模块通信。
  • 随后,代码发送一系列AT指令配置蓝牙模块:
  • 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;
    }
    

    代码解释:

  • 这段代码是按键扫描函数的框架。你需要根据你的硬件电路实现具体的按键扫描逻辑。
  • 一般来说,你需要使用GPIO模拟矩阵键盘的扫描方式,检测哪个按键被按下。
  • 函数最后需要返回被按下的按键码,如果没有按键按下则返回0。
  • 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; // 未旋转
    }
    

    代码解释:

  • 这段代码实现了读取旋转编码器数值的逻辑。
  • 它首先读取编码器的A、B两相的电平状态。
  • 然后通过对比当前状态和上次状态,判断编码器的旋转方向。
  • 如果顺时针旋转,返回1;逆时针旋转,返回-1;没有旋转,返回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 函数替换为你实际使用的串口发送函数。
  • 四、电脑端软件

    为了实现自定义快捷键功能,你需要开发一个电脑端软件,该软件需要实现以下功能:

    1. 连接蓝牙键盘: 搜索并连接你的蓝牙键盘设备。
    2. 接收数据: 持续接收来自蓝牙键盘的数据。
    3. 解析数据: 根据预定义的数据格式解析接收到的数据,识别按键事件和旋钮事件。
    4. 执行快捷键: 根据用户预先设置的快捷键映射关系,执行相应的操作。例如,用户可以将 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()
    

    代码说明:

    1. 导入库: 导入 bluetooth 库用于蓝牙通信,导入 keyboard 库用于模拟键盘操作。
    2. 定义常量: 定义蓝牙键盘的设备地址 BT_ADDR 和服务 UUID BT_UUID
    3. handle_data() 函数: 该函数用于处理接收到的数据,根据数据类型和数据值执行相应的操作。
    4. main() 函数: 该函数是程序的入口点,负责搜索蓝牙设备、连接设备、接收数据并调用 handle_data() 函数处理数据。
    5. 模拟快捷键: 在 handle_data() 函数中,你可以使用 keyboard 库提供的函数模拟键盘操作来实现快捷键功能。例如,使用 keyboard.press_and_release('ctrl+c') 模拟 Ctrl+C 快捷键。

    注意:

  • 你需要将 BT_ADDR 替换为你的蓝牙键盘的实际地址。
  • 你需要根据你的键盘硬件和数据协议修改代码。
  • 你需要根据你的需求修改 handle_data() 函数中的快捷键映射关系。
  • 五、总结

    本文介绍了如何使用STM32制作一款蓝牙客制化键盘,并详细讲解了硬件设计、软件设计以及数据传输协议等方面的内容。通过该项目,你可以学习到蓝牙通信、按键扫描、编码器读取等知识,并锻炼嵌入式系统开发能力。

    你可以根据自己的需求,进一步扩展键盘的功能,例如增加RGB背光、支持多层配置、实现宏定义等。

    作者:极客小张

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32蓝牙HID实战:打造低功耗、高性能的客制化键盘

    发表回复