共计 1869 个字符,预计需要花费 5 分钟才能阅读完成。
旋转编码器测速原理
旋转编码器通过光电或磁电方式,将机械转动转换为电脉冲信号。常见的有增量式编码器(A/ B 相)和绝对式编码器,我们主要讨论增量式。当电机转动时,编码器输出两路相位差 90 度的方波(正交信号),通过检测脉冲数量和方向即可计算转速。

测速需求广泛存在于:
- 直流电机闭环控制
- 工业设备转速监控
- 机器人关节位置反馈
外部中断法 vs 定时器捕获法
外部中断法
- 优点:硬件资源占用少,只需一个 IO 口 + 外部中断
- 缺点:高速时可能丢失脉冲,需要严格优化中断服务程序
定时器捕获法
- 优点:自动记录脉冲时间戳,测量更精确
- 缺点:需要特定定时器硬件支持,资源受限时难以实现
对于 51 单片机,通常推荐外部中断法 + 定时器辅助的方案。
核心实现步骤
硬件电路设计
- 上拉电阻 :编码器输出接 10kΩ 上拉电阻,避免浮空状态
- 消抖电路 :并联 0.1uF 电容 +100Ω 电阻构成低通滤波
- 信号隔离 :高速场合建议使用光耦隔离(如 HCPL2630)
// 硬件连接示例(STC89C52)#define ENCODER_A P3_2 // INT0
#define ENCODER_B P3_3 // 普通 IO
状态机脉冲计数
采用 4 倍频计数可提高分辨率:
volatile long pulse_count = 0; // 必须加 volatile
void INT0_ISR() interrupt 0 {
static unsigned char last_state = 0;
unsigned char new_state = (ENCODER_A << 1) | ENCODER_B;
// 状态转移判断
switch(last_state) {case 0: if(new_state == 2) pulse_count++; else if(new_state == 1) pulse_count--; break;
case 1: if(new_state == 0) pulse_count++; else if(new_state == 3) pulse_count--; break;
case 2: if(new_state == 3) pulse_count++; else if(new_state == 0) pulse_count--; break;
case 3: if(new_state == 1) pulse_count++; else if(new_state == 2) pulse_count--; break;
}
last_state = new_state;
}
移动平均滤波
#define FILTER_WINDOW 5
float speed_buffer[FILTER_WINDOW] = {0};
float get_filtered_speed(float new_speed) {
static int index = 0;
float sum = 0;
speed_buffer[index++] = new_speed;
if(index >= FILTER_WINDOW) index = 0;
for(int i=0; i<FILTER_WINDOW; i++) {sum += speed_buffer[i];
}
return sum / FILTER_WINDOW;
}
转速计算公式
假设编码器每转产生 N 个脉冲,采样周期为 T 秒:
转速 (RPM) = (Δpulse_count / N) × (60 / T)
性能测试数据
| 设定转速 (RPM) | 测量均值 (RPM) | 误差率 |
|---|---|---|
| 300 | 298.2 | 0.6% |
| 1000 | 992.4 | 0.76% |
| 2000 | 1973.8 | 1.31% |
中断响应时间测试方法:
- 在中断入口翻转 IO 电平
- 用示波器测量信号跳变间隔
- 实测 STC89C52 响应时间约 2.5μs
常见问题解决方案
信号干扰处理
- 使用双绞线传输信号
- 信号线长度不超过 50cm
- 添加 TVS 二极管防护
脉冲丢失优化
- 降低中断服务程序复杂度
- 改用 PCA 模块硬件计数
- 提高单片机时钟频率
电源噪声抑制
- 编码器电源与 MCU 电源隔离
- 每颗 IC 加 0.1uF 去耦电容
- 模拟地 / 数字地单点连接
拓展思考
如何实现双向测速?提示:
- 同时监测 A / B 相相位差
- 设置方向标志位
- 脉冲计数根据方向增减
// 方向判断示例
if((last_state == 0 && new_state == 2) ||
(last_state == 2 && new_state == 3) ||
(last_state == 3 && new_state == 1) ||
(last_state == 1 && new_state == 0)) {direction = FORWARD;} else {direction = BACKWARD;}
通过本文介绍的方法,我在多个项目中实现了 50-5000RPM 范围内的稳定测速,误差控制在 1.5% 以内。特别提醒新手注意:一定要用示波器观察实际波形,很多问题都是信号质量引起的。
正文完
