数码之家

 找回密码
 立即注册

QQ登录

只需一步,快速开始

微信登录

微信扫一扫,快速登录

搜索
查看: 260|回复: 11

[Arduino] 简单按钮程序没必要折腾中断

[复制链接]
发表于 2025-8-1 22:16:44 | 显示全部楼层 |阅读模式

刚开始接触单片机,按钮总是绕不开的。开头就是一句:接个上拉电阻到Vcc。。。嗯,淘宝还贴心的推出按钮模块,尤其是那种套件里面这和烟雾传感器一样算108个模块之一。



上拉搞清楚了,那还有一个消抖问题。。。一大堆文字和实测抖动波形采集图。。。。

消抖搞清楚了,还有中断,还有 上升沿 下降沿。。。哇。。。真是博大精深呢

鼠标不是有 单击 双击 长按 释放 好多按钮事件。。。

实在脑力有富裕,还有中断嵌套。。。

最后还有 多线程,RTOS这些。。。

对简单应用来说,真的需要吗?

我只是想做个按钮,按一下有对应代码被执行而已! 就好比只想吃个蛋炒饭,要去养鸡下蛋一样的离谱。

--------------------------------------------------------------------------------------------------
下面的代码演示了几行就可以执行按钮检测,也可以增加按钮(多几个if),在主程序对延时不敏感的场合很好的实现了按钮功能。
现代单片机很多有内置上拉模式,所以电阻不需要的。
消抖?那个延时既是长按时变量的增长间隔时间,也是消抖,至于为什么,试试感受下。
实验简单到极致了: 一个arduino板子,一颗按钮。实现按一下等亮,再按一下灯灭,长按那就。。。(也可以理解为变量持续增加嘛)





本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册 微信登录

x
发表于 2025-8-1 22:31:07 | 显示全部楼层
为什么不while(!digitRead(3));呢
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-8-1 22:41:09 | 显示全部楼层
soma 发表于 2025-8-1 22:31
为什么不while(!digitRead(3));呢

就这几行代码,你试试不就知道了。单片机是个接地气的东东,能动手实验就别只想
回复 支持 反对

使用道具 举报

发表于 2025-8-2 15:55:14 | 显示全部楼层
这样的按键程序完全不合格,只能作为原理性说明。

在loop() 中使用 delay(200),会造成实时性很差,随机漏掉按键检测。
(现象表现:按键按了,但程序刚好处于delay(200)中,无法检测到按键状态变化 。)
回复 支持 2 反对 0

使用道具 举报

 楼主| 发表于 2025-8-3 18:45:04 来自手机浏览器 | 显示全部楼层
wangbeng 发表于 2025-8-2 15:55
这样的按键程序完全不合格,只能作为原理性说明。

在loop() 中使用 delay(200),会造成实时性很差,随机漏 ...

代码好不好不重要,能用,好用就是硬道理。分析下主循环的时间占用,就能在合适的位置插入按键检测,一处不够就多几处,起码可以快捷实现功能,总比自以为是非要去用中断但又搞不定好。
回复 支持 反对

使用道具 举报

发表于 6 天前 | 显示全部楼层
本帖最后由 fryefryefrye 于 2025-8-5 11:26 编辑

我都是每0.1秒,读取一次IO口状态。简单易行。

Arduino 参考代码:

unsigned long LastMillis = 0;
void TenthSecondsSinceStartTask()
{
        unsigned long CurrentMillis = millis();
        if (CurrentMillis - LastMillis > 100)
        {
                LastMillis = CurrentMillis;
                TenthSecondsSinceStart++;
                OnTenthSecond();
        }
}

回复 支持 反对

使用道具 举报

发表于 5 天前 | 显示全部楼层
我之前也是这样搞的~,但是主程序在运行的时候,会发生按了按钮,偶尔能生效,偶尔不能生效的情况
回复 支持 反对

使用道具 举报

发表于 5 天前 | 显示全部楼层
正好,你说那么多,你试一下,下面是我的代码,按按钮不偶尔能生效。
   这个代码是这么回事!在1206上显示脉冲计数 ,然后脉冲的间隔时间  通过按钮可调



#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <OneButton.h>

OneWire oneWire_9(9);
DallasTemperature sensors_9(&oneWire_9);
DeviceAddress insideThermometer;
LiquidCrystal_I2C mylcd(0x27,16,2);
volatile int g;  //1206屏幕上显示的脉冲个位数
volatile int s;  //  十位数
volatile int b;  //百位数
volatile int q;  //千位数
volatile int w;  //万位数
volatile int sw;
volatile int bw;
volatile int qw;
volatile int y;
volatile int ms; //这个变量 ms  意思就是 脉冲的延时时间  就是它 通过按钮调整
OneButton button10(10,false);   //按钮接到 10 号引脚  
OneButton button11(11,false);  //按钮接到 11号引脚


float ds18b20_9_getTemp(int w) {
  sensors_9.requestTemperatures();
  if(w==0) {
    return sensors_9.getTempC(insideThermometer);
  }
  else {
    return sensors_9.getTempF(insideThermometer);
  }
}

void attachClick10() {
  ms = ms + 5;
}
       //变量 ms  通过按钮 每按一下 增加 5毫秒的时间
void attachClick11() {
  ms = ms - 5;
}
     //变量 ms  通过按钮 每按一下 减少 5毫秒的时间

void setup(){
  sensors_9.getAddress(insideThermometer, 0);
  sensors_9.setResolution(insideThermometer, 9);
  pinMode(7, OUTPUT);
  mylcd.init();
  mylcd.backlight();
  g = 0;
  s = 0;
  b = 0;
  q = 0;
  w = 0;
  sw = 0;
  bw = 0;
  qw = 0;
  y = 0;
  ms = 50;
  mylcd.setCursor(1-1, 1-1);
  mylcd.print("S");
  mylcd.setCursor(2-1, 1-1);
  mylcd.print(":");
  mylcd.setCursor(3-1, 1-1);
  mylcd.print((30 + ms));
  mylcd.setCursor(1-1, 2-1);
  mylcd.print("Count");
  button10.attachClick(attachClick10);
  button11.attachClick(attachClick11);
}

void loop(){
  mylcd.setCursor(12-1, 1-1);
  mylcd.print('C');
  mylcd.setCursor(7-1, 1-1);
  mylcd.print(ds18b20_9_getTemp(0));
  mylcd.setCursor(16-1, 2-1);
  mylcd.print(g);
  mylcd.setCursor(15-1, 2-1);
  mylcd.print(s);
  mylcd.setCursor(14-1, 2-1);
  mylcd.print(b);
  mylcd.setCursor(13-1, 2-1);
  mylcd.print(q);
  mylcd.setCursor(12-1, 2-1);
  mylcd.print(w);
  mylcd.setCursor(11-1, 2-1);
  mylcd.print(sw);
  mylcd.setCursor(10-1, 2-1);
  mylcd.print(bw);
  mylcd.setCursor(9-1, 2-1);
  mylcd.print(qw);
  mylcd.setCursor(8-1, 2-1);
  mylcd.print(y);
  digitalWrite(7,HIGH);   // 7号引脚接的脉冲输出  高电平时间  30 毫秒
  delay(30);
  digitalWrite(7,LOW);
  g++;  //低电平每来一下,个位数计数加一

  delay(ms);     //delay 时间就是  变量ms赋值
  if (g > 9) {
    g = 0;
    s++;

  }
  if (s > 9) {
    s = 0;
    b++;

  }
  if (b > 9) {
    b = 0;
    q++;

  }
  if (q > 9) {
    q = 0;
    w++;

  }
  if (w > 9) {
    w = 0;
    sw++;

  }
  if (sw > 9) {
    sw = 0;
    bw++;

  }
  if (bw > 9) {
    bw = 0;
    qw++;

  }
  if (qw > 9) {
    qw = 0;
    y++;

  }

  button10.tick();
  button11.tick();
}


回复 支持 反对

使用道具 举报

发表于 4 天前 | 显示全部楼层
我写了一个超牛的,支持单击、多击、长按、单击触发、单击释放后触发、长按触发、长按释放后触发、      
一个按键N多种功能. 不使用中断,没有使用阻塞,没有使用状态机,牛牛牛
回复 支持 反对

使用道具 举报

发表于 4 天前 | 显示全部楼层
sadfun 发表于 2025-8-6 16:30
正好,你说那么多,你试一下,下面是我的代码,按按钮不偶尔能生效。
   这个代码是这么回事!在1206上显示 ...

delay(ms)是个阻塞式的,在delay时,程序停在这个函数里的,所以无法响应你的按键了。
delay(30)时还好一点,当ms比较大时,比如100ms以上,就能很明显的会感觉到按键不能实时响应了。
可以用以下类似程序,当然最好还是用定时器或中断。
unsigned long lastTime = 0;
unsigned int ms;

void setup()
{
    ms = 50;
    ...
}

void loop()
{
    unsigned long currentTime = millis();
    if(currentTime - lastTime) > ms)
    {
        lastTime = currentTime;
        digitalWrite(7,HIGH);   
        delay(30);  // 这个30 毫秒延迟也可以类似处理
        digitalWrite(7,LOW);
    }
      ...
}
回复 支持 反对

使用道具 举报

发表于 4 天前 | 显示全部楼层
wangbeng 发表于 2025-8-7 10:31
delay(ms)是个阻塞式的,在delay时,程序停在这个函数里的,所以无法响应你的按键了。
delay(30)时还好一 ...

谢谢解答~,学习了!!!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册 微信登录

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2025-8-11 11:10 , Processed in 0.124800 second(s), 9 queries , Redis On.

Powered by Discuz!

© 2006-2025 MyDigit.Net

快速回复 返回顶部 返回列表