شیلد درایور موتور پله‌ای

(A4988/DRV8825)

راه‌حل جامع کنترل موتورهای پله‌ای با دقت و قدرت بالا

شیلد درایور موتور پله‌ای یک برد توسعه کامل برای کنترل حرفه‌ای موتورهای پله‌ای با استفاده از درایورهای A4988 یا DRV8825 است. این شیلد امکان کنترل تا ۲ موتور پله‌ای را به صورت همزمان فراهم کرده و با قابلیت‌هایی مانند تنظیم جریان، میکرواستپینگ، حفاظت حرارتی و قطع ارتباط خودکار، انتخاب ایده‌آلی برای پروژه‌های CNC، پرینترهای ۳ بعدی، رباتیک و اتوماسیون صنعتی می‌باشد.


ویژگی‌های کلیدی

  • پشتیبانی از دو درایور: قابلیت استفاده همزمان از A4988 و DRV8825

  • تنظیم جریان دقیق: پتانسیومتر برای تنظیم جریان هر موتور به صورت مجزا

  • میکرواستپینگ پیشرفته: پشتیبانی تا ۱/۳۲ میکرواستپ (DRV8825) و ۱/۱۶ (A4988)

  • حفاظت کامل: محافظ در برابر اتصال کوتاه، اضافه جریان و اضافه دما

  • خنک‌کننده یکپارچه: هیت‌سینک آلومینیومی برای هر درایور

  • منبع تغذیه دوگانه: ورودی منطقی ۵V و ورودی موتور تا ۳۵V

  • اتصالات صنعتی: ترمینال پیچی برای اتصال آسان موتورها

  • کاهش نویز: خازن‌های سرامیکی و الکترولیتی برای فیلتر نویز

  • سازگاری کامل: پین‌های سازگار با آردوینو Uno، Mega، Nano


مشخصات فنی

مشخصات کلی:

  • ولتاژ ورودی منطق: 3.3V – 5V (از آردوینو)

  • ولتاژ موتور: 8V – 35V (A4988) / 8.2V – 45V (DRV8825)

  • جریان پیوسته: تا 2A در هر فاز (با خنک‌کاری)

  • جریان پالسی: تا 2.5A (A4988) / تا 2.5A (DRV8825)

  • میکرواستپینگ: 1, 1/2, 1/4, 1/8, 1/16 (A4988) + 1/32 (DRV8825)

  • ابعاد برد: 70mm × 54mm (مطابق آردوینو Uno)

  • حفاظت حرارتی: Shutdown در 150°C (A4988) / 165°C (DRV8825)

پین‌های مهم:

موتور 1:
ENABLE1 (D8)   - فعال/غیرفعال کردن درایور
STEP1 (D5)     - پالس قدم
DIR1 (D6)      - جهت چرخش
MS1, MS2, MS3  - انتخاب میکرواستپ

موتور 2:
ENABLE2 (D9)
STEP2 (D7)
DIR2 (D4)
MS1, MS2, MS3

منبع تغذیه:
VMOT  - ولتاژ موتور (8-35V)
GND   - زمین موتور
VDD   - ولتاژ منطق (3.3-5V)

مقایسه درایورها:

ویژگی A4988 DRV8825
ولتاژ کاری 8-35V 8.2-45V
جریان پیوسته 1-2A 1.5-2.5A
میکرواستپینگ تا 1/16 تا 1/32
مقاومت سنس جریان 0.05Ω 0.1Ω
راندمان متوسط بالا
قیمت ارزان‌تر گران‌تر

اتصال موتور پله‌ای NEMA 17

موتور پله‌ای ← شیلد
-------------------
فاز 1A → 1A
فاز 1B → 1B
فاز 2A → 2A
فاز 2B → 2B
گرمایش ← GND (اختیاری)

نکته: ترتیب فازها بسیار مهم است. اگر موتور درست کار نکرد، سیم‌های یک فاز را جابجا کنید.


کد پایه: کنترل موتور پله‌ای

/*
 * کنترل ساده موتور پله‌ای با شیلد A4988/DRV8825
 * حرکت در دو جهت با سرعت‌های مختلف
 */

// پایه‌های موتور 1
const int ENABLE1 = 8;
const int STEP1 = 5;
const int DIR1 = 6;

// پایه‌های موتور 2
const int ENABLE2 = 9;
const int STEP2 = 7;
const int DIR2 = 4;

// پایه‌های میکرواستپ (مشترک برای هر دو موتور)
const int MS1 = 10;
const int MS2 = 11;
const int MS3 = 12;

// تنظیمات موتور
const int STEPS_PER_REV = 200; // برای موتور NEMA 17
int microstep = 16; // مقدار میکرواستپ (1, 2, 4, 8, 16, 32)
int totalStepsPerRev; // مراحل کامل در هر دور

// سرعت موتور (تأخیر بین استپ‌ها در میکروثانیه)
int stepDelay = 1000; // 1000 میکروثانیه = 1ms

void setup() {
  Serial.begin(9600);
  
  // تنظیم پایه‌ها به عنوان خروجی
  pinMode(ENABLE1, OUTPUT);
  pinMode(STEP1, OUTPUT);
  pinMode(DIR1, OUTPUT);
  
  pinMode(ENABLE2, OUTPUT);
  pinMode(STEP2, OUTPUT);
  pinMode(DIR2, OUTPUT);
  
  pinMode(MS1, OUTPUT);
  pinMode(MS2, OUTPUT);
  pinMode(MS3, OUTPUT);
  
  // غیرفعال کردن موتورها در ابتدا (LOW = فعال)
  digitalWrite(ENABLE1, HIGH);
  digitalWrite(ENABLE2, HIGH);
  
  // تنظیم میکرواستپ
  setupMicrostepping(microstep);
  
  // محاسبه مراحل کامل
  totalStepsPerRev = STEPS_PER_REV * microstep;
  
  Serial.println("\n\n========================================");
  Serial.println("   Stepper Motor Shield Controller");
  Serial.println("========================================");
  Serial.print("Motor: NEMA 17 (");
  Serial.print(STEPS_PER_REV);
  Serial.println(" steps/rev)");
  Serial.print("Microstepping: 1/");
  Serial.println(microstep);
  Serial.print("Total steps/rev: ");
  Serial.println(totalStepsPerRev);
  Serial.println("Commands: F=Forward, B=Backward, S=Stop");
  Serial.println("         +=Faster, -=Slower, ?=Help");
  Serial.println("----------------------------------------");
}

void loop() {
  if (Serial.available()) {
    char command = Serial.read();
    processCommand(command);
  }
  
  // می‌توانید حرکات خودکار را اینجا اضافه کنید
  // autoDemo();
  
  delay(10);
}

void processCommand(char cmd) {
  switch (cmd) {
    case 'F':
    case 'f':
      Serial.println("Moving FORWARD");
      moveMotor(1, true, totalStepsPerRev); // 1 دور به جلو
      break;
      
    case 'B':
    case 'b':
      Serial.println("Moving BACKWARD");
      moveMotor(1, false, totalStepsPerRev); // 1 دور به عقب
      break;
      
    case 'S':
    case 's':
      Serial.println("STOPPING");
      stopMotors();
      break;
      
    case '+':
      increaseSpeed();
      break;
      
    case '-':
      decreaseSpeed();
      break;
      
    case '1':
      moveMotor(1, true, totalStepsPerRev / 4); // 90 درجه
      break;
      
    case '2':
      moveMotor(2, true, totalStepsPerRev / 2); // 180 درجه
      break;
      
    case '3':
      moveBothMotors(true, totalStepsPerRev); // هر دو موتور
      break;
      
    case '?':
      showHelp();
      break;
      
    case 'M': // تغییر میکرواستپ
      changeMicrostepping();
      break;
  }
}

void moveMotor(int motor, bool forward, int steps) {
  // فعال کردن موتور
  if (motor == 1) {
    digitalWrite(ENABLE1, LOW);
    digitalWrite(DIR1, forward ? HIGH : LOW);
  } else {
    digitalWrite(ENABLE2, LOW);
    digitalWrite(DIR2, forward ? HIGH : LOW);
  }
  
  delay(10); // تأخیر برای فعال شدن
  
  // ارسال پالس‌های استپ
  for (int i = 0; i < steps; i++) {
    if (motor == 1) {
      digitalWrite(STEP1, HIGH);
      delayMicroseconds(stepDelay);
      digitalWrite(STEP1, LOW);
    } else {
      digitalWrite(STEP2, HIGH);
      delayMicroseconds(stepDelay);
      digitalWrite(STEP2, LOW);
    }
    delayMicroseconds(stepDelay);
  }
  
  // غیرفعال کردن موتور پس از حرکت
  if (motor == 1) {
    digitalWrite(ENABLE1, HIGH);
  } else {
    digitalWrite(ENABLE2, HIGH);
  }
  
  Serial.print("Motor ");
  Serial.print(motor);
  Serial.print(" moved ");
  Serial.print(steps);
  Serial.print(" steps (");
  Serial.print((steps * 360.0) / totalStepsPerRev, 1);
  Serial.println(" degrees)");
}

void moveBothMotors(bool forward, int steps) {
  // فعال کردن هر دو موتور
  digitalWrite(ENABLE1, LOW);
  digitalWrite(ENABLE2, LOW);
  
  digitalWrite(DIR1, forward ? HIGH : LOW);
  digitalWrite(DIR2, forward ? HIGH : LOW);
  
  delay(10);
  
  // حرکت همزمان
  for (int i = 0; i < steps; i++) {
    digitalWrite(STEP1, HIGH);
    digitalWrite(STEP2, HIGH);
    delayMicroseconds(stepDelay);
    
    digitalWrite(STEP1, LOW);
    digitalWrite(STEP2, LOW);
    delayMicroseconds(stepDelay);
  }
  
  // غیرفعال کردن
  digitalWrite(ENABLE1, HIGH);
  digitalWrite(ENABLE2, HIGH);
  
  Serial.print("Both motors moved ");
  Serial.print(steps);
  Serial.println(" steps");
}

void stopMotors() {
  digitalWrite(ENABLE1, HIGH);
  digitalWrite(ENABLE2, HIGH);
  Serial.println("Motors disabled");
}

void setupMicrostepping(int mode) {
  // جدول تنظیمات میکرواستپ:
  // MS1 MS2 MS3 | Microstep Resolution
  //  L   L   L  | Full Step
  //  H   L   L  | Half Step
  //  L   H   L  | 1/4 Step
  //  H   H   L  | 1/8 Step
  //  H   H   H  | 1/16 Step (A4988) / 1/32 Step (DRV8825)
  
  switch (mode) {
    case 1: // Full Step
      digitalWrite(MS1, LOW);
      digitalWrite(MS2, LOW);
      digitalWrite(MS3, LOW);
      break;
      
    case 2: // Half Step
      digitalWrite(MS1, HIGH);
      digitalWrite(MS2, LOW);
      digitalWrite(MS3, LOW);
      break;
      
    case 4: // 1/4 Step
      digitalWrite(MS1, LOW);
      digitalWrite(MS2, HIGH);
      digitalWrite(MS3, LOW);
      break;
      
    case 8: // 1/8 Step
      digitalWrite(MS1, HIGH);
      digitalWrite(MS2, HIGH);
      digitalWrite(MS3, LOW);
      break;
      
    case 16: // 1/16 Step (A4988)
      digitalWrite(MS1, HIGH);
      digitalWrite(MS2, HIGH);
      digitalWrite(MS3, HIGH);
      break;
      
    case 32: // 1/32 Step (DRV8825)
      // برای DRV8825 تنظیمات متفاوت است
      digitalWrite(MS1, HIGH);
      digitalWrite(MS2, HIGH);
      digitalWrite(MS3, HIGH);
      break;
  }
  
  // به‌روزرسانی مراحل کامل
  totalStepsPerRev = STEPS_PER_REV * mode;
}

void changeMicrostepping() {
  // چرخش بین حالت‌های مختلف میکرواستپ
  int modes[] = {1, 2, 4, 8, 16, 32};
  static int modeIndex = 2; // شروع از 1/4 استپ
  
  modeIndex = (modeIndex + 1) % 6;
  microstep = modes[modeIndex];
  
  setupMicrostepping(microstep);
  
  Serial.print("Microstepping changed to: 1/");
  Serial.println(microstep);
  Serial.print("Total steps/rev: ");
  Serial.println(totalStepsPerRev);
}

void increaseSpeed() {
  if (stepDelay > 100) {
    stepDelay -= 100;
    Serial.print("Speed increased. Delay: ");
    Serial.print(stepDelay);
    Serial.println("µs");
  } else {
    Serial.println("Maximum speed reached");
  }
}

void decreaseSpeed() {
  if (stepDelay < 5000) {
    stepDelay += 100;
    Serial.print("Speed decreased. Delay: ");
    Serial.print(stepDelay);
    Serial.println("µs");
  } else {
    Serial.println("Minimum speed reached");
  }
}

void showHelp() {
  Serial.println("\n=== Stepper Motor Control Help ===");
  Serial.println("F / f  - Move motor 1 forward 1 revolution");
  Serial.println("B / b  - Move motor 1 backward 1 revolution");
  Serial.println("S / s  - Stop/disables motors");
  Serial.println("+      - Increase speed");
  Serial.println("-      - Decrease speed");
  Serial.println("1      - Move motor 1 90 degrees");
  Serial.println("2      - Move motor 2 180 degrees");
  Serial.println("3      - Move both motors together");
  Serial.println("M      - Change microstepping mode");
  Serial.println("?      - Show this help");
  Serial.println("\nCurrent Settings:");
  Serial.print("  Step delay: ");
  Serial.print(stepDelay);
  Serial.println(" µs");
  Serial.print("  Microstepping: 1/");
  Serial.println(microstep);
  Serial.print("  Steps per revolution: ");
  Serial.println(totalStepsPerRev);
  Serial.println("==============================\n");
}

void autoDemo() {
  // نمایش خودکار قابلیت‌ها
  static unsigned long lastDemo = 0;
  static int demoState = 0;
  
  if (millis() - lastDemo > 3000) {
    switch (demoState) {
      case 0:
        Serial.println("Demo: Moving forward 180 degrees");
        moveMotor(1, true, totalStepsPerRev / 2);
        break;
      case 1:
        Serial.println("Demo: Moving backward 180 degrees");
        moveMotor(1, false, totalStepsPerRev / 2);
        break;
      case 2:
        Serial.println("Demo: Changing microstepping to 1/8");
        microstep = 8;
        setupMicrostepping(microstep);
        break;
      case 3:
        Serial.println("Demo: Moving both motors");
        moveBothMotors(true, totalStepsPerRev / 4);
        break;
    }
    
    demoState = (demoState + 1) % 4;
    lastDemo = millis();
  }
}

کد پیشرفته: کنترل موقعیت با شتاب

/*
 * کنترل موقعیت موتور پله‌ای با شتاب/کاهش شتاب
 * مناسب برای CNC و پرینترهای 3 بعدی
 */

#include <AccelStepper.h>

// تعریف موتورها با کتابخانه AccelStepper
// استفاده از حالت Driver (پین STEP و DIR)
AccelStepper stepper1(AccelStepper::DRIVER, 5, 6); // STEP, DIR
AccelStepper stepper2(AccelStepper::DRIVER, 7, 4); // STEP, DIR

// پایه‌های کنترل
const int ENABLE_PIN = 8;
const int LIMIT_SWITCH_X = 9;  // سوئیچ محدودیت محور X
const int LIMIT_SWITCH_Y = 10; // سوئیچ محدودیت محور Y
const int EMERGENCY_STOP = 11; // توقف اضطراری

// تنظیمات موتور
const float STEPS_PER_MM = 80.0; // مراحل بر میلی‌متر (بسته به موتور و پولی)
const int MAX_SPEED = 1000;      // حداکثر سرعت (steps/sec)
const int MAX_ACCEL = 500;       // حداکثر شتاب (steps/sec^2)
const float TRAVEL_X = 200.0;    // محدوده حرکت X (mm)
const float TRAVEL_Y = 200.0;    // محدوده حرکت Y (mm)

// متغیرهای سیستم
bool motorsEnabled = false;
bool homingComplete = false;
float currentX = 0;
float currentY = 0;
float targetX = 0;
float targetY = 0;

enum MachineState {
  STATE_IDLE,
  STATE_HOMING,
  STATE_MOVING,
  STATE_EMERGENCY
};

MachineState currentState = STATE_IDLE;

void setup() {
  Serial.begin(115200);
  
  // تنظیم پایه‌ها
  pinMode(ENABLE_PIN, OUTPUT);
  pinMode(LIMIT_SWITCH_X, INPUT_PULLUP);
  pinMode(LIMIT_SWITCH_Y, INPUT_PULLUP);
  pinMode(EMERGENCY_STOP, INPUT_PULLUP);
  
  // غیرفعال کردن موتورها در ابتدا
  disableMotors();
  
  // تنظیمات موتورها
  setupSteppers();
  
  Serial.println("\n\n========================================");
  Serial.println("   CNC Stepper Control System");
  Serial.println("========================================");
  Serial.print("Steps per mm: ");
  Serial.println(STEPS_PER_MM);
  Serial.print("Travel X: ");
  Serial.print(TRAVEL_X);
  Serial.println(" mm");
  Serial.print("Travel Y: ");
  Serial.print(TRAVEL_Y);
  Serial.println(" mm");
  Serial.println("\nCommands:");
  Serial.println("  HOME    - Home both axes");
  Serial.println("  ENABLE  - Enable motors");
  Serial.println("  DISABLE - Disable motors");
  Serial.println("  G0 X Y  - Rapid move to position");
  Serial.println("  G1 X Y F - Linear move with feedrate");
  Serial.println("  POS     - Show current position");
  Serial.println("  STATUS  - Show system status");
  Serial.println("  ?       - Show help");
  Serial.println("----------------------------------------");
}

void loop() {
  // خواندن دستورات سریال
  checkSerialCommands();
  
  // بررسی توقف اضطراری
  checkEmergencyStop();
  
  // بررسی سوئیچ‌های محدودیت
  checkLimitSwitches();
  
  // اجرای منطق بر اساس حالت
  switch (currentState) {
    case STATE_IDLE:
      idleState();
      break;
      
    case STATE_HOMING:
      homingState();
      break;
      
    case STATE_MOVING:
      movingState();
      break;
      
    case STATE_EMERGENCY:
      emergencyState();
      break;
  }
  
  // اجرای موتورها
  stepper1.run();
  stepper2.run();
  
  delay(1);
}

void setupSteppers() {
  // تنظیمات موتور 1 (محور X)
  stepper1.setMaxSpeed(MAX_SPEED);
  stepper1.setAcceleration(MAX_ACCEL);
  stepper1.setEnablePin(ENABLE_PIN);
  stepper1.setPinsInverted(false, false, true); // DIR, STEP, ENABLE
  
  // تنظیمات موتور 2 (محور Y)
  stepper2.setMaxSpeed(MAX_SPEED);
  stepper2.setAcceleration(MAX_ACCEL);
  stepper2.setPinsInverted(false, false, true);
}

void enableMotors() {
  if (!motorsEnabled) {
    digitalWrite(ENABLE_PIN, LOW);
    motorsEnabled = true;
    Serial.println("Motors ENABLED");
  }
}

void disableMotors() {
  if (motorsEnabled) {
    digitalWrite(ENABLE_PIN, HIGH);
    motorsEnabled = false;
    Serial.println("Motors DISABLED");
  }
}

void checkSerialCommands() {
  if (Serial.available()) {
    String command = Serial.readStringUntil('\n');
    command.trim();
    command.toUpperCase();
    
    processGCode(command);
  }
}

void processGCode(String gcode) {
  if (gcode == "HOME") {
    startHoming();
  }
  else if (gcode == "ENABLE") {
    enableMotors();
  }
  else if (gcode == "DISABLE") {
    disableMotors();
  }
  else if (gcode == "POS") {
    showPosition();
  }
  else if (gcode == "STATUS") {
    showStatus();
  }
  else if (gcode == "?") {
    showHelp();
  }
  else if (gcode.startsWith("G0")) {
    // حرکت سریع: G0 X100 Y50
    parseMoveCommand(gcode, true);
  }
  else if (gcode.startsWith("G1")) {
    // حرکت خطی: G1 X100 Y50 F500
    parseMoveCommand(gcode, false);
  }
  else if (gcode.startsWith("G28")) {
    // رفتن به home
    startHoming();
  }
  else if (gcode.startsWith("M84")) {
    // غیرفعال کردن موتورها
    disableMotors();
  }
  else if (gcode.startsWith("M17")) {
    // فعال کردن موتورها
    enableMotors();
  }
  else {
    Serial.print("Unknown command: ");
    Serial.println(gcode);
  }
}

void parseMoveCommand(String gcode, bool rapidMove) {
  // تجزیه دستور حرکت
  float x = currentX;
  float y = currentY;
  float f = MAX_SPEED / STEPS_PER_MM; // سرعت پیش‌فرض mm/sec
  
  // پیدا کردن مقادیر X و Y و F
  int xIndex = gcode.indexOf('X');
  int yIndex = gcode.indexOf('Y');
  int fIndex = gcode.indexOf('F');
  
  if (xIndex != -1) {
    String xStr = gcode.substring(xIndex + 1);
    // حذف هر چیزی بعد از عدد
    xStr = splitValue(xStr);
    x = xStr.toFloat();
  }
  
  if (yIndex != -1) {
    String yStr = gcode.substring(yIndex + 1);
    yStr = splitValue(yStr);
    y = yStr.toFloat();
  }
  
  if (fIndex != -1) {
    String fStr = gcode.substring(fIndex + 1);
    fStr = splitValue(fStr);
    f = fStr.toFloat();
  }
  
  // بررسی محدوده
  if (x < 0 || x > TRAVEL_X || y < 0 || y > TRAVEL_Y) {
    Serial.println("ERROR: Target position out of range");
    return;
  }
  
  // حرکت به موقعیت هدف
  moveToPosition(x, y, f, rapidMove);
}

String splitValue(String str) {
  // جدا کردن مقدار عددی از رشته
  for (int i = 0; i < str.length(); i++) {
    if (!isdigit(str.charAt(i)) && str.charAt(i) != '.' && str.charAt(i) != '-') {
      return str.substring(0, i);
    }
  }
  return str;
}

void startHoming() {
  if (currentState == STATE_IDLE) {
    Serial.println("Starting homing sequence...");
    currentState = STATE_HOMING;
    homingComplete = false;
    enableMotors();
    
    // تنظیم سرعت کم برای homing
    stepper1.setMaxSpeed(200);
    stepper2.setMaxSpeed(200);
    stepper1.setAcceleration(200);
    stepper2.setAcceleration(200);
    
    // حرکت به سمت سوئیچ‌های محدودیت
    stepper1.moveTo(-10000); // حرکت به سمت منفی
    stepper2.moveTo(-10000);
  }
}

void homingState() {
  // بررسی برخورد با سوئیچ‌ها
  bool xHomed = (digitalRead(LIMIT_SWITCH_X) == LOW);
  bool yHomed = (digitalRead(LIMIT_SWITCH_Y) == LOW);
  
  // توقف موتور وقتی سوئیچ فشرده شد
  if (xHomed && stepper1.isRunning()) {
    stepper1.stop();
    stepper1.setCurrentPosition(0);
    Serial.println("X axis homed");
  }
  
  if (yHomed && stepper2.isRunning()) {
    stepper2.stop();
    stepper2.setCurrentPosition(0);
    Serial.println("Y axis homed");
  }
  
  // اگر هر دو محور هوم شدند
  if (xHomed && yHomed && !homingComplete) {
    // حرکت کمی به عقب برای آزاد کردن سوئیچ‌ها
    stepper1.moveTo(10);
    stepper2.moveTo(10);
    
    // انتظار برای حرکت
    delay(500);
    
    // تنظیم موقعیت صفر
    stepper1.setCurrentPosition(0);
    stepper2.setCurrentPosition(0);
    currentX = 0;
    currentY = 0;
    
    // بازگرداندن سرعت‌ها به حالت عادی
    stepper1.setMaxSpeed(MAX_SPEED);
    stepper2.setMaxSpeed(MAX_SPEED);
    stepper1.setAcceleration(MAX_ACCEL);
    stepper2.setAcceleration(MAX_ACCEL);
    
    homingComplete = true;
    currentState = STATE_IDLE;
    
    Serial.println("Homing complete!");
    Serial.println("All axes at zero position");
  }
}

void moveToPosition(float x, float y, float feedrate, bool rapid) {
  if (!homingComplete) {
    Serial.println("ERROR: Please home axes first");
    return;
  }
  
  // تبدیل میلی‌متر به استپ
  long xSteps = x * STEPS_PER_MM;
  long ySteps = y * STEPS_PER_MM;
  
  // تنظیم سرعت
  float speed = feedrate * STEPS_PER_MM; // تبدیل به steps/sec
  if (rapid) {
    speed = MAX_SPEED; // حداکثر سرعت برای حرکت سریع
  }
  
  stepper1.setMaxSpeed(speed);
  stepper2.setMaxSpeed(speed);
  
  // حرکت به موقعیت
  stepper1.moveTo(xSteps);
  stepper2.moveTo(ySteps);
  
  targetX = x;
  targetY = y;
  currentState = STATE_MOVING;
  
  Serial.print("Moving to: X");
  Serial.print(x, 1);
  Serial.print(" Y");
  Serial.print(y, 1);
  Serial.print(" at ");
  Serial.print(feedrate);
  Serial.println(" mm/sec");
}

void movingState() {
  // بررسی رسیدن به موقعیت
  if (!stepper1.isRunning() && !stepper2.isRunning()) {
    // به‌روزرسانی موقعیت فعلی
    currentX = stepper1.currentPosition() / STEPS_PER_MM;
    currentY = stepper2.currentPosition() / STEPS_PER_MM;
    
    currentState = STATE_IDLE;
    
    Serial.print("Move complete. Current position: X");
    Serial.print(currentX, 1);
    Serial.print(" Y");
    Serial.println(currentY, 1);
  }
}

void idleState() {
  // حالت آماده‌باش
  // می‌توانید عملیات پس‌زمینه را اینجا انجام دهید
}

void checkEmergencyStop() {
  if (digitalRead(EMERGENCY_STOP) == LOW) {
    emergencyStop();
  }
}

void emergencyStop() {
  stepper1.stop();
  stepper2.stop();
  disableMotors();
  currentState = STATE_EMERGENCY;
  
  Serial.println("!!! EMERGENCY STOP !!!");
  Serial.println("System halted. Reset required.");
}

void emergencyState() {
  // حالت توقف اضطراری
  // فقط منتظر ریست شدن سیستم
  digitalWrite(ENABLE_PIN, HIGH);
  
  // چشمک زدن LED یا بوق هشدار
  static unsigned long lastBlink = 0;
  static bool blinkState = false;
  
  if (millis() - lastBlink > 500) {
    blinkState = !blinkState;
    // digitalWrite(LED_BUILTIN, blinkState);
    lastBlink = millis();
  }
}

void checkLimitSwitches() {
  // اگر در حال حرکت هستیم و سوئیچ فشرده شد، توقف اضطراری
  if (currentState == STATE_MOVING || currentState == STATE_HOMING) {
    if (digitalRead(LIMIT_SWITCH_X) == LOW && !homingComplete) {
      Serial.println("X limit switch triggered!");
      stepper1.stop();
    }
    
    if (digitalRead(LIMIT_SWITCH_Y) == LOW && !homingComplete) {
      Serial.println("Y limit switch triggered!");
      stepper2.stop();
    }
  }
}

void showPosition() {
  Serial.print("Current position: X");
  Serial.print(currentX, 2);
  Serial.print("mm Y");
  Serial.print(currentY, 2);
  Serial.println("mm");
  
  Serial.print("Motor steps: X");
  Serial.print(stepper1.currentPosition());
  Serial.print(" Y");
  Serial.println(stepper2.currentPosition());
}

void showStatus() {
  Serial.println("\n=== SYSTEM STATUS ===");
  Serial.print("State: ");
  switch (currentState) {
    case STATE_IDLE: Serial.println("IDLE"); break;
    case STATE_HOMING: Serial.println("HOMING"); break;
    case STATE_MOVING: Serial.println("MOVING"); break;
    case STATE_EMERGENCY: Serial.println("EMERGENCY STOP"); break;
  }
  
  Serial.print("Motors: ");
  Serial.println(motorsEnabled ? "ENABLED" : "DISABLED");
  
  Serial.print("Homing: ");
  Serial.println(homingComplete ? "COMPLETE" : "NOT HOMED");
  
  Serial.print("Position: X");
  Serial.print(currentX, 1);
  Serial.print("mm Y");
  Serial.print(currentY, 1);
  Serial.println("mm");
  
  Serial.print("Limit switches: X=");
  Serial.print(digitalRead(LIMIT_SWITCH_X) ? "OPEN" : "TRIGGERED");
  Serial.print(" Y=");
  Serial.println(digitalRead(LIMIT_SWITCH_Y) ? "OPEN" : "TRIGGERED");
  
  Serial.print("Emergency stop: ");
  Serial.println(digitalRead(EMERGENCY_STOP) ? "READY" : "TRIGGERED");
  
  Serial.println("===================\n");
}

void showHelp() {
  Serial.println("\n=== CNC Control System Help ===");
  Serial.println("G-Code Commands:");
  Serial.println("  G0 X[pos] Y[pos]     - Rapid move (maximum speed)");
  Serial.println("  G1 X[pos] Y[pos] F[feedrate] - Linear move");
  Serial.println("  G28                  - Home all axes");
  Serial.println("  M84                  - Disable steppers");
  Serial.println("  M17                  - Enable steppers");
  Serial.println("\nSystem Commands:");
  Serial.println("  HOME    - Home both axes");
  Serial.println("  ENABLE  - Enable motors");
  Serial.println("  DISABLE - Disable motors");
  Serial.println("  POS     - Show current position");
  Serial.println("  STATUS  - Show system status");
  Serial.println("  ?       - Show this help");
  Serial.println("\nExamples:");
  Serial.println("  G0 X100 Y50          - Move to X100 Y50 rapidly");
  Serial.println("  G1 X50 Y75 F300      - Move to X50 Y75 at 300mm/sec");
  Serial.println("==============================\n");
}

کد تنظیم جریان موتور

/*
 * تنظیم جریان موتور برای A4988/DRV8825
 * مهم: تنظیم نادرست جریان می‌تواند به موتور آسیب بزند!
 */

// فرمول محاسبه جریان:
// Vref = I_max * 8 * R_sense
// A4988: R_sense = 0.05Ω
// DRV8825: R_sense = 0.1Ω

const int MOTOR_CURRENT_POT = A0; // پتانسیومتر تنظیم جریان
const int TEST_BUTTON = 2;        // دکمه تست
const int MOTOR_ENABLE = 8;
const int MOTOR_STEP = 5;
const int MOTOR_DIR = 6;

// تنظیمات موتور
const float RSENSE = 0.1; // اهم (برای DRV8825)
const float MAX_CURRENT = 1.5; // آمپر (برچسب موتور را بررسی کنید)
float targetCurrent = 0.5; // جریان هدف (آمپر)

void setup() {
  Serial.begin(9600);
  
  pinMode(MOTOR_ENABLE, OUTPUT);
  pinMode(MOTOR_STEP, OUTPUT);
  pinMode(MOTOR_DIR, OUTPUT);
  pinMode(TEST_BUTTON, INPUT_PULLUP);
  
  digitalWrite(MOTOR_ENABLE, HIGH); // غیرفعال در ابتدا
  
  Serial.println("\n\n========================================");
  Serial.println("   Stepper Motor Current Calibration");
  Serial.println("========================================");
  Serial.println("WARNING: Adjust current slowly!");
  Serial.println("Too high current can damage motor/driver.");
  Serial.println();
  Serial.print("R_sense: ");
  Serial.print(RSENSE);
  Serial.println(" Ω");
  Serial.print("Max motor current: ");
  Serial.print(MAX_CURRENT);
  Serial.println(" A");
  Serial.println();
  Serial.println("Turn potentiometer to adjust current.");
  Serial.println("Press button to test motor.");
  Serial.println("----------------------------------------");
}

void loop() {
  // خواندن پتانسیومتر
  int potValue = analogRead(MOTOR_CURRENT_POT);
  
  // تبدیل به جریان (0 تا MAX_CURRENT)
  targetCurrent = (potValue / 1023.0) * MAX_CURRENT;
  
  // محاسبه Vref مورد نیاز
  float vref = targetCurrent * 8.0 * RSENSE;
  
  // نمایش وضعیت
  static unsigned long lastDisplay = 0;
  if (millis() - lastDisplay > 500) {
    Serial.print("Current: ");
    Serial.print(targetCurrent, 2);
    Serial.print("A | Vref: ");
    Serial.print(vref, 3);
    Serial.print("V | Pot: ");
    Serial.print(potValue);
    
    // نمایش پیشنهاد
    Serial.print(" | Suggestion: ");
    if (targetCurrent < MAX_CURRENT * 0.3) {
      Serial.println("TOO LOW - Motor may skip steps");
    } else if (targetCurrent < MAX_CURRENT * 0.7) {
      Serial.println("GOOD - Normal operation");
    } else if (targetCurrent < MAX_CURRENT * 0.9) {
      Serial.println("HIGH - Good for high torque");
    } else {
      Serial.println("MAX - Use with caution!");
    }
    
    lastDisplay = millis();
  }
  
  // تست موتور با دکمه
  if (digitalRead(TEST_BUTTON) == LOW) {
    testMotor();
    delay(300); // Debouncing
  }
  
  // نکته: در این کد Vref به صورت نرم‌افزاری محاسبه می‌شود
  // در عمل باید پتانسیومتر روی درایور را تنظیم کنید
  
  delay(10);
}

void testMotor() {
  Serial.println("\n--- Motor Test ---");
  Serial.print("Testing at ");
  Serial.print(targetCurrent, 2);
  Serial.println("A");
  
  // فعال کردن موتور
  digitalWrite(MOTOR_ENABLE, LOW);
  
  // حرکت کوچک به جلو
  digitalWrite(MOTOR_DIR, HIGH);
  for (int i = 0; i < 100; i++) {
    digitalWrite(MOTOR_STEP, HIGH);
    delayMicroseconds(500);
    digitalWrite(MOTOR_STEP, LOW);
    delayMicroseconds(500);
  }
  
  // حرکت کوچک به عقب
  digitalWrite(MOTOR_DIR, LOW);
  for (int i = 0; i < 100; i++) {
    digitalWrite(MOTOR_STEP, HIGH);
    delayMicroseconds(500);
    digitalWrite(MOTOR_STEP, LOW);
    delayMicroseconds(500);
  }
  
  // غیرفعال کردن موتور
  digitalWrite(MOTOR_ENABLE, HIGH);
  
  // بررسی عملکرد
  Serial.println("Test complete. Check motor behavior:");
  Serial.println("- Smooth movement: Good current");
  Serial.println("- Jerky/skipping: Current too low");
  Serial.println("- Overheating: Current too high");
  Serial.println("--- End Test ---\n");
}

// تابع برای تنظیم دستی Vref با مولتی‌متر
void manualCalibrationGuide() {
  Serial.println("\n=== Manual Calibration Guide ===");
  Serial.println("1. Connect multimeter to Vref pin on driver");
  Serial.println("2. Adjust potentiometer on driver board");
  Serial.println("3. Measure and set Vref according to formula:");
  Serial.println("   Vref = I_max × 8 × R_sense");
  Serial.println();
  Serial.println("Example calculations:");
  Serial.println("For 1A current with DRV8825 (R_sense=0.1Ω):");
  Serial.println("   Vref = 1.0 × 8 × 0.1 = 0.8V");
  Serial.println();
  Serial.println("For 1.5A current with A4988 (R_sense=0.05Ω):");
  Serial.println("   Vref = 1.5 × 8 × 0.05 = 0.6V");
  Serial.println("===============================\n");
}

نکات حیاتی نصب و استفاده

تنظیم جریان صحیح:

  1. محاسبه Vref:

    Vref = جریان_موردنیاز × 8 × R_sense
    
    مثال برای موتور 1.2A با DRV8825:
    Vref = 1.2 × 8 × 0.1 = 0.96V
  2. روش تنظیم:

    • مولتی‌متر را روی ولتاژ DC تنظیم کنید

    • پراب مثبت به پین Vref درایور

    • پراب منفی به GND درایور

    • پتانسیومتر را تا رسیدن به ولتاژ محاسبه‌شده بچرخانید

خنک‌کاری ضروری:

// مانیتور دمای درایور
void monitorTemperature() {
  // A4988: Shutdown at 150°C, Thermal hysteresis 25°C
  // DRV8825: Shutdown at 165°C
  
  // علائم گرمای بیش از حد:
  // 1. کاهش گشتاور
  // 2. توقف ناگهانی
  // 3. بوی سوختگی
  
  // راه‌حل‌ها:
  // 1. اضافه کردن هیت‌سینک بزرگتر
  // 2. استفاده از فن 5V یا 12V
  // 3. کاهش جریان موتور
  // 4. افزایش زمان خاموش بودن
}

اتصالات صحیح:

مشکل علت راه‌حل
موتور حرکت نمی‌کند اتصال نادرست فازها سیم‌های یک فاز را جابجا کنید
موتور داغ می‌شود جریان بیش از حد کاهش Vref
درایور داغ می‌شود عدم خنک‌کاری اضافه کردن هیت‌سینک و فن
حرکت ناصاف میکرواستپ نادرست تنظیم صحیح MS1, MS2, MS3
نویز در سیستم سیم‌کندی ضعیف استفاده از کانکتورهای مناسب

کاهش نویز و EMI:

  1. خازن‌های بای‌پس:

    • 100µF الکترولیتی نزدیک ورودی VMOT

    • 0.1µF سرامیکی بین هر VMOT و GND

  2. سیم‌کندی:

    • استفاده از کابل به هم تابیده برای موتور

    • جدا کردن سیم‌های سیگنال از سیم‌های قدرت

    • اتصال زمین صحیح

  3. شیلدینگ:

    • محفظه فلزی برای درایور

    • فریت‌بید روی کابل موتور


پروژه‌های پیشنهادی

1. پرینتر 3 بعدی DIY

// کنترل 3 محور X, Y, Z با اکستروژر

2. دستگاه CNC کوچک

// کنترل محورهای X, Y برای حکاکی

3. ربات پلوتار

// کنترل 4 موتور برای حرکت ربات

4. اسکنر 3 بعدی

// کنترل موتور برای چرخش قطعه

5. تلسکوپ ردیاب ستاره‌ها

// کنترل دقیق موتور برای ردیابی اجرام آسمانی

جدول انتخاب موتور مناسب

کاربرد نوع موتور جریان میکرواستپ نکات
پرینتر 3 بعدی NEMA 17 1.2-1.5A 1/16 یا 1/32 دقت بالا، نویز کم
CNC کوچک NEMA 17 1.5-1.8A 1/8 یا 1/16 گشتاور متوسط
رباتیک NEMA 14 0.8-1.2A 1/8 سبک، کم مصرف
صنعتی سبک NEMA 23 2.0-2.5A 1/4 گشتاور بالا
میکرو موقعیت‌یابی NEMA 8 0.5-0.8A 1/32 دقت بسیار بالا

پکیج خرید

پکیج پایه:

  • 1x شیلد درایور موتور پله‌ای

  • 2x درایور A4988 با هیت‌سینک

  • 1x موتور پله‌ای NEMA 17

  • 1x آداپتور 12V/2A

  • کابل اتصال موتور

  • پیچ و مهره نصب

پکیج پرینتر 3 بعدی:

  • 1x شیلد درایور

  • 4x درایور DRV8825 با فن خنک‌کننده

  • 4x موتور NEMA 17

  • 1x منبع تغذیه 24V/10A

  • 1x کیت CNC shield

  • 1x مجموعه ابزار تنظیم

پکیج توسعه‌دهنده:

  • 1x شیلد درایور

  • 2x A4988 + 2x DRV8825

  • 4x موتور مختلف (NEMA 8, 14, 17, 23)

  • 1x کیت تست جریان و دما

  • 1x مولتی‌متر دیجیتال

  • کتابچه 20 پروژه عملی


گارانتی و پشتیبانی

  • پشتیبانی فنی: رایگان از طریق واتس‌اپ
  • آموزش: ویدیوهای تنظیم جریان و میکرواستپ

  • کتابخانه: دسترسی به آخرین نسخه AccelStepper

  • جامعه: گروه کاربران موتور پله‌ای


سؤالات متداول

Q1: تفاوت A4988 و DRV8825 چیست؟
A4988 ارزان‌تر و برای کاربردهای عمومی. DRV8825 با میکرواستپ 1/32 و ولتاژ بالاتر برای دقت بیشتر.

Q2: چرا موتور من داغ می‌شود؟
جریان بیش از حد تنظیم شده. Vref را کاهش دهید یا از خنک‌کننده استفاده کنید.

Q3: میکرواستپ چیست و کدام بهتر است؟
تقسیم هر استپ به بخش‌های کوچک‌تر برای حرکت نرم‌تر. 1/16 تعادل خوبی بین دقت و گشتاور دارد.

Q4: حداکثر سرعت چقدر است؟
حدود 2000-3000 استپ بر ثانیه بسته به موتور و ولتاژ.

Q5: چگونه از سوختن درایور جلوگیری کنم؟

  1. جریان صحیح تنظیم شود

  2. خنک‌کننده مناسب

  3. اتصال کوتاه نشود

  4. ولتاژ ورودی از حد مجاز بیشتر نباشد


هشدار: تنظیم نادرست جریان می‌تواند به موتور و درایور آسیب جدی بزند. همیشه با کمترین جریان شروع کنید و به تدریج افزایش دهید. برای کاربردهای حساس با مهندسین ما مشورت کنید.