Arduino串口控制电机异常:遮挡传感器后电机无法自动停止
问题描述
我用Winform GUI连接Arduino控制电机,设置了L(低速)、M(中速)、H(高速)、S(停止)四个按钮,点击按钮向Arduino发送对应字符。配合红外避障传感器(输出接Arduino引脚2),设定电机运行时长:L档3秒、M档5秒、H档10秒、S档停止。但现在遇到问题:遮挡传感器时点击L/M/H按钮,松开手后电机还一直转,只有再次点击L/M/H或S按钮才会停。
Arduino代码
#include<AFMotor.h> AF_DCMotor motor(4); int sensor=2; char sig; int t; void setup() { Serial.begin(9600); pinMode(sensor,INPUT); } void loop() { t = digitalRead(sensor); delay(100); sig = Serial.read(); if(sig=='L') { if(t==LOW) { runMotor(3000); } else if(t==HIGH) { stopMotor(); } } else if(sig=='M') { if(t==LOW) { runMotor(5000); } else if(t==HIGH) { stopMotor(); } } else if(sig=='H') { if(t==LOW) { runMotor(10000); } else if(t==HIGH) { stopMotor(); } } else if(sig == 'S') { stopMotor(); } } void runMotor(int n) { motor.setSpeed(200); motor.run(FORWARD); delay(n); } void stopMotor() { motor.setSpeed(200); motor.run(RELEASE); delay(200); }
Winform代码
private: System::Void run_Click(System::Object^ sender, System::EventArgs^ e) { serialPort1->Close(); serialPort1->Open(); if (listBox1->Text == "Low") { serialPort1->Write("L"); } else if (listBox1->Text == "Medium") { serialPort1->Write("M"); } else if (listBox1->Text == "High") { serialPort1->Write("H"); } } private: System::Void stop_Click(System::Object^ sender, System::EventArgs^ e) { serialPort1->Close(); serialPort1->Open(); serialPort1->Write("S"); }
问题根源
- Arduino逻辑缺陷:当前代码只在收到串口指令的瞬间读取一次传感器状态,之后直接进入
delay(n)的阻塞延时。延时过程中Arduino完全无法响应传感器状态变化,哪怕你松开手让传感器变为HIGH,电机也会硬跑完设定时长才停。 - 串口操作不合理:Winform每次点击按钮都重复关闭再打开串口,容易引发串口冲突,且完全没必要频繁开关。
修复方案
1. 修改Arduino代码(核心逻辑调整)
去掉阻塞式的delay,改用非阻塞的毫秒计时,同时在电机运行过程中持续监测传感器状态,一旦传感器变为HIGH就立即停止电机:
#include<AFMotor.h> AF_DCMotor motor(4); const int sensorPin = 2; char serialCmd = '\0'; unsigned long motorStartTime = 0; int motorRunDuration = 0; bool isMotorRunning = false; void setup() { Serial.begin(9600); pinMode(sensorPin, INPUT); motor.run(RELEASE); // 初始状态停止电机 } void loop() { // 优先处理串口指令 if (Serial.available() > 0) { serialCmd = Serial.read(); handleSerialCommand(serialCmd); } // 电机运行时持续监测传感器 if (isMotorRunning) { int sensorState = digitalRead(sensorPin); // 传感器未遮挡(HIGH)或到达设定时长,停止电机 if (sensorState == HIGH || (millis() - motorStartTime >= motorRunDuration)) { stopMotor(); } } } void handleSerialCommand(char cmd) { int sensorState = digitalRead(sensorPin); switch(cmd) { case 'L': if (sensorState == LOW) { // 传感器遮挡时启动 startMotor(3000); } else { stopMotor(); } break; case 'M': if (sensorState == LOW) { startMotor(5000); } else { stopMotor(); } break; case 'H': if (sensorState == LOW) { startMotor(10000); } else { stopMotor(); } break; case 'S': stopMotor(); break; default: break; } } void startMotor(int duration) { motor.setSpeed(200); motor.run(FORWARD); motorStartTime = millis(); motorRunDuration = duration; isMotorRunning = true; } void stopMotor() { motor.run(RELEASE); isMotorRunning = false; }
2. 优化Winform代码(串口操作)
避免重复开关串口,在窗体加载时初始化打开一次,点击按钮直接发送指令:
// 窗体加载时初始化串口 private: System::Void Form1_Load(System::Object^ sender, System::EventArgs^ e) { if (!serialPort1->IsOpen) { try { serialPort1->Open(); } catch (System::Exception^ ex) { MessageBox::Show("串口打开失败:" + ex->Message); } } } private: System::Void run_Click(System::Object^ sender, System::EventArgs^ e) { if (!serialPort1->IsOpen) { MessageBox::Show("串口未打开"); return; } if (listBox1->Text == "Low") { serialPort1->Write("L"); } else if (listBox1->Text == "Medium") { serialPort1->Write("M"); } else if (listBox1->Text == "High") { serialPort1->Write("H"); } } private: System::Void stop_Click(System::Object^ sender, System::EventArgs^ e) { if (!serialPort1->IsOpen) { MessageBox::Show("串口未打开"); return; } serialPort1->Write("S"); }
内容的提问来源于stack exchange,提问作者Mr. Nothing
相关产品推荐
相关产品推荐

