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

共1条 1/1 1 跳转至

静音步进电机控制实践成果帖

专家
2026-05-10 21:29:19     打赏

一、概述

进过一番周折,终于完成TMC2209静音步进电机实践活动的基础任务了。主控板使用树莓派RP2040开发板,使用的是Thonny开发工具、MicroPython语言。Thonny开发工具中需要针对RP2040开发板做配置后才能进行后续开发。

菜单“工具” - “选项” - “解释器”

图片1.png

二、系统构成

1、系统框图

图片2.png

2、电路原理图

图片3.png

3、实际搭建效果

图片4.png

三、硬件组成简单说明

1、主控板RP2040

图片5.png

拥有诸如I2C、SPI、UART、ADC等丰富的接口,在本次实验中只使用简单的GPIO控制。

2、电机驱动板TMC2209

图片6.png

这是一个控制功能很全面的电机驱动模组,可以通过单线UART进行发出调控指令。不过这个控制功能需要通过焊接、改变跳线才行。对于最简单的实验,只需要使用默认的设置,外部只需控制电机转向和步进信号即可,非常实用。

主要性能参数:

·驱动电压 (VM) : 4.75V ~ 29V DC

·逻辑电压 (VIO ): 3V ~ 5.5V(兼容 3.3V/5V 单片机)

·输出电流      : 2A RMS(有效值),2.8A 峰值

·控制接口      : STEP/DIR(脉冲/方向)和 单线 UART

·微步分辨率    : 使用引脚设置时:8、16、32、64 微步; 使用UART 配置时 :最高 256 微步

·输入脉冲低于 256 微步时,内部插值至 256 微步,运行更平滑

3、步进电机

这里使用购自得捷的电机SM42HB34F08AB,与我之前测试用的电机不一样,主要是接口中绕组所在的插件引脚不一致。

图片7.png

主要性能参数:

·额定电流 (Current):0.8 A - 1.33 A

·相电阻 (Resistance):约 2.2Ω - 6.2Ω

·保持扭矩 (Holding Torque):约 0.22 - 0.24 N·m

SM42HB34F08AB步进电机的接线:

电机引线颜色步进电机绕组
蓝线A+
红线A-
绿线B+
黑线B-

因此继续使用之前的连接线,需要改变和TMC2209的连接方式。之前还担心散热不够的问题,在查了资料后才搞明白,电机的工作电流是两组绕组工作时的总电流,不是每个绕组的电流。这样的话,之前担心的工作电流太大、烧坏TMC2209的问题,应该就不是问题了。但保险起见,还是给TMC2209加上了个散热器。

4、OLED

作为操作过程信息的显示部件,选择0.96英寸的I2C接口OLED,由RP2040的I2C外设驱动。单色显示屏,分辨率为128 * 64像素。

图片8.png

引脚功能
GND电源地
VDD电源3.3V
SCLI2C通讯时钟线
SDAI2C通讯数据线

 5、电源

电源一个是主控板以及TMC2209的数字逻辑部分的电源,都是直接使用USB的5V电源;步进电机需要12V电源,根据默认设置,需要使用1.33A一下的电流,因而选择了一个输出能力2A的电源应该可以应对的。

四、功能的实现

1、测试用程序代码

from machine import Pin, Timer
import time
import utime
from machine import I2C, Pin
from ssd1306 import SSD1306_I2C
from scaled_text import ScaledText
import sys
import select
 
SCREEN_WIDTH = 128
SCREEN_HEIGHT = 64
 
# 检查是否有来自USB口数据可读(非阻塞)
def usb_data_available():
    return select.select([sys.stdin], [], [], 0)[0]
 
# I2C设置,驱动OLED
i2c = I2C(0, scl=Pin(1), sda=Pin(0))
utime.sleep_ms(50)  # delay to setup screen
oled = SSD1306_I2C(SCREEN_WIDTH, SCREEN_HEIGHT, i2c)
 
# 创建缩放文本对象
big_text = ScaledText(oled)
 
# 配置GPIO引脚(这里以板上LED引脚为例,实际可改为任意引脚如 Pin(0))
# 注意:RP2040的板载LED通常为Pin 25或"LED"
led = Pin(25, Pin.OUT)  # 如需外接,改为 Pin(0) 等
motor_dir = machine.Pin(20, machine.Pin.OUT)
motor_step = machine.Pin(19, machine.Pin.OUT)
motor_en = machine.Pin(16, machine.Pin.OUT)
 
btn_dir = machine.Pin(9, machine.Pin.IN, machine.Pin.PULL_UP)
btn_speed = machine.Pin(13, machine.Pin.IN, machine.Pin.PULL_UP)
btn_run = machine.Pin(15, machine.Pin.IN, machine.Pin.PULL_UP)
 
'''
==============================
MS2   MS1   SPREAD  细分模式
------------------------------
GND   GND   GND     1/8 细分
GND   VIO   GND     1/32 细分
VIO   GND   GND     1/64 细分
VIO   VIO   GND     1/16 细分
==============================
'''
motor_ms1 = machine.Pin(17, machine.Pin.OUT)
motor_ms2 = machine.Pin(18, machine.Pin.OUT)
 
# 设置微步初始设置
motor_ms1.value(0)
motor_ms2.value(0)
# 调整微步(速度)用计数变量
step_config_count = 0
 
motor_en.value(1)
status_en = 1
 
motor_dir.value(0)
status_dir = 0
 
# 步进脉冲数
steps = 0
# 最大步进步数
MAX_STEP = 16000
# 消除都懂用定时器
debounce_timer = None
 
# 按键中断处理
def button_action(pin):
    global step_config_count
    global status_dir
    global status_en
    
    """根据触发中断的引脚执行不同操作"""
    if pin == btn_dir:
        # 方向控制
        # print("按钮1按下")
        if status_dir == 1:
            status_dir = 0
            print("按钮1按下,改为逆时针方向")
        else:
            status_dir = 1
            print("按钮1按下,改为顺时针方向")
        motor_dir.value(status_dir)
        led.value(status_dir)
    elif pin == btn_speed:
        print("按钮2按下,改变微步设置")
        step_config_count = (step_config_count + 1) % 4
        print("  step_config_count=", step_config_count)
        # 根据微步计数值,调整微步的引脚设置
        if step_config_count == 0:
            motor_ms1.value(0)
            motor_ms2.value(0)
            print("    1/8")
        elif step_config_count == 2:
            motor_ms2.value(0)
            motor_ms1.value(1)
            print("    1/32")
        elif step_config_count == 3:
            motor_ms1.value(0)
            motor_ms2.value(1)
            print("    1/64")
        elif step_config_count == 1:
            motor_ms1.value(1)
            motor_ms2.value(1)
            print("    1/16")
    elif pin == btn_run:
        # 启停控制
        print("按钮3按下,启停控制")
        if status_en == 1:
            print("    启动 -> 0")
            status_en = 0
        else:
            print("    停止 -> 1")
            status_en = 1
        motor_en.value(status_en)
 
# 按键中断消除抖动处理
def debounce_handler(pin):
    """消抖处理函数"""
    global debounce_timer
    
    # 停止之前的定时器
    if debounce_timer:
        debounce_timer.deinit()
    
    # 启动一个新的定时器,延迟50ms后执行实际功能
    debounce_timer = Timer()
    debounce_timer.init(mode=Timer.ONE_SHOT, period=50, callback=lambda t: button_action(pin))
 
# 设置中断
btn_dir.irq(trigger=Pin.IRQ_FALLING, handler=debounce_handler)
btn_speed.irq(trigger=Pin.IRQ_FALLING, handler=debounce_handler)
btn_run.irq(trigger=Pin.IRQ_FALLING, handler=debounce_handler)
 
# 定时器产生步进脉冲信号
def handller(timer):
    global steps
    """定时器回调函数:每隔1ms自动翻转引脚电平"""
#     led.toggle()  # 每1ms翻转一次 → 产生500Hz方波,高电平持续0.5ms
    steps = (steps + 1) % (MAX_STEP * 2)
    # 步进脉冲
    motor_step.toggle()
 
# 创建定时器,周期设置为1毫秒,模式为周期性执行
tim = Timer()
tim.init(period=1, mode=Timer.PERIODIC, callback=handller)
# 初始化串口接收
line = ''
                
try:
    steps = 0
    while True:
        if usb_data_available():
            line = sys.stdin.readline()
            if len(line) > 2:
                line = line[:2]
                if line == "D0":
                    motor_dir.value(0)    
                    led.value(0)
                    status_dir = 0
                elif line == "D1":
                    motor_dir.value(1)    
                    led.value(1)
                    status_dir = 1
                elif line == "S0":    
                    motor_ms1.value(0)
                    motor_ms2.value(0)
                    step_config_count = 0
                elif line == "S1":    
                    motor_ms1.value(1)
                    motor_ms2.value(1)
                    step_config_count = 1                    
                elif line == "S2":    
                    motor_ms1.value(1)
                    motor_ms2.value(0)
                    step_config_count = 2
                elif line == "S3":    
                    motor_ms1.value(0)
                    motor_ms2.value(1)
                    step_config_count = 3                   
                elif line == "E0":    
                    motor_en.value(0)
                    status_en = 0
                elif line == "E1":    
                    motor_en.value(1)
                    status_en = 1  
        # 刷新状态的显示            
        oled.fill(0)
        if status_dir == 0:
            big_text.text('CW  ', 0, 0, color=1, scale=2)
        else:
            big_text.text('CCW ', 0, 0, color=1, scale=2)
            
        # 根据微步计数值,调整微步的引脚设置
        if step_config_count == 0:
            big_text.text('S=1/8 ', 0, 20, color=1, scale=2)
        elif step_config_count == 2:
            big_text.text('S=1/32', 0, 20, color=1, scale=2)
        elif step_config_count == 3:
            big_text.text('S=1/64', 0, 20, color=1, scale=2)
        elif step_config_count == 1:
            big_text.text('S=1/16', 0, 20, color=1, scale=2)
 
        # 启停控制
        if status_en == 0:
            big_text.text('Run  ', 0, 40, color=1, scale=2)
        else:
            big_text.text('Stop ', 0, 40, color=1, scale=2)
        oled.show()
 
except KeyboardInterrupt:
    # 退出时清理定时器,停止脉冲输出
    tim.deinit()

2、处理说明

1)电机的运转,通过TMC2209的EN端操作。在电脑端发送“E0”,运转;发送“E1”,则停转。

2)TMC2209的STEP信号,由定时器产生,为周期2mS、占空比50%的脉冲。

3)TMC2209的MS1和MS2信号用来控制电机转速。在电脑端发送“S0”~“S3”,实现四个速度的变换,对应的是TMC2209微步1/8,1/16,1/32,1/64,又快到慢。

4)TMC2209的DIR信号控制电机转向。在电脑端发送“D0”为顺时针;发送“D1”为逆时针。

这几个操作本来是准备用按钮控制,但发现按钮的响应上容易因为抖动出问题,所以最后选择使用电脑端发送指令给主控板操作。

3、演示效果

4、注意点

绝对不要在驱动器通电的状态下连接或断开电机线,否则会瞬间烧毁驱动器。千万千万要注意。最好电机电源单独加上开关,装置搭建无误后,在接通电源。如果绕组线接错了,没有按照预想的运转,一定要先断掉VM电源后再改变电机接线。

五、心得体会

在参加本次活动之前,使用过电机及几种电机驱动。使用中产生的噪音,我一直以为是电机固有的缺点。觉得因为是机械转动,产生噪音是难免的。从来没想过驱动方面做得好,是可以减弱噪音的。只能说,任何一个点,只要能投入精力,完善理论知识和实践,都有可能产生突破。大概这就是所谓的匠人。

 

 

 

 

 

 

 



共1条 1/1 1 跳转至

回复

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