一、项目概述
1.1 项目背景
步进电机因其开环控制精度高、定位准确、成本适中等特点,广泛应用于3D打印机、数控机床、机器人关节、自动化设备等领域。本项目基于ST公司NUCLEO-U575ZI-Q开发板和TMC2209步进电机驱动器,设计并实现了一套完整的步进电机控制系统,支持启动/停止、方向切换、平滑加减速、串口远程控制等功能。
1.2 任务介绍
主题一:基础步进电机运动控制系统
使用MCU控制TMC2209驱动步进电机,实现一个可控制转动方向和速度的基础运动控制系统
1. 使用PWM方式生成STEP脉冲步进
2. 使用DIR控制电机正反转
3. 支持电机多种转速,可设(100Hz-20000Hz)
4. 支持串口发送命令调节电机工作(启动、停止、反转、调速、数据查看)
5. 支持梯形加减速控制
6. 支持反转保护(速度降为0后再反转)
1.3 系统框图

1.4 硬件接线图
TMC2209引脚功能连接Nucleo引脚STM32引脚连接器位置
| STEP | 步进脉冲输入 | D10 | PD14 (TIM4_CH3) | CN7 Pin16 |
| DIR | 方向控制 | D11 | PA7 | CN7 Pin14 |
| EN | 使能控制(低有效) | D9 | PD15 | CN7 Pin18 |
| VDD_IO | 逻辑电源 | 3V3 | — | CN8 Pin7 |
| GND | 逻辑地 | GND | — | CN8 Pin11/13 |
| VM | 电机电源(4.75-28V) | 外部电源 | — | 直接连接 |
| M1A、1B、2A、2B | 电机线圈连接 | — | — | 步进电机引脚 |
注意:TMC2209的VM引脚需要外接7-24V电源供电,不能使用Nucleo板的5V供电;细分引脚使用默认即可。
二、硬件介绍
2.1 主控器:NUCLEO-U575ZI-Q开发板
NUCLEO-U575ZI-Q是基于STM32U575ZIT6Q芯片的Nucleo-144开发板,采用LQFP144封装,具备以下关键特性:
特性说明
| 内核 | ARM Cortex-M33 with TrustZone |
| 存储 | 2MB Flash + 768KB RAM |
| 调试接口 | 板载STLINK-V3E调试器/编程器 |
| 扩展接口 | ARDUINO® Uno V3兼容的Zio连接器(CN7-CN10) |
| 用户IO | 3个用户LED(LD1绿/LD2蓝/LD3红)、1个用户按钮(B1)、1个复位按钮(B2) |
| 时钟 | 32.768kHz晶振(LSE),内部HSI可作为主时钟 |
本项目的控制信号(STEP/DIR/EN)均通过ARDUINO®兼容的Zio连接器(CN7)引出,便于连接标准扩展模块。
2.2 执行器:TMC2209步进电机驱动器
TMC2209是一款超静音两相步进电机驱动芯片,具备以下特点:
参数规格| 驱动电流 | 2A RMS / 2.8A 峰值 |
| 电压范围 | 4.75V - 28V |
| 控制方式 | STEP/DIR 或 UART |
| 细分模式 | 1 ~ 1/256微步 |
| 静音技术 | StealthChop2 |
本项目采用最基础的STEP/DIR控制方式,仅需连接STEP、DIR、EN三个信号引脚即可实现完整控制。
2.3 应用方向及应用场景
本系统可广泛应用于以下场景:
3D打印机:精确控制挤出机和平台的步进运动
数控机床:实现X/Y/Z轴的精准定位
机器人关节:驱动机械臂的旋转关节
自动化设备:传送带、旋转台、分拣机构等
三、设计思路
3.1 功能模块划分
板载模块实现功能
| TIM4_CH3 (PD14) | PWM输出,产生步进脉冲信号(STEP),频率决定电机转速 |
| GPIO (PA7) | 方向控制(DIR),高电平正转/低电平反转 |
| GPIO (PD15) | 使能控制(EN),低电平使能/高电平失能 |
| USART1 (PA9/PA10) | 串口通信,接收上位机命令并回显状态 |
| 内部HSI时钟 | 无需外部晶振即可提供稳定的系统时钟 |
3.2 核心控制逻辑
PWM频率调速:通过修改定时器TIM4的自动重装载值(ARR)改变PWM频率,频率越高电机转速越快。
平滑加减速:采用10ms定时斜坡策略,每10ms将当前速度向目标速度调整一个固定步长(10Hz),实现无冲击的速度过渡。
反转保护:运行时收到反转命令,先减速至0,再切换DIR方向,最后从最低速重新加速,避免电机和驱动器受损。
状态机管理:通过 STATE_IDLE、STATE_RUNNING、STATE_DECEL_REVERSE、STATE_REVERSE_RECOVER 四个状态管理电机行为,逻辑清晰可靠。
速度恢复:stop 命令保存当前速度,start 命令恢复该速度,避免每次启动都从默认速度开始。
速度到达反馈:当实际速度达到目标速度时,串口自动输出“速度已到达目标 X Hz”提示。
四、如何开启运行
4.1 硬件连接
按硬件接线图连接NUCLEO-U575ZI-Q与TMC2209模块
TMC2209的VM引脚接入7-24V外部电源
将Nucleo板通过USB线连接电脑
4.2 软件准备
使用STM32CubeIDE打开工程
编译并烧录程序
打开串口终端(波特率115200, 8N1)
4.3 基本操作流程

4.4 命令列表

五、主要参数情况
参数名称数值说明
| 系统时钟 | 80 MHz | 使用内部HSI,无需外部晶振 |
| 定时器计数时钟 | 100 kHz | TIM4预分频后(160M/1600) |
| 速度范围 | 100 ~ 20000 Hz | 对应步进脉冲频率 |
| 默认速度 | 1000 Hz | 上电初始速度 |
| 加减速步长 | 10 Hz / 10ms | 即1000 Hz/s的加速度 |
| 串口波特率 | 115200 | 8N1格式 |
六、实现步骤
6.1 CubeMX配置
时钟配置:HSE禁用,使用HSI作为PLL时钟源,配置PLL使SYSCLK=80MHz
GPIO配置:
PD14 → TIM4_CH3 (PWM输出)
PA7 → GPIO_Output (DIR)
PD15 → GPIO_Output (EN)
TIM4配置:Channel3 PWM模式,Prescaler=1599(计数时钟100kHz),自动重装载预装载使能
USART1配置:异步模式,波特率115200,开启接收中断
ICACHE:使能指令缓存提升性能
PWR:启用SMPS降低功耗
6.2 代码实现
代码采用模块化设计,核心函数包括:
set_target_speed():设置目标速度
apply_speed():立即应用速度到定时器
speed_ramp_handler():10ms周期斜坡处理(加减速+换向+停止检测)
handle_command():串口命令解析执行
6.3 调试与验证
烧录程序后通过串口发送各命令验证功能
使用 status 命令实时监控速度变化
观察速度到达反馈确认加减速完成
七、代码展示
7.1 关键宏定义调试与验证
#define SPEED_DEFAULT 1000 // 默认速度 (Hz) #define SPEED_MIN 100 // 最低速度限制 #define SPEED_MAX 20000 // 最高速度限制 #define TIM_CLK 100000 // 定时器计数时钟 (Hz) = 160M / 1600 #define ACCEL_STEP 10 // 每10ms调整的频率步长 (Hz)
说明:这些宏定义了电机运行的基本参数,速度范围100-20000Hz,加减速步长10Hz/10ms。
7.2 速度控制核心函数
/**
* @brief 立即将频率写入定时器(强制输出)
*/
static void apply_speed(uint32_t freq) {
current_speed = freq;
if (freq == 0) {
__HAL_TIM_SET_AUTORELOAD(&htim4, 0);
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_3, 0);
return;
}
// 计算ARR = 计数时钟 / 频率 - 1
uint32_t arr = (TIM_CLK / freq) - 1;
if (arr > 0xFFFF) arr = 0xFFFF; // TIM4为16位
__HAL_TIM_SET_AUTORELOAD(&htim4, arr);
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_3, arr / 2);
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_3);
}说明:通过修改定时器自动重装载值(ARR)改变PWM频率,同时保持50%占空比。
7.3 速度斜坡处理(核心)
void speed_ramp_handler(void) {
static uint32_t last_tick = 0;
uint32_t now = HAL_GetTick();
if ((now - last_tick) < 10) return; // 10ms周期
last_tick = now;
if (!motor_enabled) {
// 清空辅助状态
if (pending_reverse) {
pending_reverse = 0;
saved_target_speed = 0;
}
speed_reached_flag = 0;
return;
}
// 第一步:速度斜坡(加减速)
if (current_speed < target_speed) {
uint32_t step = ACCEL_STEP;
if (target_speed - current_speed < step)
step = target_speed - current_speed;
apply_speed(current_speed + step);
speed_reached_flag = 0;
} else if (current_speed > target_speed) {
uint32_t step = ACCEL_STEP;
if (current_speed - target_speed < step)
step = current_speed - target_speed;
apply_speed(current_speed - step);
speed_reached_flag = 0;
} else {
// 速度到达,输出反馈(仅一次)
if (current_speed != 0 && target_speed != 0 && !speed_reached_flag) {
printf("速度已到达目标 %lu Hz\r\n", current_speed);
speed_reached_flag = 1;
}
}
// 第二步:换向保护(速度=0时换向)
if (current_speed == 0 && pending_reverse) {
motor_direction = !motor_direction;
HAL_GPIO_WritePin(DIR_GPIO_Port, DIR_Pin,
motor_direction ? GPIO_PIN_SET : GPIO_PIN_RESET);
printf("方向已切换为 %s\r\n", motor_direction ? "正转" : "反转");
pending_reverse = 0;
if (saved_target_speed > 0) {
target_speed = saved_target_speed;
saved_target_speed = 0;
if (current_speed == 0) {
apply_speed(SPEED_MIN); // 从最低速重新起步
}
printf("恢复目标速度 %lu Hz,开始加速\r\n", target_speed);
speed_reached_flag = 0;
}
}
// 第三步:自动停止检测
if (current_speed == 0 && target_speed == 0 && motor_enabled == 1) {
motor_enabled = 0;
HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, GPIO_PIN_SET);
HAL_TIM_PWM_Stop(&htim4, TIM_CHANNEL_3);
printf("电机已停止\r\n");
speed_reached_flag = 0;
}
}说明:这是整个程序最核心的函数,每10ms执行一次,完成三项关键任务:
平滑加减速:逐步逼近目标速度,避免突变
换向保护:速度降到0后才切换方向,防止冲击
自动停止:速度降至0且目标为0时自动失能
7.4 反转命令处理
else if (strcmp(cmd, "reverse") == 0) {
if (!motor_enabled || current_speed == 0) {
// 停止时直接换向
motor_direction = !motor_direction;
HAL_GPIO_WritePin(DIR_GPIO_Port, DIR_Pin,
motor_direction ? GPIO_PIN_SET : GPIO_PIN_RESET);
printf("方向已切换为 %s(电机停止)\r\n",
motor_direction ? "正转" : "反转");
} else {
// 运行时换向:先减速到0,再换向,再加速
if (pending_reverse) {
printf("正在换向中,请等待完成\r\n");
return;
}
saved_target_speed = target_speed;
if (saved_target_speed == 0) saved_target_speed = SPEED_MIN;
set_target_speed(0); // 触发减速
pending_reverse = 1; // 标记等待换向
printf("正在减速至零以换向...\r\n");
}
}说明:区分两种场景——停止时直接换向(安全),运行时先减速再换向(保护电机和驱动器)。
7.5 主循环
while (1)
{
if (cmd_ready) {
handle_command(); // 解析串口命令
}
speed_ramp_handler(); // 速度斜坡(每10ms有效)
HAL_Delay(1); // 1ms延时,释放CPU
}说明:主循环结构简洁,每秒执行约1000次,其中速度斜坡每10ms(10次循环)调整一次。
7.6 串口接收中断
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART1) {
if (rx_char == '\r' || rx_char == '\n') {
cmd_ready = 1; // 完整命令接收完成
} else if (cmd_index < sizeof(cmd_buffer) - 1) {
cmd_buffer[cmd_index++] = rx_char; // 存入缓冲区
}
HAL_UART_Receive_IT(&huart1, (uint8_t*)&rx_char, 1);
}
}说明:中断方式接收串口数据,收到回车/换行时标记命令就绪,不阻塞主循环。
八、必完成任务的功能演示
任务名称演示方法预期结果
| 电机启动 | 串口发送 start | 电机从最低速(100Hz)平滑加速到目标速度,到达后输出反馈 |
| 速度调节 | 串口发送 speed 2000 | 电机从当前速度平滑加减速到2000Hz,到达后输出反馈 |
| 速度微调 | 串口发送 speed+ / speed- | 目标速度±100Hz,平滑变化 |
| 方向切换 | 串口发送 reverse | 运行时先减速至0→换向→重新加速;停止时立即换向 |
| 停止控制 | 串口发送 stop | 电机减速至0,自动失能,串口输出“电机已停止” |
| 速度恢复 | stop 后发送 start | 恢复上一次运行速度,无需重新设置 |
| 状态查询 | 串口发送 status | 显示运行状态、当前速度、目标速度、方向等信息 |
8.1 STM32初上电,串口打印消息,默认电机上电停机

8.2 串口发送help命令,打印可用命令

8.3 串口发送start命令,电机以1000Hz初始速度启动


8.4 串口发送speed 5000命令,电机以5000Hz速度为目标缓慢加速


8.4 串口发送status命令,串口打印当前电机状态

8.5 串口发送reverse命令,电机开始减速、停止、反转、加速、直到达到之前目标速度


8.6 串口发送stop命令,电机开始减速、停止、失能。


我要赚赏金
