Arduino旋转编码器程序入门指南:从硬件连接到中断处理

1次阅读
没有评论

共计 2045 个字符,预计需要花费 6 分钟才能阅读完成。

image.webp

1. 旋转编码器基础认知

增量式编码器 就像自行车码表:通过两个相位差 90°的脉冲信号(A/ B 相)判断转动方向和步数。常见 EC11 型号每圈产生 20 个脉冲(PPR=20),这意味着每个脉冲对应 18°的机械旋转。

Arduino 旋转编码器程序入门指南:从硬件连接到中断处理

  • AB 相信号特点
  • 顺时针旋转时 A 相领先 B 相 90°
  • 逆时针时 B 相领先 A 相
  • 信号抖动通常在 5 -10ms 内(机械触点特性)

2. 硬件连接实战

使用 EC11 编码器与 Arduino Uno 的典型连接方案:

EC11 引脚 → Arduino 引脚
CLK → D2(中断 0)DT → D3(中断 1)SW → D4(按钮功能)+ → 5V
GND → GND

关键细节

  • 必须添加 10KΩ 上拉电阻(内部上拉可能不够稳定)
  • 信号线并联 0.1μF 电容可减少高频干扰
  • 长导线传输时应采用双绞线

3. 核心代码实现

3.1 中断服务程序(ISR)

#include <Arduino.h>

volatile int counter = 0;
volatile bool lastCLK;

void setup() {pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  attachInterrupt(0, encoderISR, CHANGE);
  Serial.begin(115200);
  lastCLK = digitalRead(2);
}

void encoderISR() {bool currentCLK = digitalRead(2);
  if(currentCLK != lastCLK) {
    // 状态变化时检测 DT 电平判断方向
    if(digitalRead(3) != currentCLK) {counter++;} else {counter--;}
    lastCLK = currentCLK;
  }
}

3.2 消抖处理方案对比

方法 响应速度 CPU 占用 实现复杂度
硬件 RC 滤波 简单
软件延时法 简单
状态机实现 中等

推荐的状态机消抖实现:

enum {STATE_STABLE, STATE_CHANGED, STATE_DEBOUNCE};
byte encoderState = STATE_STABLE;

void checkEncoder() {
  static unsigned long lastTime;
  bool current = digitalRead(2);

  switch(encoderState) {
    case STATE_STABLE:
      if(current != lastCLK) {encoderState = STATE_CHANGED;}
      break;

    case STATE_CHANGED:
      if(millis() - lastTime > 5) { // 5ms 消抖窗口
        encoderState = STATE_DEBOUNCE;
      }
      break;

    case STATE_DEBOUNCE:
      if(current == digitalRead(2)) { // 确认稳定状态
        // 执行计数逻辑
        encoderState = STATE_STABLE;
      }
      lastTime = millis();
      break;
  }
}

4. 进阶技巧

4.1 四倍频计数

通过检测 A / B 相的上升沿和下降沿,可将分辨率提高 4 倍:

void encoderISR() {
  static byte oldAB = 0;
  oldAB <<= 2; // 移位保留历史状态
  oldAB |= (digitalRead(2) | (digitalRead(3) << 1));

  // 状态变化表:0b00011101 → 有效脉冲模式
  if((oldAB & 0x0F) == 0x0D) counter++;
  if((oldAB & 0x0F) == 0x07) counter--;
}

4.2 定时器中断优化

对于高速旋转场景(>300RPM),建议使用 Timer1 定时采样:

#include <TimerOne.h>

void setup() {Timer1.initialize(1000); // 1ms 采样周期
  Timer1.attachInterrupt(encoderSampler);
}

void encoderSampler() {
  static byte lastState;
  byte currentState = digitalRead(2) | (digitalRead(3) << 1);

  // 格雷码状态转换检测
  if((lastState == 0 && currentState == 2) || 
     (lastState == 2 && currentState == 3) ||
     (lastState == 3 && currentState == 1) || 
     (lastState == 1 && currentState == 0)) {counter++;}
  // 反向转换检测代码略...
  lastState = currentState;
}

5. 实践练习题

  1. 速度计算功能:扩展代码使其能计算并串口输出转速(RPM)
  2. 按键复合功能:实现长按 3 秒重置计数器功能
  3. EEPROM 存储:每次重启后能恢复上次的计数值

调试建议:
– 使用逻辑分析仪观察 AB 相信号时序
– 在 ISR 内添加 digitalWrite(13, !digitalRead(13))可视化中断触发
– 通过串口绘图仪观察计数器变化曲线

正文完
 0
评论(没有评论)