Arduino 平台 MCP23017 中断服务函数设计与按键检测

1. 硬件配置与中断原理
  • MCP23017 是 16 位 I/O 扩展芯片,通过 I²C 通信。
  • 中断触发:当输入引脚状态变化时,INT 引脚输出低电平,触发 Arduino 外部中断。
  • 接线示例
    • MCP23017 INT 引脚 → Arduino 外部中断引脚(如 D2)
    • 按键一端 → MCP23017 GPIO(如 GPA0)
    • 按键另一端 → GND(低电平有效,需启用内部上拉)
2. 关键寄存器配置

配置 MCP23017 寄存器(地址参考数据手册):

// 初始化配置(I²C 地址默认为 0x20)
void setupMCP() {
  // 设置 GPA0 为输入
  writeRegister(IODIRA, 0x01);  // 0x01 = GPA0 输入模式

  // 启用 GPA0 内部上拉电阻
  writeRegister(GPPUA, 0x01);   // 0x01 = GPA0 上拉

  // 配置中断:
  writeRegister(GPINTENA, 0x01); // 启用 GPA0 中断
  writeRegister(INTCONA, 0x00);  // 比较前值变化(电平变化触发)
  writeRegister(DEFVALA, 0x00);  // 默认值(非必需)
}

3. 中断服务函数设计
volatile bool keyPressed = false; // 中断标志

// Arduino 中断服务函数(简短高效)
void handleInterrupt() {
  keyPressed = true; // 仅设置标志位
}

void setup() {
  pinMode(2, INPUT_PULLUP); // INT 引脚(D2)上拉
  attachInterrupt(digitalPinToInterrupt(2), handleInterrupt, FALLING);
  setupMCP(); // 初始化 MCP23017
}

void loop() {
  if (keyPressed) {
    keyPressed = false; // 清除标志
    processKey();      // 处理按键(避免在 ISR 中执行)
  }
}

4. 按键检测与消抖处理
void processKey() {
  // 读取中断捕获寄存器(锁定中断时的状态)
  uint8_t intVal = readRegister(INTCAPA); 

  // 检查 GPA0 状态(bit 0)
  if (!(intVal & 0x01)) { // 低电平表示按下
    delay(50); // 软件消抖(50ms)
    if (readRegister(GPIOA) & 0x01 == 0) { // 再次确认
      Serial.println("按键按下");
      // 执行操作...
    }
  }
}

5. 关键注意事项
  1. 中断服务函数(ISR)优化
    • ISR 中仅设置标志位,避免复杂操作(如 I²C 通信)。
    • 使用 volatile 关键字声明全局标志。
  2. 消抖设计
    • 硬件消抖:并联 0.1μF 电容。
    • 软件消抖:两次状态检测 + 延时。
  3. 多按键扩展
    • 支持同时监控 16 个按键。
    • 通过 INTCAP 寄存器值识别具体触发引脚: $$ \text{触发引脚} = \log_2(\text{INTCAP} \oplus \text{GPIO}) $$
  4. 中断冲突处理
    • 读取 INTCAP 后中断自动清除。
    • 若多引脚中断,需读取所有值后处理。
6. 完整示例代码
#include <Wire.h>
#define MCP_ADDR 0x20

void writeRegister(uint8_t reg, uint8_t val) {
  Wire.beginTransmission(MCP_ADDR);
  Wire.write(reg);
  Wire.write(val);
  Wire.endTransmission();
}

uint8_t readRegister(uint8_t reg) {
  Wire.beginTransmission(MCP_ADDR);
  Wire.write(reg);
  Wire.endTransmission();
  Wire.requestFrom(MCP_ADDR, 1);
  return Wire.read();
}

// 其余代码参考上文...

最佳实践

  • 使用逻辑分析仪调试中断时序
  • 按键扫描周期建议 > 20ms
  • 避免在 ISR 中调用 delay()Serial.print()
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐