添加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
相关产品推荐
相关产品推荐

