一个简单易用的步进电机驱动库 (A simple and easy-to-use stepper motor driver)
本项目为基于 STM32 HAL 库的步进电机驱动程序,采用梯形/三角形加减速算法,通过定时器 PWM 输出脉冲控制步进电机运动,支持非阻塞式控制。
- ✅ 梯形加减速算法 - 平滑启停,保护机械结构
- ✅ 三角形加减速 - 短距离运动自动切换
- ✅ 非阻塞控制 - 后台定时器中断处理,不占用主循环
- ✅ 精确角度控制 - 基于角度输入,自动计算步数
- ✅ 可配置参数 - 加速度、减速度、目标速度可调
- ✅ PWM 脉冲输出 - 使用高级定时器生成精确脉冲
- STM32 系列(默认配置基于 STM32F4,168MHz 主频)
- 需要至少 1 个高级定时器(默认使用 TIM8)
- 需要 GPIO 控制方向和使能信号
支持通用步进电机驱动器(如 DRV8825、A4988、TB6600 等),需要以下接口:
- PUL+/PUL-(脉冲信号) → 连接定时器 PWM 输出
- DIR+/DIR-(方向信号) → 连接 GPIO
- ENA+/ENA-(使能信号) → 连接 GPIO
STM32 步进驱动器
TIM8_CH1 (PWM) -----> PUL+ (脉冲)
GPIO (DIR) -----> DIR+ (方向)
GPIO (ENA) -----> ENA+ (使能)
GND -----> PUL-/DIR-/ENA-
将 step_motor_v1 目录下的文件添加到您的工程:
step_motor_v1/
├── step_motor.c # 主驱动实现
└── step_motor.h # 头文件
-
时钟源: Internal Clock
-
模式: PWM Generation CH1
-
参数配置:
Prescaler (PSC): 0 Counter Mode: Up Counter Period (ARR): 1000 (初始值,运行时会动态调整) Pulse (CCR1): 500 (50% 占空比) -
中断使能:
- ✅ 勾选
Update interrupt
- ✅ 勾选
// 示例配置(根据实际硬件修改)
DIR_GPIO_Port: GPIOB, Pin: GPIO_PIN_0
ENA_GPIO_Port: GPIOB, Pin: GPIO_PIN_1在 CubeMX 中生成 HAL 库代码。
#include "step_motor.h"int main(void)
{
/* HAL 初始化 */
HAL_Init();
SystemClock_Config();
/* 初始化外设 */
MX_GPIO_Init();
MX_TIM8_Init();
/* 设置电机使能和方向 */
HAL_GPIO_WritePin(ENA_GPIO_Port, ENA_Pin, GPIO_PIN_RESET); // 使能电机
HAL_GPIO_WritePin(DIR_GPIO_Port, DIR_Pin, GPIO_PIN_SET); // 设置方向
/* 启动电机旋转 360 度 */
start(360.0);
while (1)
{
// 主循环可以执行其他任务
// 电机运动在中断中自动完成
}
}在 stm32f4xx_it.c 或 main.c 中确保定时器中断已启用:
// 此函数已在 step_motor.c 中实现
// 无需额外编写,但需确保中断向量表正确配置
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);void start(float angle);功能说明:
- 控制步进电机旋转指定角度
- 自动计算加减速曲线
- 非阻塞式执行,函数立即返回
参数说明:
angle: 目标旋转角度(单位:度)- 正值:正向旋转
- 负值:反向旋转(需先通过 GPIO 设置方向)
- 相对角度:以上电时刻为 0° 基准
注意事项:
⚠️ 不可重复调用:必须等待上一次运动完成后再调用- 默认电机步距角为 1.8°/步(200 步/转)
- 如使用细分驱动器,需修改源码中的步数计算公式
示例:
// 旋转 360 度(一圈)
start(360.0);
// 旋转 180 度(半圈)
start(180.0);
// 旋转 90 度(四分之一圈)
start(90.0);在 step_motor.c 中可修改以下全局变量:
float accel = 0.5; // 加速度(可调整范围:0.1 ~ 2.0)
float decel = 0.5; // 减速度(可调整范围:0.1 ~ 2.0)
float speed = 20; // 目标速度(可调整范围:10 ~ 50)参数说明:
accel: 加速度值,越大加速越快,但过大可能导致失步decel: 减速度值,越大减速越快speed: 最大运行速度,单位为内部计算单位(非物理单位)
调整建议:
// 平滑运动(适合重载、精密场合)
accel = 0.3;
decel = 0.3;
speed = 15;
// 快速运动(适合轻载、快速定位)
accel = 1.0;
decel = 1.0;
speed = 40;int status; // 运动状态| 值 | 状态 | 说明 |
|---|---|---|
| 0 | 停止 | 电机静止 |
| 1 | 加速 | 正在加速阶段 |
| 2 | 匀速 | 正在匀速运行 |
| 3 | 减速 | 正在减速阶段 |
int mode; // 运动模式| 值 | 模式 | 说明 |
|---|---|---|
| 1 | 三角形 | 短距离运动,无匀速段,直接从加速转减速 |
| 2 | 梯形 | 长距离运动,包含加速-匀速-减速三个阶段 |
适用于长距离运动,速度曲线分为三个阶段:
速度
^
| ___________ ← 匀速段
| / \
| / \
| / \
| / \
|/__________________\___> 时间
加速段 匀速段 减速段
阶段划分:
- 加速段: 从静止加速到目标速度
- 匀速段: 保持目标速度运行
- 减速段: 从目标速度减速到停止
适用于短距离运动,无法达到目标速度:
速度
^
| /\
| / \
| / \
| / \
| / \
| / \
|/_____________\___> 时间
加速段 减速段
自动切换:
- 当目标步数 < (加速步数 + 减速步数) 时,自动切换为三角形模式
start()函数启动定时器 TIM8 并使能中断- 每个定时器周期触发一次中断(产生一个脉冲)
- 中断回调函数
HAL_TIM_PeriodElapsedCallback()中:- 根据当前阶段计算下一步的定时��周期
- 更新定时器参数(动态调整脉冲频率)
- 步数计数器
step_count递增
- 当
step_count达到total_steps时停止定时器
- 周期 ↑ → 频率 ↓ → 速度 ↓
- 周期 ↓ → 频率 ↑ → 速度 ↑
加减速通过逐步调整定时器周期实现平滑过渡。
可能原因:
- 定时器配置错误 → 检查 TIM8 是否配置为 PWM 模式
- 脉冲信号未连接 → 用示波器检查 PWM 输出
- 驱动器使能信号未拉低 → 检查 ENA 引脚电平
- 加速度设置过大 → 降低
accel和speed值
解决方案:
- 降低加速度和速度参数
- 增加驱动电流(在驱动器上调节)
- 检查负载是否过大
解决方案:
- 手动控制 DIR 引脚电平实现方向切换:
HAL_GPIO_WritePin(DIR_GPIO_Port, DIR_Pin, GPIO_PIN_SET); // 正向 HAL_GPIO_WritePin(DIR_GPIO_Port, DIR_Pin, GPIO_PIN_RESET); // 反向
方法一:轮询状态变量
extern float step_count;
extern float total_steps;
if (step_count >= total_steps) {
// 运动完成
}方法二:检查定时器状态
if (__HAL_TIM_GET_FLAG(&htim8, TIM_FLAG_UPDATE) == RESET) {
// 定时器已停止,运动完成
}支持,修改 start() 函数中的步数计算:
// 原代码(1.8°/步)
total_steps = angle / 1.8;
// 改为 0.9°/步(400 步/转)
total_steps = angle / 0.9;
// 改为 1.8° + 16 细分(3200 步/转)
total_steps = (angle / 1.8) * 16;T1_FREQ = 0.676 * 10000 / 10; // 定时器频率参数
A_SQ = 2 * 0.0314 * 100000; // 加速度参数这些常数基于 168MHz 系统时钟和特定的算法推导,如需移植到其他平台,需重新计算。
n1 = speed * speed / (0.0628 * accel); // 加速段步数
n2 = speed * speed / (0.0628 * decel); // 减速段步数基于运动学方程
- 版本: v1.0
- 日期: 2025-04-10
- 作者: SimpleAstronaut
- 平台: STM32F4 + HAL 库
本项目采用 GNU General Public License v3.0 开源协议。
详见 LICENSE 文件。
#include "main.h"
#include "step_motor.h"
extern float step_count;
extern float total_steps;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM8_Init();
// 使能电机
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
while (1)
{
// 正向旋转 360 度
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
start(360.0);
// 等待运动完成
while (step_count < total_steps) {
HAL_Delay(10);
}
HAL_Delay(1000);
// 反向旋转 360 度
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
start(360.0);
while (step_count < total_steps) {
HAL_Delay(10);
}
HAL_Delay(1000);
}
}如有问题或建议,请提交 Issue 或 Pull Request。
GitHub: SimpleAstronaut/Step_motor
一个简易易用的步进电机驱动
注意:本项目遵循GPL3.0开源协议
-
简单易用,仅需调用一个函数即可
-
轻量化,不依赖其他外部库
-
负载低,空闲时不开启定时器占用资源
驱动使用定时器输出单脉冲控制步进电机PUL脉冲,DIR和ENA需要自行配置,驱动只包含PUL脉冲输出,注意使用的是高级定时器,定时器主频168MHz
在正确完整导入驱动,正确设置定时器和pwm单脉冲之后,在需要的地方调用
void start(float angle);注意angle为相对角度,默认上电时刻为角度0点,会自动计算梯形加减速
此时驱动会自行开启定时器,进入定时器中断,驱动会自动向TIM8_CH1对应IO发送脉冲
驱动计算相关参数都在step_motor.c的全局变量中,可供修改的部分如下所示
//参数设置
float accel = 0.5; //加速度
float decel = 0.5; //减速度
float speed = 20; //最高速度此外,驱动提供状态监测接口,全局变量如下所示
float step_count = 0.0f; // 当前步数
float total_steps = 0.0f; // 总步数
int status = 0; // 0停止 1加速 2匀速 3减速
int mode = 0; //三角形或梯形另外,如果需要,可以修改定时器相关配置