,我使用USB转TTL模块,通过串口UART通信方式实现了TMC2209步进电机驱动板的基础控制,包括电机启停、正反转等功能。
本次使用RP2040开发板,基于MicroPython,实现电流调节、寄存器配置以及电机运动控制的进阶玩法。
1、硬件说明
硬件清单:RP2040 开发板、RP2040 扩展板、面包板、杜邦线、TMC2209 驱动模块、步进电机。
按照下面示意图完成接线:

其他引脚说明:
VM 和相邻的GND:接12V直流电源。
VIO 和相邻的GND:接RP2040的 3.3V 和 GND,GND共地。
EN :接RP2040的GPIO2。
连接实物图:

2、电机控制
① CRC校验函数
TMC2209的UART通信协议规定,每条命令的最后一个字节必须是8位CRC校验码。数据手册第20页给出了CRC多项式及计算示例。我们根据该算法编写了crc8函数,用于计算任意字节序列的CRC值。
# ========== CRC计算函数 ==========
def crc8(datagram):
"""计算 TMC2209 UART 通信的 CRC8 值"""
crc = 0
for byte in datagram:
for _ in range(8):
if (crc >> 7) ^ (byte & 0x01):
crc = ((crc << 1) ^ 0x07) & 0xFF
else:
crc = (crc << 1) & 0xFF
byte >>= 1
return crc
② 串口命令发送
根据串口通信要求,实现了三个核心函数:
send_tmc2209_command:发送带CRC校验的完整命令。该函数先调用crc8计算校验值,再将原数据和CRC拼接后通过UART发送。
write_tmc2209_register:向指定寄存器写入32位数据。写操作需要将寄存器地址的bit7置1(即地址加上0x80),然后将32位数据按大端顺序(高位在前)填入命令包。
read_tmc2209_register:读取指定寄存器的值。发送读请求后,TMC2209会返回以0x05 0xFF开头的8字节数据包,从中提取32位数据并返回。
③ 关键寄存器配置
根据TMC2209数据手册第5章,本次实验配置了以下寄存器:
IHOLD_IRUN(0x10):设置电机运行电流和保持电流。我们将IRUN和IHOLD均设为3,对应约9.4%的最大电流,防止电机过流发热。
CHOPCONF(0x6C):配置斩波器参数。其中TOFF位必须大于0才能让电机转动,我们设为3;其他参数保持默认值0x00010053。
VACTUAL(0x22):内部脉冲发生器速度控制寄存器。写入正数可使电机正向转动,写入负数(补码形式)则反向转动,写入0则停止。
④ 完整程序流程
from machine import UART, Pin
import time
# ========== CRC计算函数 ==========
def crc8(datagram):
"""计算 TMC2209 UART 通信的 CRC8 值"""
crc = 0
for byte in datagram:
for _ in range(8):
if (crc >> 7) ^ (byte & 0x01):
crc = ((crc << 1) ^ 0x07) & 0xFF
else:
crc = (crc << 1) & 0xFF
byte >>= 1
return crc
# ========== 发送TMC2209命令的函数 ==========
def send_tmc2209_command(uart, data_bytes):
"""发送带CRC的TMC2209命令"""
crc = crc8(data_bytes)
full_cmd = data_bytes + bytes([crc])
uart.write(full_cmd)
return full_cmd
def write_tmc2209_register(uart, reg_addr, value_32bit):
"""
写TMC2209寄存器
reg_addr: 寄存器地址 (如 0x22)
value_32bit: 32位整数值
"""
write_addr = reg_addr | 0x80
byte3 = (value_32bit >> 24) & 0xFF
byte2 = (value_32bit >> 16) & 0xFF
byte1 = (value_32bit >> 8) & 0xFF
byte0 = value_32bit & 0xFF
cmd_bytes = bytes([0x05, 0x00, write_addr, byte3, byte2, byte1, byte0])
full_cmd = send_tmc2209_command(uart, cmd_bytes)
return full_cmd
def read_tmc2209_register(uart, reg_addr):
"""
读TMC2209寄存器
reg_addr: 寄存器地址 (如 0x06)
返回: 32位整数值,如果失败返回None
"""
cmd_bytes = bytes([0x05, 0x00, reg_addr])
full_cmd = send_tmc2209_command(uart, cmd_bytes)
print(f"读取命令已发送: {full_cmd.hex()}")
time.sleep_ms(50)
if uart.any():
response = uart.read()
if response and len(response) >= 8:
for i in range(len(response) - 7):
if response[i] == 0x05 and response[i+1] == 0xFF:
value = (response[i+3] << 24) | (response[i+4] << 16) | \
(response[i+5] << 8) | response[i+6]
print(f"读取响应: {response.hex()}")
print(f"寄存器 0x{reg_addr:02X} = 0x{value:08X}")
return value
print(f"意外的响应: {response.hex() if response else '无'}")
else:
print("无响应")
return None
# ========== 初始化引脚 ==========
en_pin = Pin(2, Pin.OUT)
en_pin.value(1) # 拉高,禁用驱动器
print("EN引脚已设为高电平 - 驱动器已禁用")
# ========== 初始化UART ==========
uart = UART(0, baudrate=115200, tx=Pin(0), rx=Pin(1))
print("UART 初始化完成")
time.sleep_ms(100)
# 清空缓冲区
while uart.any():
uart.read()
# ========== 配置TMC2209寄存器 ==========
print("\n--- 正在配置 TMC2209 ---")
# 1. 设置运行电流和保持电流 (IHOLD_IRUN, 地址 0x10)
# IRUN=3, IHOLD=3, IHOLDDELAY=4
ihold_irun_value = (3 << 8) | 3 | (4 << 16)
write_tmc2209_register(uart, 0x10, ihold_irun_value)
time.sleep_ms(50)
# 2. 设置CHOPCONF (地址 0x6C) - TOFF=3 使能驱动器
chopconf_value = 0x00010053
write_tmc2209_register(uart, 0x6C, chopconf_value)
time.sleep_ms(50)
# 3. 设置VACTUAL为0 (停止)
write_tmc2209_register(uart, 0x22, 0x00000000)
time.sleep_ms(50)
print("配置完成")
# ========== 验证配置 ==========
print("\n--- 正在验证配置 ---")
read_tmc2209_register(uart, 0x10)
ioin_value = read_tmc2209_register(uart, 0x06)
if ioin_value:
version = (ioin_value >> 24) & 0xFF
print(f"TMC2209 版本: 0x{version:02X}")
# ========== 准备启动 ==========
print("\n--- 准备控制电机 ---")
print("正在使能驱动器...")
en_pin.value(0)
time.sleep_ms(100)
print("驱动器已使能")
# ========== 控制命令(已验证的CRC) ==========
cmd_forward = bytes([0x05, 0x00, 0xA2, 0x00, 0xFF, 0xFC, 0x18, 0x57]) # 正向转
cmd_stop = bytes([0x05, 0x00, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x0E]) # 停止
cmd_reverse = bytes([0x05, 0x00, 0xA2, 0x00, 0x00, 0x03, 0xE8, 0x86]) # 反向转
def motor_forward():
"""电机正转"""
print("\n>>> 电机正转")
uart.write(cmd_forward)
def motor_stop():
"""电机停止"""
print("\n>>> 电机停止")
uart.write(cmd_stop)
def motor_reverse():
"""电机反转"""
print("\n>>> 电机反转")
uart.write(cmd_reverse)
# ========== 演示程序 ==========
print("\n--- 2秒后开始演示程序 ---")
time.sleep(2)
try:
# 正转3秒
motor_forward()
time.sleep(3)
# 停止2秒
motor_stop()
time.sleep(2)
# 反转3秒
motor_reverse()
time.sleep(3)
# 停止
motor_stop()
print("\n--- 演示程序完成 ---")
except KeyboardInterrupt:
print("\n\n用户中断程序")
motor_stop()
finally:
# ========== 停止电机并禁用驱动器 ==========
print("\n--- 正在停止电机并禁用驱动器 ---")
motor_stop() # 发送停止命令
time.sleep_ms(100) # 等待电机完全停止
en_pin.value(1) # 拉高EN引脚,禁用驱动器
print("EN引脚已设为高电平 - 驱动器已禁用")
print("程序结束")程序按照以下步骤执行:
初始化GPIO:将EN引脚设为输出,初始拉高,确保上电时驱动器处于禁用状态。
初始化UART:配置波特率115200、TX引脚和RX引脚,等待100毫秒稳定,并清空接收缓冲区。
配置寄存器:依次写入IHOLD_IRUN、CHOPCONF和VACTUAL(初始为0),每步间隔50毫秒。
使能驱动器:将EN引脚拉低,使TMC2209开始工作。
电机控制测试:按顺序执行正转、停止、反转操作,每个动作持续数秒。
结束清理:发送停止命令,将EN引脚拉高禁用驱动器。
测试效果如下视频:
我要赚赏金
