Arduino程序设计(四)按键消抖+按键计数

按键消抖+按键计数

  • 前言
  • 一、按键消抖
  • 二、按键计数
    • 1、示例代码
    • 2、按键计数实验
  • 参考资料


前言

  • 本文主要介绍两种按键控制LED实验:
  • 第一种是采用软件消抖的方法检测按键按下的效果;
  • 第二种是根据按键按下次数,四个LED灯呈现不同的流水灯效果。

一、按键消抖

  • 按键在按下时,由于机械和物理特定的原因,经常会产生一些开关变换,而这些变换会让程序误认为是短时间内进行了多次按键。
  • 如何对输入信号进行消抖?也就是在一段短时间内进行两次检查来确保按键确实被按下。如果没有消抖的话,按下一次按键会产生很多不可预知的结果。
  • 所以Arduino按键消抖是为了解决按键在物理接触瞬间可能产生多次触发的现象。
  • 下面介绍两种常见的按键消抖方法:
  • ① 使用外部电容(硬件消抖):通过在按键引脚和地之间并联一个适当大小的电容(例如:MCU复位电路采用0.1uF陶瓷电容),减少了按键连接和断开时产生的电压突变,同时也可以减少按键在短时间内多次触发的可能性。MCU复位电路如下图所示:
    在这里插入图片描述
  • ② 软件消抖:利用Arduino的延时函数或计时器来检测按键状态的变化,只有在按键状态保持一段时间后才认为按键有效。例如,当检测到按键按下时,可以设定一个延时时间,在延时时间内如果检测到按键保持按下状态,则认为按键有效。

按键消抖实验:

  • 1、本实验采用Arduino UNO R3开发板及自主搭建电路的方式,实现预设功能。

  • 2、按键消抖的电路图如下图所示:
    在这里插入图片描述

  • 3、功能实现:按下一个按键,控制LED灯亮50ms然后熄灭。

  • 4、实验要求:采用延时消抖(方法1),编写按键扫描程序(方法2),计数器消抖(方法3)三种按键消抖方式实现功能。

代码实现(方法1):

//延时消抖,按键控制LED
//按下一个按键,控制LED灯亮50ms然后熄灭

int buttonPin = 7;
int ledPin = 12;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);

}
void loop() {
  if (digitalRead(buttonPin) == LOW)
  {
    delay(10);
    if (digitalRead(buttonPin) == LOW)
    {
      digitalWrite(ledPin, HIGH);
      delay(50);
      digitalWrite(ledPin, LOW);
      while (digitalRead(buttonPin) == LOW);
    }
  }
}

代码实现(方法2):

//编写按键扫描程序,实现按键消抖
//按下一个按键,控制LED灯亮50ms然后熄灭

#define LED 12
#define KEY 7

int KEY_NUM = 0;                   //按键键值存放变量,不等于1说明有按键按下

void setup()
{
  pinMode(LED, OUTPUT);         //定义LED为输出引脚
  pinMode(KEY, INPUT_PULLUP);   //定义KEY为带上拉输入引脚
}

void loop()
{
  ScanKey();                   //按键扫描程序,当按键按下时候,该子程序会修改KEY_NUM的值
  if (KEY_NUM == 1)            //是否按键按下
  {
    digitalWrite(LED, HIGH);
    delay(50);
    digitalWrite(LED, LOW);
  }
}

void ScanKey()                        //按键扫描程序
{
  KEY_NUM = 0;                        //清空变量
  if (digitalRead(KEY) == LOW)        //有按键按下
  {
    delay(10);                        //延时去抖动
    if (digitalRead(KEY) == LOW)      //有按键按下
    {
      KEY_NUM = 1;                    //变量设置为1
      while (digitalRead(KEY) == LOW); //等待按键松手
    }
  }
}

代码实现(方法3):

//计数器消抖,按键控制LED
//按下一个按键,控制LED灯亮50ms然后熄灭

const int buttonPin = 7;  // 按键引脚
const int ledPin = 12;    //LED引脚

int buttonState = HIGH;   // 按键状态
int lastButtonState = HIGH;  // 上一次的按键状态
unsigned long lastDebounceTime = 0;  // 上一次的触发时间
unsigned long debounceDelay = 10;    // 消抖延时

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin,OUTPUT);
}

void loop() {
  int reading = digitalRead(buttonPin);  // 读取按键引脚状态

  // 如果当前状态与上一次状态不同,更新上一次状态和触发时间
  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }

  // 如果经过了消抖延时,且当前状态与按键状态不同,更新按键状态
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonState) {
      buttonState = reading;

      // 按键按下时执行的操作
      if (buttonState == HIGH) {
        digitalWrite(ledPin, HIGH);
        delay(50);
        digitalWrite(ledPin, LOW);
      }
    }
  }

  lastButtonState = reading;
}

二、按键计数

1、示例代码

  • 使用Arduino来实现按键计数。简单的示例代码如下:
//按键计数示例
const int buttonPin = 2;   // 按钮连接到数字引脚2
int buttonState = 0;       // 保存按钮状态
int count = 0;             // 计数器

void setup() {
  pinMode(buttonPin, INPUT);     // 设置按钮引脚为输入模式
  Serial.begin(9600);            // 打开串口通信
}

void loop() {
  buttonState = digitalRead(buttonPin);   // 读取按钮状态

  if (buttonState == HIGH) {    // 如果按钮按下
    count++;                   // 计数器加1
    Serial.print("Button pressed. Count: ");
    Serial.println(count);
    delay(200);                // 等待200毫秒,避免连续多次计数
  }
}

示例中,我们将一个按钮连接到Arduino的数字引脚2。循环中,我们读取按钮的状态,如果按钮被按下(高电平),计数器就会加1,并通过串口打印出计数器的值。为了避免按钮按下时的抖动,我们在每次计数后延迟200毫秒。

上传这个代码到Arduino板,然后打开串口监视器(波特率设置为9600),当你按下按钮时,你将看到计数器的值递增。

2、按键计数实验

  • (1)本实验采用Arduino UNO R3开发板及自主搭建电路的方式,实现预设功能。

  • (2)按键计数的电路图如下图所示:
    在这里插入图片描述

  • (3)实现功能(基础):

  • ① 第一次按下按键,LED1点亮;

  • ② 第二次按下按键,LED1和LED2点亮;

  • ③ 第三次按下按键,LED1~LED3点亮;

  • ④ 第四次按下按键,LED1~LED4点亮;

  • ⑤ 第五次按下按键,LED1~LED4熄灭;

  • ⑥ 第六次按下按键,重复①现象;

  • ⑦ 第七次按下按键,重复②现象……,以此类推。

代码实现:

//编写按键扫描程序,实现按键计数
/*实验现象:
① 第一次按下按键,LED1点亮;
② 第二次按下按键,LED1和LED2点亮;
③ 第三次按下按键,LED1~LED3点亮;
④ 第四次按下按键,LED1~LED4点亮;
⑤ 第五次按下按键,LED1~LED4熄灭;
⑥ 第六次按下按键,重复①现象;
⑦ 第七次按下按键,重复②现象……,以此类推。
*/

const int KEY = 7;      //按键引脚
const int LED1 = 9;     //LED1引脚
const int LED2 = 10;    //LED2引脚
const int LED3 = 11;    //LED3引脚
const int LED4 = 12;    //LED4引脚

int KEY_count = 0;      //按键计数

void setup()
{
  pinMode(KEY, INPUT_PULLUP);    //定义KEY为带上拉输入引脚
  pinMode(LED1, OUTPUT);         //定义LED1为输出引脚
  pinMode(LED2, OUTPUT);         //定义LED2为输出引脚
  pinMode(LED3, OUTPUT);         //定义LED3为输出引脚
  pinMode(LED4, OUTPUT);         //定义LED4为输出引脚
}

void loop()
{
  ScanKey();                   //按键扫描程序,当按键按下时候,该子程序会修改KEY_count的值

  switch (KEY_count) {
    case 0:
      {
        digitalWrite(LED1, LOW);
        digitalWrite(LED2, LOW);
        digitalWrite(LED3, LOW);
        digitalWrite(LED4, LOW);
      }
      break;
    case 1:
      {
        digitalWrite(LED1, HIGH);
      }
      break;
    case 2:
      {
        digitalWrite(LED1, HIGH);
        digitalWrite(LED2, HIGH);
      }
      break;
    case 3:
      {
        digitalWrite(LED1, HIGH);
        digitalWrite(LED2, HIGH);
        digitalWrite(LED3, HIGH);
      }
      break;
    case 4:
      {
        digitalWrite(LED1, HIGH);
        digitalWrite(LED2, HIGH);
        digitalWrite(LED3, HIGH);
        digitalWrite(LED4, HIGH);
      }
      break;
    default:
      {
        KEY_count = 0;
      }
  }
}

void ScanKey()                        //按键扫描程序
{
  if (digitalRead(KEY) == LOW)        //有按键按下
  {
    delay(10);                        //延时去抖动
    if (digitalRead(KEY) == LOW)      //有按键按下
    {
      KEY_count++;                   //按键计数
      while (digitalRead(KEY) == LOW); //等待按键松手
    }
  }
}
  • (4)实现功能(进阶):
  • ① 第一次按下按键,LED1和LED3亮500ms后熄灭,间隔150ms后,LED2和LED4亮150ms后熄灭,间隔150ms后,LED1和LED3亮150ms后熄灭……,重复操作。
  • ② 第二次按下按键,LED1~LED4从左往右依次点亮,等LED4熄灭后,再从左往右依次点亮,重复操作,相邻两个LED灯亮灭的时间间隔为50ms。
  • ③ 第三次按下按键,LED1~LED4从右往左依次点亮,等LED1熄灭后,再从右往左依次点亮,重复操作,相邻两个LED灯亮灭的时间间隔为50ms。
  • ④ 第四次按下按键,LED1~LED4从左往右依次点亮,再从右往左依次点亮,重复操作,相邻两个LED灯亮灭的时间间隔为50ms。
  • ⑤ 第五次按下按键,LED1~LED4熄灭。
  • ⑥ 第六次按下按键,重复①现象
  • ⑦ 第七次按下按键,重复②现象……,以此类推。

代码实现:

//编写按键扫描程序,实现按键计数
//注意:按下按键后,即下一次按下按键前,时间间隔>10s
/*实验现象:
  ① 第一次按下按键,LED1和LED3亮150ms后熄灭,间隔150ms后,LED2和LED4亮150ms后熄灭,间隔150ms后,LED1和LED3亮150ms后熄灭……,重复操作。
  ② 第二次按下按键,LED1~LED4从左往右依次点亮,等LED4熄灭后,再从左往右依次点亮,重复操作,相邻两个LED灯亮灭的时间间隔为50ms。
  ③ 第三次按下按键,LED1~LED4从右往左依次点亮,等LED1熄灭后,再从右往左依次点亮,重复操作,相邻两个LED灯亮灭的时间间隔为50ms。
  ④ 第四次按下按键,LED1~LED4从左往右依次点亮,再从右往左依次点亮,重复操作,相邻两个LED灯亮灭的时间间隔为50ms。
  ⑤ 第五次按下按键,LED1~LED4熄灭。
  ⑥ 第六次按下按键,重复①现象
  ⑦ 第七次按下按键,重复②现象……,以此类推。
*/

const int KEY = 7;      //按键引脚
const int LED1 = 9;     //LED1引脚
const int LED2 = 10;    //LED2引脚
const int LED3 = 11;    //LED3引脚
const int LED4 = 12;    //LED4引脚

int KEY_count = 0;      //按键计数

void setup()
{
  pinMode(KEY, INPUT_PULLUP);    //定义KEY为带上拉输入引脚
  pinMode(LED1, OUTPUT);         //定义LED1为输出引脚
  pinMode(LED2, OUTPUT);         //定义LED2为输出引脚
  pinMode(LED3, OUTPUT);         //定义LED3为输出引脚
  pinMode(LED4, OUTPUT);         //定义LED4为输出引脚
}

void loop()
{
  ScanKey();                   //按键扫描程序,当按键按下时候,该子程序会修改KEY_count的值

  switch (KEY_count) {
    case 0:
      {
        digitalWrite(LED1, LOW);
        digitalWrite(LED2, LOW);
        digitalWrite(LED3, LOW);
        digitalWrite(LED4, LOW);
      }
      break;
    case 1:
      {
        //第一次按下按键
        digitalWrite(LED1, HIGH);
        digitalWrite(LED2, LOW);
        digitalWrite(LED3, HIGH);
        digitalWrite(LED4, LOW);
        delay(150);
        digitalWrite(LED1, !digitalRead(LED1));
        digitalWrite(LED2, !digitalRead(LED2));
        digitalWrite(LED3, !digitalRead(LED3));
        digitalWrite(LED4, !digitalRead(LED4));
        delay(150);
      }
      break;
    case 2:
      {
        //刷新LED1~LED4状态
        digitalWrite(LED1, LOW);
        digitalWrite(LED2, LOW);
        digitalWrite(LED3, LOW);
        digitalWrite(LED4, LOW);

        //第二次按下按键
        digitalWrite(LED1, HIGH);
        delay(50);
        digitalWrite(LED1, !digitalRead(LED1));
        digitalWrite(LED2, HIGH);
        delay(50);
        digitalWrite(LED2, !digitalRead(LED2));
        digitalWrite(LED3, HIGH);
        delay(50);
        digitalWrite(LED3, !digitalRead(LED3));
        digitalWrite(LED4, HIGH);
        delay(50);
        digitalWrite(LED4, !digitalRead(LED4));
        delay(50);
      }
      break;
    case 3:
      {
        //刷新LED1~LED4状态
        digitalWrite(LED1, LOW);
        digitalWrite(LED2, LOW);
        digitalWrite(LED3, LOW);
        digitalWrite(LED4, LOW);

        //第三次按下按键
        digitalWrite(LED4, HIGH);
        delay(50);
        digitalWrite(LED4, !digitalRead(LED4));
        digitalWrite(LED3, HIGH);
        delay(50);
        digitalWrite(LED3, !digitalRead(LED3));
        digitalWrite(LED2, HIGH);
        delay(50);
        digitalWrite(LED2, !digitalRead(LED2));
        digitalWrite(LED1, HIGH);
        delay(50);
        digitalWrite(LED1, !digitalRead(LED1));
        delay(50);
      }
      break;
    case 4:
      {
        //刷新LED1~LED4状态
        digitalWrite(LED1, LOW);
        digitalWrite(LED2, LOW);
        digitalWrite(LED3, LOW);
        digitalWrite(LED4, LOW);

        //第四次按下按键
        digitalWrite(LED1, HIGH);
        delay(50);
        digitalWrite(LED1, !digitalRead(LED1));
        digitalWrite(LED2, HIGH);
        delay(50);
        digitalWrite(LED2, !digitalRead(LED2));
        digitalWrite(LED3, HIGH);
        delay(50);
        digitalWrite(LED3, !digitalRead(LED3));
        digitalWrite(LED4, HIGH);
        delay(50);
        digitalWrite(LED4, !digitalRead(LED4));
        delay(50);
        digitalWrite(LED4, HIGH);
        delay(50);
        digitalWrite(LED4, !digitalRead(LED4));
        digitalWrite(LED3, HIGH);
        delay(50);
        digitalWrite(LED3, !digitalRead(LED3));
        digitalWrite(LED2, HIGH);
        delay(50);
        digitalWrite(LED2, !digitalRead(LED2));
        digitalWrite(LED1, HIGH);
        delay(50);
        digitalWrite(LED1, !digitalRead(LED1));
        delay(50);
      }
      break;
    default:
      {
        digitalWrite(LED1, LOW);
        digitalWrite(LED2, LOW);
        digitalWrite(LED3, LOW);
        digitalWrite(LED4, LOW);
        KEY_count = 0;
      }
  }
}

void ScanKey()                        //按键扫描程序
{
  if (digitalRead(KEY) == LOW)        //有按键按下
  {
    delay(10);                        //延时去抖动
    if (digitalRead(KEY) == LOW)      //有按键按下
    {
      KEY_count++;                   //按键计数
      while (digitalRead(KEY) == LOW); //等待按键松手
    }
  }
}

注意:按下按键后,即下一次按下按键前,时间间隔>10s。


参考资料

参考资料1: 【Arduino官方教程】数字处理示例(三):按键防抖
参考资料2: 【Arduino官方教程】数字处理示例(五):按键状态变化检测

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

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

相关文章

python二维索引转一维索引 多维索引转一维索引

将二维索引转为1维索引 原博客地址:https://blog.csdn.net/qq_42424677/article/details/123011642 将n维索引映射成1维索引 def ravel_index(x, dims):i 0for dim, j in zip(dims, x):i * dimi jreturn i

Vue2(状态管理Vuex)

目录 一,状态管理Vuex二,state状态三,mutations四,actions五,modules最后 一,状态管理Vuex Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并…

opencv 水果识别+UI界面识别系统,可训练自定义的水果数据集

目录 一、实现和完整UI视频效果展示 主界面: 测试图片结果界面: 自定义图片结果界面: 二、原理介绍: 图像预处理 HOG特征提取算法 数据准备 SVM支持向量机算法 预测和评估 完整演示视频: 完整代码链接 一、…

基于SpringBoot的在线聊天系统

基于SpringBoot的在线聊天系统 一、系统介绍二、功能展示三.其他系统实现五.获取源码 一、系统介绍 源码编号:F-S03点击查看 项目类型:Java EE项目 项目名称:基于SpringBoot的在线聊天系统 项目架构:B/S架构 开发语言&#x…

线性代数的学习和整理13: 函数与向量/矩阵

目录 1 函数与 向量/矩阵 2 函数的定义域,值域,到达域 3 对应关系 1 函数与 向量/矩阵 下面两者形式类似,本质也类似 函数的: axy ,常规函数里,a,x,y 一般都是单个数矩阵: AXY &a…

【CSS】CSS 背景设置 ( 背景半透明设置 )

一、背景半透明设置 1、语法说明 背景半透明设置 可以 使用 rgba 颜色值设置半透明背景 ; 下面的 CSS 样式中 , 就是 设置黑色背景 , 透明度为 20% ; background: rgba(0, 0, 0, 0.2);颜色的透明度 alpha 取值范围是 0 ~ 1 之间 , 在使用时 , 可以 省略 0.x 前面的 0 , 直接…

在线OJ平台项目

一、项目源码 Online_Judge yblhlk/Linux课程 - 码云 - 开源中国 (gitee.com) 二、所用技术与开发环境 1.所用技术: MVC架构模式 (模型-视图-控制器) 负载均衡系统设计 多进程、多线程编程 C面向对象编程 & C 11 & STL 标准库 C Boost 准标…

Docker打包JDK20镜像

文章目录 Docker 打包 JDK 20镜像步骤1.下载 jdk20 压缩包2.编写 dockerfile3.打包4.验证5.创建并启动容器6.检查 Docker 打包 JDK 20镜像 步骤 1.下载 jdk20 压缩包 https://www.oracle.com/java/technologies/downloads/ 2.编写 dockerfile #1.指定基础镜像,并…

网络:RIP协议

1. RIP协议原理介绍 RIP是一种比较简单的内部网关协议(IGP协议),RIP基于距离矢量的贝尔曼-福特算法(Bellman - Ford)来计算到达目的网络的最佳路径。最初的RIP协议开发时间较早,所以在带宽、配置和管理方面的要求也较低。 路由器运…

如何管理多个大型数据中心,这回总算说全了!

当谈及现代科技基础设施中的关键元素时,数据中心无疑占据着核心地位。然而,无论多么强大的硬件和网络系统,都无法摆脱电力供应的依赖。 在电力中断或突发情况下,数据中心的稳定运行将受到威胁,进而导致业务中断和数据丢…

Postman测WebSocket接口

01、WebSocket 简介 WebSocket是一种在单个TCP连接上进行全双工通信的协议。 WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直…

pandas数据分析——groupby得到分组后的数据

groupbyagg分组聚合对数据字段进行合并拼接 Pandas怎样实现groupby聚合后字符串列的合并(四十) groupby得到分组后的数据 pandas—groupby如何得到分组里的数据 date_range补齐缺失日期 在处理时间序列的数据中,有时候会遇到有些日期的数…

Linux安装FRP(内网穿透)

项目简介需求背景 1.FileBrowser访问地址:http://X.X.X.X:8181,该地址只能在局域网内部访问而无法通过互联网访问,想要通过互联网 访问到该地址需要通过公网IP来进行端口转发,通常家里的路由器IP都不是公网IP,通常公司…

如何用Dockerfile部署LAMP架构

目录 构建LAMP镜像(Dockerfile) 第一步 创建工作目录 第二步 编写dockerfile文件 Dockerfile文件配置内容 第三步 编写网页执行文件 第四步 编写启动脚本 第五步 赋权并且构建镜像 第六步 检查镜像 第七步 创建容器 第八步 浏览器测试 构建LA…

mysql 存储引擎系列 (一) 基础知识

当前支持存储引擎 show engines; 显示默认存储引擎 select default_storage_engine; show variables like ‘%storage%’; 修改默认引擎 set default_storage_enginexxx 或 set default_storage_enginexxx; my.ini 或者 my.cnf ,需要重启 服务才能生效 systemctl …

mongodb聚合排序的一个巨坑

现象: mongodb cpu动不动要100%,如下图 分析原因: 查看慢日志发现,很多条这样的查询,一直未执行行完成,占用大量的CPU [{$match: {"tags.taskId": "64dae0a9deb52d2f9a1bd71e",grnty: …

【Unity学习笔记】DOTween(1)基础介绍

本文中大部分内容学习来自DOTween官方文档 文章目录 什么是DOTween?DOSetOnTweenerSequenceTweenNested tween 初始化使用方式 什么是DOTween? DOTween是一个动画插件,Tween是补间的意思。这个插件以下简称DOT,DOT很方便使用&…

可扩展的单核至四核Cortex-A53@1.4GHz工业级核心板规格书

1 核心板简介 创龙科技SOM-TL62x是一款基于TI Sitara系列AM62x单/双/四核ARM Cortex-A53 + 单核ARM Cortex-M4F多核处理器设计的高性能低功耗工业核心板,通过工业级B2B连接器引出2x TSN Ethernet、9x UART、3x CAN-FD、GPMC、2x USB2.0、CSI、DISPLAY等接口。处理器ARM Corte…

C++string类

目录 一、为什么学习string 二、标准库中的string类 2.1 string类的简介 2.2 成员类型 2.3 成员函数 2.3.1 构造、析构与运算符重载 2.3.2 迭代器 2.3.3 容量 2.3.4 元素的存取 2.3.5 修改 2.3.6 字符串操作 2.4 成员常量 2.5 非成员函数重载 三、string编程题练…

【Unity3D赛车游戏】【六】如何在Unity中为汽车添加发动机和手动挡变速?

👨‍💻个人主页:元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏:Uni…