You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

添加attachInterrupt后NRF24L01+停止工作的Arduino问题

问题解决:添加编码器中断后nRF24L01通信代码无法执行

问题描述

移除attachInterrupt(digitalPinToInterrupt(encoder1),readEncoder,RISING);后代码正常运行,添加该行后radio.available()之后的代码完全无法执行,经反复验证确认是该行导致的问题。

问题根源分析

  • 共享变量未加volatile修饰:全局变量counter在中断和主循环中被读写,编译器可能对其进行优化,导致主循环读取到错误值,破坏程序逻辑。
  • 中断触发过于频繁:编码器信号抖动或不合理的触发方式(仅用RISING)会导致中断频繁触发,占据大量CPU时间,使得主循环中处理nRF24L01的代码得不到执行机会。
  • 无原子操作访问共享变量:主循环中读取和修改counter时,可能被中断打断,导致数据混乱。

解决方案

1. 给共享变量添加volatile修饰

将int counter = 0;修改为volatile int counter = 0;,强制编译器每次都从内存读取变量最新值,避免优化导致的错误。

2. 添加中断去抖逻辑

在中断函数中加入时间判断,忽略短时间内的重复触发,减少无效中断占用CPU。

3. 原子操作访问共享变量

在主循环中读取counter时,先关闭中断,完成读取和重置后再开启中断,避免被中断打断导致数据错误。

4. 优化编码器引脚配置

使用INPUT_PULLUP模式配置编码器引脚,减少外部信号抖动,同时无需额外上拉电阻。

5. 移除主循环中的重复调用

主循环中的readEncoder()调用属于重复处理,应移除,避免重复计数。

修改后的完整代码

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

RF24 radio(7, 8); // CE, CSN

const byte address[6] = "00001";

struct InputData  // 定义数据结构
{
   int x;
   int y;
};

InputData data;

// 电机A引脚定义
int motor_enA = 9;
int motor_in1 = 10;
int motor_in2 = 6;

// 编码器引脚
int encoder1 = 2;
int encoder2 = 3;

// 共享变量添加volatile修饰,确保中断与主循环间数据同步
volatile int counter = 0;
int angle = 0;

void setup() {
  Serial.begin(9600);
  radio.begin();
  radio.openReadingPipe(1, address);
  radio.setPALevel(RF24_PA_MIN);
  radio.startListening();

  // 设置电机控制引脚为输出
  pinMode(motor_enA, OUTPUT);
  pinMode(motor_in1, OUTPUT);
  pinMode(motor_in2, OUTPUT);

  // 初始状态关闭电机
  digitalWrite(motor_in1, LOW);
  digitalWrite(motor_in2, LOW);
  analogWrite(motor_enA, 255);

  // 使用内部上拉电阻,减少信号抖动
  pinMode(encoder1, INPUT_PULLUP);
  pinMode(encoder2, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(encoder1), readEncoder, RISING);
}

void loop() {
  // 原子操作读取并重置counter,避免被中断打断
  int tempCounter = 0;
  noInterrupts();
  tempCounter = counter;
  counter = 0;
  interrupts();

  // 更新角度值
  if(tempCounter > 1){
    angle += 2;
  }else if(tempCounter < -1){
    angle -= 2;
  }

  // 处理nRF24L01接收的数据
  if (radio.available()) {
    radio.read(&data, sizeof(data));
    // Serial.println(data.y);
    if (data.y > 5) {
      digitalWrite(motor_in1, HIGH);
      digitalWrite(motor_in2, LOW);
    }
    else if (data.y < -5) {
      digitalWrite(motor_in1, LOW);
      digitalWrite(motor_in2, HIGH);
    }
    else {
      digitalWrite(motor_in1, LOW);
      digitalWrite(motor_in2, LOW);
    }
  }

  Serial.print("Position: ");
  Serial.println(angle);
}

void readEncoder()
{
  // 去抖逻辑:忽略1ms内的重复触发
  static unsigned long lastInterruptTime = 0;
  unsigned long interruptTime = millis();
  if (interruptTime - lastInterruptTime > 1) {
    if(digitalRead(encoder1) == HIGH){
      int b = digitalRead(encoder2);
      // 根据编码器类型调整逻辑,这里适配INPUT_PULLUP模式
      if(b == HIGH){
        counter++;
      }
      else{
        counter--;
      }
    }
    lastInterruptTime = interruptTime;
  }
}

内容的提问来源于stack exchange,提问作者Gunvir Dhesi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 10:37:00