共计 2595 个字符,预计需要花费 7 分钟才能阅读完成。
认识 EC11 旋转编码器
EC11 是一种增量式旋转编码器,通过内部机械结构产生两路相位差 90°的 正交信号(CLK 和 DT)。旋转时:

- 顺时针转动:CLK 引脚先产生低电平,DT 引脚随后变化
- 逆时针转动:DT 引脚先产生低电平,CLK 引脚随后变化
编码器还带有一个 按键开关(SW),按下时会将公共端(通常接地)与 SW 引脚导通。
硬件连接方案
EC11 引脚说明:------------------
| 引脚 | 功能 |
|------|-------|
| CLK | 脉冲 A |
| DT | 脉冲 B |
| SW | 按键 |
| + | VCC |
| GND | 地线 |
------------------
推荐连接方式(需接10K 上拉电阻):
Arduino EC11
-------------------
5V —— + (VCC)
GND —— GND
D2 —— CLK (接 10K 上拉)
D3 —— DT (接 10K 上拉)
D4 —— SW (接 10K 上拉)
基础轮询实现
const int pinCLK = 2; // 接 EC11 的 CLK
const int pinDT = 3; // 接 EC11 的 DT
const int pinSW = 4; // 接 EC11 的 SW
int lastCLK = HIGH;
void setup() {pinMode(pinCLK, INPUT_PULLUP); // 启用内部上拉
pinMode(pinDT, INPUT_PULLUP);
pinMode(pinSW, INPUT_PULLUP);
Serial.begin(9600);
}
void loop() {int currentCLK = digitalRead(pinCLK);
if (currentCLK != lastCLK) {if (digitalRead(pinDT) != currentCLK) {Serial.println("顺时针旋转");
} else {Serial.println("逆时针旋转");
}
lastCLK = currentCLK;
}
if (digitalRead(pinSW) == LOW) {delay(50); // 简单防抖
if (digitalRead(pinSW) == LOW) {Serial.println("按键按下");
while(digitalRead(pinSW) == LOW); // 等待释放
}
}
}
高级优化方案
中断驱动实现
volatile int encoderPos = 0;
void setup() {attachInterrupt(digitalPinToInterrupt(pinCLK), encoderISR, CHANGE);
}
void encoderISR() {if (digitalRead(pinCLK) == digitalRead(pinDT)) {encoderPos++;} else {encoderPos--;}
}
状态机消抖算法
enum EncoderState {IDLE, CLK_LOW, DT_LOW};
EncoderState state = IDLE;
void checkEncoder() {switch(state) {
case IDLE:
if (digitalRead(pinCLK) == LOW) state = CLK_LOW;
break;
case CLK_LOW:
if (digitalRead(pinDT) == LOW) {
state = DT_LOW;
// 确认旋转方向
} else if (digitalRead(pinCLK) == HIGH) {state = IDLE;}
break;
case DT_LOW:
// 等待信号恢复
if (digitalRead(pinCLK) == HIGH && digitalRead(pinDT) == HIGH) {state = IDLE;}
break;
}
}
常见问题解决
- 机械抖动问题
- 硬件方案:在 CLK/DT 引脚对地接0.1μF 电容
-
软件方案:采用状态机或延时确认
-
中断冲突预防
- 避免在中断服务程序中执行耗时操作
-
使用
volatile修饰共享变量 -
长线传输建议
- 使用双绞线
- 信号线串接 100Ω 电阻
完整示例代码
#include <Arduino.h>
// 引脚定义
#define ENC_CLK 2
#define ENC_DT 3
#define ENC_SW 4
// 全局变量
volatile int encoderPos = 0;
int lastPos = 0;
void encoderISR() {
static unsigned long lastInterrupt = 0;
unsigned long now = millis();
// 消抖时间间隔(ms)
if (now - lastInterrupt > 5) {if (digitalRead(ENC_CLK) == digitalRead(ENC_DT)) {encoderPos++;} else {encoderPos--;}
}
lastInterrupt = now;
}
void setup() {
// 引脚初始化
pinMode(ENC_CLK, INPUT_PULLUP);
pinMode(ENC_DT, INPUT_PULLUP);
pinMode(ENC_SW, INPUT_PULLUP);
// 中断设置
attachInterrupt(digitalPinToInterrupt(ENC_CLK), encoderISR, CHANGE);
Serial.begin(115200);
}
void loop() {
// 检测位置变化
if (encoderPos != lastPos) {Serial.print("Position:");
Serial.println(encoderPos);
lastPos = encoderPos;
}
// 检测按键
if (digitalRead(ENC_SW) == LOW) {delay(50);
if (digitalRead(ENC_SW) == LOW) {Serial.println("Button pressed!");
while(digitalRead(ENC_SW) == LOW);
}
}
}
性能对比测试
| 检测方式 | CPU 占用率 | 响应延迟 |
|---|---|---|
| 轮询 | ~15% | 1-2ms |
| 中断 | <1% | <50μs |
实测建议:
– 简单项目可用轮询
– 需要快速响应或多任务时推荐中断
经验总结
经过实际项目验证,EC11 编码器的稳定性主要取决于三点:合理的硬件滤波、有效的软件消抖、以及正确的中断处理。建议初次使用时先用示波器观察信号质量,再逐步添加防抖措施。对于需要精确计数的场景,可以结合光电编码器使用,但 EC11 在大多数旋钮交互场合已经足够可靠。
正文完
