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

二、系统构成
1、系统框图

2、电路原理图

3、实际搭建效果

三、硬件组成简单说明
1、主控板RP2040

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

这是一个控制功能很全面的电机驱动模组,可以通过单线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,与我之前测试用的电机不一样,主要是接口中绕组所在的插件引脚不一致。

主要性能参数:
·额定电流 (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像素。

| 引脚 | 功能 |
| GND | 电源地 |
| VDD | 电源3.3V |
| SCL | I2C通讯时钟线 |
| SDA | I2C通讯数据线 |
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电源后再改变电机接线。
五、心得体会
在参加本次活动之前,使用过电机及几种电机驱动。使用中产生的噪音,我一直以为是电机固有的缺点。觉得因为是机械转动,产生噪音是难免的。从来没想过驱动方面做得好,是可以减弱噪音的。只能说,任何一个点,只要能投入精力,完善理论知识和实践,都有可能产生突破。大概这就是所谓的匠人。
我要赚赏金
