Arduino技术求助:如何用millis()实现按钮中断直流电机延时
嘿,我来帮你搞定这个Plinko游戏机的问题!
解决Plinko游戏机电机换向与按钮停止的问题
先说说你现有代码的核心问题
- 逻辑语法问题:
while ( btn_state == HIGH )后面没有用大括号包裹代码块,导致程序只会执行紧跟的digitalWrite(Pin_1A, HIGH);,之后的换向、按钮检测逻辑完全混乱,甚至会卡死在这个循环里。 delay()的阻塞缺陷:用delay()做延时的时候,程序在整个延时周期里完全没法响应任何外部输入——这就是为什么你没法在电机移动中途按按钮停止它的根本原因,而millis()正是解决这个问题的关键。
用millis()实现非阻塞延时的核心思路
millis()会返回Arduino开机以来的累计毫秒数,我们的思路是:
- 记录上次执行电机换向动作的时间点
- 每次进入
loop()循环时,获取当前时间 - 计算当前时间与上次动作时间的差值,当差值达到设定的延时时长(比如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




