共计 1221 个字符,预计需要花费 4 分钟才能阅读完成。
背景与痛点
编码器作为常见的旋转位置传感器,在电机控制、机器人等领域应用广泛。但在 51 单片机开发中,新手常遇到以下问题:

- 机械触点抖动导致信号毛刺(通常持续 5 -10ms)
- 长线传输引入电磁干扰造成误触发
- 低速查询方式漏计高速脉冲
- 电源噪声引起计数器异常跳变
技术选型:查询 VS 中断
查询方式
- 优点:实现简单,不占用中断资源
- 缺点:CPU 占用率高,易丢失脉冲(当主循环执行时间 > 脉冲间隔时)
中断方式
- 优点:实时响应,适合高速脉冲(建议 >1kHz 时采用)
- 缺点:需要合理设置中断优先级,防止与其他中断冲突
推荐选择 :
– 低速场景(<500Hz):查询法
– 中高速场景:外部中断 + 定时器组合
硬件电路设计
必备电路
- 上拉电阻:10kΩ 上拉至 VCC,确保默认高电平
- RC 低通滤波:推荐 100Ω 电阻 +104 电容(截止频率≈1.6kHz)
- 施密特触发器:如 74HC14,可进一步整形信号
A 端 —— 100Ω ——┬—— 到单片机 IO
104
┴—— GND
软件实现核心
消抖算法(状态机实现)
- 检测下降沿触发
- 启动 10ms 延时定时器
- 定时器到期后再次检测电平
- 确认为有效跳变后执行计数
中断服务程序
void EX0_ISR() interrupt 0 {
static unsigned char lastState;
unsigned char currentState = ENCODER_A;
if((lastState == 1) && (currentState == 0)) { // 下降沿
if(ENCODER_B) counter++;
else counter--;
}
lastState = currentState;
}
完整代码示例
#include <reg52.h>
#define ENCODER_A P3_2
#define ENCODER_B P3_3
volatile long counter = 0;
void init_encoder() {
IT0 = 1; // 下降沿触发
EX0 = 1; // 允许 INT0 中断
EA = 1; // 总中断允许
}
void main() {init_encoder();
while(1) {// 主程序可通过 counter 变量获取计数值}
}
性能优化技巧
- 定时器去抖:用 TIMER0 替代 delay() 函数,避免阻塞
- 四倍频计数:同时捕获 A / B 相的上升 / 下降沿(精度 x4)
- 中断嵌套:设置编码器中断为高优先级(IP 寄存器)
常见问题解决方案
问题 1:计数方向错误
解决 :检查 A / B 相接线顺序,或交换程序中的判断逻辑
问题 2:高速时漏计数
解决 :
1. 改用 16 位自动重装定时器模式(模式 1)
2. 降低机械转速或选用更高主频单片机
问题 3:电源干扰
解决 :
1. 在编码器电源端并联 100uF+104 电容
2. 使用屏蔽线并单点接地
实践建议
- 先用示波器观察原始信号质量
- 从低速开始测试(手动旋转编码器)
- 逐步提高转速验证可靠性
扩展思考
如何改进程序实现以下功能?
1. 双向计数(正反转识别)
2. 速度计算(单位时间脉冲数)
3. 掉电保存累计值(EEPROM 存储)
(提示:可利用 B 相信号的相位差判断方向,定时中断计算速度)
正文完
