﻿#include <Arduino.h>
#include <SimpleFOC.h>
/* =====================================================================
 * SimpleFOC 开环步进 — Nucleo-G474RE + A4950 + 42步进
 * 兼容: motor_control.py Commander + Industrial_ui gimbaldll
 *
 * gimbaldll 串口习惯（从 DLL 字符串还原）:
 *   open:  AE1 / BE1
 *   speed: %cE1  %cC1  %c<vel>
 *   pos:   %cC2  %c<pos> <speed?>
 * Commander: C1=velocity → 映射 velocity_openloop
 *            C2=angle    → 映射 angle_openloop（无编码器）
 *
 * A4950: AIN1=5 AIN2=6 BIN1=9 BIN2=10；A/B 前缀同一台电机
 * 串口: VCP USART2 PA2/PA3 → COM14 @ 115200
 * ===================================================================== */

const uint8_t PIN_AIN1 = 5;
const uint8_t PIN_AIN2 = 6;
const uint8_t PIN_BIN1 = 9;
const uint8_t PIN_BIN2 = 10;

const int   STEPPER_STEPS  = 200;
const float SUPPLY_VOLTAGE = 12.0f;
const float VOLTAGE_LIMIT  = 4.5f;
const float MAX_TARGET_VEL = 20.0f;
const float TARGET_RAMP_RPS = 8.0f;

StepperMotor motor(STEPPER_STEPS);
StepperDriver4PWM driver(PIN_AIN1, PIN_AIN2, PIN_BIN1, PIN_BIN2);
Commander command = Commander(Serial);

float cmdTarget = 0.0f;
float rampedTarget = 0.0f;
bool  velRampEnable = true;   // 仅速度模式做斜坡

static void forceOpenLoop() {
    // 无 sensor：把闭环模式改成对应开环，否则 move() 不驱动
    if (motor.controller == MotionControlType::velocity)
        motor.controller = MotionControlType::velocity_openloop;
    else if (motor.controller == MotionControlType::angle)
        motor.controller = MotionControlType::angle_openloop;
}

static bool isVelMode() {
    return motor.controller == MotionControlType::velocity ||
           motor.controller == MotionControlType::velocity_openloop;
}

void onMotorCmd(char* cmd) {
    command.motor(&motor, cmd);
    forceOpenLoop();
    velRampEnable = isVelMode();
    if (velRampEnable) {
        cmdTarget = motor.target;
    }
    // 角度模式：斜坡状态与目标对齐，避免回切速度时突跳
    rampedTarget = motor.target;
}

void setup() {
    Serial.begin(115200);
    delay(400);

    Serial.println();
    Serial.println(F("========== SimpleFOC STEPPER + A4950 =========="));
    Serial.println(F("open-loop + gimbal Commander (C1/C2 remap)"));
    Serial.println(F("cmd: AE1  A2  AC2 +A0.17  or Industrial_ui"));

    driver.voltage_power_supply = SUPPLY_VOLTAGE;
    driver.voltage_limit = VOLTAGE_LIMIT;
    driver.pwm_frequency = 20000;
    driver.init();

    motor.linkDriver(&driver);
    motor.controller = MotionControlType::velocity_openloop;
    motor.voltage_limit = VOLTAGE_LIMIT;
    motor.velocity_limit = MAX_TARGET_VEL;

    motor.PID_velocity.P = 0.2f;
    motor.PID_velocity.I = 1.0f;
    motor.PID_velocity.D = 0.0f;
    motor.PID_velocity.output_ramp = 50.0f;
    motor.LPF_velocity.Tf = 0.02f;

    motor.init();
    motor.disable();

    command.add('A', onMotorCmd, (char*)"stepper");
    command.add('B', onMotorCmd, (char*)"stepper_alias");

    Serial.println(F("Ready. AE1 then A2 / industrial SingleToPosition"));
    Serial.println(F("========================================"));
}

void loop() {
    static uint32_t lastUs = micros();
    uint32_t now = micros();
    float dt = (now - lastUs) * 1e-6f;
    lastUs = now;
    if (dt > 0.05f) dt = 0.05f;

    if (velRampEnable && isVelMode()) {
        float maxStep = TARGET_RAMP_RPS * dt;
        float err = cmdTarget - rampedTarget;
        if (err > maxStep) err = maxStep;
        if (err < -maxStep) err = -maxStep;
        rampedTarget += err;
        motor.target = rampedTarget;
    }
    // 角度模式：保留 Commander 写入的 motor.target，不做速度斜坡覆盖

    motor.move();
    command.run();
}
