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

共3条 1/1 1 跳转至

【let'sdo|2026年第1期】静音步进电机控制实践过程贴——ATOMS3R+TMC2209步进电机驱动测试

助工
2026-05-24 15:19:53     打赏

近期设计了一块TMC2209的扩展板,搭配ATOMS3R-CAM进行控制,进行TMC2209驱动步进电机的基础功能测试。本次的主要目标是测试通过调节STEP脉冲频率来控制电机速度。

1、TMC2209扩展板介绍

这块TMC2209扩展板的设计很简单,将TMC2209驱动模块与ATOMS3R-CAM主控连接,并补全独立供电与控制接口。

板上集成了DC电源输入插座与电源开关,预留了SPRD(展频)、MS1、MS2微步配置跳线,以及用于后续高级调试的UART串口接口。整体布局紧凑,在保证功能完整的前提下尽可能缩小了体积,方便与ATOMS3R-CAM堆叠使用。

原理图:

1779606888992028.jpg

PCB图:

1779606898535569.jpg

焊接后实物图:

1779606938448097.jpg

2、ATOMS3R-CAM固件

使用M5Stack官方提供的M5Burner,为ATOMS3R-CAM烧录UIFlow2固件。

1779607032768354.jpg

该固件同时支持图形化编程与MicroPython。本次测试采用MicroPython编写驱动代码,通过调节STEP引脚方波的频率来控制电机转速。

3、电机驱动测试

在上一篇内容中,我已实现对步进电机的基本控制,本次测试的重点是验证不同速度档位的运行控制。步进电机的线速度完全由脉冲频率决定,换算公式为:

频率(Hz)=目标速度(mm/s) / 每脉冲移动距离(mm/pulse)

以本次测试使用的电机为例,在8细分模式下,单脉冲对应位移约为0.00065mm。当设定max_speed=10时,对应的脉冲频率约为15.4kHz;设定max_speed=20时,频率升至约30.8kHz。

测试程序实现了基础的往复运动逻辑:先以较高速度单向移动到限位位置,再以较低速度匀速返回原点,以此验证驱动器在不同频率下的响应稳定性与加减速平滑度。

测试代码如下:

import os, sys, io
import M5
from M5 import *
from machine import Pin
import time


# ========== 硬件与运动参数配置 ==========
PIN_DIR  = 7    # 方向引脚
PIN_STEP = 6    # 脉冲引脚
PIN_EN   = 5    # 使能引脚(低有效)

MM_PER_PULSE = 0.0052 / 8   # 1/8微步下的单脉冲位移(mm)
START_SPEED  = 1.0          # 起步安全速度(mm/s)

# ========== 全局状态变量 ==========
motor_dir  = None
motor_step = None
motor_en   = None

# 运动状态机
move_state = "IDLE"         # IDLE / MOVING
total_pulses = 0
accel_pulses = 0
const_pulses = 0
pulse_index = 0
speed_step = 0
current_half_period_us = 0
last_pulse_time_us = 0

# 任务队列(按需添加多个移动指令)
move_queue = []


def _calc_move_params(distance_mm, max_speed, accel):
    """预计算梯形加减速的所有参数"""
    global total_pulses, accel_pulses, const_pulses, speed_step, move_state
    global pulse_index, current_half_period_us, last_pulse_time_us

    if distance_mm == 0:
        return False

    # 设置方向
    motor_dir.value(0 if distance_mm > 0 else 1)
    distance_mm = abs(distance_mm)

    total_pulses = int(distance_mm / MM_PER_PULSE)
    if total_pulses == 0:
        return False

    # 计算加速段脉冲数 s = v²/(2a)
    accel_dist = (max_speed ** 2) / (2 * accel)
    accel_pulses = int(accel_dist / MM_PER_PULSE)

    # 距离不足时退化为三角形速度曲线
    if accel_pulses > total_pulses // 2:
        accel_pulses = total_pulses // 2

    const_pulses = total_pulses - 2 * accel_pulses
    speed_step = (max_speed - START_SPEED) / accel_pulses if accel_pulses > 0 else 0

    # 初始化运行状态
    pulse_index = 0
    current_half_period_us = int(1_000_000 / (START_SPEED / MM_PER_PULSE)) // 2
    last_pulse_time_us = time.ticks_us()
    move_state = "MOVING"

    print(f"[TMC2209] 目标:{distance_mm}mm 总脉冲:{total_pulses} "
          f"加速段:{accel_pulses} 匀速段:{const_pulses}")
    return True


def _send_one_pulse():
    """非阻塞发送单个步进脉冲,返回True表示该次移动完成"""
    global pulse_index, current_half_period_us, last_pulse_time_us, move_state

    now = time.ticks_us()
    elapsed = time.ticks_diff(now, last_pulse_time_us)

    # 未到脉冲间隔时间,直接返回(让出CPU)
    if elapsed < current_half_period_us * 2:
        return False

    # 发出一个完整脉冲
    motor_step.value(1)
    time.sleep_us(max(current_half_period_us, 30))
    motor_step.value(0)
    last_pulse_time_us = time.ticks_us()

    # ---- 根据当前位置更新下一脉冲的速度 ----
    pulse_index += 1
    if pulse_index >= total_pulses:
        move_state = "IDLE"
        return True  # 本次移动完成

    # 计算当前应处速度
    if pulse_index < accel_pulses:
        spd = min(START_SPEED + pulse_index * speed_step, 
                  START_SPEED + accel_pulses * speed_step)
    elif pulse_index >= total_pulses - accel_pulses:
        decel_i = total_pulses - pulse_index - 1
        spd = max(START_SPEED + decel_i * speed_step, START_SPEED)
    else:
        spd = START_SPEED + accel_pulses * speed_step  # 即 max_speed

    freq = spd / MM_PER_PULSE
    current_half_period_us = max(int(1_000_000 / freq) // 2, 30)
    return False


def setup():
    global motor_dir, motor_step, motor_en

    M5.begin()

    motor_dir  = Pin(PIN_DIR, Pin.OUT)
    motor_step = Pin(PIN_STEP, Pin.OUT)
    motor_en   = Pin(PIN_EN, Pin.OUT)

    motor_en.value(0)       # 使能驱动器
    motor_step.value(0)     # 脉冲默认低电平

    # ====== 在此添加你的运动任务 ======
    move_queue.append((30,  20.0, 80.0))   # 快速向外 30mm
    move_queue.append((-30, 6.0,  20.0))   # 慢速返回 30mm
    # ==================================

    print("[TMC2209] 初始化完成,等待执行...")


def loop():
    global move_queue
    M5.update()

    if move_state == "MOVING":
        # 持续发送脉冲直到当前移动完成
        if _send_one_pulse():
            print("[TMC2209] 当前段移动完成")
            time.sleep_ms(200)  # 段间停顿(对应原代码的 utime.sleep_ms(200))

    elif move_queue:
        # 当前空闲且队列中有任务,取出下一个执行
        dist, spd, acc = move_queue.pop(0)
        _calc_move_params(dist, spd, acc)

    else:
        # 所有任务执行完毕,安全禁用电机
        motor_en.value(1)
        # 防止loop空转消耗CPU
        time.sleep_ms(10)


if __name__ == '__main__':
    try:
        setup()
        while True:
            loop()
    except (Exception, KeyboardInterrupt) as e:
        try:
            motor_en.value(1)
            print("[TMC2209] 异常中断,电机已安全禁用")
        except Exception:
            pass
        try:
            from utility import print_error_msg
            print_error_msg(e)
        except ImportError:
            print("please update to latest firmware")

1779607069277053.jpg







关键词: ATOMS3R+TMC2209    

院士
2026-05-24 20:38:52     打赏
2楼

动手能力一级的棒


助工
2026-05-24 21:10:27     打赏
3楼

哇!收到货了,这么速度


共3条 1/1 1 跳转至

回复

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