亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Manufacturing Technology >> 制造工艺

Arduino 的 64 键原型键盘矩阵

组件和用品

Arduino UNO
× 1
1N4148 – 通用快速切换
× 64
触觉开关,顶部驱动
× 64
Pin headers
× 1
74HC595 移位寄存器
× 1

必要的工具和机器

烙铁(通用)

应用和在线服务

Arduino IDE

关于这个项目


更新:我已经改进了代码,请参阅最后的“更新”。

我目前正在从事一个将有一个集成键盘的项目,这提出了一个问题:如何在开发板原型中包含一个键盘?我不能使用 USB 键盘或现有的基于 Arduino 的键盘,因为实际项目中的键盘直接连接到处理所有其他功能的微控制器。所以我设计了这个基于 PCB 的基本 64 键原型键盘矩阵。

此 PCB 不含任何 IC(集成电路)。键盘矩阵的行和列直接连接到排针,以便键盘可以连接到 Arduino 或任何其他微控制器。它非常适合制作包含集成键盘的项目原型。

我已经包含了详细的、大量注释的代码,以使其适用于任何具有足够可用 I/O 引脚的 Arduino 兼容开发板——需要 11 个引脚。键盘有 64 个键,包括 shift、caps、ctrl、alt、fn 和“special”修饰符。还有六个额外的键可用于您喜欢的任何操作。每个键的功能都可以单独定义,包括当修改器处于活动状态时每个键的功能。在我看来,这显着 比严重限制您自定义按键行为能力的现有键盘代码更有用。

提供的代码将文本打印到串行。如果您希望文本转到其他地方,可以轻松更改此设置。

关于程序大小的说明:

我提供的代码非常大,因为它没有使用任何现有的库。我完全从头开始编写了这段代码,以实现我所需的可定制性。在 Arduino UNO 上,这将使用 9100 字节 (28%) 的程序存储空间,全局变量使用 394 字节 (19%) 的动态内存。

我的代码可能会更高效,键盘的库和草图肯定更小,但这是我设计的唯一方法,可以为每个键的每个修饰符提供完全的灵活性。它还考虑了现实世界的键盘使用情况。例如,我的代码在启用 Caps Lock 的同时按下 Shift 键将产生一个小写字母。默认情况下,按住 FN 键的同时按 ESC 不会执行任何操作。但这种行为是完全可定制的,因此您可以随心所欲地更改它。

补给:

  • 定制 PCB
  • 6x6x5mm 触觉瞬时按钮 (x64)
  • 1N4148 开关二极管 (x64)
  • 1x8 排针,母头或公头 (x2)
  • 74HC595 移位寄存器
  • 跳线
  • 面包板
  • Arduino Uno 或任何兼容 Arduino 的微控制器开发板

第 1 步:键盘矩阵的工作原理

为什么需要键盘矩阵?

这个键盘有 64 个键。如果要将这些按钮中的每一个都直接连接到开发板,则需要 64 个 I/O 引脚。那是很多 引脚和比大多数开发板可用的更多。为了得到一个更合理的数字,我们可以使用键盘矩阵,它只需要与键数的平方根(向上取整)相等的引脚数。

设置了一个键盘矩阵,因此连接了一行中的每个键开关并连接了列中的每个键开关。当我们想查看按下了哪些键时,我们“激活”第一行,然后检查每一列。如果特定列处于活动状态,我们知道该列和第 1 行中的键已被按下。然后我们停用第 1 行并激活第 2 行,然后再次检查所有列。激活所有行后,我们只需从第一行重新开始。

我们如何扫描键盘矩阵:

因为我们正在使用微控制器,“激活”意味着将该行设置为低或高。在这种情况下,我们将行设置为低,因为我们使用的是微控制器的内置上拉电阻 在我们的列输入引脚上。如果没有上拉或下拉电阻,输入引脚会因接口而做出不可预测的反应,从而记录错误的按钮按下。

Arduino UNO 中使用的 ATmega328P 微控制器没有任何内置下拉电阻,只有上拉电阻。所以我们正在使用这些。上拉电阻将每个输入引脚连接到 5V,确保在按下按钮之前它们始终读取为高电平。

所有行通常也设置为高电平,这可以防止列引脚连接到行引脚,无论是否按下按钮。但是当我们准备好检查一行时,我们可以将该行设置为 LOW .如果按下该行中的按钮,这将为将输入引脚拉至地提供路径——导致该列现在读为低 .

所以,总结一下:我们将一行设置为低,然后我们检查哪些列引脚现在正在读取低。这些对应于按下的按钮。这个过程非常发生 很快,所以我们可以每秒多次扫描整个键盘。我的代码将其限制为每秒 200 次,这样可以平衡性能、弹跳并确保每次按键都被捕获。

二极管、重影和 n 键翻转:

电路中的二极管可防止在按住某些按钮组合时意外按下按键。二极管只允许电流沿一个方向流动,从而防止重影。如果我们不使用二极管,那么按下某些键可能会导致另一个未按下的键被注册,因为电流会流过相邻的开关。这在简化图形中显示,其中按下任何三个相邻的键会导致第四个角落的键被注册,即使它没有被按下。二极管可以防止这种情况发生并启用“n 键翻转”,这意味着我们可以按任意组合按任意数量的键而不会出现任何问题。

使用移位寄存器保存引脚:

聪明的你们可能注意到我说键盘矩阵需要的引脚数等于键数的平方根,但我 说我的键盘设计只需要11针。应该是16吧?不,因为我们使用的是 74HC595 移位寄存器。这个移位寄存器让我们只使用 Arduino 的三个 I/O 引脚来控制多达八个输出引脚。这三个引脚让我们向移位寄存器发送一个字节(八位),移位寄存器将其八个输出引脚设置为高电平或低电平。通过将移位寄存器用于输出行引脚,我们节省了 5 个完整的 I/O 引脚!

“那么为什么不在输入引脚上也使用移位寄存器呢?”你问。最简单的答案是输入需要不同类型的移位寄存器,而我手头没有这种类型。但使用移位寄存器进行输入也会使我们读取列的方式复杂化,并可能导致噪声和“弹跳”问题。我只想说,在这种情况下,我不需要承担这个头疼问题。

原理图.pdf

第 2 步:PCB 设计

原理图设计

现在您了解了键盘矩阵的工作原理,我的 PCB 设计应该很简单。我在 KiCAD 中设计了 PCB(对不起 Eagle 评委)并从原理图开始。我简单地放置了一个按钮符号和一个二极管符号,然后复制并粘贴它们,直到我拥有 64 个键的网格。然后我添加了两个 1x8 针头符号,一个用于行,一个用于列。按钮的一侧按列连接,按钮的另一侧按行连接。

下一步是为每个原理图符号分配 PCB 封装。 KiCAD 包含的封装库内置了必要的封装。当您设计自己的 PCB 时,您必须非常小心地选择正确的封装,因为这些封装实际上最终会出现在您的 PCB 上。有许多组件具有非常相似的封装,但间距略有不同。确保选择与您的实际组件相匹配的组件。

脚印和密码

请特别注意引脚编号。 KiCAD 有一个奇怪的问题,即原理图二极管符号引脚编号与封装引脚编号不匹配。这会导致二极管反向,考虑到它们的极性,这是一个严重的问题。我没有发现那个错误,不得不扔掉我订购的第一批 PCB。为了在第二次修订中解决这个问题,我必须创建一个自定义二极管封装,并交换引脚编号。

PCB 布局

完成原理图并分配封装后,我开始进行实际的 PCB 布局。电路板轮廓是在 Autodesk Fusion 360 中创建的,导出为 DXF,然后在 Edge Cuts 层上导入 KiCAD。之后的大部分工作只是简单地排列按钮,使其具有类似于普通键盘的布局。

然后所有的痕迹都被路由了。因为实际的按钮布局与原理图中整洁的矩阵不匹配,这部分有点乱,我不得不在某些地方求助于使用过孔。过孔可让您将走线从一层布线到另一层,这在您使用具有大量重叠走线的 2 层板时非常有用。最后,我添加了填充区域,因为这是很好的做法。

PCB 制造

随着电路板的设计,我简单地绘制了所有图层并将它们添加到一个 zip 文件夹中。该文件夹在此处提供,可以直接上传到 JLCPCB 等 PCB 制造服务。

这是 PCB Gerber 文件的链接:https://drive.google.com/file/d/10YriLLtghV0Sb84Wm...

第 3 步:PCB 组装

这是整个项目中最简单但最乏味的步骤。只需将所有组件焊接到位。它们都是通孔元件,易于焊接。特别注意二极管的方向。二极管上的标记应与PCB上的标记一致。

根据我的经验,最简单的方法是用第三只手将 PCB 固定到位,然后先将所有二极管放入。然后翻转电路板并将它们全部焊接,然后夹住引线。然后放置所有按钮并焊接它们。然后将排针焊接到位。您可以使用母头或公头排针,这完全取决于您。如果您使用男性头并放在 下方 板,间距正确,可以将它们直接粘贴到面包板中。

第 4 步:将键盘连接到您的 Arduino

布线看起来很复杂,但当你注意一切的进展时,它真的没那么糟糕。

八根跳线将从列标题直接连接到以下 Arduino 引脚:

  • 第 1 列> A0
  • 第 2 列> A1
  • 第 3 列> A2
  • 第 4 栏> A3
  • 第 5 栏> A4
  • 第 6 栏> A5
  • 第 7 列> 5
  • 第 8 列> 6

接下来,将 74HC595 移位寄存器放在跨越中间休息点的面包板上。注意芯片的方向!圆点表示引脚 1

查看接线图以了解 5V 和接地连接的位置。移位寄存器有两个引脚连接到5V,两个引脚连接到地。

只需三根线即可将移位寄存器连接到 Arduino。它们是:

  • Shift(时钟)11> 4
  • Shift (Latch) 12> 3
  • Shift(数据)14> 2

由于一些愚蠢的原因,移位寄存器的输出引脚以违反直觉的方式排列。将它们连接到行引脚时,请特别注意移位寄存器引脚图。它们是:

  • 第 1 行> Shift (Q0) 15
  • 第 2 行> Shift (Q1) 1
  • 第 3 行> Shift (Q2) 2
  • 第 4 行> Shift (Q3) 3
  • 第 5 行> Shift (Q4) 4
  • 第 6 行> Shift (Q5) 5
  • Shift 7> Shift (Q6) 6
  • Shift 8> Shift (Q7) 7

没有任何东西连接到 Arduino 0 或 1 引脚,因为它们也用于串行端口并导致冲突。

第 5 步:刷入 Arduino 代码

使用此处提供的代码刷新您的 Arduino。这没有什么特别之处,只需像上传任何其他 Arduino 项目一样上传代码即可。

代码中的所有内容都有详细的注释,您可以通读一遍,因此这里不再赘述。基本上,引脚设置为输入和输出。主循环只包含一个计时器功能。每 5ms 调用一次函数来扫描键盘。在检查每一列之前,该函数调用一个单独的函数来设置移位寄存器。按下的键将它们的值打印到串行。

如果您想更改按键时打印的内容,只需更改 Serial.print("_"); 在与条件对应的 if 语句中。例如,您可以设置按住 FN 并按 N 时打印的内容。对于每个其他带有每个修饰符的键也是如此。

在这段代码中,许多键根本不做任何事情,因为它只是打印到串行。这意味着退格键没有效果,因为您无法从串行监视器中删除——该数据已被接收。但是,如果您愿意,您可以自由使用更改。

在您自己的项目中使用键盘

打印到串行是很好的,但这并不是这款键盘的真正意义所在。此键盘的用途是为更复杂的项目制作原型。这就是为什么很容易改变功能的原因。例如,如果您想将键入的文本打印到 OLED 屏幕,您可以简单地替换每个 Serial.print(display.print( 或任何您的特定显示器需要。 Arduino IDE 的全部替换 工具非常适合快速一步替换所有这些内容。

感谢您阅读本文,希望这款键盘对您的项目有所帮助!

ProtoKeyboardV1.1-Shifted.ino



1/30/21 更新:

这个新代码完全重写,性能比原始代码更好。这样做主要是为了解决我的算法的问题,即每次按下键时都无法输入字符。检查原始代码以确保特定键不是最后一个 要按下的键。如果按住 2 个或更多键,这会导致问题,这会导致输入类似“fgfgfgfgfgfgfgfgfgfg”的内容。这也可以防止您快速地一遍又一遍地输入相同的键,例如当您在单词“bummer”中输入两个 m 时。

新代码解决了这两个问题,也更加优雅。我们没有跟踪要按下的最后一个键,而是检查整个键盘的状态,并将其与最后一个循环中的整个键盘状态进行比较。这意味着循环可以运行得更快,并且您还可以非常快速地一遍又一遍地输入相同的键。性能得到显着提高。所有字符也都在顶部的数组中,因此您可以轻松找到它们并更改它们。每个修饰符都有独立的数组。代码也更短。

这种新方法的唯一缺点是它使用了更多的动态内存——尽管它使用的程序空间要少得多。在 Arduino Uno 上,它现在使用:3532 字节 (10%) 的程序存储空间和 605 字节 (29%) 的动态内存。

作为额外的好处,此代码在 ARM Cortex-M4 等快速微控制器上也能正常工作。检查键盘的间隔计时器以微秒为单位,因此它会在任何板上执行相同的操作。您还可以轻松调整检查键盘的频率。默认情况下,它每 500 微秒运行一次循环。检查键盘需要 8 次循环,总共 4000 微秒(4 毫秒,或每秒 250 次)——尽管如果微速度不够快,无法快速运行代码,则可能需要更长的时间。

代码

  • ProtoKeyboardV1.1-Shifted.ino
  • ProtoKeyboardV1-Bits.ino
ProtoKeyboardV1.1-Shifted.inoArduino
无预览(仅限下载)。
ProtoKeyboardV1-Bits.inoArduino
更新 Arduino 代码
/* Sketch for Prototyping Keyboard V1.2 * by Cameron Coward 1/30/21 * * 在 Arduino Uno 上测试。需要定制 PCB * 和一个 74HC595 移位寄存器。 * * 更多信息:https://www.hackster.io/cameroncoward/64-key-prototyping-keyboard-matrix-for-arduino-4c9531 */const int rowData =2; // 移位寄存器数据引脚为rowsconst int rowLatch =3; // 移位寄存器锁存管脚为rowsconst int rowClock =4; // 行的移位寄存器时钟引脚// 这些是我们的列输入引脚。引脚 0 和引脚 1 未使用,// 因为它们会导致问题(大概是因为它们是 TX 和 RX)const int colA =A0; const int colB =A1; const int colC =A2; const int colD =A3;const int colE =A4;const int colF =A5;const int colG =5;const int colH =6;// shiftRow 是每行所需的移位寄存器字节,rowState 将包含每个行的按键rowconst字节行移位[] ={B01111111,B10111111,B11011111,B11101111,B11110111,B11111011,B11111101,B11111110};字节的RowState [] ={B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000};字节prevRowState [ ] ={B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000};//未按下修饰符的键的ASCII码。修饰符为 NULL (0),// 因为我们将分别检查它们,不应打印它们的值。const char key[] ={ 0, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 0, 9, 113, 119, 101, 114, 116, 121, 117, 105, 111, 112, 91, 93, 92, 7, 97, 115, 100, 100, 100 106, 107, 108, 59, 39, 0, 0, 122, 120, 99, 118, 98, 110, 109, 44, 46, 47, 0, 0, 0, 0, 32, 0, 0, 0 0, 0, 0, 0};// 按下shift键和大写字母的ASCII码是activeconst char capsShiftKey[] ={ 0, 33, 64, 35, 36, 37, 94, 38, 42, 40, 41, 95, 43, 0, 9, 113, 119, 101, 114, 116, 121, 117, 105, 111, 112, 123, 125, 124, 7, 97, 115, 100, 10, 10, 10, 4 107, 108, 58, 22, 0, 0, 122, 120, 99, 118, 98, 110, 109, 44, 46, 47, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0};// 按下 shift 键的 ASCII 码。const char shiftKey[] ={ 0, 33, 64, 35, 36, 37, 94, 38, 42, 40, 41, 95, 43, 0, 9, 81, 87, 69, 82, 84, 89, 85, 73, 79, 80, 123, 125, 124, 7, 65, 83, 68, 70, 71, 72, 74, 6, 75, 7 58, 22, 0, 0, 90, 88, 67, 86, 66, 78, 77, 44, 46, 47, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0};// 按下ctrl键的ASCII码.const char ctrlKey[] ={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0};// 按下spcl的键的ASCII码.const char spclKey[] ={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};// 按下alt键的ASCII码.const char altKey[] ={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};// fn被按下的键的ASCII码.const char fnKey[] ={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};// 带大写的键的 ASCII 码是 activeconst char capsKey[] ={ 0, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 0 , 9, 81, 87, 69, 82, 84, 89, 85, 73, 79, 80, 91, 93, 92, 7, 65, 83, 68, 70, 71, 72, 74, 75, 76, 5 , 39, 0, 0, 90, 88, 67, 86, 66, 78, 77, 44, 46, 47, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0 };long previousKeyboardMicros =0; // 将存储上次检查键盘的时间 // 后续变量很长,因为时间以毫秒为单位,// 将很快变成一个比 int.long keyboardInterval =500 中可以存储的更大的数字; // 检查键盘的时间间隔(微秒)int rowToCheck =0; // 我们在 checkKeyboard() 的每个循环中检查一行,这与 keyboardInterval 相结合 // 使 shiftRegister 有时间在行之间完全更新// 是大写锁定吗?bool shift =false; // 是按下左移还是右移?bool capsShift =false; // 移位和大写是否有效?bool ctrl =false; // 是否按下了 ctrl 键?bool spcl =false; // 是否按下了 spcl 键?bool alt =false; // 是否按下了 alt 键?bool fn =false; // 是否按下了功能键?void setup() { Serial.begin(9600); // 使用内部上拉电阻将所有列引脚设置为输入 pinMode(colA, INPUT_PULLUP); pinMode(colB,INPUT_PULLUP); pinMode(colC,INPUT_PULLUP); pinMode(colD, INPUT_PULLUP); pinMode(colE, INPUT_PULLUP); pinMode(colF,INPUT_PULLUP); pinMode(colG,INPUT_PULLUP); pinMode(colH,INPUT_PULLUP); // 控制 74HC595 移位寄存器 pinMode(rowLatch, OUTPUT) 所需的输出; pinMode(rowClock, OUTPUT); pinMode(rowData, OUTPUT); updateShiftRegister(B11111111); // 确保移位寄存器从高位开始}void loop() { mainTimer();}void mainTimer() { unsigned long currentMicros =micros(); // Arduino 运行了多少微秒? if(currentMicros - previousKeyboardMicros> keyboardInterval) { // 如果自上次检查以来经过的时间超过间隔 // 保存上次检查键盘的时间 previousKeyboardMicros =currentMicros;检查键盘(); // 检查所有键并将结果打印到串行 }}void updateShiftRegister(byte row) { //此函数根据传递给它的字节设置移位寄存器 digitalWrite(rowLatch, LOW); // 将锁存器设置为低电平,以便我们可以一次写入整个字节 shiftOut(rowData, rowClock, MSBFIRST, row); // 写入该字节 digitalWrite(rowLatch, HIGH); // 将锁存器设置回高电平,以便移位寄存器在下一次更改之前保持稳定}void checkKeyboard() { // 将移位寄存器设置为当前行的字节值,来自 shiftRow[] 字节数组 updateShiftRegister(shiftRow[rowToCheck]); // 检查每一列 if (digitalRead(colA) ==LOW) { bitSet(rowState[rowToCheck], 0); } else { bitClear(rowState[rowToCheck], 0); } if (digitalRead(colB) ==LOW) { bitSet(rowState[rowToCheck], 1); } else { bitClear(rowState[rowToCheck], 1); } if (digitalRead(colC) ==LOW) { bitSet(rowState[rowToCheck], 2); } else { bitClear(rowState[rowToCheck], 2); } if (digitalRead(colD) ==LOW) { bitSet(rowState[rowToCheck], 3); } else { bitClear(rowState[rowToCheck], 3); } if (digitalRead(colE) ==LOW) { bitSet(rowState[rowToCheck], 4); } else { bitClear(rowState[rowToCheck], 4); } if (digitalRead(colF) ==LOW) { bitSet(rowState[rowToCheck], 5); } else { bitClear(rowState[rowToCheck], 5); } if (digitalRead(colG) ==LOW) { bitSet(rowState[rowToCheck], 6); } else { bitClear(rowState[rowToCheck], 6); } if (digitalRead(colH) ==LOW) { bitSet(rowState[rowToCheck], 7); } else { bitClear(rowState[rowToCheck], 7); } // 将所有移位寄存器引脚设置为高电平,这可以防止值“流血”到下一个循环 updateShiftRegister(B11111111); rowToCheck =rowToCheck + 1; // 迭代到下一行 // 检查第 8 行后,检查状态(按钮按下),然后从第 1 行重新开始 if (rowToCheck> 7 ) { checkPressedKeys();行检查 =0; }}void checkPressedKeys() { // 检查是否按下了任一 shift 键 if (bitRead(rowState[5], 1) | bitRead(rowState[6], 4)) { shift =true; } else { shift =false; } // 检查是否按下了 ctrl 键 if (bitRead(rowState[6], 5) | bitRead(rowState[7], 3)) { ctrl =true; } else { ctrl =false; } // 检查是否按下了任一 spcl 键 if (bitRead(rowState[6], 6) | bitRead(rowState[7], 2)) { spcl =true; } else { spcl =false; } // 检查是否按下任一 alt 键 if (bitRead(rowState[6], 7) | bitRead(rowState[7], 1)) { alt =true; } else { alt =false; } // 检查 FN 键是否被按下 if (bitRead(rowState[7], 4)) { fn =true; } else { fn =false; } // 检查 caps 是否激活并按下 shift if (shift ==true &&caps ==true) { capsShift =true; } else { capsShift =false; } for (int i =8; i>=0; i--) { // 遍历每一行 for (int j =7; j>=0; j--) { // 遍历该行中的每一位bool newBit =bitRead(rowState[i], j); // 检查该位的状态 bool prevBit =bitRead(prevRowState[i], j); // 检查该位的先前状态 if ((newBit ==1) &&(prevBit ==0)) { // 如果状态已更改为真,则仅允许按下按钮 int thisChar =(i * 8) + j; // 计算要选择的字符数组中的哪个位置 if (capsShift ==true) { processKey(capsShiftKey[thisChar]); } else if (shift ==true) { processKey(shiftKey[thisChar]); } else if (ctrl ==true) { processKey(ctrlKey[thisChar]); } else if (alt ==true) { processKey(altKey[thisChar]); } else if (spcl ==true) { processKey(spclKey[thisChar]); } else if (fn ==true) { processKey(fnKey[thisChar]); } else if (caps ==true) { processKey(capsKey[thisChar]); } else { processKey(key[thisChar]); } } if (newBit ==1) { bitSet(prevRowState[i], j); // 如果按下某个键,则将前一个位状态设置为真 } else { bitClear(prevRowState[i], j); // 如果键没有被按下,则将之前的位状态设置为 false,以便可以再次按下 } } }}void processKey(char receivedKey) { if (receivedKey ==7) { // 以相同的方式检查特殊功能as caps(添加新的“else if”语句) caps =!caps; } else { Serial.print(receivedKey); // 如果 char 不对应一个特殊的函数,只需打印那个 char }}

定制零件和外壳

将整个 .zip 文件上传到您的 PCB 制造服务 keyplots_4xipOSLHzg.zip

示意图


制造工艺

  1. 带有 Arduino 的用于飞行模拟器的 LCD 面板
  2. 用于 FS2020 的带有 Arduino 的开关/LED 面板
  3. Arduino + LEDs + MIDI 键盘 + MuseScore =Piano Tutor
  4. 使用 Arduino Uno 控制 LED 矩阵
  5. 学校的智能温度监测
  6. Arduino 的 8 位 IO 端口库
  7. Arduino Nano 的 TFT 扩展板 - 开始
  8. Arduino 的隔离模拟输入
  9. Arduino 计算器
  10. 控制直流电机的技巧
  11. Arduino 的闪电探测器
  12. 超酷室内导航机器人