这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【let'sdo静音步进电机控制实践】成果贴

共1条 1/1 1 跳转至

【let'sdo静音步进电机控制实践】成果贴

工程师
2026-06-28 00:44:30   被打赏 20 分(兑奖)     打赏

        接下来就是最后功能实现的时刻了,我们本次主要实现的功能想要都融合在一起,不管是启停还是方向控制还有速度控制,希望都可以用这个开发板的现有资源实现,也就是说尽量不再进行额外的硬件资源,当然了步进电机是必不可少的。

        这次主要的控制对象就是步进电机,我们找了一个42步进电机,作为本次的控制目标:

de1e7396-bc3e-4f69-8dad-e128c7d718b6.png

        为了方便观察我们在输出轴上放了一个胶条,然后和咱们得控制系统的板卡连接在一起,搭建好完整硬件连接效果如下:

21ebb7a3-6ac4-47c0-b0a1-3597806d2d02.png

        注意电机的主要供电是需要单独供电的。

步进电机控制软件功能实现

        通过板载用户按键(PC13)循环切换电机工作状态,所有电机动作由 TIM12 1ms 中断驱动:

按键次数工作状态速度LED电机使能
0(初始)停止EN=HIGH(禁用)
1速度1 往复运动~333 步/秒EN=LOW(使能)
2速度2 往复运动~500 步/秒EN=LOW(使能)
3停止EN=HIGH(禁用)
4回到速度1循环往复
        往复运动:每个方向运行 MOTOR_STEPS_PER_DIR(默认 400)步后自动反向。

硬件资源

资源用途配置
PC0STEP 脉冲输出推挽输出,低速
PC2DIR 方向控制推挽输出,低速
PC3EN 使能(低有效)推挽输出,低速
PC13用户按键(BUTTON_USER)EXTI 上升沿触发
TIM121 ms 定时基准预分频 144−1,周期 999(144 MHz 下得 1 ms)
LED_GREEN状态指示BSP_LED 控制

软件架构

        整体由两条中断路径驱动,主循环保持空闲:

+-------------------+        +----------------------+
按键 PC13 ─► EXTI13_IRQHandler ──►  BSP_PB_Callback ──►  Motor_CycleState()
        |(200ms 消抖)        |        |(切换 state, 重置相位)|
        +-------------------+        +----------------------+
                                                  │
                                                  ▼
        +-------------------+        +----------------------+
TIM12 1ms ─► TIM12_IRQHandler ──►  HAL_TIM_PeriodElapsedCallback
        +-------------------+        |(生成 STEP 脉冲,自动换向)|
                                     +----------------------+

功能函数详解

Motor_CycleState() — 按键事件处理

        当EXTI13中断触发时,依次调用BSP_PB_Callback函数,进而调用Motor_CycleState功能。依据当前状态来计算下一状态,达成STOPPED → SPEED1 → SPEED2 → STOPPED的循环转换。关键步骤:借助motor.state,利用switch语句切换至下一状态,并同步设定 motor.period_us 的值。重置步进相位:将phase、acc_us、pulse_ticks、settling_ticks、steps_in_dir以及 dir_change_pending全部清零,以此保证在状态切换后,首个STEP脉冲能够干净利落地启动,不会携带上一个状态的残余脉冲。调用 Motor_ApplyStateGpio() 函数,同步更新 EN 与 LED 的状态。设计意义:在速度切换过程中,新的 period_us 仅在相位重置后的下一次 READY 开始计时时才会生效,如此一来,当前正在输出的脉冲不会被中途打断,从而实现平稳的速度过渡。

    代码如下:

static void Motor_CycleState(void)
{
    /* 根据当前状态计算下一状态 */
    switch (motor.state)
    {
        case MOTOR_STATE_STOPPED:
            motor.state = MOTOR_STATE_SPEED1;
            motor.period_us = MOTOR_STEP_PERIOD_S1_US;
            break;

        case MOTOR_STATE_SPEED1:
            motor.state = MOTOR_STATE_SPEED2;
            motor.period_us = MOTOR_STEP_PERIOD_S2_US;
            break;

        case MOTOR_STATE_SPEED2:
        default:
            motor.state = MOTOR_STATE_STOPPED;
            break;
    }

    /* 切换状态后重置步进相位,避免速度切换/启动时脉冲被截断导致丢步 */
    motor.phase = MOTOR_PHASE_READY;
    motor.acc_us = 0;
    motor.pulse_ticks = 0;
    motor.settling_ticks = 0;
    motor.steps_in_dir = 0;
    motor.dir_change_pending = 0;

    /* 速度切换时,新的 period_us 在下一次 READY 相位开始计时时生效,
       这样当前正在输出的脉冲不会被中途打断,实现平稳过渡 */

    Motor_ApplyStateGpio();
}

Motor_SetDirection(Motor_Dir_t new_dir) — 安全换向

        仅当TIM12中断服务程序(ISR)处于READY阶段,且STEP信号确定处于低电平时,方可进行调用。执行motor.dir的翻转操作,并将翻转后的值写入DIR引脚。在调用此功能前,必须确保STEP引脚为低电平状态,以防止在STEP信号的边沿附近更改DIR设置,进而引发TMC2209芯片内部时序出现错乱或导致丢步现象。

        代码如下:

tatic void Motor_SetDirection(Motor_Dir_t new_dir)
{
    motor.dir = new_dir;
    HAL_GPIO_WritePin(Dir_GPIO_Port, Dir_Pin,
                      (new_dir == MOTOR_DIR_CW) ? GPIO_PIN_RESET : GPIO_PIN_SET);
}

Motor_ApplyStateGpio() — 状态同步到 GPIO / LED

        该功能依据 motor.state 的状态进行同步操作。当处于STOPPED状态时,将EN设置为HIGH(鉴于TMC2209为低有效,此操作可禁用驱动器),同时让LED熄灭(LED_OFF);当处于SPEED1或SPEED2状态时,把EN设置为LOW(使能驱动器),并点亮LED(LED_ON)。此功能可被 Motor_CycleState() 函数以及TIM12中断服务程序(ISR)调用,以此保证在停止状态下,EN与LED的状态始终得以维持。

        代码如下:
static void Motor_ApplyStateGpio(void)
{
    if (motor.state == MOTOR_STATE_STOPPED)
    {
        HAL_GPIO_WritePin(En_GPIO_Port, En_Pin, GPIO_PIN_SET);
        BSP_LED_Off(LED_GREEN);
    }
    else
    {
        HAL_GPIO_WritePin(En_GPIO_Port, En_Pin, GPIO_PIN_RESET);
        BSP_LED_On(LED_GREEN);
    }
}

HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) — 核心ISR

        代码如下:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance != TIM12)
    {
        return;
    }

    /* 停止状态: 关闭输出,等待按键 */
    if (motor.state == MOTOR_STATE_STOPPED)
    {
        /* 确保 EN 处于禁用状态, LED 熄灭 */
        HAL_GPIO_WritePin(En_GPIO_Port, En_Pin, GPIO_PIN_SET);
        BSP_LED_Off(LED_GREEN);
        motor.phase = MOTOR_PHASE_READY;
        motor.acc_us = 0;
        motor.pulse_ticks = 0;
        motor.settling_ticks = 0;
        motor.dir_change_pending = 0;
        motor.steps_in_dir = 0;
        return;
    }

    /* 运行状态: 使能电机, 点亮 LED */
    HAL_GPIO_WritePin(En_GPIO_Port, En_Pin, GPIO_PIN_RESET);
    BSP_LED_On(LED_GREEN);

    switch (motor.phase)
    {
        case MOTOR_PHASE_READY:
        {
            /* 累计时间,达到设定的步进周期则发出一个上升沿 */
            motor.acc_us += 1000U;
            if (motor.acc_us >= motor.period_us)
            {
                motor.acc_us -= motor.period_us;

                /* 如需换向: 此刻 STEP 必然为低,安全翻转 DIR */
                if (motor.dir_change_pending != 0U)
                {
                    motor.dir_change_pending = 0U;
                    Motor_SetDirection((motor.dir == MOTOR_DIR_CW) ? MOTOR_DIR_CCW : MOTOR_DIR_CW);
                    motor.steps_in_dir = 0;
                    motor.settling_ticks = MOTOR_DIR_SETUP_MS;
                    motor.phase = MOTOR_PHASE_SETTLING;
                    break;
                }

                HAL_GPIO_WritePin(step_GPIO_Port, step_Pin, GPIO_PIN_SET);
                motor.pulse_ticks = 0;
                motor.phase = MOTOR_PHASE_HIGH;
            }
            break;
        }

        case MOTOR_PHASE_HIGH:
        {
            /* STEP 上升沿之后保持 MOTOR_PULSE_HIGH_MS ms 高电平,
               下一次进入本分支时拉低,产生下降沿 */
            motor.pulse_ticks++;
            if (motor.pulse_ticks >= MOTOR_PULSE_HIGH_MS)
            {
                HAL_GPIO_WritePin(step_GPIO_Port, step_Pin, GPIO_PIN_RESET);
                motor.steps_in_dir++;

                if (motor.steps_in_dir >= MOTOR_STEPS_PER_DIR)
                {
                    /* 已走完单方向步数,标记下一次 READY 相位进行换向 */
                    motor.dir_change_pending = 1U;
                }

                motor.phase = MOTOR_PHASE_READY;
                motor.pulse_ticks = 0;
            }
            break;
        }

        case MOTOR_PHASE_SETTLING:
        {
            /* 方向切换后等待 DIR 建立,期间不输出 STEP 脉冲 */
            if (motor.settling_ticks > 0U)
            {
                motor.settling_ticks--;
            }
            else
            {
                motor.phase = MOTOR_PHASE_READY;
                motor.acc_us = 0;   /* 清零累加器,平稳启动新方向的第一步 */
            }
            break;
        }

        default:
            motor.phase = MOTOR_PHASE_READY;
            break;
    }
}
效果展示:

【静音步进电机控制实践】 https://www.bilibili.com/video/BV1uE7W6FEXo/?share_source=copy_web&vd_source=2176e73645ba9710d1c29e12a1f03ada

 







关键词: 步进     电机     控制     实践     成果    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]