介绍:
本代码为arduino nano固件,外设为:A4988步进电机驱动、三个电位器、三个按钮、两个指示灯、步进电机等;适合制作送锡器、出丝器、蠕动挤出等设备的控制器,其功能是按钮1控制电机点动,由电位器调整速度,且不反转。按钮2控制单次触发电机按电位器3指定速度正转(电位器1控制旋转角度)并反转(电位器2控制旋转角度),当电位器2临近最小值时关闭反转。3开关为急停功能,急停按钮按下时电机停止工作,按钮解除时回复待机状态。待机时LED1常亮,电机运行时LED2常亮。
使用说明:
-
电路:
- 电位器中间引脚 → Arduino模拟输入
- 按钮 → Arduino数字输入(使用内部上拉电阻)
- LED → Arduino数字输出(串联限流电阻)
- A4988 EN → D9, DIR → D8, STEP → D7
-
工作流程:
- 上电后进入待机状态(LED1亮)
- 点动模式:按住JOG按钮电机正转,速度由速度电位器控制
- 自动循环:按RUN按钮执行正转→反转循环
- 紧急停止:任何时候按STOP按钮立即停止
-
反转保护:
- 当反转步数电位器值<50时,自动禁用反转功能
- 自动循环将只执行正转部分
按钮防抖处理、非阻塞步进控制和状态机实现,可直接烧录到Arduino Nano使用。
完整引脚定义:
功能 | Arduino引脚 |
---|---|
正转步数电位器 | A0 |
反转步数电位器 | A1 |
速度电位器 | A2 |
点动按钮 | D2 |
运行按钮 | D3 |
停止按钮 | D4 |
待机LED | D5 |
运行LED | D6 |
步进脉冲(STEP) | D7 |
方向控制(DIR) | D8 |
驱动器使能(EN) | D9 |
功能说明:
-
电位器功能:
- POT_STEPS_CW (A0):控制正转步数 (0-4000步)
- POT_STEPS_CCW (A1):控制反转步数 (值<50时禁用反转)
- POT_SPEED (A2):控制电机速度 (100-1000μs延迟)
-
按钮功能:
- BUTTON_JOG (D2):点动控制(按住时正转,松开停止)
- BUTTON_RUN (D3):启动自动循环(正转→反转)
- BUTTON_STOP (D4):紧急停止(任何状态下返回待机)
-
LED指示:
- LED_IDLE (D5):待机状态常亮
- LED_RUN (D6):电机运行时常亮
-
步进电机控制:
- DIR_PIN (D8):方向控制(HIGH=正转,LOW=反转)
- STEP_PIN (D7):步进脉冲(5μs高电平脉冲)
-
运行按钮行为:
- 短按:触发一次自动循环
- 长按:仍然只触发一次自动循环
- 在自动循环执行期间,忽略后续按钮按下
-
操作流程:
- 待机状态:LED1亮,LED2灭
- 点动模式:按住JOG按钮电机正转,速度由速度电位器控制
- 自动模式:按RUN按钮一次,执行完整正转→反转循环
- 停止:任何时候按STOP按钮立即停止
-
安全特性:
- 点动模式不反转
- 反转步数过小自动禁用反转
- 状态机确保操作原子性
-
运行按钮单次触发机制:
- 新增
runButtonActive
和runButtonConsumed
状态变量 - 按下运行按钮时激活
runButtonActive
- 在IDLE状态检测到激活且未消耗时启动自动循环
- 启动后立即标记为已消耗 (
runButtonConsumed = true
)
- 新增
-
按钮处理优先级:
- 点动按钮具有最高优先级(按下立即响应)
- 停止按钮次之(立即停止任何操作)
- 运行按钮最后处理(确保长按只触发一次)
-
状态机优化:
- 简化了按钮检测逻辑
- 分离了按钮检测和状态处理
- 自动循环完成后自动返回待机状态
-
反转保护逻辑:
- 当反转步数电位器值 < 50时,自动禁用反转
- 自动循环将只执行正转部分
-
ENABLE引脚控制:
- 增加
ENABLE_PIN
定义(默认为D9) - 添加
enableMotor()
和disableMotor()
函数 - 驱动器使能逻辑:低电平有效(大多数A4988驱动器的标准)
- 增加
-
电机驱动器状态管理:
- 系统启动时:禁用电机驱动器
- 进入JOG模式或AUTO_CYCLE模式时:启用电机驱动器
- 返回IDLE状态时:禁用电机驱动器
-
状态转换优化:
- 在JOG按钮释放时禁用驱动器
- 在自动循环完成时禁用驱动器
- 在紧急停止时立即禁用驱动器
-
串口调试功能:
代码:
//by 扫地僧
//2025/08/13
#include <Arduino.h>
// Pin definitions
const int POT_STEPS_CW = A0; // Potentiometer 1: CW steps
const int POT_STEPS_CCW = A1; // Potentiometer 2: CCW steps
const int POT_SPEED = A2; // Potentiometer 3: Speed control
const int BUTTON_JOG = 2; // Jog button
const int BUTTON_RUN = 3; // Run button
const int BUTTON_STOP = 4; // Stop/Emergency button
const int LED_IDLE = 5; // Idle indicator LED
const int LED_RUN = 6; // Running indicator LED
const int STEP_PIN = 7; // Step pulse
const int DIR_PIN = 8; // Direction control
const int ENABLE_PIN = 9; // Driver enable control (active low)
// State definition
enum State { IDLE, JOG, AUTO_CYCLE };
State currentState = IDLE;
// Emergency stop state
bool emergencyStop = false;
// Auto cycle parameters
unsigned long stepsToMoveCW = 0;
unsigned long stepsToMoveCCW = 0;
unsigned long stepsMoved = 0;
unsigned long stepDelay = 0;
int autoPhase = 0; // 0:CW phase, 1:CCW phase
// Time tracking
unsigned long lastStepTime = 0;
unsigned long lastButtonCheck = 0;
unsigned long lastSerialPrint = 0;
// Button state management
bool runButtonActive = false;
bool runButtonConsumed = false;
// Debug flag
const bool ENABLE_SERIAL_DEBUG = true; // Set to false to disable serial
void setup() {
if (ENABLE_SERIAL_DEBUG) {
Serial.begin(115200);
Serial.println(F("\nStepper Motor Control System Started"));
Serial.println(F("----------------------------------"));
Serial.println(F("Status: Initialization complete"));
}
// Initialize pins
pinMode(BUTTON_JOG, INPUT_PULLUP);
pinMode(BUTTON_RUN, INPUT_PULLUP);
pinMode(BUTTON_STOP, INPUT_PULLUP);
pinMode(LED_IDLE, OUTPUT);
pinMode(LED_RUN, OUTPUT);
pinMode(STEP_PIN, OUTPUT);
pinMode(DIR_PIN, OUTPUT);
pinMode(ENABLE_PIN, OUTPUT);
// Initial state - driver disabled
digitalWrite(LED_IDLE, HIGH);
digitalWrite(LED_RUN, LOW);
digitalWrite(DIR_PIN, HIGH); // Default CW direction
disableMotor(); // Disable driver initially
if (ENABLE_SERIAL_DEBUG) {
Serial.println(F("System ready"));
printDebugInfo();
}
}
void loop() {
unsigned long currentMicros = micros();
unsigned long currentMillis = millis();
// Button state check (every 50ms)
if (currentMillis - lastButtonCheck >= 50) {
checkButtons();
lastButtonCheck = currentMillis;
}
// State machine processing (motor control priority)
switch (currentState) {
case IDLE:
handleIdleState();
break;
case JOG:
handleJogState(currentMicros);
break;
case AUTO_CYCLE:
handleAutoCycleState(currentMicros);
break;
}
// Periodic debug output (every 1000ms)
if (ENABLE_SERIAL_DEBUG && currentMillis - lastSerialPrint >= 1000) {
printDebugInfo();
lastSerialPrint = currentMillis;
}
}
// Enable motor driver
void enableMotor() {
if (emergencyStop) return; // Ignore if in emergency stop
digitalWrite(ENABLE_PIN, LOW); // Active low
if (ENABLE_SERIAL_DEBUG) {
Serial.println(F("Motor driver: Enabled"));
}
}
// Disable motor driver
void disableMotor() {
digitalWrite(ENABLE_PIN, HIGH); // Disable driver
if (ENABLE_SERIAL_DEBUG) {
Serial.println(F("Motor driver: Disabled"));
}
}
// Button check function
void checkButtons() {
// Emergency stop button has highest priority
if (digitalRead(BUTTON_STOP) == LOW) {
if (!emergencyStop) {
emergencyStop = true;
currentState = IDLE;
digitalWrite(LED_IDLE, HIGH);
digitalWrite(LED_RUN, LOW);
disableMotor();
if (ENABLE_SERIAL_DEBUG) {
Serial.println(F("EMERGENCY STOP ACTIVATED!"));
Serial.println(F("System locked. Release STOP button to reset."));
}
}
return; // Skip other button checks
}
else if (emergencyStop) {
// Reset emergency stop when button is released
emergencyStop = false;
if (ENABLE_SERIAL_DEBUG) {
Serial.println(F("Emergency stop released. System reset."));
}
}
// Jog button check
if (digitalRead(BUTTON_JOG) == LOW && currentState == IDLE && !emergencyStop) {
if (ENABLE_SERIAL_DEBUG) {
Serial.println(F("Jog button pressed"));
}
currentState = JOG;
digitalWrite(LED_IDLE, LOW);
digitalWrite(LED_RUN, HIGH);
enableMotor();
return;
}
// Run button check (single trigger)
if (digitalRead(BUTTON_RUN) == LOW && !emergencyStop) {
if (!runButtonActive) {
if (ENABLE_SERIAL_DEBUG) {
Serial.println(F("Run button pressed"));
}
runButtonActive = true;
runButtonConsumed = false;
}
} else {
runButtonActive = false;
}
}
// Idle state handling
void handleIdleState() {
// Maintain LED state
digitalWrite(LED_IDLE, HIGH);
digitalWrite(LED_RUN, LOW);
// Check for run button activation
if (runButtonActive && !runButtonConsumed && !emergencyStop) {
startAutoCycle();
runButtonConsumed = true;
}
}
// Jog state handling
void handleJogState(unsigned long currentMicros) {
// Read speed and calculate delay (100-1000us)
int speedValue = analogRead(POT_SPEED);
stepDelay = map(speedValue, 0, 1023, 1000, 100);
// Ensure CW direction
digitalWrite(DIR_PIN, HIGH);
// Non-blocking step control
if (currentMicros - lastStepTime >= stepDelay) {
digitalWrite(STEP_PIN, HIGH);
delayMicroseconds(2);
digitalWrite(STEP_PIN, LOW);
lastStepTime = currentMicros;
}
// Detect button release
if (digitalRead(BUTTON_JOG) == HIGH) {
if (ENABLE_SERIAL_DEBUG) {
Serial.println(F("Jog button released"));
}
currentState = IDLE;
digitalWrite(LED_IDLE, HIGH);
digitalWrite(LED_RUN, LOW);
disableMotor();
}
}
// Start auto cycle
void startAutoCycle() {
// Read parameters
stepsToMoveCW = map(analogRead(POT_STEPS_CW), 0, 1023, 0, 2000);
stepsToMoveCCW = map(analogRead(POT_STEPS_CCW), 0, 1023, 0, 2000);
stepDelay = map(analogRead(POT_SPEED), 0, 1023, 1000, 100);
// Check CCW steps validity
if (stepsToMoveCCW < 50) {
stepsToMoveCCW = 0;
if (ENABLE_SERIAL_DEBUG) {
Serial.println(F("Warning: CCW steps too low, disabled"));
}
}
// Initialize auto cycle
currentState = AUTO_CYCLE;
autoPhase = 0;
stepsMoved = 0;
digitalWrite(DIR_PIN, HIGH); // Initial CW direction
digitalWrite(LED_IDLE, LOW);
digitalWrite(LED_RUN, HIGH);
enableMotor();
if (ENABLE_SERIAL_DEBUG) {
Serial.print(F("Starting auto cycle | CW steps: "));
Serial.print(stepsToMoveCW);
Serial.print(F(" | CCW steps: "));
Serial.print(stepsToMoveCCW);
Serial.print(F(" | Step delay: "));
Serial.print(stepDelay);
Serial.println(F(" us"));
}
}
// Auto cycle state handling
void handleAutoCycleState(unsigned long currentMicros) {
// Non-blocking step control
if (currentMicros - lastStepTime >= stepDelay) {
// Generate step pulse
digitalWrite(STEP_PIN, HIGH);
delayMicroseconds(2);
digitalWrite(STEP_PIN, LOW);
lastStepTime = currentMicros;
stepsMoved++;
// Check phase completion
if ((autoPhase == 0 && stepsMoved >= stepsToMoveCW) ||
(autoPhase == 1 && stepsMoved >= stepsToMoveCCW)) {
nextAutoPhase();
}
}
}
// Switch to next phase
void nextAutoPhase() {
if (autoPhase == 0 && stepsToMoveCCW > 0) {
// Switch to CCW phase
autoPhase = 1;
stepsMoved = 0;
digitalWrite(DIR_PIN, LOW); // Set CCW direction
if (ENABLE_SERIAL_DEBUG) {
Serial.print(F("Switching to CCW phase | Steps: "));
Serial.println(stepsToMoveCCW);
}
} else {
// Cycle complete, return to idle
if (ENABLE_SERIAL_DEBUG) {
Serial.println(F("Auto cycle completed"));
}
currentState = IDLE;
digitalWrite(LED_IDLE, HIGH);
digitalWrite(LED_RUN, LOW);
disableMotor();
}
}
// Print debug information
void printDebugInfo() {
if (!ENABLE_SERIAL_DEBUG) return;
Serial.println(F("\n--- SYSTEM STATUS ---"));
// Print current state
Serial.print(F("State: "));
switch (currentState) {
case IDLE:
Serial.print(F("IDLE"));
break;
case JOG:
Serial.print(F("JOG"));
break;
case AUTO_CYCLE:
Serial.print(F("AUTO"));
Serial.print(autoPhase == 0 ? F("(CW)") : F("(CCW)"));
break;
}
// Print emergency status
Serial.print(F(" | Emergency: "));
Serial.print(emergencyStop ? F("STOPPED") : F("Normal"));
// Print motor status
Serial.print(F(" | Motor: "));
Serial.print(digitalRead(ENABLE_PIN) == HIGH ? F("OFF") : F("ON"));
// Print auto cycle progress
if (currentState == AUTO_CYCLE) {
Serial.print(F(" | Progress: "));
Serial.print(stepsMoved);
Serial.print(F("/"));
Serial.print(autoPhase == 0 ? stepsToMoveCW : stepsToMoveCCW);
}
// Print potentiometer values
Serial.print(F("\nPots: CW="));
Serial.print(analogRead(POT_STEPS_CW));
Serial.print(F(" CCW="));
Serial.print(analogRead(POT_STEPS_CCW));
Serial.print(F(" SPD="));
Serial.println(analogRead(POT_SPEED));
Serial.println(F("-------------------"));
}
使用注意事项:
-
A4988连接:
- 注意:大多数A4988模块是低电平使能,即:
- EN=HIGH:驱动器禁用(电机无电流)
- EN=LOW:驱动器使能(电机有电流)
- 注意:大多数A4988模块是低电平使能,即:
-
节能与安全:
- 空闲状态下电机驱动器自动禁用,减少发热和能耗
- 紧急停止时立即切断电机电流
- 操作完成后自动进入节能模式
-
反转保护:
- 当反转步数电位器值 < 50时,自动禁用反转功能
- 自动循环将只执行正转部分