共计 1643 个字符,预计需要花费 5 分钟才能阅读完成。
背景与痛点
对于刚接触嵌入式开发的工程师来说,用 51 单片机驱动带编码器的直流电机往往是个不小的挑战。最常见的问题包括:

- 编码器信号干扰 :电机的电磁干扰常常导致编码器脉冲丢失或误计数
- PWM 驱动能力不足 :51 单片机的 IO 口驱动电流有限,无法直接驱动电机
- 速度控制不稳定 :简单的开环控制容易导致电机转速波动大,难以精确控制
这些问题如果不解决,轻则导致控制精度不达标,重则可能损坏硬件。下面我将分享一套经过验证的完整解决方案。
硬件设计
编码器信号调理电路
编码器输出通常是正交脉冲信号(A 相和 B 相),需要经过适当的信号调理:
- 硬件滤波 :在编码器输出端添加 RC 低通滤波(推荐 100Ω 电阻 +0.1μF 电容)
- 施密特触发器 :使用 74HC14 等芯片对信号进行整形
- 光耦隔离 :在工业环境中建议添加 PC817 等光耦隔离电路
电机驱动芯片选型
常见选项对比:
- L298N:
- 优点:价格便宜,驱动能力强(2A 持续电流)
- 缺点:发热量大,需要散热片
- TB6612:
- 优点:效率高,内置死区保护
- 缺点:最大电流较小(1.2A)
对于大多数小型直流电机,TB6612 是更好的选择。
51 单片机资源分配
以 STC89C52 为例建议配置:
- 定时器 0 :用于 PWM 生成
- 定时器 1 :用于编码器脉冲计数
- 外部中断 0 /1:用于编码器方向判断
- P1 口 :连接电机驱动芯片控制信号
软件实现
编码器脉冲计数算法
// encoder.c
volatile int encoder_count = 0;
void Encoder_Init() {
IT0 = 1; // 外部中断 0 下降沿触发
EX0 = 1; // 允许外部中断 0
EA = 1; // 开总中断
}
void EX0_ISR() interrupt 0 {if(P3^2) { // 判断方向
encoder_count++;
} else {encoder_count--;}
}
增量式 PID 控制
与位置式 PID 相比,增量式更适合 51 单片机:
// pid.c
typedef struct {
float Kp, Ki, Kd;
float last_error, prev_error;
} PID;
float PID_Calculate(PID* pid, float error) {float output = pid->Kp * (error - pid->last_error)
+ pid->Ki * error
+ pid->Kd * (error - 2*pid->last_error + pid->prev_error);
pid->prev_error = pid->last_error;
pid->last_error = error;
return output;
}
PWM 输出配置
// pwm.c
void PWM_Init() {
TMOD |= 0x01; // 定时器 0 模式 1
TH0 = 0xFF; // 设置初始值
TL0 = 0x00;
ET0 = 1; // 允许定时器 0 中断
TR0 = 1; // 启动定时器 0
EA = 1; // 开总中断
}
void Timer0_ISR() interrupt 1 {
static unsigned char pwm_count = 0;
pwm_count++;
if(pwm_count < duty_cycle) {PWM_PIN = 1;} else {PWM_PIN = 0;}
if(pwm_count >= 100) pwm_count = 0;
}
调试技巧
- 示波器使用 :
- 先确认编码器输出波形是否干净
-
检查 PWM 信号占空比是否正确
-
PID 参数整定 :
- 先调 Kp,使系统有响应但不振荡
- 再调 Ki,消除静差
-
最后调 Kd,抑制超调
-
常见故障排查 :
- 电机抖动:降低 P 增益或增加死区
- 编码器计数丢失:检查硬件滤波电路
安全考量
- 堵转保护 :
- 监测电机电流
-
超过阈值时切断电源
-
软件限速 :
- 设置最大 PWM 占空比
- 限制加速度变化率
总结与思考
通过这套方案,我们实现了 51 单片机对带编码器电机的精确控制。整个过程涵盖了从硬件设计到软件实现的完整流程,特别适合嵌入式开发新手作为入门项目。
最后留个思考题:如果想进一步实现位置闭环控制,需要考虑哪些额外因素?比如:
- 如何避免积分饱和?
- 位置精度与速度响应如何权衡?
- 是否需要加入轨迹规划算法?
欢迎在评论区分享你的想法!
正文完
