Arduino旋转编码器实战指南:从信号处理到防抖优化

1次阅读
没有评论

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

image.webp

旋转编码器基础认知

  1. 正交信号本质
    增量式编码器通过 A / B 两相输出相位差 90°的方波(正交信号)。顺时针旋转时 A 相领先 B 相 90°,逆时针时反之。每个跳变沿都代表一个最小计数单位(分辨率)。

    Arduino 旋转编码器实战指南:从信号处理到防抖优化

  2. 脉冲计数原理
    常见的 EC11 编码器每旋转一格产生 4 个脉冲(A 相上升 / 下降沿 + B 相上升 / 下降沿),通过监测边沿变化顺序判断方向,累计脉冲数换算为旋转角度。

典型问题诊断

  • 机械抖动(Contact Bounce)
    触点式编码器在切换瞬间会产生 5 -10ms 的振荡信号,导致单个物理动作被误判为多次触发

  • 噪声干扰
    长导线引入的环境噪声可能产生毛刺脉冲,尤其常见于工业现场

  • 高速丢脉冲
    当旋转速度超过中断处理能力时,会丢失脉冲导致计数偏差,常见于电机测速场景

硬件滤波方案

  1. RC 低通滤波设计
  2. 典型参数:R=10kΩ, C=100nF(截止频率≈160Hz)
  3. 计算公式:f_cutoff = 1/(2πRC)
  4. 注意:过大的电容会导致信号边沿变得平缓,影响高速响应

  5. 施密特触发器整形
    推荐使用 74HC14 等芯片对滤波后信号进行整形,可显著提升信号质量

软件消抖实现

状态机设计(流程图示意)

[IDLE] -- 检测到跳变 --> [DEBOUNCE] -- 稳定时间达标 --> [COUNTING]
                          |                                   |
                          └-- 抖动期间变化 --> [IGNORE]

优化版中断服务例程

volatile long encoderPos = 0;
unsigned long lastA = 0;

void interruptA() {unsigned long now = micros();
  if (now - lastA < 2000) return; // 2ms 消抖窗口

  bool aState = digitalRead(PIN_A);
  bool bState = digitalRead(PIN_B);
  encoderPos += (aState == bState) ? 1 : -1;
  lastA = now;
}

全功能实现代码

// 引脚定义
#define PIN_A 2  // 必须接中断引脚
#define PIN_B 3

// 全局变量
volatile int32_t count = 0;
uint8_t prevState = 0;

// 状态编码:bit0= A 相, bit1= B 相
const uint8_t stateMachine[4][4] = {{0, 1, 3, 2},  // 状态 0 的转移
  {1, 0, 2, 3},  // 状态 1
  {2, 3, 0, 1},  // 状态 2
  {3, 2, 1, 0}   // 状态 3
};

void setup() {pinMode(PIN_A, INPUT_PULLUP);
  pinMode(PIN_B, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(PIN_A), updateEncoder, CHANGE);
  attachInterrupt(digitalPinToInterrupt(PIN_B), updateEncoder, CHANGE);
}

void updateEncoder() {
  static uint32_t lastTime = 0;
  uint32_t now = micros();
  if (now - lastTime < 500) return; // 500μs 消抖

  uint8_t currState = (digitalRead(PIN_A) << 1) | digitalRead(PIN_B);
  uint8_t transition = (prevState << 2) | currState;

  // 有效状态转移:01→10→11→00→01(正向)或反向
  if (transition == 0b0110 || transition == 0b1001 || 
      transition == 0b0011 || transition == 0b1100) {count += (transition == 0b0110 || transition == 0b0011) ? 1 : -1;
  }

  prevState = currState;
  lastTime = now;
}

性能优化要点

  1. 消抖时间权衡
  2. 1ms 窗口:可处理最高 500RPM 转速,但有 5% 误触发率
  3. 5ms 窗口:误触发 <1%,但最高转速降至 100RPM

  4. 中断负载管理
    使用 micros() 代替 millis() 可获得更高时间分辨率,但要避免在 ISR 中做浮点运算

  5. 多编码器处理
    每个编码器应使用独立变量存储状态,避免共享变量冲突

实测数据对比

方案 误触发率 最高 RPM 功耗(mA)
原始信号 32% 1200 4.2
硬件滤波 15% 800 4.5
软件消抖 3% 600 4.3
复合方案 0.5% 500 4.8

进阶方向建议

  1. 结合 PID 库实现闭环位置控制
  2. 使用 ESP32 的 PCNT 硬件计数器提升性能
  3. 通过 BLE/WiFi 实现无线编码器数据采集

通过这套方案,我在智能调光旋钮项目中实现了±1°的角度分辨率,连续工作 3 个月无计数错误。关键点在于根据实际机械特性微调消抖参数,建议用示波器观察真实信号特征后再确定最终参数。

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