功能概览
通过步数实现简单位置管理,即将长度映射到具体的步数(总长度分为N个位置),可以准确指定移动到准确的位置
支持以下控制功能:回到原点、移动到指定位置、设定移动距离(移动相对位置)
实现平滑加减速控制,避免电机突然启动或停止
回零操作
硬件介绍
NUCLEO-G474RE 开发板 1 块:作为系统核心控制器,负责数据采集、逻辑处理、阈值修改及模式切换功能。
Grove - 0.96英寸黄蓝双色OLED显示屏 1 个:显示实时电流、阈值、工作模式及状态信息,采用I²C通信。
TMC2209 SilentStepStick 模块 1 个
面包板 1 块
杜邦线 若干
按下图所示,将各模块连接起来,组成本次项目的演示电路。

设计思路
系统架构设计
本系统采用经典的三层架构设计模式,自底向上依次为硬件抽象层、业务逻辑层和应用接口层。硬件抽象层负责初始化和操作各类外设接口,包括GPIO引脚配置、TMC2209驱动通信、OLED显示驱动等。业务逻辑层实现了电机运动控制、位置管理、加减速算法等核心功能。应用接口层提供了串口命令解析、按键事件处理等用户交互接口。
在任务划分方面,将系统功能拆分为三个独立任务:电机控制任务负责步进脉冲生成和位置计数,是系统中最关键、对实时性要求最高的任务;显示任务负责定期刷新OLED屏幕内容,由于OLED更新速度较慢且对实时性要求不高,分配较低优先级;串口任务负责接收和解析上位机命令,命令处理结果通过队列传递给电机任务执行。
任务调度设计
FreeRTOS的任务调度采用基于优先级的抢占式调度策略。本系统配置三个任务优先级:电机控制任务优先级为3(最高),显示任务优先级为2,串口任务优先级为2。相同优先级的任务采用时间片轮转调度算法,确保各任务能够公平地获得CPU时间片。
电机控制任务采用事件驱动模式,平常处于阻塞状态等待命令队列消息。当串口任务解析到有效命令后,通过队列向电机任务发送控制指令,电机任务被唤醒后执行相应的运动控制操作。这种设计避免了轮询方式带来的CPU资源浪费,同时保证了命令响应的及时性。
显示任务采用周期性模式,以100毫秒为周期定时唤醒执行。每次唤醒后,任务首先获取显示互斥信号量,然后读取系统状态并更新OLED屏幕显示内容,最后释放信号量并再次进入延时等待。
串口任务同样采用周期性模式,以50毫秒为周期扫描串口缓冲区。当检测到有数据到达时,任务读取一行命令字符串并进行解析,根据命令类型执行相应操作。对于需要电机执行的控制命令,任务构建命令结构体并通过队列发送给电机任务;对于查询类命令,任务直接读取系统状态并通过串口返回结果。
功能实现
软件架构

核心代码实现
全局变量定义
// 调试串口定义 - 用于与上位机通信和调试信息输出 #define SERIAL_DEBUG Serial // TMC2209步进电机驱动串口定义 - 用于与TMC2209芯片进行UART通信 #define SERIAL_TMC Serial1 // TMC2209器件从机地址定义 - 二进制00地址 (TMC2209默认地址) #define TMC2209_SLAVE_ADDR 0b00 // TMC2209寄存器地址定义 - 用于UART通信配置 #define TMC2209_REG_SLAVE_CONF 0x00 // 从机配置寄存器 #define TMC2209_REG_SLAVE_STATUS 0x01 // 从机状态寄存器 // TMC2209控制引脚配置 - 连接到SilentStepStick模块 const uint8_t TMC2209_EN_PIN = D3; // 使能引脚 - 低电平使能电机输出 const uint8_t TMC2209_STEP_PIN = D4; // 步进脉冲引脚 - 每脉冲触发一步 const uint8_t TMC2209_DIR_PIN = D5; // 方向控制引脚 - 高低电平控制旋转方向 const uint8_t TMC2209_UART_RX = D2; // UART接收引脚 (预留) const uint8_t TMC2209_UART_TX = D1; // UART发送引脚 (预留) // OLED显示屏配置 - Grove 0.96英寸黄蓝双色OLED const uint8_t OLED_I2C_ADDR = 0x3C; // OLED的I2C从机地址 const uint8_t OLED_SDA_PIN = D11; // I2C数据线引脚 const uint8_t OLED_SCL_PIN = D13; // I2C时钟线引脚
步进脉冲生成
// 步进脉冲生成核心逻辑
if (currentTime - lastStepTime >= stepInterval) {
lastStepTime = currentTime; // 更新上次步进时间
// 根据方向更新位置计数器
if (g_direction) {
g_systemState.currentPosition++; // 前进则位置加1
} else {
g_systemState.currentPosition--; // 后退则位置减1
}
g_stepTarget--; // 剩余步数减1
g_accelProfile.stepCount++; // 已执行步数加1
// 产生步进脉冲 - 拉高至少2微秒
digitalWrite(TMC2209_STEP_PIN, HIGH);
delayMicroseconds(2);
digitalWrite(TMC2209_STEP_PIN, LOW);
// 检查是否到达目标位置
if (g_stepTarget <= 0) {
stopMotor(); // 停止电机
// 如果是回零操作,到达原点后位置清零
if (g_systemState.status == STATUS_HOMING) {
g_systemState.currentPosition = 0;
g_systemState.status = STATUS_IDLE;
}
}
}FreeRTOS任务创建
// 创建FreeRTOS通信机制 commandQueue = xQueueCreate(10, sizeof(Command)); // 创建命令队列,深度10 stateQueue = xQueueCreate(10, sizeof(SystemState)); // 创建状态队列,深度10 displaySemaphore = xSemaphoreCreateMutex(); // 创建显示互斥信号量 // 创建FreeRTOS任务 xTaskCreate(vMotorTask, // 任务函数 "MotorTask", // 任务名称 256, // 堆栈大小 NULL, // 参数 3, // 优先级 NULL); // 任务句柄 xTaskCreate(vDisplayTask, // 任务函数 "DisplayTask", // 任务名称 256, // 堆栈大小 NULL, // 参数 2, // 优先级 NULL); // 任务句柄 xTaskCreate(vSerialTask, // 任务函数 "SerialTask", // 任务名称 256, // 堆栈大小 NULL, // 参数 2, // 优先级 NULL); // 任务句柄 // 启动FreeRTOS调度器 vTaskStartScheduler();
毫米到步数转换
// 将毫米转换为微步数 float targetMm = param.substring(7).toFloat(); int32_t targetSteps = (int32_t)(targetMm * (STEPS_PER_REV * DEFAULT_MICROSTEPS) / LEAD_SCREW_PITCH_MM); // 计算: 毫米 × (200步/转 × 8微步) / 2.0mm = 毫米 × 800步/毫米
电机控制任务核心
void vMotorTask(void* pvParameters) {
Command cmd;
cmd.type = CMD_NONE;
cmd.value = 0;
for (;;) {
// 非阻塞方式接收命令
BaseType_t received = xQueueReceive(commandQueue, &cmd, 0);
if (received == pdTRUE) {
switch (cmd.type) {
case CMD_FORWARD:
g_systemState.status = STATUS_RUNNING;
stepper.setSpeed(DEFAULT_SPEED_STEPS);
stepper.move(cmd.value);
break;
case CMD_STOP:
stepper.stop();
g_systemState.status = STATUS_STOPPED;
break;
// ... 其他命令处理
}
cmd.type = CMD_NONE;
}
// 频繁调用stepper.run()处理步进脉冲
for (int i = 0; i < 10; i++) {
stepper.run();
}
vTaskDelay(1); // 让出CPU给其他任务
// 更新位置和状态
g_systemState.currentPosition = stepper.currentPosition();
if (g_systemState.status == STATUS_RUNNING && !stepper.isRunning()) {
g_systemState.status = STATUS_IDLE;
}
}
}功能展示
OLED显示内容
OLED屏幕显示布局如下:
-------------------- | Run Current: 50% | | Threshold: 1.00A | | Mode: DIRECT | | Pos: 500/1000 | | Status: STOPPED | --------------------

串口命令演示
系统上电后,通过串口调试助手发送命令可实现以下功能:
基本运动控制:
发送"F500" → 电机前进500步,响应"OK:MOVE=FORWARD:500"
发送"B200" → 电机后退200步,响应"OK:MOVE=BACKWARD:200"
发送"S" → 立即停止电机,响应"OK:STOPPED"
位置控制:
发送"H" → 执行回零操作,响应"OK:HOMING..."
发送"P?" → 查询位置,响应"POS:500:1000:STOPPED"
参数设置:
发送"SET:POS=500" → 设置总位置数为500,响应"OK:SET:POS=500"
{% asset_img 2026-06-20-21-33-42.png 【EEPW-2026-Let-s-do-第1期】DigiKey陪你走过春夏秋冬-静音步进电机控制实践 %}
运行特性
在500步/秒默认速度下,系统完成了以下性能测试:加减速过程平滑,无明显冲击振动;位置控制精度达到±1步;响应延迟小于5毫秒;连续运行1小时无异常发热。系统各项功能均正常工作,满足设计要求。
演示视频
传送门
【2026 Let's do 第1期】静音步进电机控制实践开箱帖
【2026 Let's do 第1期】静音步进电机控制实践过程帖
我要赚赏金
