基于stm32的自定义游戏键盘与RGB(最初为Osu!)

2967年

71

4.

介绍:基于stm32的自定义游戏键盘与RGB(最初为Osu!)

关于:我的名字是xiehi,我是一个15岁的融合360和tinkercad用户。我目前正在使用大型Corexy 3D打印机。

这是什么?

这是一个自定义游戏键盘,有四个热插拔机械键和可定制的RGB led。它由STM32F103Cx单片机供电,通过Arduino IDE编程。它的密钥可以通过提供的驱动程序或通过复合串行通信动态编程。

功能列表:

  • FAST STM32F103CX微控制器允许低输入延迟。
  • USB全速与1000hz轮询率。
  • 热插拔钥匙。
  • eepm设置存储。
  • 在飞行的关键自定义(使用GUI for Windows用户)
  • 三种RGB模式,可以添加更多。
  • 完全开源。
  • arduino兼容。
  • 模块化,便于携带。

钥匙是怎么排列的?

这些键是按照大多数键盘上出现的“A S Z X”键的模式排列的。这意味着,如果两个键是用于游戏(如Z和X轻击在osu),那么其余的可以用于其他应用程序。

我需要什么来建立这个?

除了列出的材料,你还需要先进的焊接技巧(用于小间距LQFP封装和micro-USB接口焊接)。用于焊接LQFP封装的焊接技术被称为拖焊,这可能看起来很复杂,但经过一些实践后并不难做。

供应

材料:

我将我的材料基于Digikey组件,但如果您可以在其他分销商身上找到它们,那么也有效。

  • 定制印刷电路板
  • 1x STM32F103CxT6 (x可以用4、6、8或B替换,但请记住,您需要检查编译代码的大小,以确保它们适合闪存)(DigiKey
  • 2x 7.3mm高度触觉开关(DigiKey
  • 4x反向安装4-PLCC LED(DigiKey
  • 6x 0805 10K欧姆电阻(DigiKey
  • 4x 0805 68欧姆电阻器(DigiKey
  • 5x 0805 1.5K欧姆电阻(DigiKey
  • 3x n沟道SOT-23-3 3.3V级mosfet (DigiKey
  • 3x 0805 100nF电容器(DigiKey
  • 2x 0805 1uF电容(DigiKey
  • 2x 0805 20pf电容器(Digikey
  • 1x 0805 4.7UF电容器(DigiKey
  • 1x MCP1703T-3302T / DB 3.3V电压稳压器(DigiKey
  • 1x 1210 500mA PTC可复位保险丝(DigiKey
  • 1 * USB3090 micro USB A/B接口(DigiKey
  • 1x排90度标题(DigiKey
  • 1x 8MHz HC-49晶体(DigiKey
  • 4x圆形橡胶缓冲器(DigiKey
  • 4x kailh热插拔插座(kbdfans.
  • 4x Cherry MX RGB或等效开关(kbdfans.

工具:

  • 烙铁(最好带有锥形、凿子和刀尖)
  • 助焊剂
  • 3 d打印机
  • ST-Link V2或兼容版本
  • (推荐)焊锡丝

第1步:设计概念 - 硬件

这是对设计概念的概述,它将被分成几个部分。

微控制器

单片机是本课题的主要控制芯片。它作为一个复合USB设备(HID和CDC同时),也处理按键扫描。通常情况下,一个ATMega32u4对于一个普通的键盘项目来说就足够了,但是因为它是为游戏设计的,所以我们需要一个能够提供低延迟的微控制器。

本项目中使用的微控制器是STM32F103系列MCU。它运行在72MHz上,非常适合Arduino IDE。

力量

电源通过USB端口的电源引脚供电,但USB上的电压为5v,而STM32F103单片机只能耐受3.3v。因此,需要一个电压调节器。MCP1703T被选择用于这项任务。

开关

Cherry MX风格的机械开关可能是任何键盘项目的最佳选择,除非有一个特定的开关要求。在这种情况下,使用热插拔插座来方便更换开关。

微控制器使用for循环来读取四个单独的键之间的切换。因为只有4个键,所以不需要矩阵。

RGB LED控制

按钮和RGB LED适用于游戏外围设备上的签名RGB。按钮控制LED模式,颜色和亮度。

第2步:硬件设计 - 原理图

我使用EAGLE来设计PCB。上图是示意图。

这肯定不是最清晰的示意图,但它完成了任务。:)

你可能会注意到并没有ST设计指南推荐的那么多去耦电容。根据我的经验,在所有电源引脚上没有100nF电容的情况下,芯片工作得很好(尽管我不否认增加额外的电容是很好的设计实践)。

第3步:硬件设计 - PCB

PCB布局大约是58mm × 59mm。除了电压调节器周围的元件外,它的大部分元件都放置在底层。

大多数SMD被动部件为080​​5,这对焊接相对容易。然而,STM32 MCU毫无疑问是所有组件的最难焊料。借助该组件的技术将稍后讨论。

附件

第4步:硬件设计 - PCB订购

由于赋予指令不允许上传ZIP文件,因此我已经包含了PCB原理图,电路板和Gerber文件GitHub.

我建议通过PCB服务订购PCB,包括但不限于JLCPCB和PCBWay。在这种情况下,不建议蚀刻您自己的PCB。

第5步:硬件组件 - PCB - 概述

首先,使用PCB组件,您需要多个工具和材料。如果您从未尝试过焊接细尺的表面安装部件,也为某些性交焊接做好准备。

烙铁

对于烙铁,我强烈建议具有多个用于组装该PCB的提示。常规凿子提示穿孔焊接很棒,锥尖非常适合拖动LQFP封装的STM32,以及刀尖是我焊接极其棘手的USB端口的经验。

助势

通量是极其重要的在SMD焊接中,特别是对于像LQFP STM32或USB端口这样的精细组件。我用的是山寨的全球速卖通的不干净的助焊剂,但是任何质量好的助焊剂都可以。请确保助熔剂膏是膏状的,而不是固体松香。

焊料

对于焊料,任何类型都可以,但我建议使用高通量含量的焊料,这样使用体验会更好。

镊子

在SMD焊接中镊子几乎是必需的。请确保您有镊子,否则无源组件将是一个噩梦焊接。

显微镜

虽然不是必需的,强烈建议焊接显微镜,并使工作更容易。如果你没有担心,不要担心!手机的相机可能足够强大,以充当廉价的显微镜,简单地安装显微镜应用。

第6步:硬件组装 - PCB - 焊接 - STM32微控制器

这是硬件组件中焊接最难的部分,这就是为什么在组装任何其他东西之前应该开始焊接的原因。

以下是一些视频,显示了我在本教程中使用的技术(拖动焊接):

提示推荐此组件:锥尖(C型)

  1. 开始时,首先确保您已接地,以避免静电损坏。
  2. 然后使用镊子将芯片放置到PCB上。
  3. 将一点点通量分配到芯片的一个角落,覆盖尽可能小的区域。
  4. 在烙铁尖端熔化一点焊料。
  5. 重新检查芯片对准引脚并尝试在保持对齐的同时保持芯片。这可以用手或用镊子(不要燃烧手指)完成
  6. 将烙铁尖端放在施加助焊剂的地方。磁通量应熄灭,焊料应自动流到垫上。如果它没有流动,请尝试添加一点压力。
  7. 在那里握住尖端,直到此步骤结束直到才会删除它。进行微调,以确保芯片的每个单引脚都可以尽可能完美对齐。
  8. 从芯片上释放焊锡,同时仍将芯片保持到位。
  9. 再检查一次对齐。如果只需要做小的改变,不需要回流焊,你可以简单地用镊子轻轻地在一边施加一些力。如果有很大的偏差,重复步骤6-8。
  10. 一旦对齐完善。将通量涂抹于芯片的所有引脚,用镊子将芯片牢牢固定到位,将焊料施加到铁的尖端,它慢慢地穿过每一排针。
  11. 如果正确完成,焊料应该流到销上,每个销应具有闪亮的关节。如果没有,那么再回复并再次拖动它。如果需要,可以添加更多磁带。
  12. 进行视觉检查以确保每个销都是焊接的。

步骤7:硬件组装- PCB -焊接- USB接口

现在是另一个具有挑战性的组件,USB端口。

这是一个微型usb a /B端口与细针,意味着回流焊。然而,这并不意味着我们不能轻易地手工焊接它。

提示推荐:刀尖(K型)

  1. 首先,放置USB端口,您可能会注意到端口实际上是自我对准的。
  2. 翻转板和焊接在两个通孔金属针底部的USB端口。确保端口保持相对对齐时,你焊接这些引脚。
  3. 把电路板翻回来,现在是时候焊接电源,数据和接地引脚了。
  4. 在别针周围涂抹很多助焊剂。
  5. 在烙铁头末端加入少量焊料。强烈建议使用刀尖或任何扁平且相对较薄的刀尖。
  6. 将带焊料的尖端放在焊盘上。拖动它多次,并添加一点力量,如果需要。你可能会注意到焊接也会流到USB端口的外壳上。这很好,稍后可以删除。
  7. 焊料应该像在STM32上的那样流动。您可以通过插入微USB线并使用万用表来检查从电线的一端到焊盘的连续性来测试连接。还要确保没有任何引脚都短路。

恭喜!这就是焊接的所有努力部分!

第八步:硬件组装- PCB -焊接-功率调节

完成前面的PCB,焊锡的地方,其余的组件。(我将制作一个图表,显示组件的去向,很快就会发布)

提示推荐:凿尖(D型)

PCB前部的电容应为1UF,电阻应为1.5K。

1210占地面积应该有500mA的PTC保险丝,但可以用电线旁路。

步骤9:硬件组装- PCB -焊接-振荡电路

现在我们焊接位于PCB背面的微控制器下面的振荡器电路。

提示推荐:凿尖(D型)

这相对容易。简单地焊接8MHz晶体,然后将0805电容焊接到位。

第10步:硬件组件 - PCB - 焊接 - 热插拔插座

现在我们焊接电路板背面的热插拔插座。

提示推荐:凿尖(D型)

按照丝印上显示的方向放置热交换插座,然后将焊料涂在焊盘上。这应该很简单。

步骤11:硬件组装- PCB -焊接- PCB背面

显示的图片是我的第一次迭代。您可以注意到“错误导向”MOSFET和不同的电容器设置。这是固定在PCB的发布版本中,这意味着您应该以正确的方式使MOSFET定位。

完成PCB的后面。我将很快展示所有组件的图表。现在这是在文中描述的。

PCB背面的设置。一个是RGB LED,一个是没有。两个设置显示在左侧的图片和右侧的图片,左侧的图片没有LED。请注意MOSFET LED和LED电阻如何丢失。

提示推荐:凿尖(D型)

设置1(不含RGB led):

  • 两个10K电阻在顶部靠近焊锡跳线。
  • 一个10k电阻最近的复位按钮和电阻旁边的一个100nf电容。
  • 如图所示,在热插拔插座旁各有一个1.5K电阻。
  • 一个4.7uF电容,放置在任意位置,为功率迹线上的电容器预留。
  • 一个1uF电容,放置在任意位置,为功率迹线上的电容器预留。
  • 在剩余的位置上每个100nF的电容器预留给功率跟踪和电源引脚上的电容器。

设置2(带RGB LED):

  • 所有设置1,除了:
  • MOSFET沿三个地方为MOSFET,以正确的方式为导向,而不是所示的方式。
  • 每个MOSFET一个10K电阻。
  • 四个RGB led,每个键一个,方向如图所示。
  • 一个68欧姆电阻为每个RGB LED在他们的斑点。

第12步:硬件组装 - PCB - 焊接 - 按钮和标头

最后,焊接头部和按钮。这应该是焊接过程中最容易的部分。

提示推荐:凿尖(D型)

步骤13:硬件组装- ST-Link连接

现在是时候在这块板上编写STM32了。

头的前四个引脚是编程引脚。将它们分别连接到相应的ST-Link引脚上。

  • SWDIO——SWDIO
  • SWCLK——SWCLK
  • 接地,接地
  • 3V3 - 3.3V.

第14步:硬件装配 - 连接!

它终于看到了你是否正确地做了一切!将st-link插入计算机,希望没有任何东西爆炸......一切似乎都很好?伟大的!

  1. 下载ST-Link Utility来自STMicroelectronics。
  2. 一旦你打开它,点击目标-连接。
  3. 如果一切正常工作,您应该看到第二张照片所示的成功连接。
  4. 您可以通过游戏查看附加信息,但您也可以现在就断开连接。

步骤15:设计概念-软件

让我们返回设计概念,在我们安装固件之前查看软件。

这是该键盘后面的软件设计概念的概述。

启动

  1. 启动时,微控制器检查其EEPROM中的任何存储设置,如键定义或RGB颜色。如果有,它将初始化设置,如果没有,它将给出这些设置的默认值。
  2. 然后微控制器初始化其他库,并在RGB led上执行一点自检。

循环

代码的这一部分将循环键盘上的持续时间。

  1. 微控制器首先检查是否有以前没有按下的键现在被按下。如果它发现发生了这样的事件,则将在该键上启动一个解除按键计时器,在解除按键时间内不会注册任何按键释放。这是我能想到的在不牺牲延迟的情况下解除开关的最好方法。这个方法基本上是从检测到高而不是低的那一刻开始注册一个按键。这可能不是最健全的方式来解除开关,但它工作。
  2. 然后微控制器检查按键释放,按键已被按下,并不在反弹期间。
  3. 之后,微控制器检查RGB模式按钮的任何压力机。它记录按下/保持RGB模式按钮的时间量,并使用该信息来确定用户是否尝试设置光模式,颜色或亮度。
  4. 如果需要,单片机通过对RGB LED控制进行任何更改来执行代码。
  5. MCU检查任何可用的串行通信从其复合CDC端口。串行通信是我实现实时键更改控制的方式。我写了一个。net c#程序,使用键盘打开的串口与MCU通信,并映射键,而不需要重新编程芯片。串行命令的细节将在后面进一步提到。
  6. 最后,当然,代码会返回。

第16步:装配 - 软件 - 两个选项

将代码上传到STM32微控制器有两个选项。

选项1:预编译的bin文件

这是上传代码的最简单方法。预编译的bin文件已载入常规固件,其中包含串行通信和RGB LED启用。

选项2:通过Arduino IDE手动编译。

这个选项提供了更多的灵活性和可定制性,并且不难做到。利用STM32Duino内核和Arduino IDE进行代码编译。

步骤17:汇编-软件-预编译的Bin文件

如果您选择使用预编译的bin文件进行上传,请执行此步骤。

可以找到预编译的bin文件GitHub.

要用bin文件对微控制器进行编程,请在下载/解压bin文件并打开ST-Link Utility后按照下面的说明进行操作。

  1. 通过单击目标连接到STM32。
  2. 然后在“目标”菜单中,选择“程序和验证”。
  3. 选择您下载的bin文件。
  4. 如图2所示的窗口将弹出。在验证设置是否正确后,单击Start。
  5. 程序应该成功闪烁,led应该亮起来,并通过它们的颜色切换。

步骤18:组装 - 软件 - 手动编译 - STM32Duino Core

如果您选择使用手工编译进行上传,请遵循以下步骤。

  1. 您首先需要在Arduino IDE上安装STM32Duino Core,因为STM32不受本地支持。为此,打开File - Preferences,并将以下url粘贴到附加板管理器url文本框中:http://dan.drown.org/stm32duino/package_stm32duino_index.json.
  2. 单击确定,然后导航到工具 - 板 - 板管理器。
  3. 在“STM32”中搜索“STM32”在“STM32”框中,然后通过STM32Duino安装STM32F1XX / GD32F1XX板库库,如第二张图片所示。
  4. 电路板应正确安装。

第19步:组装 - 软件 - 手动编译 - 轮询间隔更改

默认情况下,STM32Duino有一个内置的USB复合库,这个库为USB HID使用10ms轮询间隔。

虽然10ms轮询间隔对于许多键盘应用程序来说已经足够了,但是对于游戏,尤其是节奏游戏来说,它并不是首选。不过,值得庆幸的是,我们可以通过编辑库的代码来更改轮询间隔。

  1. 要更改轮询间隔,首先导航到这个文件夹:当地C:\Users\ {username} \ AppData \ \ Arduino15 \ \ stm32duino \硬件包\ STM32F1 \{版本名称}\图书馆\ USBComposite(本地appdata文件夹可以通过按下Windows + R并输入“%localappdata%”找到。然后找到Arduino15文件夹,然后继续。)
  2. 到达该文件夹后,在代码编辑器中打开usb_hid.c,如notepad++或visual studio,并使用快捷键Ctrl + F搜索术语“bInterval”。
  3. 将bInterval的值更改为0x01,如下所示。这将轮询间隔减少到1ms,这意味着轮询速率现在是1000hz。

第20步:组装 - 软件 - 手动编译 - Arduino代码

现在我们可以上传实际的代码了。有关代码定制,请参阅下一步。

/ * Xiehi Zhang Aka CrazyBlackstone的OSU键盘代码在GPL V3 Https://www.gnu.org/licenses/gpl-3.0.en下使用STM32F103C8T6和在CC-By-NC-SA代码下发布的Arduino IDE项目。HTML * /#包括的#include  // EEPROM文库本身安装在stm32duino或STM32芯为const char ManufacturerName [] = “Xieshi章”;const char devicename [] =“自定义OSU!键盘”;const char deviceerial [] =“000000000000000000000001”;//引脚定义#define红色pa1 #define绿色pa2 #define蓝色pa3 #define switch1 pa4 #define switch2 pa5 #define switch3 pa6 #define switch4 pa7 #define modesw pb0 //键定义char键[4] = {'a','s','z','x'};//设置#define debouncetime 5 //这是键盘交换机的毫秒中的去抖时间。更改这不会影响延迟,但将确定可能的最短按键。Cherry指定了5ms的最大反弹时间。#define maxpwm 255 //这是最大PWM值。它应该是255默认情况下#define rgmbled //这是可以焊接到PCB的RGB LED的选项。如果您不使用任何LED,请注释出来。 CAUTION: ALTHOUGH RGB LEDS DO NOT WASTE SIGNIFICANT CPU CYCLES, PLEASE DISABLE THIS OPTION IF NO LEDS ARE USED FOR MAXIMUM SPEED! #define serialCommunication // This is the option for enabling/disabling composite serial, which lets you configure keys on the fly with the driver or with serial commands. WARNING: ALTHOUGH STM32F1 IS VERY FAST, LEAVING SERIAL INITIALIZED MAY WASTE CPU CYCLES! #define ledDelay 2 //This is the LED color switching delay/speed. #define colorIncrementDelay 5000 //This is the amount of time the color will stay before incrementing. //variables const int switchPins[4] = {switch1, switch2, switch3, switch4}; const int ledPins[3] = {red, green, blue}; bool oldSwitchState[4] = {LOW, LOW, LOW, LOW}; bool switchState[4] = {LOW, LOW, LOW, LOW}; bool oldModeState = HIGH; unsigned long debounce[4] = {0, 0, 0, 0}; unsigned long modePressTime; unsigned long ledTime; unsigned long colorTime; unsigned long currentMillis; byte lightMode = 0; byte rgbMode = 0; byte colorCount; int brightness = 50; //percent float rgbBrightness[3] = {1, 1, 1}; float targetRGBBrightness[3] = {1, 1, 1}; byte pwmRGB[3] = {maxPWM * rgbBrightness[1] * brightness / 100, maxPWM * rgbBrightness[2] * brightness / 100, maxPWM * rgbBrightness[3] * brightness / 100}; String prefix = "i"; USBHID HID; HIDKeyboard Keyboard(HID); #ifdef serialCommunication USBCompositeSerial CompositeSerial; #endif void setup() { pinMode(modeSw, INPUT_PULLUP); pinMode(switch1, INPUT); pinMode(switch2, INPUT); pinMode(switch3, INPUT); pinMode(switch4, INPUT); #ifdef RGBLED pinMode(red, OUTPUT); pinMode(green, OUTPUT); pinMode(blue, OUTPUT); if (EEPROM.read(7) != 1) { EEPROM.write(0, rgbMode); EEPROM.write(1, lightMode); EEPROM.write(2, brightness); EEPROM.write(7, 1); } rgbMode = EEPROM.read(0); lightMode = EEPROM.read(1); brightness = EEPROM.read(2); #endif USBComposite.setManufacturerString(ManufacturerName); USBComposite.setProductString(DeviceName); USBComposite.setSerialString(DeviceSerial); USBComposite.setVendorId(0x987); #ifdef serialCommunication HID.begin(CompositeSerial, HID_KEYBOARD); if (EEPROM.read(8) != 1) { EEPROM.write(3, keys[0]); EEPROM.write(4, keys[1]); EEPROM.write(5, keys[2]); EEPROM.write(6, keys[3]); EEPROM.write(8, 1); } keys[0] = EEPROM.read(3); keys[1] = EEPROM.read(4); keys[2] = EEPROM.read(5); keys[3] = EEPROM.read(6); #else HID.begin(HID_KEYBOARD); #endif Keyboard.begin(); #ifdef RGBLED digitalWrite(red, HIGH); delay(400); digitalWrite(red, LOW); digitalWrite(green, HIGH); delay(400); digitalWrite(green, LOW); digitalWrite(blue, HIGH); delay(400); digitalWrite(blue, LOW); checkRGBMode(); if (lightMode == 0) turnOnLED(); else turnOffLED(); #endif } void loop() { //key polling and debouncing for (int i = 0; i < 4; i++) { switchState[i] = digitalRead(switchPins[i]); if (switchState[i] && !oldSwitchState[i] && (millis() - debounce[i]) > debounceTime) { debounce[i] = millis(); oldSwitchState[i] = switchState[i]; Keyboard.press(keys[i]); #ifdef RGBLED if (lightMode == 2) turnOnLED(); #endif } else if (!switchState[i] && oldSwitchState[i] && (millis() - debounce[i]) > debounceTime) { debounce[i] = millis(); oldSwitchState[i] = switchState[i]; Keyboard.release(keys[i]); #ifdef RGBLED if (lightMode == 2) turnOffLED(); #endif } } #ifdef serialCommunication if (CompositeSerial.available()) { String command = CompositeSerial.readString(); if (command.startsWith("i")) { prefix = "i"; printInfo(); } else if (command.startsWith("s")) //s:1:k: { String setKey = getValue(command, ':', 1); String setString = getValue(command, ':', 2); int intSetKey = setKey.toInt(); if (setString[0] != ':') keys[intSetKey - 1] = setString[0]; else keys[intSetKey - 1] = NULL; int EPValue = intSetKey + 2; EEPROM.update(EPValue, setString[0]); prefix = "d"; printInfo(); } } #endif //rgb led control, will be disabled if not defined #ifdef RGBLED if (digitalRead(modeSw) != oldModeState) { delay(10); bool modeState = digitalRead(modeSw); if (modeState == LOW && modeState != oldModeState) { modePressTime = millis(); oldModeState = modeState; } else if (modeState == HIGH && modeState != oldModeState) { oldModeState = modeState; if ((millis() - modePressTime) < 300) { if (lightMode < 2) lightMode++; else lightMode = 0; EEPROM.update(1, lightMode); } else if ((millis() - modePressTime) <= 4000 && lightMode != 1) { if (rgbMode < 7) rgbMode++; else rgbMode = 0; EEPROM.update(0, rgbMode); checkRGBMode(); } if (lightMode == 0) turnOnLED(); else turnOffLED(); if (lightMode == 1) { rgbBrightness[0] = 0; rgbBrightness[1] = 0; rgbBrightness[2] = 0; } //turnOnLED(); } } else if (!digitalRead(modeSw) && (millis() - modePressTime) > 4000 && modePressTime) { currentMillis = millis(); if ((currentMillis - modePressTime) < 5000) { brightness = 100; turnOnLED(); } else if ((currentMillis - modePressTime) < 5500) { brightness = 90; turnOnLED(); } else if ((currentMillis - modePressTime) < 6000) { brightness = 80; turnOnLED(); } else if ((currentMillis - modePressTime) < 6500) { brightness = 70; turnOnLED(); } else if ((currentMillis - modePressTime) < 7000) { brightness = 60; turnOnLED(); } else if ((currentMillis - modePressTime) < 7500) { brightness = 50; turnOnLED(); } else if ((currentMillis - modePressTime) < 8000) { brightness = 40; turnOnLED(); } else if ((currentMillis - modePressTime) < 8500) { brightness = 30; turnOnLED(); } else if ((currentMillis - modePressTime) < 9000) { brightness = 20; turnOnLED(); } else if ((currentMillis - modePressTime) < 9500) { brightness = 10; turnOnLED(); } else if ((currentMillis - modePressTime) < 10000) { brightness = 5; turnOnLED(); } else { brightness = 0; turnOnLED(); } EEPROM.update(2, brightness); } if (lightMode == 1) { if ((millis() - ledTime) >= ledDelay) { ledTime = millis(); for (int i = 0; i < 3; i++) { if (rgbBrightness[i] > targetRGBBrightness[i]) rgbBrightness[i] = rgbBrightness[i] - 0.01; else if (rgbBrightness[i] < targetRGBBrightness[i]) rgbBrightness[i] = rgbBrightness[i] + 0.01; if (rgbBrightness[i] > 1) rgbBrightness[i] = 1; } turnOnLED(); } if ((millis() - colorTime) >= colorIncrementDelay) { colorTime = millis(); incrementColor(); } } #endif } #ifdef RGBLED void turnOnLED() { for (int i = 0; i < 3; i++) { pwmRGB[i] = maxPWM * rgbBrightness[i] * brightness / 100; analogWrite(ledPins[i], pwmRGB[i]); } } void turnOffLED() { for (int i = 0; i < 3; i++) { analogWrite(ledPins[i], 0); } } void checkRGBMode() { if (rgbMode == 0) { rgbBrightness[0] = 1; rgbBrightness[1] = 0; rgbBrightness[2] = 0; } else if (rgbMode == 1) { rgbBrightness[0] = 0.7; rgbBrightness[1] = 0.4; rgbBrightness[2] = 0; } else if (rgbMode == 2) { rgbBrightness[0] = 0; rgbBrightness[1] = 1; rgbBrightness[2] = 0; } else if (rgbMode == 3) { rgbBrightness[0] = 0; rgbBrightness[1] = 0.5; rgbBrightness[2] = 0.5; } else if (rgbMode == 4) { rgbBrightness[0] = 0; rgbBrightness[1] = 0; rgbBrightness[2] = 1; } else if (rgbMode == 5) { rgbBrightness[0] = 0.5; rgbBrightness[1] = 0; rgbBrightness[2] = 0.5; } else if (rgbMode == 6) { rgbBrightness[0] = 0.36; rgbBrightness[1] = 0.14; rgbBrightness[2] = 0.12; } else if (rgbMode == 7) //ffffe0 { rgbBrightness[0] = 0.8; rgbBrightness[1] = 0.2; rgbBrightness[2] = 0.1; } } void incrementColor() { if (colorCount < 1) colorCount++; else colorCount = 0; if (colorCount == 0) { targetRGBBrightness[0] = 1; targetRGBBrightness[1] = 0; targetRGBBrightness[2] = 0; } else if (colorCount == 1) { targetRGBBrightness[0] = 0; targetRGBBrightness[1] = 0; targetRGBBrightness[2] = 1; } } #endif #ifdef serialCommunication String getValue(String data, char separator, int index) { int found = 0; int strIndex[] = { 0, -1 }; int maxIndex = data.length() - 1; for (int i = 0; i <= maxIndex && found <= index; i++) { if (data.charAt(i) == separator || i == maxIndex) { found++; strIndex[0] = strIndex[1] + 1; strIndex[1] = (i == maxIndex) ? i+1 : i; } } return found > index ? data.substring(strIndex[0], strIndex[1]) : ""; } void printInfo() { CompositeSerial.print(prefix); CompositeSerial.print(":"); if (keys[0] != ':') CompositeSerial.print(keys[0]); CompositeSerial.print(":"); if (keys[1] != ':') CompositeSerial.print(keys[1]); CompositeSerial.print(":"); if (keys[2] != ':') CompositeSerial.print(keys[2]); CompositeSerial.print(":"); if (keys[3] != ':') CompositeSerial.print(keys[3]); CompositeSerial.print(":"); CompositeSerial.print("v1.0.0"); #ifdef RGBLED CompositeSerial.print(":"); CompositeSerial.print(lightMode); CompositeSerial.print(":"); CompositeSerial.print(rgbMode); CompositeSerial.print(":"); CompositeSerial.print(brightness); #endif CompositeSerial.print("\n"); } #endif

步骤21:汇编—软件—手动编译—自定义

在代码中,您可以自定义一些设置。

防反跳时间

这是检测到键按后键拒绝登记键释放的时间。弹跳时间是必需的,因为机械开关在按下后自然“弹跳”,这可能导致在短时间内注册多个假按键,如果没有使用弹跳计时器。在这种情况下,debounce计时器的设置方式不会影响延迟,所以更改这个值不会使键延迟更多。Cherry指定了5ms的弹跳时间,这个值应该适用于大多数机械开关。

RGB LED

禁用此选项将禁用所有与RGB led控制相关的代码。这可能会节省一些CPU周期,但不足以有效地改变键盘的性能。

串行交流

禁用此选项将禁止通过串行或驱动软件编程键盘,但它将节省几个CPU周期,因为微控制器不再需要不断地听串行命令。虽然理论上微控制器应该在没有串行启动的情况下运行得更快,但速度差异是可以忽略不计的。

(高级)RGB LED颜色和图案

要自定义RGB LED颜色,必须直接编辑代码。checkRGBMode()下定义的颜色存储所有光模式0的颜色(静态),而incrementColor()下定义的颜色存储光模式2的颜色(循环颜色)。

要改变颜色,只需改变rgb亮度[]值。这些值以百分比表示该颜色的亮度,0为0%,1为100%。rgb亮度[]中1-3的数组分别表示红色、绿色和蓝色。

光模式2自定义(在设置中定义):

导致延迟:这表示在光模式2中切换颜色的过程之间的延迟,这意味着如果延迟越高,颜色切换越慢,而如果延迟越低,颜色切换越快。

颜色增加延迟:这表示光模式2中的开关颜色之间的时间量。

第22步:组装 - 软件 - 手动编译

把Arduino的代码上传到STM32上。要做到这一点,请确保您的ST-Link仍然插着。

  1. 打开Arduino IDE和软件设计- Arduino代码中的。ino文件。
  2. 选择如图所示的选项,点击上传,不需要选择端口或程序员。
  3. 如果一切正常,代码应该上传成功,你应该在终端上得到一个成功的消息,如果你的键盘有RGB led,它应该亮起来。

步骤23:组装- 3D打印盒

附件是键盘3D打印盒的STL文件。

要组装,请将PCB放入两件之间,确保切换匹配组件所在,然后关闭组件。您可能需要使用M3x8mm自攻螺钉来固定它到位。

第24步:装配 - 视频动画

这是如何组装键盘的视频动画。

下面的步骤将更详细地介绍程序集。

第25步:装配 - 底部件

要开始装配,请将底部与PCB对齐。

底部有一个切口对应于HC49晶体的位置。确保使用正确的方向,以便晶体放入切口内。

第26步:组装 - 顶板

将开关与顶部件上的切口(用作板上)对齐,然后按下开关。如果您提供一点点力,它们应清洁。

请勿将钥匙性胶带放在现在以防止损坏。

第27步:装配 - 组合和安全

将两个件相结合在一起,如图所示,并在底部(优选地)插入M3x8mm螺钉,以将两个件固定在一起。

您现在可以将Keycaps放在上面。

您也可以在键盘下放置硅胶保险杠,以便它没有幻灯片。

第28步:如何使用

使用这个键盘非常简单。你把它插入你的计算机,它应该作为一个正常的,低延迟,1000hz轮询率HID键盘。

不过,您可以在很大程度上根据自己的喜好定制这个键盘。

RGB LED(如果有)

您可以使用模式按钮更改RGB LED颜色和模式。按下模式按钮小于0.3s更改光模式。

光照模式为:

  1. 静态颜色
  2. 按下键时灯亮
  3. 交替(默认固件是红色和蓝色之间,但可以多种颜色)

按下按钮超过0.3s,但短于5s更改LED颜色,可以通过代码自定义。

保持按钮超过5S将开始更改LED亮度,并且随着按钮保持按钮,亮度将以1S增量下降。亮度将在按钮释放上设置。

所有设置都存储在EEPROM中,并记住,除非删除EEPROM。

关键设置(如果可用)

此键盘具有一个惊人的功能,它是复合串行通信。在代码中启用此功能可能意味着对代码的执行速度较慢,但​​这不应在很大程度上影响延迟。您可以通过串行连接到此设备的串行COM端口,并能够通过串行通信。

串行命令并不具有用户友好性,这意味着不幸的是,动态设置键的最简单方法是使用下一个步骤中显示的GUI。

但是,如果你的设备不能运行。exe文件,那么你可以使用命令:" s:{key number}:{character}:"

  • 例如:“s:1:k:”将键1设置为“k”(小写)。

第29步:额外的驱动程序

我在visual studio中做了一个额外的驱动程序,允许用户动态地定制密钥。这是通过STM32Duino核心包含的惊人的复合USB库实现的。

exe文件打开了GitHub.并没有混淆,这意味着如果你担心它是恶意的,你可以直接通过ILSpy等程序查看它的源代码,或将其上传到VirusTotal。(Windows可能认不出我是可验证的开发人员,所以它可能仍然会发出SmartScreen警告,但可以忽略不计)

步骤30:附加驱动程序-如何使用

首先要启动该程序,Windows可能会警告您这个应用程序的发布者未知并提供智能屏幕错误。要绕过此操作,请单击“更多信息”和“无论如何”。

这个软件不是恶意的,但如果您怀疑它可能是恶意,我建议上传可执行文件以Virustotal或使用Decompiler(如ILSPY)以查看源代码,因为这不是以任何方式混淆。

联系

在驱动程序成功启动后,驱动程序将首先尝试寻找到设备的串行连接。连接设备有两种方式,一种是手动选择端口,另一种是自动检测。虽然自动检测可能听起来很有希望,但事实是,它将连接到任何名为“USB串行设备”的设备,这是不可靠的。手动连接总是首选的,除非自动检测是已知的工作,这可以通过取消自动检测框。

键线

你现在可以在连接设备后用程序动态设置键绑定。要做到这一点,使用面板称为“键绑定”。您可以通过下拉菜单选择键号(参考帮助-键图的键图),然后可以通过文本框输入键绑定值。据我所知,只接受Ascii字符和数字,不幸的是,冒号不是一个可行的选项,因为它被用作分隔符。如果您希望禁用该键,只需在文本框中不输入任何内容,然后按set。

填满两个选项后,只需按set按钮,如果一切顺利,将设置键绑定。即使在断电后,这个新设置也会保存在微控制器上。

原始序列和状态

这两部分用于诊断目的。原始串行是设备发送回的串行数据的显示,状态面板显示存储在设备上的不同设置。

如果有任何错误,请通过评论,私人消息或GitHub报告他们。

31步:故障排除

以下是您在制作此键盘时可能遇到的问题列表。如果这些提示无法解决问题,请随时通过评论或私信与我联系。

我组装了所有的东西,但当我试图连接到微控制器时,ST-Link Utility不认识芯片。

首先,仔细检查微控制器上的焊接情况。有时针脚可能出现焊接,即使没有焊接应用。还要检查针内是否有桥接。我建议你使用显微镜或带有显微镜应用程序的照相手机作为临时显微镜来检查焊接情况。我发现用一把锋利的镊子戳针是检查针是否焊接的好方法。

如果不工作,尝试连接到芯片复位。要做到这一点,在ST-Link实用程序的目标-设置,并确保模式是“在重置下连接”。然后按住重置按钮并单击连接。如果做的正确,程序应该冻结,直到你释放复位,芯片将被成功连接。

芯片通过ST-Link连接和编程正常,但将它连接到USB给我一个错误。

这个问题很可能是由于USB端口没有正确焊接。USB端口是该PCB上的最棘手的焊接部件之一,因此如果发生此问题,我建议使用刀尖,焊料和焊剂进行USB端口引脚。我建议对引脚施加轻微压力以确保它们正确润湿。

要检查USB引脚连接,您可以使用USB电缆,并使用万用表来测量USB公连接器上的引脚之间的连续性,从PCB上的焊盘上。还要确保D +和D-PIN没有短路。

我在2021年5月9日之前用提供的Gerber文件订购了PCB,为什么led不亮?

这是由于我错误的将之前版本的PCB Gerber文件上传到GitHub(非常抱歉)。你可以通过旋转mosfet来解决这个问题,就像在之前的PCB图片中显示的那样。如果你需要任何帮助,请随时PM我。

这个问题现在用新的Gerber文件修复了,但是在订购前请再次检查。

第32步:游戏视频

这是一个OSU!我的朋友在使用我的键盘时制作的游戏视频(并上传在他的频道上,如果您喜欢OSU内容,请随时查看它)。

如果嵌入不起作用,你可能不得不点击“观看Youtube”。

步骤33:结论

恭喜!您构建了自己的自定义键盘!

当我不断努力改进它时,我将保持这个项目更新几个月。

我还要感谢制造商中心俱乐部为此项目提供资金。

现在,谢谢阅读或制作,祝你有美好的一天!

单片机竞赛

亚军
单片机竞赛

是第一个分享

    建议书

    • Arduino比赛

      Arduino比赛
    • 玩具游戏大赛

      玩具游戏大赛
    • 家具比赛

      家具比赛

    4评论

    0.
    DVANCLEEF.

    14天前

    这是_NOT_现在的低成本项目,目前的STM32F103零件的成本每人约20美元(每年2美元起)。

    0.
    CrazyBlackStone

    14天前回复

    不幸的是,我们目前处于全球半导体短缺的情况下,如果不花很多钱,就不可能获得真正的STM32芯片。
    我认为像GD32或CS32这样的克隆版本可以作为微控制器的替代品。

    1
    jessyratfink

    2个月前

    设计得很好!感谢分享 :)