Arduino IDE下使用STM32开发板脉冲控制步进电机(二)按钮控制

改用按钮(常开,脉冲)控制

接线和代码如下:

  • 每个按钮一端接 3.3V

  • 另一端分别接 PB0、PB1、PB5(无PB2,实际上是Boot1非普通IO,PB3 默认是 JTDO,调试引脚)

/*
 * CA-4022M 步进电机驱动器控制程序
 * 功能:通过串口命令或物理按钮控制电机
 * 硬件:STM32F103C8T6 + CA-4022M驱动器
 * 
 * 按钮定义:
 *   Button A (PB0) - 低速往复5次(脉冲触发)
 *   Button B (PB1) - 高速往复5次(脉冲触发)
 *   Button C (PB2) - 停止运动(脉冲触发)
 */

// ==================== 引脚定义 ====================
#define PUL_PIN PA0     // 脉冲输出引脚
#define DIR_PIN PA1     // 方向控制引脚
#define LED_PIN PC13    // 板载LED引脚

// ==================== 按钮引脚定义 ====================
#define BTN_A_PIN PB0   // 按钮A - 低速往复
#define BTN_B_PIN PB1   // 按钮B - 高速往复
#define BTN_C_PIN PB5   // 按钮C - 停止

// ==================== 按钮状态变量 ====================
bool lastBtnAState = LOW;
bool lastBtnBState = LOW;
bool lastBtnCState = LOW;
unsigned long lastDebounceTimeA = 0;
unsigned long lastDebounceTimeB = 0;
unsigned long lastDebounceTimeC = 0;
#define DEBOUNCE_DELAY 50  // 消抖延时50ms

// ==================== LED状态枚举 ====================
enum LEDState {
  LED_OFF,
  LED_BLINK_FAST,
  LED_ON
};
LEDState currentLEDState = LED_OFF;
unsigned long lastLEDUpdate = 0;
bool ledBlinkState = false;

// ==================== 运动控制变量 ====================
String inputString = "";
boolean stringComplete = false;
int currentFreq = 0;
bool motorRunning = false;

// 往复运动相关变量
volatile long pulseCount = 0;
int targetPulses = 0;
bool isReversing = false;
bool needReverse = false;

// 往复次数控制变量
int totalCycles = 0;
int currentCycle = 0;
bool cycleComplete = false;

// 暂停控制变量
bool isPausing = false;
unsigned long pauseStartTime = 0;
#define PAUSE_DURATION_MS 500

// ==================== 运动模式枚举 ====================
enum MotionMode {
  MODE_IDLE,
  MODE_LOW_SPEED,
  MODE_HIGH_SPEED,
  MODE_MANUAL
};
MotionMode currentMode = MODE_IDLE;

// ==================== 运动参数定义 ====================
#define LOW_SPEED_PULSES  1600
#define HIGH_SPEED_PULSES 500
#define MOTION_TIME_MS    3000
#define LOW_SPEED_FREQ  (LOW_SPEED_PULSES * 1000 / MOTION_TIME_MS)
#define HIGH_SPEED_FREQ (HIGH_SPEED_PULSES * 1000 / MOTION_TIME_MS)

// ==================== 硬件定时器对象 ====================
HardwareTimer *pwmTimer;

// ==================== 定时器中断服务函数 ====================
void onPulseCompare() {
  pulseCount++;
  if (pulseCount >= targetPulses) {
    needReverse = true;
  }
}

// ==================== LED控制函数 ====================
void setLEDState(LEDState newState, const char* reason) {
  if (currentLEDState != newState) {
    currentLEDState = newState;
    
    Serial.print("[LED] ");
    switch(newState) {
      case LED_OFF:
        Serial.println("熄灭 - 电机空闲或停止");
        break;
      case LED_BLINK_FAST:
        Serial.println("快速闪烁(300ms) - 电机正在运行");
        break;
      case LED_ON:
        Serial.println("常亮 - 到达正向终点");
        break;
    }
    
    if (strlen(reason) > 0) {
      Serial.print("      原因: ");
      Serial.println(reason);
    }
  }
}

// ==================== 更新LED物理状态 ====================
void updateLED() {
  unsigned long currentMillis = millis();
  
  switch(currentLEDState) {
    case LED_OFF:
      digitalWrite(LED_PIN, HIGH);
      break;
    case LED_ON:
      digitalWrite(LED_PIN, LOW);
      break;
    case LED_BLINK_FAST:
      if (currentMillis - lastLEDUpdate >= 300) {
        ledBlinkState = !ledBlinkState;
        digitalWrite(LED_PIN, ledBlinkState ? LOW : HIGH);
        lastLEDUpdate = currentMillis;
      }
      break;
  }
}

// ==================== 按钮处理函数 ====================
// ==================== 极简按钮处理(专门用于导线测试)====================
void checkButtons() {
  static unsigned long lastBtnTime = 0;
  
  // 每500ms最多检测一次,避免连续触发
  if (millis() - lastBtnTime < 500) return;
  
  if (digitalRead(BTN_A_PIN) == HIGH) {
    lastBtnTime = millis();
    Serial.println("\n[按钮] PB0 高电平 - 启动低速往复");
    currentMode = MODE_LOW_SPEED;
    startOscillation(LOW_SPEED_PULSES, LOW_SPEED_FREQ, 5, "低速(按钮)");
  }
  
  if (digitalRead(BTN_B_PIN) == HIGH) {
    lastBtnTime = millis();
    Serial.println("\n[按钮] PB1 高电平 - 启动高速往复");
    currentMode = MODE_HIGH_SPEED;
    startOscillation(HIGH_SPEED_PULSES, HIGH_SPEED_FREQ, 5, "高速(按钮)");
  }
  
  if (digitalRead(BTN_C_PIN) == HIGH) {
    lastBtnTime = millis();
    Serial.println("\n[按钮] PB2 高电平 - 停止");
    stopMotion();
  }
}

// ==================== 初始化设置 ====================
void setup() {
  Serial.begin(115200);
  
  Serial.println();
  Serial.println("========================================");
  Serial.println("  CA-4022M 电机控制程序 - 按钮控制版");
  Serial.println("========================================");
  
  pinMode(DIR_PIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);
  
  // ===== 初始化按钮引脚(启用下拉电阻)=====
  pinMode(BTN_A_PIN, INPUT_PULLDOWN);
  pinMode(BTN_B_PIN, INPUT_PULLDOWN);
  pinMode(BTN_C_PIN, INPUT_PULLDOWN);
  // 对于PB3/PB4,需要禁用JTAG功能
  //GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
  
  digitalWrite(DIR_PIN, LOW);
  digitalWrite(LED_PIN, HIGH);
  
  // 初始化PWM
  pwmTimer = new HardwareTimer(TIM2);
  pwmTimer->setPrescaleFactor(1);
  pwmTimer->setOverflow(1000, HERTZ_FORMAT);
  pwmTimer->setCaptureCompare(1, 50, PERCENT_COMPARE_FORMAT);
  pwmTimer->setMode(1, TIMER_OUTPUT_COMPARE_PWM1, PUL_PIN);
  pwmTimer->setCaptureCompare(1, 50, PERCENT_COMPARE_FORMAT);
  pwmTimer->attachInterrupt(1, onPulseCompare);
  pwmTimer->pause();
  
  Serial.println("[信息] 硬件PWM中断初始化完成");
  Serial.println("[信息] 按钮已初始化:A-低速 B-高速 C-停止");
  
  setLEDState(LED_OFF, "系统初始化完成");
  
  printMenu();
  inputString.reserve(64);
}

// ==================== 设置PWM频率 ====================
void setPWMFrequency(uint32_t freqHz) {
  if (freqHz == 0) {
    pwmTimer->pause();
    return;
  }
  
  if (freqHz < 10) freqHz = 10;
  if (freqHz > 100000) freqHz = 100000;
  
  pwmTimer->pause();
  pwmTimer->setOverflow(freqHz, HERTZ_FORMAT);
  pwmTimer->setCaptureCompare(1, 50, PERCENT_COMPARE_FORMAT);
  pwmTimer->resume();
}

// ==================== 开始往复运动 ====================
void startOscillation(int pulsesPerCycle, int freqHz, int cycles, const char* modeName) {
  stopMotion();
  
  currentFreq = freqHz;
  targetPulses = pulsesPerCycle;
  totalCycles = cycles;
  currentCycle = 0;
  pulseCount = 0;
  needReverse = false;
  isReversing = false;
  isPausing = false;
  cycleComplete = false;
  
  digitalWrite(DIR_PIN, LOW);
  
  setPWMFrequency(freqHz);
  motorRunning = true;
  currentMode = (freqHz == LOW_SPEED_FREQ) ? MODE_LOW_SPEED : MODE_HIGH_SPEED;
  
  setLEDState(LED_BLINK_FAST, "开始运动");
  
  Serial.print("[信息] 开始");
  Serial.print(modeName);
  Serial.print("往复运动 (");
  Serial.print(pulsesPerCycle);
  Serial.print("脉冲/单程,");
  Serial.print(cycles);
  Serial.println("次往复后自动停止)");
}

// ==================== 停止所有运动 ====================
void stopMotion() {
  setPWMFrequency(0);
  motorRunning = false;
  currentMode = MODE_IDLE;
  targetPulses = 0;
  totalCycles = 0;
  currentCycle = 0;
  pulseCount = 0;
  needReverse = false;
  isPausing = false;
  cycleComplete = false;
  
  setLEDState(LED_OFF, "运动停止");
  
  Serial.println("[信息] 运动停止");
}

// ==================== 主循环 ====================
void loop() {
  // 处理串口命令
  if (stringComplete) {
    processCommand(inputString);
    inputString = "";
    stringComplete = false;
  }
  
  // ===== 新增:检查按钮状态 =====
  checkButtons();
  
  // 处理往复运动的方向切换和次数控制
  if (motorRunning && (currentMode == MODE_LOW_SPEED || currentMode == MODE_HIGH_SPEED)) {
    
    if (isPausing) {
      if (millis() - pauseStartTime >= PAUSE_DURATION_MS) {
        isPausing = false;
        
        if (currentCycle >= totalCycles) {
          stopMotion();
          Serial.println("[信息] 已完成指定次数,运动结束");
        } else {
          isReversing = true;
          digitalWrite(DIR_PIN, HIGH);
          pulseCount = 0;
          needReverse = false;
          
          setPWMFrequency(currentFreq);
          setLEDState(LED_BLINK_FAST, "暂停结束,开始反向运动");
          Serial.println("[信息] 暂停结束,开始反向运动");
        }
      }
    }
    
    if (needReverse && !isPausing) {
      needReverse = false;
      
      if (!isReversing) {
        Serial.println("[提示] 正向单程终点到达");
        setLEDState(LED_ON, "到达正向终点");
        
        setPWMFrequency(0);
        isPausing = true;
        pauseStartTime = millis();
        
        Serial.println("[信息] 暂停500ms...");
      } else {
        currentCycle++;
        
        Serial.print("[信息] 已完成 ");
        Serial.print(currentCycle);
        Serial.print("/");
        Serial.print(totalCycles);
        Serial.println(" 次往复");
        
        if (currentCycle >= totalCycles) {
          stopMotion();
          Serial.println("[信息] 已完成指定次数,运动结束");
        } else {
          isReversing = false;
          digitalWrite(DIR_PIN, LOW);
          pulseCount = 0;
          setLEDState(LED_BLINK_FAST, "开始下一个正向运动");
        }
      }
    }
  }
  
  updateLED();
}

// ==================== 串口接收事件 ====================
void serialEvent() {
  while (Serial.available()) {
    char inChar = (char)Serial.read();
    Serial.print(inChar);
    
    if (inChar == '\n' || inChar == '\r') {
      if (inputString.length() > 0) {
        stringComplete = true;
        Serial.println();
      }
    } else {
      inputString += inChar;
    }
  }
}

// ==================== 处理用户命令 ====================
void processCommand(String cmd) {
  cmd.trim();
  cmd.toLowerCase();
  
  Serial.println();
  Serial.print("[收到] ");
  Serial.println(cmd);
  
  if (cmd.length() == 0) {
    printMenu();
    return;
  }
  
  char cmdType = cmd.charAt(0);
  
  switch (cmdType) {
    case 'a':
    case 'b':
    {
      int spaceIndex = cmd.indexOf(' ');
      int cycles = 5;
      
      if (spaceIndex != -1) {
        String numStr = cmd.substring(spaceIndex + 1);
        int num = numStr.toInt();
        if (num > 0) {
          cycles = num;
        }
      }
      
      if (cmdType == 'a') {
        startOscillation(LOW_SPEED_PULSES, LOW_SPEED_FREQ, cycles, "低速");
      } else {
        startOscillation(HIGH_SPEED_PULSES, HIGH_SPEED_FREQ, cycles, "高速");
      }
      break;
    }
      
    case 'f':
    case 'r':
      stopMotion();
      currentMode = MODE_MANUAL;
      handleManualCommand(cmd);
      if (motorRunning) {
        setLEDState(LED_BLINK_FAST, "手动模式运行");
      }
      break;
      
    case 's':
      stopMotion();
      break;
      
    case 'p':
      printStatus();
      break;
      
    case 'h':
    case '?':
      printMenu();
      break;
      
    default:
      Serial.println("[错误] 未知命令");
      printMenu();
  }
}

// ==================== 处理手动F/R命令 ====================
void handleManualCommand(String cmd) {
  char dir = cmd.charAt(0);
  
  int spaceIndex = cmd.indexOf(' ');
  if (spaceIndex == -1) {
    Serial.println("[错误] 格式错误,正确格式: f 1000 或 r 1000");
    return;
  }
  
  String freqStr = cmd.substring(spaceIndex + 1);
  int freq = freqStr.toInt();
  
  if (freq < 0) {
    Serial.println("[错误] 频率不能为负数");
    return;
  }
  
  digitalWrite(DIR_PIN, (dir == 'f') ? LOW : HIGH);
  Serial.print("[信息] 手动模式 - 方向: ");
  Serial.println(dir == 'f' ? "正转" : "反转");
  
  setPWMFrequency(freq);
  
  motorRunning = (freq > 0);
  currentFreq = freq;
}

// ==================== 打印帮助菜单 ====================
void printMenu() {
  Serial.println("\n********** 可用命令 **********");
  Serial.println("  a        - 低速往复5次");
  Serial.println("  a 10     - 低速往复10次");
  Serial.println("  b        - 高速往复5次");
  Serial.println("  b 8      - 高速往复8次");
  Serial.println("  s        - 停止运动");
  Serial.println("  f 1000   - 手动正转");
  Serial.println("  r 500    - 手动反转");
  Serial.println("  p        - 显示状态");
  Serial.println("  h 或 ?   - 帮助");
  Serial.println("\n物理按钮:");
  Serial.println("  Button A - 低速往复5次");
  Serial.println("  Button B - 高速往复5次");
  Serial.println("  Button C - 停止");
  Serial.println("\nLED指示:");
  Serial.println("  熄灭 - 空闲/停止");
  Serial.println("  快闪 - 运行中");
  Serial.println("  常亮 - 正向终点");
  Serial.println("********************************\n");
}

// ==================== 打印当前状态 ====================
void printStatus() {
  Serial.println("\n********** 当前状态 **********");
  
  Serial.print("运行模式: ");
  switch(currentMode) {
    case MODE_IDLE: Serial.println("空闲"); break;
    case MODE_LOW_SPEED: Serial.println("低速往复"); break;
    case MODE_HIGH_SPEED: Serial.println("高速往复"); break;
    case MODE_MANUAL: Serial.println("手动"); break;
  }
  
  Serial.print("方向: "); 
  Serial.println(digitalRead(DIR_PIN) ? "反转" : "正转");
  
  Serial.print("频率: "); 
  Serial.print(currentFreq); 
  Serial.println(" Hz");
  
  Serial.print("LED状态: ");
  switch(currentLEDState) {
    case LED_OFF: Serial.println("熄灭"); break;
    case LED_BLINK_FAST: Serial.println("快闪(300ms)"); break;
    case LED_ON: Serial.println("常亮"); break;
  }
  
  if (currentMode == MODE_LOW_SPEED || currentMode == MODE_HIGH_SPEED) {
    Serial.print("往复次数: ");
    Serial.print(currentCycle);
    Serial.print("/");
    Serial.println(totalCycles);
    Serial.print("已发送脉冲: ");
    Serial.println(pulseCount);
    Serial.print("目标脉冲/单程: ");
    Serial.println(targetPulses);
    Serial.print("当前行程: ");
    Serial.println(isReversing ? "反向" : "正向");
    Serial.print("暂停状态: ");
    Serial.println(isPausing ? "暂停中" : "运行中");
    if (isPausing) {
      Serial.print("剩余暂停: ");
      Serial.print(PAUSE_DURATION_MS - (millis() - pauseStartTime));
      Serial.println("ms");
    }
  }
  
  Serial.print("电机状态: "); 
  Serial.println(motorRunning ? "运行中" : "停止");
  Serial.println("********************************\n");
}

 

posted @ 2026-03-04 20:22  尼古拉-卡什  阅读(0)  评论(0)    收藏  举报