3. 脉冲方向接口驱动:步进电机原理、脉冲频率与速度关系、加减速曲线
步进电机驱动,说白了就是「给脉冲,它就转」。但怎么给、给多快、怎么停,这里面门道很深。我刚开始做运动控制那会儿,以为只要定时器输出方波就行了,结果电机要么丢步,要么震动得像拖拉机。嗯,今天咱们就把这块彻底讲透。
3.1 步进电机的工作原理
步进电机本质上是一个同步电机。你给它一个脉冲,它就转一个固定的角度,这个角度叫「步距角」。常见的步距角有1.8°、0.9°,也有更小的。
为什么会这样?因为电机内部有永磁转子和多相定子绕组。我们按顺序给绕组通电,磁场就会旋转,转子跟着磁场走。每换一次相,转子就走一步。
关键参数:
- 步距角:一个脉冲对应的机械角度。1.8° 意味着 200 个脉冲转一圈。
- 细分数:驱动器通过电流控制,把一个整步细分成多个微步。比如 16 细分,200×16=3200 个脉冲转一圈。
- 保持转矩:电机静止时能抵抗外力矩的能力。
我在项目中遇到过一个问题:客户说电机低速运行时噪音大。查了半天,发现是驱动器细分数设得太低,导致低速时步进振动明显。换成 32 细分后,问题立刻解决。所以,细分数不是越大越好,但也不能太小。
3.2 脉冲频率与速度的关系
这个关系很简单,但很多人会算错。公式如下:
速度 (rpm) = (脉冲频率 × 60) / (步数每圈)
其中「步数每圈」= 360° / 步距角 × 细分数。举个例子:
- 步距角 1.8°,16 细分 → 步数每圈 = 200 × 16 = 3200
- 脉冲频率 10 kHz → 速度 = (10000 × 60) / 3200 = 187.5 rpm
反过来,如果你想要 300 rpm 的速度:
脉冲频率 = (300 × 3200) / 60 = 16 kHz
我的习惯:在代码里直接定义一个宏,把步数每圈算好。然后写一个函数,输入目标速度(rpm),输出定时器周期值。这样调试时改细分数,只需要改一个宏定义。
3.3 加减速曲线:为什么需要它?
你想想看,电机从静止直接跳到 1000 rpm,相当于你开车一脚油门踩到底。步进电机会怎么样?丢步、堵转、甚至损坏驱动器。
加减速曲线的本质,就是让速度平滑变化。常见的曲线有两种:梯形和S型。
3.3.1 梯形加减速
梯形曲线分为三个阶段:加速、匀速、减速。加速和减速阶段,速度线性变化,加速度恒定。
优点:实现简单,计算量小。缺点:加速度突变,在加速起点和终点会有冲击。低速时尤其明显。
// 梯形加减速:速度计算
// current_speed: 当前速度 (脉冲/秒)
// target_speed: 目标速度
// accel: 加速度 (脉冲/秒²)
// dt: 时间步长
if (current_speed < target_speed) {
current_speed += accel * dt;
if (current_speed > target_speed) current_speed = target_speed;
} else if (current_speed > target_speed) {
current_speed -= accel * dt;
if (current_speed < target_speed) current_speed = target_speed;
}
我曾经踩过的坑:梯形加减速在加速结束时,如果目标速度刚好是整数,没问题。但如果目标速度不是整数,累积误差会导致最终速度偏差。我的解决办法是:每次计算都用浮点数,只在最后赋值给定时器时取整。
3.3.2 S型加减速
S型曲线引入了「加加速度」(Jerk)的概念。速度变化不是线性的,而是先慢后快再慢,像一个S形。
优点:运动极其平滑,没有冲击。缺点:计算复杂,需要实时计算三角函数或查表。
实际工程中,我常用的是「分段S型」:把加速过程分成三段——加加速度段、匀加速段、减加速度段。这样既保留了S型的平滑,又可以用查表法实现。
// S型加减速:查表法实现
// 预计算一个周期的速度表
#define TABLE_SIZE 256
uint16_t speed_table[TABLE_SIZE];
void init_s_curve_table(void) {
for (int i = 0; i < TABLE_SIZE; i++) {
float t = (float)i / (TABLE_SIZE - 1);
// S型曲线:f(t) = 3t² - 2t³
float s = 3.0f * t * t - 2.0f * t * t * t;
speed_table[i] = (uint16_t)(s * MAX_SPEED);
}
}
我的建议:如果MCU资源紧张,用梯形曲线就够了。如果做高精度定位(比如3D打印机、数控机床),必须上S型。我做过一个项目,用梯形曲线时定位精度±0.1mm,换成S型后直接到±0.02mm。
3.4 PWM生成与定时器配置
脉冲方向接口,说白了就是两路信号:PUL(脉冲)和 DIR(方向)。PUL的频率决定速度,DIR的电平决定方向。
生成PWM最常用的方式是用定时器的PWM输出模式。以STM32为例:
// 定时器配置:输出PWM脉冲
void timer_pwm_init(void) {
TIM_HandleTypeDef htim;
htim.Instance = TIM2;
htim.Init.Prescaler = 72 - 1; // 72MHz / 72 = 1MHz
htim.Init.Period = 1000 - 1; // 1MHz / 1000 = 1kHz
htim.Init.CounterMode = TIM_COUNTERMODE_UP;
HAL_TIM_PWM_Init(&htim);
TIM_OC_InitTypeDef sConfigOC;
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 500; // 50% 占空比
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&htim, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim, TIM_CHANNEL_1);
}
// 动态改变频率:修改Period
void set_pulse_frequency(uint32_t freq_hz) {
// 假设定时器时钟1MHz
uint32_t period = 1000000 / freq_hz - 1;
__HAL_TIM_SET_AUTORELOAD(&htim, period);
__HAL_TIM_SET_COMPARE(&htim, TIM_CHANNEL_1, period / 2);
}
注意事项:
- 占空比一般设为50%,这样信号质量最好。
- 改变频率时,要同时更新Period和Compare,否则会出现一个异常脉冲。
- 如果频率变化频繁,建议用DMA或双缓冲机制,避免CPU频繁中断。
嗯,这里要注意:有些驱动器对脉冲宽度有要求,比如最小高电平时间2μs。如果你的定时器频率太高,脉冲太窄,驱动器可能识别不到。我一般把脉冲宽度设在5~10μs,兼容性最好。
3.5 知识体系总览
下面这张图,是我自己总结的脉冲方向接口驱动知识体系。你可以把它当作开发时的检查清单。
这张图把整个脉冲方向接口驱动拆成了三个核心模块。你开发时,先搞定「频率与速度」的换算,再实现加减速曲线,最后用定时器把PWM输出出来。每一步都有坑,但每一步也都有成熟的解法。
最后提醒一句:调试步进电机时,一定要先接好限位开关和急停。我曾经有一次调试时忘记接限位,电机直接撞到机械硬限位,把联轴器都撞断了。从那以后,我每次上电前都会检查一遍安全逻辑。
公众号:蓝海资料掘金营,微信deep3321