首页/新闻资讯/正文详情

按键为什么会乱跳:一次按下为什么变成很多次

发布时间:2026/9/24 17:45:42 来源:云帆数科 栏目:资讯中心
按键为什么会乱跳:一次按下为什么变成很多次
LED 亮起来之后我又插上了两个轻触按键。A 按键接 PB11B 按键接 PB1两个按键的另一端都接 GND。A 按一下数值加一B 按一下数值减一三颗外接 LED 把这个数值显示成三位二进制。第一次测试问题立刻出现了手指只按了一下数字却往前跳了一长串。松开按键数字才停住。我先问自己“是不是按键坏了”然后回头检查代码。代码看起来很规矩检测到按下就一直等到松手松手以后再给出一个结果。按下、等待、输出顺序没有少任何一步if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)Bit_RESET){while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)Bit_RESET){}value;}按下时进入if按住时卡在while松手后才执行value。乍一看一次按键应该只加一次逻辑几乎挑不出毛病。但实际运行还是会多次触发究竟是为什么呢按住一次程序为什么能数很多次接线是A 按键一端接 PB11B 按键一端接 PB1两个按键的另一端接 GNDPB11、PB1 都配成内部上拉输入PC13、PC14、PC15 接三颗外接 LED分别显示 bit0、bit1、bit2。接线沿用这三颗灯。手册第 20 页注明PC13~PC15作输出时不能作为电流源驱动 LED而且同一时间只能有一个作输出。按手册看这个接法是出规格的——这里不改接线只把限制写出来。先把输入初始化好voidKey_Init(void){GPIO_InitTypeDef gpio;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);gpio.GPIO_PinGPIO_Pin_11|GPIO_Pin_1;gpio.GPIO_ModeGPIO_Mode_IPU;GPIO_Init(GPIOB,gpio);}GPIO_Mode_IPU 里的 IPU 是上拉输入。松开按键时PB11 通过内部上拉保持高电平按下按键时PB11 被按键接到 GND读到低电平。uint8_tKey_A_IsPressed(void){returnGPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)Bit_RESET;}uint8_tKey_B_IsPressed(void){returnGPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)Bit_RESET;}这些函数只回答一个问题“现在是不是按下状态”按住不动它们会连续返回 1。如果主循环这样写while(1){if(Key_A_IsPressed()){count;}}手指按住几十毫秒主循环已经转了成千上万圈count 也就增加了那么多。为什么一定要上拉如果 PB11 既没有接到高电平也没有接到低电平松开按键后它就悬在空中读到的电平没有依据。这就是浮空输入。把模式临时改成gpio.GPIO_ModeGPIO_Mode_IN_FLOATING;然后断开按键连续读取 PB11。LED 可能自己闪计数可能自己变手指靠近时读数还会变化。你没有按它芯片却收到了一个没有固定答案的输入。内部上拉的作用很直接没人按键时给 PB11 一个默认高电平。按键按下后GND 的连接比内部上拉更强把它拉成低电平。也可以使用外部上拉电阻。内部上拉省了一颗电阻接线更简单外部电阻的阻值和电气特性更容易控制。对于这个入门按键内部上拉已经够用。一次按下和当前按着不是一回事边沿检测可以把“松开到按下”抓出来。A、B 各自保存一份上一次状态uint8_tKey_A_DetectPressEdge(void){staticuint8_twas_pressed0;uint8_tis_pressedKey_A_IsPressed();uint8_tevent;event(uint8_t)(is_pressed!was_pressed);was_pressedis_pressed;returnevent;}uint8_tKey_B_DetectPressEdge(void){staticuint8_twas_pressed0;uint8_tis_pressedKey_B_IsPressed();uint8_tevent;event(uint8_t)(is_pressed!was_pressed);was_pressedis_pressed;returnevent;}主循环不再对每个低电平都计数而是只在状态从 0 变成 1 的那一轮得到事件。三颗灯这一侧用一个LED_ShowBinary()它把这个数的低三位分别写到PC13、PC14、PC15上按键这边只负责决定什么时候该调它while(1){if(Key_A_DetectPressEdge()){value(uint8_t)((value1U)0x07U);LED_ShowBinary(value);}if(Key_B_DetectPressEdge()){value(uint8_t)((value7U)0x07U);LED_ShowBinary(value);}}三颗灯那边一位对一根引脚voidLED_ShowBinary(uint8_tvalue){if(value0x01U){GPIO_SetBits(GPIOC,GPIO_Pin_13);}else{GPIO_ResetBits(GPIOC,GPIO_Pin_13);}if(value0x02U){GPIO_SetBits(GPIOC,GPIO_Pin_14);}else{GPIO_ResetBits(GPIOC,GPIO_Pin_14);}if(value0x04U){GPIO_SetBits(GPIOC,GPIO_Pin_15);}else{GPIO_ResetBits(GPIOC,GPIO_Pin_15);}}但这还没有解决轻触按键本身的另一个问题。金属触点碰到一起时不会像理想开关那样干净地从高变低。它可能在几毫秒内弹来弹去高、低、高、低最后才稳定下来。边沿检测会把这些抖动也当成好几次按下。先用最容易看懂的办法确认一次按下发现按下后先等一小段时间再读一次确认仍然按着就等到松手最后只返回一次事件staticvoidKey_WaitShort(void){volatileuint32_ti;for(i0;i72000U;i){}}uint8_tKey_A_GetPressEvent(void){if(Key_A_IsPressed()){Key_WaitShort();if(Key_A_IsPressed()){while(Key_A_IsPressed()){}Key_WaitShort();return1U;}}return0U;}下载后按一次 A 或 B三颗 LED 的二进制数只改变一次。这个版本把触点刚碰到时的抖动过滤掉了也把按住期间的重复读取压成了一个事件。按键没有“按了一次”这个电平。它只给出一根线的高低变化一次、两次和很多次是程序在这些变化里整理出来的事件。最后橘猫说按住按键时输入电平会在一段时间内持续有效如果主循环每转一圈就把有效电平当成一次操作计数自然会飞快增加。触点抖动又会在刚按下和刚松开时制造额外的高低变化。等待一小段时间再确认、再等待松手是最容易看懂的阻塞式处理能把一次按压压成一次返回值但也会占住主循环。更精确的等待和非阻塞消抖要在稳定的时间基准建立后再做。最后蜘蛛说原来不止我的手会抖按钮也会啊。

相关推荐

手撕Transformer④:完整Encoder编码器实战(单层+多层堆叠+全链路维度闭环)
手撕Transformer④:完整Encoder编码器实战(单层+多层堆叠+全链路维度闭环)

手撕Transformer④:完整Encoder编码器实战(单层多层堆叠全链路维度闭环)摘要:前三篇我们逐一拆解了Transformer所有基础零部件:输入层(词嵌入位置编码)、多头自注意力、残差连接、层归一化、FFN… · 2026/9/24 17:45:42

Terminal.Gui 内置视图与控制组件全景指南:Views 分类、源码定位与实战选用
Terminal.Gui 内置视图与控制组件全景指南:Views 分类、源码定位与实战选用

UI组件跨平台桌面应用 【免费下载链接】Terminal.Gui Cross Platform Terminal UI toolkit for .NET 项目地址: https://gitcode.com/gh_mirrors/te/Terminal.Gui 点击查看 免费下载 Terminal.Gui(Cross Platform Terminal UI toolkit for .NET&#xf… · 2026/9/24 17:45:36

Jev 发布 3 天后,开源平替 Laya 来了:快 8 倍,权重全开
Jev 发布 3 天后,开源平替 Laya 来了:快 8 倍,权重全开

Jev 发布 3 天后,开源平替 Laya 来了:快 8 倍,权重全开一个不写一个字、只输出带概率判定的模型新品类,一周之内集齐了闭源版、开源版和两份民间复现。AI 不写字了,专管下判断 9 月 15 日,前 OpenAI 研究员… · 2026/9/24 17:45:29

WinForms图层化绘图板实战:从画完就丢到可编辑可回退
WinForms图层化绘图板实战:从画完就丢到可编辑可回退

简介:这是一份面向C#初学者与Winform桌面开发练习者的绘图程序源码,基于.NET Framework构建,可用于学习图层管理、图形绘制与图像保存等典型桌面绘图场景。压缩包共46个文件,约529KB,以cs源码、resx资源、config配置、… · 2026/9/24 18:15:23

Medical | 药品追溯系统实施的成本解构
Medical | 药品追溯系统实施的成本解构

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 18:15:23

2026.9.23学习周报
2026.9.23学习周报

本周,我继续小福星电商平台项目研发综合实训,主要围绕需求文档完善、专业工具学习以及项目优化整改展开学习,收获颇丰,同时也认清了自身存在的不足,现将本周学习情况总结如下。9月21日,我继续完善小福星电商… · 2026/9/24 18:15:23

CActor断路器与背压控制:5种策略守护高并发系统不崩的最终方案
CActor断路器与背压控制:5种策略守护高并发系统不崩的最终方案

CActor断路器与背压控制:5种策略守护高并发系统不崩的最终方案 【免费下载链接】cactor 项目地址: https://gitcode.com/Cangjie-SIG/cactor 高并发系统最怕两件事:下游服务雪崩式故障,以及消息洪流冲垮消费者。CActor 作为基于仓颉语… · 2026/9/24 18:15:23

基于CNN的猫狗图像识别:从数据准备到迁移学习实战
基于CNN的猫狗图像识别:从数据准备到迁移学习实战

简介:这份资源是面向Python与深度学习入门者的CNN猫狗图像识别实战项目包,适合想通过完整案例掌握卷积神经网络分类流程的学生与开发者。包内共26个文件,以jpg与png图片示例、py源码脚本、zip数据压缩包为主,另附PDF案例讲义、txt… · 2026/9/24 18:15:17

基于2000张植物病害图像的数据集:YOLOv8检测训练与避坑指南
基于2000张植物病害图像的数据集:YOLOv8检测训练与避坑指南

简介:这份资源是面向计算机视觉与农业AI方向的植物病害检测数据集,适合从事图像分类、目标检测研究的学生、算法工程师及科研人员使用,用于解决非实验室环境下植物病害图像样本不足、标注质量参差的问题。压缩包共约2000个文件,整… · 2026/9/24 18:15:17

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13

1D-CNN时间序列建模实战:从Conv1d原理到工业落地
1D-CNN时间序列建模实战:从Conv1d原理到工业落地

简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26

柔软的L:汉语语流中被忽视的舌肌张力控制
柔软的L:汉语语流中被忽视的舌肌张力控制

1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码