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

读取Arduino红外信号时时序偏差问题排查

问题分析与解决方案

核心问题:轮询方式导致的时序延迟

你的代码采用轮询检测引脚电平的方式,这是时序偏差过大的主要原因:

  • 循环中每次读取引脚的操作存在固定开销(即使是简单位运算和判断,也会占用数个CPU周期),当红外信号的电平变化发生在两次读取之间时,程序无法立即感知,只能等到下一次循环才记录变化,直接导致测量的时间差比实际值偏小(比如你看到的430us vs 预期562us)。
  • 红外信号的关键时序(如NEC协议的562us脉冲)本身很短,轮询带来的几十微秒延迟,会严重干扰测量精度,远超过micros()的4us误差。

代码中的显性bug

  1. 变量类型不匹配:

    • initial被定义为long int,但micros()返回的是unsigned long(32位无符号整数)。当micros()溢出(从4294967295回到0)时,你的手动溢出处理会因long int的有符号特性导致计算错误(4294967295超过32位有符号长整型最大值2147483647,会触发溢出变为负数)。
    • 正确做法是将initial改为unsigned long,直接用micros() - initial计算时间差——无符号整数减法会自动处理溢出情况,无需额外判断。
  2. 无数组边界检查:

    • bitPosition没有上限判断,若100ms内电平变化次数超过arr和bitVal的数组长度,会触发数组越界,导致程序崩溃或数据混乱。

正确实现思路:改用外部中断

要精确捕获红外信号时序,必须用外部中断替代轮询:

  1. 将红外接收引脚配置为外部中断触发(选择电平变化触发,适配红外信号的调制特性)。
  2. 在中断服务函数(ISR)中记录每次电平变化的时间戳,保证电平变化被立即捕获,延迟仅为中断响应的几微秒,远小于轮询延迟。
  3. 使用unsigned long存储时间戳,彻底避免溢出问题。

示例修正代码:

const int IR_PIN = 7; // 对应PIND的第7位(B10000000)
unsigned long arr[64]; // 预设最多存储64个时序值
int bitVal[64];
int bitPosition = 0;
unsigned long lastTime = 0;

void setup() {
  pinMode(IR_PIN, INPUT);
  attachInterrupt(digitalPinToInterrupt(IR_PIN), irInterrupt, CHANGE); // 电平变化触发中断
  Serial.begin(9600);
}

void irInterrupt() {
  if (bitPosition >= 64) return; // 防止数组越界
  unsigned long currentTime = micros();
  arr[bitPosition] = currentTime - lastTime;
  bitVal[bitPosition] = digitalRead(IR_PIN);
  bitPosition++;
  lastTime = currentTime;
}

void loop() {
  // 在此处处理捕获到的时序数据,比如解析红外协议
}

其他潜在影响因素

  • 红外接收模块延迟:部分廉价红外接收头本身存在几十微秒的响应延迟,可更换品质较好的模块测试。
  • 电源噪声:不稳定的电源可能干扰红外接收模块输出,导致电平变化检测不准,建议给模块添加100nF滤波电容。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 21:55:15