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背光、支持多层配置、实现宏定义等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/783421.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

融资融券两融技巧,开通条件和费用详情4%

融资融券费用 融资融券有两项费用&#xff0c;分别是利息和交易佣金。一般情况下融资年息是5%&#xff0c;融券年息是9.35%&#xff0c;目前有极少数一部分券商能够做到4.5%&#xff0c;融资20万一天只需要25元。 然后咱们再看看交易佣金&#xff0c;目前市面上万分之3到千分…

【基于R语言群体遗传学】-12-超显性与次显性

欢迎先看前面的博客&#xff0c;再继续进行后面的内容&#xff1a; 群体遗传学_tRNA做科研的博客-CSDN博客 当杂合子的适应度超出纯合子的范围时&#xff0c;二倍体能够展现出更多令人着迷的选择实例。这种形式的一种是杂合子优势&#xff0c;或称为“超显性”&#xff0c;其…

(三)前端javascript中的数据结构之链表上

在js中&#xff0c;没有为我们提供原生的数据结构支持的&#xff0c;但是在java中是有提供的。所以需要我们去模拟这种结构实现。 链表中最关键的一个元素&#xff0c;就是头节点&#xff0c;头节点不存储数据&#xff0c;指向第一个节点链表中几乎所有的操作都要从头结点开始。…

数字流的秩

题目链接 数字流的秩 题目描述 注意点 x < 50000 解答思路 可以使用二叉搜索树存储出现的次数以及数字的出现次数&#xff0c;方便后续统计数字x的秩关键在于构建树的过程&#xff0c;如果树中已经有值为x的节点&#xff0c;需要将该节点对应的数字出现次数加1&#xf…

味全财税数字化实践分享,百望云助力实现数电票管理能力升级!

随着金税四期的启动&#xff0c;数电票全面落地应用&#xff0c;这不仅大幅提高了发票处理效率&#xff0c;更重塑了企业的财务管理流程&#xff0c;为企业财务数字化转型指明了新的方向。 杭州味全食品有限公司&#xff08;以下简称“味全”&#xff09;正在推进财税数字化建设…

(2024,稀疏 MoE,大量小专家,参数高效专家检索 PEER,product key 检索)混合百万专家

Mixture of A Million Experts 公和众与号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 1. 简介 2. 方法 3. 实验 0. 摘要 标准 Transformer 架构中的前馈&#xff08;feedforward&a…

轻松集成,高效变现:Flat Ads SDK助力开发者轻松跨越广告变现门槛

在当今的移动应用开发领域,广告变现是开发者们普遍关注的重要话题。如何在不影响用户体验的前提下,最大化地实现广告收益,成为了许多开发者面临的挑战。为此,Flat Ads SDK 应运而生,它以“轻松集成,合规守护,高效变现”为核心理念,帮助开发者轻松解决流量变现难题。 一、高效变…

tauri + vue3 如何实现在一个页面上局部加载外部网页?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

惟客数据Q2荣誉成绩单新鲜出炉

作为众多头部企业客户的数字化经营合作伙伴 WakeData惟客数据一直坚持以客户为中心&#xff0c;以数据驱动 致力于让数据智能商业落地更敏捷 凭借值得信赖的客户经营数字化和资源管理数字化实力 惟客数据在2024年第二季度斩获多项荣誉 1、 第一新声《2023年度中国高科技高…

从入门到精通:Shopee,lazada,temu自养号测评成本、步骤、技巧详解

测评对于卖家来说是一种成本低回报快的推广方式&#xff0c;可以减少高额的平台广告费用&#xff0c;因此是一种很好的辅助手段&#xff0c;对商品的曝光、流量、转化和权重等方面起到了很好的辅助作用 建议还是自己精养一批账号&#xff0c;账号在自己手里比较安全可控&#…

python中getattr/setattr/hasattr/delattr函数都是干什么的?

目录 1、getattr&#xff1a;动态获取属性 &#x1f50d; 1.1 动态获取属性 1.2 默认值处理技巧 1.3 实战案例&#xff1a;配置文件动态加载 2、setattr&#xff1a;动态设置属性 &#x1f6e0; 2.1 修改对象属性 2.2 新增属性场景 2.3 应用场景&#xff1a;类的动态配置…

Linux 命令历史、补全和正则表达式

1.命令历史就和windows的ctrlz一个概念&#xff0c;就是返回上一次的命令。 按下小键盘上下切换 ctrlc可以终止本次输入&#xff0c;进行下一次输入。 2.tab键可以自动补全&#xff08;有点鸡肋&#xff09; 3.正则表达式 类似于word的快速搜索&#xff0c;Linux也是用*和…

浅析C++引用

浅析C引用"&" ​ C中引入了一个新的语言特性——引用(&)&#xff0c;它表示某一对象的别名&#xff0c;对象与该对象的引用都是指向统一地址。那么我们就来看看关于引用的一些知识点吧&#x1f9d0; 特性 引用在定义时必须初始化一个变量可以有多个引用引…

zdppy_api+vue3+antd前后端分离开发,使用描述列表组件渲染用户详情信息

后端代码 import api import upload import time import amcomment import env import mcrud import amuserdetailsave_dir "uploads" env.load(".env") db mcrud.new_env()app api.Api(routes[*amcomment.get_routes(db),*amuserdetail.get_routes(db…

记一次微信小程序逆向

扫码领取网安教程 遇到瓶颈了&#xff0c;不知道该干什么&#xff0c;突然想到学校的小程序 闲来无事就看一看 抓包下来的数据是这样的&#xff0c;嗯&#xff0c;下机&#xff08;hhh 一、反编译程序 加密嘛&#xff0c;之前抓了看到是加密就放弃了&#xff0c;现在重新弄一…

轻松转换!两款AI工具让word秒变ppt!

想把Word文档一键生成PPT&#xff0c;过去有一个很常见的做法&#xff1a;先在Word文档中设置标题样式&#xff0c;通过标题样式来分隔每一部分&#xff0c;之后导出为PPT&#xff0c;就能得到一份PPT的雏形&#xff0c;但这种方法无法对PPT自动进行美化&#xff0c;即得到的只…

js替换对象内部的对象名称或属性名称-(第二篇)递归

1.代码示例&#xff1a; function replaceKey(obj, oldKey, newKey) {// 如果不是对象或者oldKey不存在&#xff0c;直接返回原对象if (typeof obj ! object || !obj || !(oldKey in obj)) return obj;// 如果是数组&#xff0c;遍历数组每个元素if (Array.isArray(obj)) {obj…

【Python】一文向您详细介绍 np.inner()

【Python】一文向您详细介绍 np.inner() 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通本硕&#xff0c;曾…

君方智能设计平台-对象持久化技术方案

1.背景介绍 1.1序列化功能 序列化是将复杂的图形对象、数据结构或对象转换成一种可被存储、传输或分享的格式的过程。这个格式通常是二进制或文本形式&#xff0c;能够轻松地保存在文件中或通过网络发送到其他应用程序。序列化的目的是将数据持久化&#xff0c;使其在不同时间…

【学术会议征稿】2024年第四届工商管理与数据科学学术会议(BADS 2024)

2024年第四届工商管理与数据科学学术会议(BADS 2024) 2024 4th International Conference on Business Administration and Data Science 2024年第四届工商管理与数据科学学术会议(BADS 2024)将于2024年10月25-27日在新疆喀什召开&#xff0c;由喀什大学支持本次会议。会议旨…