成果帖
1、项目介绍
使用MCU控制TMC2209 SilentStepStick 驱动步进电机,实现一个可控制转动方向和速度的基础运动控制系统。
活动任务:
• 基础任务(入门级),控制端: •生成STEP脉冲信号 •DIR控制电机正反转 •支持至少两种转速,例如快速、慢速 •电机能够稳定运行
•进阶任务,在基础任务上实现: •速度调节功能 •加速/减速控制 •按键或串口控制运动,例如:启动、停止、反转
• 自由发挥方向(不做强制要求): •梯形加减速控制 •多种运动模式 •定位控制(步数控制) •PWM方式生成STEP
2、硬件准备
树莓派Pico主控板 x1
TMC2209步进电机驱动模块 x1
12V步进电机 x1
12V电源适配器
连接线若干
硬件连接图(参考用,请忽略很多不严谨的地方。。)

layout图(洞洞板走线,因此设置得很粗,至于未连接的线实际上是飞线了)

2、关键代码介绍
/*
* step ms1 ms2
* 1/8 0 0
* 1/16 1 1
* 1/32 1 0
* 1/64 0 1
*/
// 引脚定义
int en = 7; // 使能引脚 - 1为禁用,0为使能
int ms1 = 8; // 微步设置1
int ms2 = 9; // 微步设置2
int sprd = 10; // 斩波模式选择引脚 1-SpreadCycle,0-Stealthchop
// 低速用 StealthChop 保持安静,高速切到 SpreadCycle 保证力矩。
int step = 11; // 步进脉冲引脚
int pdn = 14; // 功耗模式 1-保持力矩,0-省电
int dir = 15; // 方向引脚 1-顺时针,0-逆时针
#define PIN_LED (25u)
// 电机控制变量
int motorSpeed = 1000; // 步进脉冲间隔(微秒),值越小速度越快
int microstepMode = 16; // 当前微步模式
bool running = false; // 电机运行状态
bool direction = true; // 旋转方向
void setup() {
// 初始化控制引脚
pinMode(en, OUTPUT);
pinMode(ms1, OUTPUT);
pinMode(ms2, OUTPUT);
pinMode(sprd, OUTPUT);
pinMode(step, OUTPUT);
pinMode(pdn, OUTPUT);
pinMode(dir, OUTPUT);
pinMode(LED_BUILTIN, OUTPUT);
// 设置初始状态
digitalWrite(en, HIGH); // 禁用驱动器
digitalWrite(dir, HIGH); // 设置方向为顺时针
digitalWrite(sprd, LOW); // 初始设置为Stealthchop模式
digitalWrite(pdn, HIGH); // 设置为保持力矩模式
digitalWrite(step, LOW); // 步进脉冲初始为低
// 设置微步模式为1/16 (MS1=1, MS2=1)
setMicrostepMode(16);
// 初始化串口通信(用于调试和控制命令)
Serial.begin(115200);
// 对于树莓派Pico RP2040,等待串口连接
while (!Serial && millis() < 5000) {
// 等待串口打开或最多等待5秒
}
Serial.println("TMC2209步进电机控制器已启动(GPIO模式)");
Serial.println("命令:");
Serial.println(" s - 启动电机");
Serial.println(" t - 停止电机");
Serial.println(" d - 切换方向");
Serial.println(" + - 加速");
Serial.println(" - - 减速");
Serial.println(" m - 更改微步模式");
Serial.println(" c - 切换斩波模式(SpreadCycle/StealthChop)");
Serial.println(" p - 切换功耗模式(保持力矩/省电)");
Serial.println(" h - 输出帮助信息");
}
void loop() {
// 检查串口命令
if (Serial.available()) {
char command = Serial.read();
handleCommand(command);
}
// 如果电机正在运行,则发送步进脉冲
if (running) {
digitalWrite(step, HIGH);
delayMicroseconds(motorSpeed / 2); // 高电平一半时间
digitalWrite(step, LOW);
delayMicroseconds(motorSpeed / 2); // 低电平一半时间
}
// 使用定时器控制LED闪烁,不受电机运行影响
static unsigned long lastLEDToggle = 0;
const unsigned long ledInterval = 500; // LED闪烁间隔(毫秒)
if (millis() - lastLEDToggle >= ledInterval) {
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
lastLEDToggle = millis();
}
}
// 设置微步模式
void setMicrostepMode(int mode) {
switch(mode) {
case 8: // 1/8步
digitalWrite(ms1, LOW);
digitalWrite(ms2, LOW);
microstepMode = 8;
break;
case 16: // 1/16步
digitalWrite(ms1, HIGH);
digitalWrite(ms2, HIGH);
microstepMode = 16;
break;
case 32: // 1/32步
digitalWrite(ms1, HIGH);
digitalWrite(ms2, LOW);
microstepMode = 32;
break;
case 64: // 1/64步
digitalWrite(ms1, LOW);
digitalWrite(ms2, HIGH);
microstepMode = 64;
break;
default:
// 默认设为1/16步
digitalWrite(ms1, HIGH);
digitalWrite(ms2, HIGH);
microstepMode = 16;
break;
}
Serial.print("微步模式设置为: 1/");
Serial.println(microstepMode);
}
// 处理串口命令
void handleCommand(char cmd) {
switch(cmd) {
case 's': // 启动电机
enableDriver();
running = true;
Serial.println("电机启动");
break;
case 't': // 停止电机
running = false;
disableDriver();
Serial.println("电机停止");
break;
case 'd': // 切换方向
direction = !direction;
digitalWrite(dir, direction);
Serial.print("方向切换为: ");
Serial.println(direction ? "顺时针" : "逆时针");
break;
case '+': // 加速
motorSpeed -= 100; // 减少延时时间,提高速度
if(motorSpeed < 100) motorSpeed = 100; // 限制最小延时
Serial.print("加速 - 步进间隔: ");
Serial.print(motorSpeed);
Serial.println("us");
break;
case '-': // 减速
motorSpeed += 100; // 增加延时时间,降低速度
if(motorSpeed > 5000) motorSpeed = 5000; // 限制最大延时
Serial.print("减速 - 步进间隔: ");
Serial.print(motorSpeed);
Serial.println("us");
break;
case 'm': // 更改微步模式
cycleMicrostepMode();
break;
case 'c': // 切换斩波模式
toggleChopperMode();
break;
case 'p': // 切换功耗模式
togglePowerMode();
break;
case 'h': // 显示帮助
printHelp();
break;
case '\n':
case '\r':
break; // 忽略换行符
default:
Serial.print("未知命令: ");
Serial.println(cmd);
Serial.println("输入'h'查看帮助");
break;
}
}
// 循环切换微步模式
void cycleMicrostepMode() {
int modes[] = {8, 16, 32, 64};
static int modeIndex = 0;
modeIndex = (modeIndex + 1) % 4;
setMicrostepMode(modes[modeIndex]);
}
// 切换斩波模式
void toggleChopperMode() {
// 当前模式是StealthChop (sprd=LOW),切换到SpreadCycle (sprd=HIGH)
bool currentMode = digitalRead(sprd);
digitalWrite(sprd, !currentMode);
Serial.print("斩波模式切换为: ");
Serial.println(!currentMode ? "SpreadCycle" : "StealthChop");
}
// 切换功耗模式
void togglePowerMode() {
// 当前模式是保持力矩 (pdn=HIGH),切换到省电模式 (pdn=LOW)
bool currentMode = digitalRead(pdn);
digitalWrite(pdn, !currentMode);
Serial.print("功耗模式切换为: ");
Serial.println(!currentMode ? "省电模式" : "保持力矩");
}
// 使能驱动器
void enableDriver() {
digitalWrite(en, LOW); // 使能驱动器
}
// 禁用驱动器
void disableDriver() {
digitalWrite(en, HIGH); // 禁用驱动器
}
// 打印帮助信息
void printHelp() {
Serial.println("=== TMC2209步进电机控制器 (GPIO模式) ===");
Serial.println("命令列表:");
Serial.println(" s - 启动电机");
Serial.println(" t - 停止电机");
Serial.println(" d - 切换方向 (顺时针/逆时针)");
Serial.println(" + - 增加速度");
Serial.println(" - - 降低速度");
Serial.println(" m - 循环切换微步模式");
Serial.println(" c - 切换斩波模式 (StealthChop/SpreadCycle)");
Serial.println(" p - 切换功耗模式 (保持力矩/省电)");
Serial.println(" h - 显示此帮助信息");
Serial.println("=========================================");
Serial.print("当前状态: ");
Serial.println(running ? "运行" : "停止");
Serial.print("当前方向: ");
Serial.println(direction ? "顺时针" : "逆时针");
Serial.print("当前速度: ");
Serial.print(1000000/motorSpeed); // 转换为Hz
Serial.println(" Hz");
Serial.print("当前微步: 1/");
Serial.println(microstepMode);
Serial.print("斩波模式: ");
Serial.println(digitalRead(sprd) ? "SpreadCycle" : "StealthChop");
Serial.print("功耗模式: ");
Serial.println(digitalRead(pdn) ? "保持力矩" : "省电");
}3、功能展示(包含成果视频)
实现情况:
| 功能 | 实现情况 |
| 生成STEP脉冲信号 | 完成 |
| DIR控制电机正反转 | 完成 |
| 支持至少两种转速,例如快速、慢速 | 完成 |
| 电机能够稳定运行 | 完成 |
| 速度调节功能 | 完成 |
| 按键或串口控制运动,例如:启动、停止、反转 | 完成 |
| PWM方式生成STEP | 完成 |
其他更多功能请见代码或帮助,除了串口控制tmc2209,基本所有功能都已经实现了。
b站视频介绍:
4、技术难点与解决方案/心得体会
之前只玩过直流有刷电机,对步进电机一直不太了解,感谢得捷给了这次机会,使我可以研究一下步进电机的控制。其实一开始看原理的时候还有点云里雾里,直到搞明白TMC2209的控制后,就明白了很多,当然还有很多进阶的控制,希望后面还有机会深入!
搞明白原理后,就开始想办法连接,之前马马虎虎搞个杜邦线连就好,但是前段时间因为反接电源烧了一个DAC模块,吃一堑长一智,决定还是回归大学初学电子时的法子——洞洞板。相比大学,现在已经可以拿EDA软件设计一下原理图,虽然要在洞洞板上走线,也大致layout规划了一番,最后达到自己满意的一个效果。焊接完,先测试有没有短路,再逐步加模块看是否正常,前面一切顺利,直到我断开12v,插上USB准备开发时,电源芯片开始冒烟,慌忙断电,最后用一颗二极管隔离开USB到电源模块的通路。而arduino 的开发就很简单了,arduino的封装很好,很容易就驱动起来该有的外设:串口、GPIO、PWM,再将要求告诉ai,调试一番,很快就实现功能了。
回顾一下,比较开心的时刻:拿到板子时、搞明白原理时、加了一个二极管解决冒烟问题时、电机转起来时。在这个AI的时代,写代码的空间不断被压缩,那实实在在跑起来的电路,或许能带给工程师更多的慰藉吧。
我要赚赏金
