步进电机移动至目标位置时实时更新速度的问题排查
问题:电位器数值无法实时更新
需求:需要实时更新电位器(速度、加速度)的数值,而非仅在启动时读取一次,但尝试多种方法均无效。
核心问题分析:
- 代码中多处使用阻塞式循环(如
while(digitalRead(gotoPositionAPin));),导致程序卡在循环内无法执行电位器读取逻辑。 AccelStepper的runToNewPosition()方法是阻塞式的,调用后会持续执行直到电机到达目标位置,期间无法处理其他任务(包括电位器采样)。- 电位器读取的
CheckPots()函数依赖loopCtr计数,一旦程序进入阻塞循环,计数无法递增,电位器数值自然无法更新。
当前代码逻辑:
- 调节速度电位器设置电机最大速度,调节加速度电位器设置加速度
- 按右键保存当前位置为A,按左键保存当前位置为B
- 按对应按钮控制电机移动至A/B位置,移动时使用之前设置的速度和加速度
解决方案:重构为非阻塞式逻辑
要实现电位器实时更新,必须消除所有阻塞代码,让loop()能持续循环执行,同时处理电机运行、按钮检测和电位器采样:
- 替换阻塞式电机控制:用
moveTo()设置目标位置,在loop()中调用stepper1.run()实现非阻塞式步进控制。 - 移除按钮阻塞循环:通过检测按钮状态变化(上升沿/下降沿)触发动作,避免用
while等待按钮释放。 - 调整电位器采样逻辑:直接在每次
loop()中采样(或合理间隔采样),确保采样不会被阻塞打断。
修改后的完整代码
#include <AccelStepper.h> // 定义步进电机引脚 AccelStepper stepper1(1, 12, 11); #define stepsPerRev 1600 #define stepPin 12 #define dirPin 11 #define ledPin 13 #define rotateLeftPin 7 #define rotateRightPin 6 #define savePositionAPin 5 #define savePositionBPin 4 #define gotoPositionAPin 3 #define gotoPositionBPin 2 #define maxSpeedPin 0 #define accelPin 1 #define printDebug 0 #define MAX_STEPS_PER_SECOND 1000 #define MIN_STEPS_PER_SECOND 27 #define ACCEL_RATIO 1 // 按钮状态变量,用于检测状态变化 bool lastLeftBtn = LOW; bool lastRightBtn = LOW; bool lastSaveABtn = LOW; bool lastSaveBBtn = LOW; bool lastGotoABtn = LOW; bool lastGotoBBtn = LOW; int maxSpeed = 0; int accel = 0; long savedPosA = 0; long savedPosB = 0; float fStepsPerSecond = 0.0; void setup() { pinMode(stepPin, OUTPUT); pinMode(dirPin, OUTPUT); pinMode(ledPin, OUTPUT); // 使用内部上拉,避免外部下拉电阻 pinMode(rotateLeftPin, INPUT_PULLUP); pinMode(rotateRightPin, INPUT_PULLUP); pinMode(savePositionAPin, INPUT_PULLUP); pinMode(savePositionBPin, INPUT_PULLUP); pinMode(gotoPositionAPin, INPUT_PULLUP); pinMode(gotoPositionBPin, INPUT_PULLUP); if (printDebug) { Serial.begin(9600); } blink(2); // 初始化电位器数值 UpdatePots(); stepper1.setMaxSpeed(fStepsPerSecond); stepper1.setAcceleration(accel); } void loop() { // 实时更新电位器数值 UpdatePots(); // 更新电机速度和加速度设置 stepper1.setMaxSpeed(fStepsPerSecond); stepper1.setAcceleration(accel); // 处理左右旋转按钮(非阻塞) HandleRotateButtons(); // 处理位置保存按钮 HandleSaveButtons(); // 处理 goto 位置按钮 HandleGotoButtons(); // 非阻塞式运行步进电机 stepper1.run(); } // 闪烁LED void blink(int howManyTimes) { for (int i=0; i < howManyTimes; i++) { digitalWrite(ledPin, HIGH); delay(200); digitalWrite(ledPin, LOW); delay(200); } } // 实时更新电位器数值 void UpdatePots(void) { // 读取速度电位器并转换 int currentSpeedPot = analogRead(maxSpeedPin); float fMaxSpeed = currentSpeedPot / 1023.0; fStepsPerSecond = MIN_STEPS_PER_SECOND + (fMaxSpeed * (MAX_STEPS_PER_SECOND - MIN_STEPS_PER_SECOND)); fStepsPerSecond = constrain(fStepsPerSecond, MIN_STEPS_PER_SECOND, MAX_STEPS_PER_SECOND); // 读取加速度电位器 accel = analogRead(accelPin) / ACCEL_RATIO; } // 处理左右旋转按钮 void HandleRotateButtons() { bool leftBtn = digitalRead(rotateLeftPin); bool rightBtn = digitalRead(rotateRightPin); // 左旋转:按钮按下时持续运行 if (leftBtn == LOW && lastLeftBtn == HIGH) { stepper1.setSpeed(-fStepsPerSecond); } else if (leftBtn == HIGH && lastLeftBtn == LOW) { stepper1.setSpeed(0); } // 右旋转 if (rightBtn == LOW && lastRightBtn == HIGH) { stepper1.setSpeed(fStepsPerSecond); } else if (rightBtn == HIGH && lastRightBtn == LOW) { stepper1.setSpeed(0); } // 更新按钮状态 lastLeftBtn = leftBtn; lastRightBtn = rightBtn; // 如果正在旋转,维持电机运行 if (leftBtn == LOW || rightBtn == LOW) { stepper1.runSpeed(); } } // 处理位置保存按钮 void HandleSaveButtons() { bool saveABtn = digitalRead(savePositionAPin); bool saveBBtn = digitalRead(savePositionBPin); // 保存位置A(按钮按下时触发一次) if (saveABtn == LOW && lastSaveABtn == HIGH) { savedPosA = stepper1.currentPosition(); if (printDebug) { Serial.print("Saved A at :"); Serial.println(savedPosA); } } // 保存位置B if (saveBBtn == LOW && lastSaveBBtn == HIGH) { savedPosB = stepper1.currentPosition(); if (printDebug) { Serial.print("Saved B at :"); Serial.println(savedPosB); } } lastSaveABtn = saveABtn; lastSaveBBtn = saveBBtn; } // 处理 goto 位置按钮 void HandleGotoButtons() { bool gotoABtn = digitalRead(gotoPositionAPin); bool gotoBBtn = digitalRead(gotoPositionBPin); // 移动到位置A(按钮按下时触发一次) if (gotoABtn == LOW && lastGotoABtn == HIGH) { if (printDebug) { Serial.print("cur pos = "); Serial.println(stepper1.currentPosition()); Serial.print("Going to A = "); Serial.println(savedPosA); Serial.print("Speed = "); Serial.println(fStepsPerSecond); Serial.print("Accel = "); Serial.println(accel); } stepper1.moveTo(savedPosA); } // 移动到位置B if (gotoBBtn == LOW && lastGotoBBtn == HIGH) { if (printDebug) { Serial.print("cur pos = "); Serial.println(stepper1.currentPosition()); Serial.print("Going to B = "); Serial.println(savedPosB); Serial.print("Speed = "); Serial.println(fStepsPerSecond); Serial.print("Accel = "); Serial.println(accel); } stepper1.moveTo(savedPosB); } lastGotoABtn = gotoABtn; lastGotoBBtn = gotoBBtn; }
关键修改说明:
- 按钮处理:使用
INPUT_PULLUP模式,通过检测按钮状态的上升沿/下降沿触发动作,彻底移除阻塞式while循环。 - 电机控制:用
moveTo()设置目标位置,stepper1.run()在每次loop()中执行步进动作,实现非阻塞运行,期间程序可以正常处理电位器采样。 - 电位器采样:
UpdatePots()在每次loop()中执行,确保数值实时更新,同时用constrain()限制速度范围,避免超出边界值。
内容的提问来源于stack exchange,提问作者Klāvs Skrastiņš
相关产品推荐
相关产品推荐

