You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Arduino技术求助:如何用millis()实现按钮中断直流电机延时

嘿,我来帮你搞定这个Plinko游戏机的问题!

解决Plinko游戏机电机换向与按钮停止的问题

先说说你现有代码的核心问题

  • 逻辑语法问题while ( btn_state == HIGH )后面没有用大括号包裹代码块,导致程序只会执行紧跟的digitalWrite(Pin_1A, HIGH);,之后的换向、按钮检测逻辑完全混乱,甚至会卡死在这个循环里。
  • delay()的阻塞缺陷:用delay()做延时的时候,程序在整个延时周期里完全没法响应任何外部输入——这就是为什么你没法在电机移动中途按按钮停止它的根本原因,而millis()正是解决这个问题的关键。

millis()实现非阻塞延时的核心思路

millis()会返回Arduino开机以来的累计毫秒数,我们的思路是:

  1. 记录上次执行电机换向动作的时间点
  2. 每次进入loop()循环时,获取当前时间
  3. 计算当前时间与上次动作时间的差值,当差值达到设定的延时时长(比如1000ms)时,再执行换向操作
    这样程序全程不会被阻塞,能随时检测按钮的状态,自然就能实现“延时中途按按钮停电机”的需求。

修改后的完整代码

#include <Servo.h>
Servo servo;

// 引脚定义
const int btn_pin = 9;
const int servo_pin = 8;
const int EN_Pin = 3;
const int Pin_1A = 4;
const int Pin_2A = 2;
const int led_pin = 7;

// 状态变量
int btn_prev = HIGH;
bool motor_running = true; // 标记电机是否处于运行状态
unsigned long previous_millis = 0; // 记录上次电机换向的时间
const long interval = 1000; // 电机换向的间隔时长(毫秒)
bool motor_direction = true; // true=正转,false=反转

void setup() {
  servo.attach(servo_pin);
  Serial.begin(9600);
  
  pinMode(btn_pin, INPUT_PULLUP);
  pinMode(led_pin, OUTPUT);
  pinMode(EN_Pin, OUTPUT);
  pinMode(Pin_1A, OUTPUT);
  pinMode(Pin_2A, OUTPUT);
  
  // 初始化电机为正转状态
  digitalWrite(Pin_1A, HIGH);
  digitalWrite(Pin_2A, LOW);
  analogWrite(EN_Pin, 255);
}

void loop() {
  int btn_state = digitalRead(btn_pin);
  
  // 检测按钮按下的边缘(INPUT_PULLUP模式下,LOW代表按下)
  if (btn_prev == HIGH && btn_state == LOW) {
    // 立即停止电机:关闭使能引脚直接切断动力
    motor_running = false;
    digitalWrite(EN_Pin, LOW);
    
    // 执行伺服爪的动作
    digitalWrite(led_pin, HIGH);
    servo.write(45);
    delay(2500); // 伺服动作短时间阻塞影响不大,若要完全非阻塞也可以用millis()改造
    servo.write(0); // 注意:Servo库的有效角度范围是0-180,-45会被自动修正为0
    digitalWrite(led_pin, LOW);
    
    btn_prev = btn_state;
    
    // 如果需要按下按钮后自动恢复电机运行,可在这里添加 motor_running = true;
  }
  
  // 更新按钮的上一次状态
  if (btn_state != btn_prev) {
    btn_prev = btn_state;
  }
  
  // 非阻塞式电机换向逻辑
  if (motor_running) {
    unsigned long current_millis = millis();
    // 检查是否到达换向时间
    if (current_millis - previous_millis >= interval) {
      previous_millis = current_millis; // 更新上次换向时间
      // 切换电机方向
      motor_direction = !motor_direction;
      if (motor_direction) {
        // 正转
        digitalWrite(Pin_1A, HIGH);
        digitalWrite(Pin_2A, LOW);
      } else {
        // 反转
        digitalWrite(Pin_1A, LOW);
        digitalWrite(Pin_2A, HIGH);
      }
      analogWrite(EN_Pin, 255); // 保持电机满转速
    }
  }
}

代码关键细节说明

  • 非阻塞控制:全程用millis()检测时间差,程序不会被卡死在延时里,按钮能随时被响应。
  • 按钮停止逻辑:检测到按钮按下的瞬间,立即关闭电机使能引脚EN_Pin,电机直接停转,完美实现“中途停止”的需求。
  • Servo角度修正:Servo库的有效角度是0-180度,你原来写的servo.write(-45)会被自动转为0,所以调整为servo.write(0),如果需要回到特定初始位置可以修改这个数值。
  • 状态可扩展性:用motor_running变量标记电机状态,后续可以轻松添加“按下按钮后重启电机”之类的功能。

内容的提问来源于stack exchange,提问作者avemoor01

火山引擎 最新活动