基于TMC4361A+TMC2160实现的步进电机控制系统
一、项目描述
1.1 项目介绍
本项目基于ESP32-S3微控制器,搭配Trinamic公司的TMC4361A运动控制器和TMC2160步进电机驱动芯片,实现了高精度、高性能的步进电机控制系统。该系统能够实现精确的位置控制、速度控制和加速度控制,适用于3D打印机、数控机床、自动化设备等需要精密运动控制的应用场景。
1.2 设计思路
运动控制策略
- 时钟系统:使用ESP32-S3的LEDC外设生成8MHz时钟信号供TMC4361A使用
- SPI总线隔离:TMC4361A使用SPI2_HOST,TMC2160使用SPI3_HOST,避免总线冲突
- 梯形速度曲线:采用梯形加减速曲线,确保运动平滑无失步
- 细分技术:256微步细分配置,200步/圈的电机达到51200步/圈精度
控制参数设计
- 速度参数:VMAX=51200 (1圈/秒),VSTART=1,VSTOP=10
- 加速度参数:AMAX=100000,DMAX=100000,ASTART=100,DSTOP=10
- 电流设置:运行电流15/32,保持电流8/32,平衡扭矩与发热
二、硬件介绍
2.1 核心硬件组成
硬件模块 | 型号 | 主要功能 | 关键参数 |
|---|---|---|---|
主控芯片 | ESP32-S3 | 系统控制、SPI主机 | 240MHz双核、2个独立SPI |
运动控制器 | TMC4361A | 运动规划、斜坡生成 | 32位位置计数器、梯形/S曲线 |
电机驱动 | TMC2160 | 电流驱动、细分控制 | 256微步、StealthChop2 |
步进电机 | 200步/圈 | 执行机构 | 1.8°步距角 |
2.2 硬件连接
TMC4361A连接 (运动控制器)
ESP32-S3 TMC4361A
GPIO11 (MOSI) --> SPI_SDI
GPIO13 (MISO) <-- SPI_SDO
GPIO12 (SCK) --> SPI_SCK
GPIO10 (CS) --> SPI_CSN
GPIO9 (CLK) --> CLK_EXT (8MHz时钟输入)
GPIO6 (RST) --> NRST (复位)
- SPI总线:SPI2_HOST,4MHz时钟,Mode 3 (CPOL=1, CPHA=1)
- 外部时钟:ESP32 LEDC生成8MHz方波
TMC2160连接 (电机驱动器)
ESP32-S3 TMC2160
GPIO17 (MOSI) --> SDI
GPIO18 (MISO) <-- SDO
GPIO16 (SCK) --> SCK
GPIO15 (CS) --> CSN
- SPI总线:SPI3_HOST (独立总线),4MHz时钟,Mode 3
- 电机接口:通过步进脉冲输入(来自TMC4361A的STEP/DIR信号)
三、软件代码说明
关键功能代码说明
功能1:TMC4361A初始化与时钟配置
// 1. 生成8MHz外部时钟
static void clk_ext_init(void)
{
// 使用LEDC产生8MHz时钟信号
ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_num = LEDC_TIMER_0,
.duty_resolution = LEDC_TIMER_1_BIT, // 1位分辨率,50%占空比
.freq_hz = TMC4361A_CLK_FREQ, // 8 MHz
.clk_cfg = LEDC_USE_APB_CLK // 使用APB时钟 (80MHz)
};
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
ledc_channel_config_t ledc_channel = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.channel = LEDC_CHANNEL_0,
.timer_sel = LEDC_TIMER_0,
.intr_type = LEDC_INTR_DISABLE,
.gpio_num = TMC4361A_PIN_CLK_EXT,
.duty = 1, // 50%占空比
.hpoint = 0
};
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
}
功能说明:
- TMC4361A需要外部时钟才能工作
- 使用ESP32的LEDC(LED PWM控制器)生成精确的8MHz方波
- 1位分辨率确保50%占空比(duty=1时为50%)
- 时钟必须在芯片复位前启动
功能2:SPI通信实现
// 全双工SPI读写,软件控制CS
static void spi_read_write(uint8_t *data, size_t length)
{
spi_transaction_t t;
uint8_t rx_data[8] = {0};
memset(&t, 0, sizeof(t));
t.length = length * 8;
t.tx_buffer = data;
t.rxlength = length * 8;
t.rx_buffer = rx_data;
// 软件控制CS - 拉低
gpio_set_level(TMC4361A_PIN_CS, 0);
ret = spi_device_polling_transmit(spi_handle, &t);
// 软件控制CS - 拉高
gpio_set_level(TMC4361A_PIN_CS, 1);
// 将接收的数据复制回data数组
memcpy(data, rx_data, length);
}
功能说明:
- TMC4361A使用SPI Mode 3 (CPOL=1, CPHA=1)
- 全双工通信:发送的同时接收数据
- 软件控制CS信号,确保时序精确
- 读寄存器需要两次传输:第一次发请求,第二次读数据
功能3:运动控制 - 位置模式
void tmc4361a_move_to(int32_t position)
{
ESP_LOGI(TAG, "移动到位置: %ld", (long)position);
// 设置位置模式 + 梯形斜坡
tmc4361a_write_register(TMC4361A_RAMPMODE,
TMC4361A_RAMP_POSITION | TMC4361A_RAMP_TRAPEZ);
// 设置目标位置
tmc4361a_write_register(TMC4361A_XTARGET, position);
}
功能说明:
- 位置模式适用于点对点运动
- RAMPMODE设置为位置模式+梯形曲线
- 写入XTARGET后自动生成加减速曲线
- 系统自动使用VMAX、AMAX、DMAX参数
功能4:运动控制 - 速度模式
void tmc4361a_rotate(int32_t velocity)
{
ESP_LOGI(TAG, "设置旋转速度: %ld", (long)velocity);
// 设置速度模式 + 梯形斜坡
tmc4361a_write_register(TMC4361A_RAMPMODE, TMC4361A_RAMP_TRAPEZ);
// 设置目标速度(需要左移8位)
tmc4361a_write_register(TMC4361A_VMAX, abs(velocity) << 8);
// 设置方向和启动
if (velocity >= 0) {
tmc4361a_write_register(TMC4361A_XTARGET, 0x7FFFFFFF); // 正向
} else {
tmc4361a_write_register(TMC4361A_XTARGET, 0x80000000); // 反向
}
}
功能说明:
- 速度模式适用于连续旋转
- 速度值需要左移8位(TMC4361A内部格式)
- 通过设置XTARGET为极值确保持续运动
- 自动按AMAX参数加速到目标速度
功能5:平滑停止
代码位置:tmc4361a.c
void tmc4361a_smooth_stop(void)
{
ESP_LOGI(TAG, "平滑停止 (速度模式)");
// 保持速度模式 + 梯形斜坡
tmc4361a_write_register(TMC4361A_RAMPMODE, TMC4361A_RAMP_TRAPEZ);
// 将目标速度设置为0,使电机平滑减速停止
tmc4361a_write_register(TMC4361A_VMAX, 0);
}
功能说明:
- 不使用急停,而是平滑减速
- 在速度模式下将VMAX设为0
- 系统按DMAX参数自动减速
- 避免失步和机械冲击
功能6:TMC2160驱动配置
代码位置:tmc2160.c (大致位置)
esp_err_t tmc2160_init(void)
{
// 1. 配置CHOPCONF - 斩波器配置
int32_t chopconf = 0x00010185; // StealthChop模式
tmc2160_write_register(TMC2160_CHOPCONF, chopconf);
// 2. 配置IHOLD_IRUN - 电流设置
int32_t ihold_irun = (8 << 0) | // IHOLD=8 (保持电流)
(15 << 8) | // IRUN=15 (运行电流)
(5 << 16); // IHOLDDELAY=5
tmc2160_write_register(TMC2160_IHOLD_IRUN, ihold_irun);
// 3. 配置PWMCONF - StealthChop PWM配置
int32_t pwmconf = 0xC40C001E; // 启用StealthChop
tmc2160_write_register(TMC2160_PWMCONF, pwmconf);
// 4. 配置GCONF - 全局配置
int32_t gconf = TMC2160_GCONF_EN_PWM_MODE; // 启用PWM模式
tmc2160_write_register(TMC2160_GCONF, gconf);
}
功能说明:
- CHOPCONF配置斩波器参数和微步细分
- IHOLD_IRUN设置运行和保持电流
- PWMCONF启用StealthChop静音技术
- GCONF全局使能PWM模式
功能7:应用层测试程序
代码位置:hello_world_main.c
// 测试1:定速旋转
case 0:
ESP_LOGI(TAG, "测试1: 电机定速旋转 (1秒一圈)");
float seconds_per_rev = 1.0;
int32_t rotation_speed = (int32_t)(STEPS_PER_REV / seconds_per_rev);
tmc4361a_set_velocity_params(STEPS_PER_REV, 1, 10);
tmc4361a_rotate(rotation_speed);
// 运行10秒观察
for (int i = 0; i < 10; i++) {
vTaskDelay(pdMS_TO_TICKS(1000));
int32_t pos = tmc4361a_get_position();
int32_t vel = tmc4361a_get_velocity();
ESP_LOGI(TAG, "位置: %ld, 速度: %ld Hz",
(long)pos, (long)(vel >> 8));
}
tmc4361a_smooth_stop();
break;
// 测试2:指定角度旋转
case 1:
ESP_LOGI(TAG, "测试2: 指定角度旋转 (90度)");
int32_t angle_90 = STEPS_PER_REV / 4; // 90度
int32_t start_pos = tmc4361a_get_position();
int32_t target_pos = start_pos + angle_90;
tmc4361a_move_to(target_pos);
// 等待到达
while (!tmc4361a_is_target_reached()) {
vTaskDelay(pdMS_TO_TICKS(100));
}
break;
功能说明:
- 4个测试用例展示不同控制模式
- 测试1:速度模式,观察匀速旋转
- 测试2:位置模式,精确角度控制
- 测试3/4:多圈旋转和反转
- 实时监控位置和速度
3.3 数据流图
用户程序
│
├─> tmc4361a_rotate(51200) [设置速度]
│ │
│ └─> RAMPMODE = 速度模式
│ └─> VMAX = 51200 << 8 [速度值左移8位]
│ └─> XTARGET = 0x7FFFFFFF [正向最大值]
│
├─> tmc4361a_move_to(12800) [移动到位置]
│ │
│ └─> RAMPMODE = 位置模式 | 梯形
│ └─> XTARGET = 12800 [目标位置]
│
└─> tmc4361a_get_position() [读取位置]
│
└─> 读XACTUAL寄存器
└─> 返回32位位置值
四、功能展示及说明
4.1 系统初始化输出
====================================
ESP32-S3 TMC4361A + TMC2160 步进电机控制测试
====================================
--- 初始化TMC4361A运动控制器 ---
[TMC4361A] 初始化CLK_EXT (8MHz)...
[TMC4361A] CLK_EXT初始化完成, GPIO9输出8MHz时钟
[TMC4361A] 执行TMC4361A硬件复位...
[TMC4361A] SPI总线初始化成功
[TMC4361A] SPI设备添加成功
[TMC4361A] TMC4361A状态: 0x00000001
[TMC4361A] 配置CLK_FREQ = 8000000 Hz
[TMC4361A] 配置STEP_CONF: FS_PER_REV=200, MSTEP_PER_FS=0 (256细分)
[TMC4361A] TMC4361A初始化完成
--- 初始化TMC2160步进驱动器 ---
[TMC2160] SPI总线初始化成功
[TMC2160] SPI设备添加成功
[TMC2160] 读取版本: 0x21
[TMC2160] TMC2160初始化完成
4.2 测试1 - 定速旋转演示
--- 测试1: 电机定速旋转 (1秒一圈) ---
[MAIN] 电机参数: 51200 步/圈
[MAIN] 目标速度: 1.0 秒/圈 = 51200 Hz (60.00 RPM)
[TMC4361A] 设置旋转速度: 51200
[MAIN] 位置: 52134, 速度: 51200 Hz <- 1秒后
[MAIN] 位置: 103268, 速度: 51200 Hz <- 2秒后
[MAIN] 位置: 154402, 速度: 51200 Hz <- 3秒后
[MAIN] 位置: 205536, 速度: 51200 Hz <- 4秒后
[MAIN] 位置: 256670, 速度: 51200 Hz <- 5秒后
[MAIN] 位置: 307804, 速度: 51200 Hz <- 6秒后
[MAIN] 位置: 358938, 速度: 51200 Hz <- 7秒后
[MAIN] 位置: 410072, 速度: 51200 Hz <- 8秒后
[MAIN] 位置: 461206, 速度: 51200 Hz <- 9秒后
[MAIN] 位置: 512340, 速度: 51200 Hz <- 10秒后
[TMC4361A] 平滑停止 (速度模式)
4.3 测试2 - 精确角度控制
--- 测试2: 指定角度旋转 (90度) ---
[MAIN] 起始位置: 512340
[MAIN] 目标角度: 90度 = 12800 步
[MAIN] 目标位置: 525140
[TMC4361A] 移动到位置: 525140
[MAIN] ✓ 到达位置: 525140 (误差: 0 步)
4.4 测试3 - 多圈旋转监控
--- 测试3: 指定圈数旋转 (2圈) ---
[MAIN] 起始位置: 525140
[MAIN] 目标圈数: 2 圈 = 102400 步
[MAIN] 目标位置: 627540
[MAIN] 进度: 0.52 圈 (位置: 551940) <- 运动中
[MAIN] 进度: 1.02 圈 (位置: 577340)
[MAIN] 进度: 1.52 圈 (位置: 602740)
[MAIN] 进度: 2.00 圈 (位置: 627540) <- 到达
[MAIN] ✓ 完成 2 圈旋转
[MAIN] 最终位置: 627540 (误差: 0 步)
4.5 测试4 - 反转控制
--- 测试4: 反转两圈 ---
[MAIN] 起始位置: 627540
[MAIN] 目标: 反转 2 圈 = -102400 步
[MAIN] 目标位置: 525140
[MAIN] 进度: -0.50 圈 (位置: 601940) <- 反向运动
[MAIN] 进度: -1.00 圈 (位置: 576340)
[MAIN] 进度: -1.50 圈 (位置: 550740)
[MAIN] 进度: -2.00 圈 (位置: 525140) <- 回到起点
[MAIN] ✓ 完成反转 2 圈
[MAIN] 最终位置: 525140 (误差: 0 步)
4.6 性能指标总结
性能指标 | 数值 | 说明 |
|---|---|---|
位置精度 | 0步误差 | 测试中所有位置到达误差为0 |
细分精度 | 256微步 | 0.00703°/步 (1.8°/256) |
最高速度 | 60 RPM | 实测稳定运行,可更高 |
加速度 | 100000 steps/s² | 快速启停 |
噪音水平 | 极低 | StealthChop静音技术 |
SPI通信速度 | 4 MHz | 稳定可靠 |
五、项目中遇到的难题和解决方法
问题1:TMC4361A初始化失败,SPI读取全0xFF
问题描述:
项目初期,初始化TMC4361A后读取寄存器全是0xFFFFFFFF,SPI通信无响应。
原因分析:
- TMC4361A需要外部时钟才能工作
- 初始化顺序错误:先初始化SPI后才启动时钟
- 复位时序不正确
解决方法:
// 修改后的初始化顺序
static void gpio_init(void)
{
gpio_config(&io_conf);
gpio_set_level(TMC4361A_PIN_CS, 1);
// ✅ 关键:先初始化CLK_EXT
clk_ext_init();
vTaskDelay(pdMS_TO_TICKS(10)); // 等待时钟稳定
// ✅ 然后执行复位
gpio_set_level(TMC4361A_PIN_NRST, 1);
vTaskDelay(pdMS_TO_TICKS(10));
gpio_set_level(TMC4361A_PIN_NRST, 0);
vTaskDelay(pdMS_TO_TICKS(10));
gpio_set_level(TMC4361A_PIN_NRST, 1);
vTaskDelay(pdMS_TO_TICKS(100)); // 充分的启动时间
}
问题2:tmc4361a_smooth_stop()无法停止电机
问题描述:
调用tmc4361a_smooth_stop()后,电机继续旋转,不减速停止。
原因分析:
初版代码尝试切换到位置模式并设置当前位置为目标:
// ❌ 错误版本
void tmc4361a_smooth_stop(void)
{
int32_t current_pos = tmc4361a_get_position();
tmc4361a_write_register(TMC4361A_RAMPMODE,
TMC4361A_RAMP_POSITION | TMC4361A_RAMP_TRAPEZ);
tmc4361a_write_register(TMC4361A_XTARGET, current_pos);
}
问题:从速度模式切换到位置模式时,电机正在高速旋转,切换瞬间位置已经变化,导致XTARGET总是滞后于XACTUAL。
解决方法:
改用速度模式停止,将目标速度设为0:
// ✅ 正确版本
void tmc4361a_smooth_stop(void)
{
// 保持速度模式
tmc4361a_write_register(TMC4361A_RAMPMODE, TMC4361A_RAMP_TRAPEZ);
// 目标速度设为0
tmc4361a_write_register(TMC4361A_VMAX, 0);
}
工作原理:
- 在速度模式下,芯片会自动按DMAX参数减速
- VMAX=0表示目标速度为0
- 系统自动生成减速曲线直到停止
- 避免了模式切换时的位置跟踪问题
六、对本活动的心得体会
从理论到实践,理解了梯形/S型加减速曲线的重要性,掌握了位置模式、速度模式、扭矩模式的应用场景,学会了如何配置细分、电流、加速度等核心参数。
遇到的挑战
TMC4361A的时钟配置问题
起初没有理解TMC4361A必须有外部时钟才能工作,导致SPI通信完全失败。通过阅读数据手册的时序图,学习LEDC外设的使用,最终解决了这个关键问题。这个经验让我深刻体会到:
- RTFM (Read The F**king Manual):数据手册是最权威的参考
- 逐步验证:先验证时钟输出,再验证SPI通信,最后验证功能
对TMC芯片的体会:
- Trinamic的芯片功能强大,但学习曲线陡峭
- 数据手册内容详尽但较长(TMC4361A手册200+页)
- 建议厂商提供更多应用笔记和快速入门指南