news 2026/10/5 5:18:35

红外遥控NEC协议全解析:从载波调制到解码实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
红外遥控NEC协议全解析:从载波调制到解码实战

按下遥控器,电视亮起来。这个动作我从小到大做了上万次,直到某天我把逻辑分析仪的探头接到红外接收头的输出脚上,按下按键,看到屏幕上跳出的一串波形,才发现自己一直以为的“红外线”根本不是一束简单的光,而是一套规则极其清晰的编码方案。

这套方案里最出名、最普及的,就是红外协议中的NEC协议。说它是红外遥控领域的“普通话”一点都不夸张——大量电视、机顶盒、空调、风扇、音箱用的都是它或它的变体。这篇文章我从物理层的载波调制讲起,一路拆到帧格式、三种变体、内核驱动适配、手写解码器,最后聊一聊我实际调试时踩过的坑。

先提个醒:很多人搜“IR”会搜出来一堆不相干的东西,比如打印机的驱动问题、热成像分析软件之类的。它们和红外遥控的IR不是一回事,别被误导。本文所有内容只围绕红外遥控里的NEC协议展开,适合嵌入式开发者、电子爱好者、智能家居玩家,以及任何想搞懂遥控器工作原理的人。

1. 红外通信的物理基础:为什么遥控器偏爱38kHz的“抖动光”

1.1 你按下的不是“一束光”,而是一串加密的闪光

红外遥控用的是波长940nm左右的红外光,人眼看不见。发射端是一个红外发光二极管(IR LED),接收端是一个光敏二极管或一体化接收头。你按下按键时,主控芯片会控制这个LED按某种规律发光、熄灭,接收头感知到光的变化后,再把光信号变回电信号,交给后级解码。

问题来了:环境光里本身就含有大量红外成分。阳光里有,白炽灯有,甚至人体本身也在辐射红外线。如果只是简单地“亮了表示1,灭了表示0”,接收端根本分不清你按的是遥控器还是太阳。生活里做个类比:你在海边朝对面的人喊话,如果周围全是海浪声,对方听不清;你得想办法让自己的声音和噪声区分开。

红外的解决办法是:让LED以固定频率快速闪烁,比如每秒钟闪38000次,也就是38kHz。发送数据时,把要表达的信息“骑”在这种高频闪烁上。接收端只识别这个特定频率的光,其他频率一概当作噪声滤掉。这样阳光、灯光这些慢变化的光源就干扰不了它了。

1.2 38kHz载波背后的抗干扰设计

为什么偏偏是38kHz而不是几十赫兹或者几百赫兹?这里有两个关键原因。

第一,一体化接收头内部有带通滤波器,中心频率就是38kHz左右。带通滤波器的作用是只放过38kHz附近的光脉冲,其他频率全部衰减。这类接收头产量巨大、价格便宜,几毛钱一颗,全行业都在用,所以发射端自然就跟着统一到38kHz。现在你能买到的VS1838B、HS0038B、TSOP38238基本都是这个频率。

第二,38kHz刚好避开了环境光的干扰频段。日光灯镇流器会产生100Hz到几十kHz的噪声,但能量集中在低频段;阳光是宽谱连续光,经过接收头内部的自动增益控制(AGC)和带通滤波后,基本不会触发误动作。当然,如果阳光直射接收头,接收距离还是会明显缩短,这个后面讲坑的时候再说。

顺便说一句,不是所有遥控器都用38kHz。很多飞利浦设备用36kHz,部分日系设备用40kHz。接收头也有对应型号,比如TSOP38236、TSOP38240。用错频率不是完全收不到,但距离会急剧下降,因为信号落在滤波器通带边缘了。

1.3 一体化接收头:把调制信号还给逻辑电平

NEC协议里的“调制”和“编码”是两个层次的东西。编码是逻辑层的事,告诉接收方“这一位是0还是1”;调制是物理层的事,决定光怎么发出来。很多初学者搞混,看到时序图里的高电平就以为接收头输出高电平,实际正好相反。

典型的一体化接收头有三个引脚:VCC、GND、OUT。OUT在空闲时输出高电平;收到38kHz红外光时,内部解调电路把载波剥掉,输出低电平。也就是说,接收头的输出是发射信号的反相版本。发射端发引导码“9ms载波+4.5ms空闲”,你从接收头OUT脚量到的就是“9ms低电平+4.5ms高电平”。这一点非常容易看反,我见过太多人因此卡了几天。

接收头输出的信号直接接单片机GPIO、逻辑分析仪或者电平转换电路都行,因为它的输出已经是标准逻辑电平了,不需要再做放大整形。这也是为什么做红外遥控接收比做射频简单那么多——物理层被一颗芯片打包好了。

项目典型值说明
载波频率38kHzNEC协议最常用,也有36/40kHz
发射波长940nm近红外,人眼不可见
接收头空闲输出高电平收到载波后输出低电平
典型接收头VS1838B / HS0038B引脚兼容,带宽略有差异

2. NEC协议帧格式:一次按键,32比特,不多不少

2.1 一帧数据的标准结构

NEC协议一帧完整数据由四部分组成:引导码(Leader Code)、地址码、地址反码、命令码、命令反码,最后还有一个结束位。这里“一帧”对应的就是“你按一次按键”所发送的完整信息。

整个帧的结构可以用下面这张表概括:

组成长度内容示例值
引导码13.5ms9ms载波 + 4.5ms空闲固定
地址码8bit设备地址,通常表示厂商或设备类型0x00
地址反码8bit地址码按位取反0xFF
命令码8bit按键功能码0x45
命令反码8bit命令码按位取反0xBA
结束位560us一个560us的载波脉冲固定

地址码在前面的叫标准NEC,地址码16位的叫扩展NEC,这个区别后面的章节单独讲。标准的8位地址加上8位反码,一共能表示256个不同设备,命令码8位能表示256种按键,对绝大多数消费电子来说完全够用。

2.2 引导码、结束位和每一位的“宽度”含义

NEC协议的时序参数非常规整,全部以560微秒为基础单位。560us这个数不是拍脑袋定的,它是38kHz载波的整数倍周期,便于发射端用定时器精确产生。

具体来说:

  • 引导码:高电平持续9ms,然后低电平持续4.5ms。9ms约等于16个560us,是所有时序里最长的,解码时用它来对齐一帧的起始位置。
  • 逻辑0:高电平560us,低电平560us,总共1.125ms。
  • 逻辑1:高电平560us,低电平1.69ms,总共2.25ms。
  • 结束位:一个560us的高电平脉冲,表示帧结束。

看出规律了吗?逻辑0和逻辑1的高电平宽度相同,都是560us,区别在于后面的低电平宽度。0的低电平是560us,1的低电平是1680us(即3个560us)。在接收头输出端看,就是低电平的持续时间不同。这种用脉冲位置来区分0和1的编码方式,术语叫脉冲位置调制(PPM)。

为什么要用位置区分而不是用脉冲宽度区分?一个很实际的好处是接收端不太容易受到LED老化、电池电压下降的影响。LED亮度变弱时,接收头输出的脉冲宽度变化不大,但幅度变化大;位置调制对幅度不敏感,抗干扰能力更强。

2.3 反码校验:为什么要发两次取反

地址码后面跟着地址反码,命令码后面跟着命令反码,四段加起来32bit。反码的作用就是校验:接收端把收到的地址码和地址反码逐位比对,如果每一位都取反相等,就认为地址部分有效;命令码同理。

比如地址码是0x00,二进制是00000000,反码就是11111111,也就是0xFF。命令码0x45(01000101)的反码是0xBA(10111010)。这种校验虽然简单原始,但它能查出绝大多数传输错误。红外遥控是单向通信,接收端不可能主动让发射端重发,所以发送方在一帧里就把校验信息带上,接收端错了就丢弃这一帧,等下一次重复码或者用户再次按键。

我发现一个很容易被忽略的细节:有些协议分析工具在界面上显示的“原始值”是反码已经换算完的十六进制,有些则直接显示反码本身。写代码做日志的时候一定要记录原始顺序,不然后面排查问题会非常痛苦。

2.4 常见的NEC协议功能码

“功能码”是命令码的具体值,它决定了一个按键对应什么操作。很多人搜“常见的nec协议功能码”其实就是想查这个表。这里有个前提必须搞清楚:NEC协议只定义了帧结构和传输规则,并没有规定0x45必须是电源键。功能码是每个厂商自己定的,但大量国产遥控器共用了一套源自某知名方案的映射,所以下面这张表在很多设备上是通用的:

功能码(十六进制)常见功能备注
0x45电源 / 待机最常用的功能码
0x46菜单 / 设置有的设备是信号源
0x47音量+部分设备是频道+
0x44音量-部分设备是频道-
0x40频道+部分设备是返回
0x43频道-部分设备是确认
0x07静音比较常见的静音码
0x0D输入/信号源常见于电视遥控器
0x16上方向菜单导航用
0x15下方向菜单导航用

注意,同一颗键在不同品牌遥控器上功能码不同,这是正常的。真正规范的做法是先抓自己手头遥控器的波形,建立一张“功能码→按键名”的映射表,而不是背一张通用表。第5章我会演示怎么从波形里把功能码读出来。

3. 标准NEC、扩展NEC与重复码:三种波形一眼分辨

3.1 标准NEC和扩展NEC到底差在哪

网上资料经常把NEC协议分成标准和扩展两种,理解起来有点绕,我用最简单的说法来区分。

标准NEC的地址部分一共16bit,格式是:地址码8bit + 地址反码8bit。也就是说,真正有效的地址只有8bit,剩下的8bit纯粹用于校验。这种方式地址空间只有256个。

扩展NEC把地址部分改成了16bit有效地址,不再发送地址反码。格式变成:地址低8bit + 地址高8bit,然后直接接命令码和命令反码。16bit地址能表示65536个不同设备,适合产品线复杂的厂商,比如某些牌子需要在一个遥控器里区分电视、机顶盒、音响等多个设备。

命令部分两种格式完全相同:命令码8bit + 命令反码8bit,所以命令空间都是256个。从校验强度上说,标准NEC的地址部分有反码校验,更可靠;扩展NEC地址部分没有校验,靠命令反码来间接发现错误,错误概率略高,但在实际应用中差别不大。

3.2 重复码:长按时的“连发信号”

按住遥控器按键不松手,你会观察到什么现象?以标准NEC为例:第一次发送一帧完整数据,然后每隔110ms左右发送一个“重复码”,直到你松手。这个重复码和时间间隔是为了解决两个问题。

第一个问题是接收端的防抖。如果按键信号只发一次,接收端可能会因为干扰而丢掉那一帧,用户就得再按一次。长按重复机制让接收端在固定周期内必然收到一个信号,体验更可靠。

第二个问题是音量调整这种需要连续响应的场景。你按住音量+想从20加到60,如果每次只发一帧就不再发,那一次长按只能加一格音量,显然不合理。重复码让接收端能持续识别“按键仍然被按住”,从而实现快速连续调整。

重复码的波形很短:9ms载波引导 + 2.25ms空闲 + 560us载波,然后进入漫长的空闲等待下一个重复码。注意它和完整帧的引导码不一样——完整帧引导码的空闲部分是4.5ms,重复码只有2.25ms。解码时可以靠这个区分是“第一次按键”还是“重复按键”。

3.3 拿到波形后怎么快速判断是哪种变体

用手里的逻辑分析仪抓一段波形,怎么判断它是标准NEC、扩展NEC还是重复码?我通常按下面这个顺序看。

先看引导码:如果低电平(接收头输出端)是4.5ms,说明这是一帧完整数据;如果是2.25ms,说明这是重复码,不用继续看了。

再数引导码后面紧跟着的32bit数据。第9位到第16位这一组(从0开始数是bit8到bit15),如果它正好是第0到第7位的逐位取反,那一定是标准NEC;如果第8到第15位是另一个任意地址的高8位,不是取反关系,那就是扩展NEC。

特征标准NEC扩展NEC重复码
引导码后空闲4.5ms4.5ms2.25ms
地址部分8bit地址 + 8bit反码16bit地址,无反码无数据
数据总量32bit32bit无
典型用途廉价遥控器、多数电视高端电视、多设备遥控所有NEC系长按场景

实际抓包中还有一种特殊情况:某些遥控器的引导码会略有偏差,比如9ms变成9.2ms,4.5ms变成4.4ms。这种偏差在接收头的容忍范围内,解码器一般按“大于3ms就认为是引导码”这样的阈值来判断,不必要求恰好等于9ms。

4. 实战:在RK3576上适配红外遥控器

4.1 硬件连接与最小电路

最近不少开发板用户在做RK3576适配IR遥控器,这步其实比想象中简单。RK3576本身没有专用的红外接收外设,但它的GPIO可以配置为中断输入,配合内核的gpio-ir-recv驱动一样能实现标准的红外遥控接收。

硬件连接上,一体化红外接收头的三个引脚分别接:

  • VCC接3.3V或5V,注意看接收头型号的供电范围。VS1838B一般支持2.7V到5.5V,直接用3.3V就行,和RK3576的GPIO电平匹配。
  • GND接开发板地线。
  • OUT接一个空闲GPIO,我习惯选带内部上拉的引脚,省得外部再挂上拉电阻。

如果用5V供电,OUT引脚输出的高电平就是5V,直接进3.3V的GPIO有风险,需要分压或者选3.3V供电的接收头。很多新手在这里烧过引脚。用3.3V供电是最省事的方案。

接收头最好尽量远离板上的高频干扰源,比如WiFi天线、DC-DC电感。红外接收头对电磁干扰不是非常敏感,但距离太近时,DC-DC的开关噪声可能让接收头输出毛刺,表现为“没按键却收到乱码”。

4.2 内核与设备树配置

RK3576跑Linux系统,适配红外遥控主要依赖内核里的RC(Remote Controller)子系统。需要确保内核开启了以下配置:

CONFIG_RC_CORE=y CONFIG_RC_DEC_NEC=y CONFIG_IR_GPIO_CIR=y

CONFIG_RC_CORE是遥控子系统的总开关,CONFIG_RC_DEC_NEC是NEC协议解码器,CONFIG_IR_GPIO_CIR是GPIO红外接收驱动。这三个缺一不可。在SDK的内核配置里一般已经默认开启,如果你用的是裁剪比较狠的发行版内核,需要自己确认一下。

设备树里新增一个节点,大概是这样:

&pinctrl { ir_int: ir-int { rockchip,pins = <0 RK_PB1 RK_FUNC_GPIO &pcfg_pull_up>; }; }; &gpio0 { ir_receiver: ir-receiver { compatible = "gpio-ir-recv"; pinctrl-names = "default"; pinctrl-0 = <&ir_int>; gpios = <&gpio0 RK_PB1 GPIO_ACTIVE_LOW>; linux,rc-map-name = "rc-anytime"; status = "okay"; }; };

compatible用的gpio-ir-recv,驱动会根据中断边沿把GPIO上的电平变化上报给RC子系统。GPIO_ACTIVE_LOW表示接收头空闲输出高电平、收到信号时拉低,对应的就是反相行为。pinctrl里配置了内部上拉,保证空闲时GPIO不会悬空。

linux,rc-map-name指定了默认按键映射表。rc-anytime是一张现成的NEC遥控器映射,如果你的遥控器功能码不在里面,之后可以用ir-keytable动态修改。

4.3 从中断到按键事件的内核解码流程

设备树配置好之后,理解一下内核里数据是怎么流动的,对排查问题帮助很大。整个链路可以概括为:

  1. 物理层:红外LED发光,接收头OUT引脚输出对应的高/低电平。
  2. 中断层:GPIO电平变化触发gpio-ir-recv驱动的中断回调。
  3. 事件层:驱动把边沿间隔转换成ir_raw_event,写入RC子系统的接收队列。
  4. 解码层:RC子系统调用已注册的协议解码器,比如nec_decode,对原始边沿脉冲序列进行匹配。
  5. 输入层:解码成功后,把scancode发给input子系统,根据键位映射表转换为Linux标准按键码,最终通过/dev/input/eventX上报给用户空间。

很多人在设备树改了节点之后发现没反应,先用evtest去看有没有input事件。如果有事件但报告的是KEY_RESERVED之类,说明映射表没对上;如果连事件都没有,就要往前查,用ir-keytable看是否收到了原始脉冲。

从调试效率上讲,建议按“原始脉冲 → 解码键值 → 按键事件”三层逐步排查。ir-keytable -t能同时看到扫描码,是定位问题最快的工具。

4.4 keymap映射与ir-keytable调试

系统起来后,先装ir-keytable工具,一般发行版包里叫v4l-utils或者ir-keytable。然后执行:

ir-keytable -p NEC -t

这条命令把接收模式设为NEC协议,并进入测试模式。按一下遥控器按键,终端会打印类似下面的信息:

29.043294: scancode 0x00ff45ba (NEC)

scancode就是这一帧的原始扫描码,0x00ff45ba里四组字节分别对应地址码、地址反码、命令码、命令反码。如果你的遥控器按键没有输出,检查接线和内核配置;如果输出了但按键在系统里没反应,那就是映射表的问题。

映射表文件在Linux上通常放在/lib/udev/rc_keymaps/,比如rockchip.toml。文件内容格式:

# table rockchip, type: NEC 0x00ff45ba KEY_POWER 0x00ff46b9 KEY_VOLUMEUP 0x00ff47b8 KEY_VOLUMEDOWN

把scancode和对应的KEY_事件写上去,然后执行:

ir-keytable -c -w /lib/udev/rc_keymaps/rockchip.toml

-c是清空旧表,-w是写入新表。写入成功后,用evtest打开/dev/input/eventX,选择对应的input设备,按按键应该能看到KEY_POWER这类标准事件。

一个很常见的坑是:ir-keytable -t显示出来的scancode可能带前后缀,不同版本的内核对scancode的编码方式不完全一样。如果你看到的是0x00ff45ba,映射表里就写0x00ff45ba,不要自己截断,保持一致即可。

5. 手写一个NEC解码器:逻辑分析仪下的详细拆解

5.1 用逻辑分析仪把遥控器的“话”录下来

内核的解码流程对很多人来说是个黑盒,想真正理解NEC协议,强烈建议自己抓一次波形。你只需要一个逻辑分析仪,把探头夹在接收头OUT引脚和GND上,然后按下遥控器按键。

我用的逻辑分析仪采样率设到1MHz就够,38kHz的载波虽然采样不到完整细节,但接收头输出的是解调后的电平信号,根本没有载波成分,所以不需要很高的采样率。协议层面的时间分辨率是560us,1MHz采样绰绰有余。

抓到的波形大概是这样的:平时一直处于高电平,按下按键后先是9ms的低电平(接收头输出端),然后4.5ms高电平,接着是一串低电平宽度不同的脉冲组合。这就是一帧完整NEC数据。对应到发射端的原始逻辑,接收头输出低=正在发载波。

5.2 从波形手工解析出一帧完整按键数据

我们拿一帧真实的遥控器数据来手工解析。假设抓到的时序如下:

  • 9ms 低电平
  • 4.5ms 高电平
  • 560us 低电平,560us 高电平(这对应一个逻辑0)
  • 560us 低电平,560us 高电平(又一个逻辑0)
  • 560us 低电平,1.69ms 高电平(这是逻辑1)
  • 后面类推...

记法其实很简单:每遇到一个“低电平560us”,看后面的高电平宽度。高电平560us记0,高电平1.69ms记1。数满32个bit,每8bit一组,转成十六进制。

比如某遥控器抓到的32bit是:

00000000 11111111 01000101 10111010

转成十六进制就是0x00 0xFF 0x45 0xBA。第一字节0x00是地址码,第二字节0xFF是地址反码,第三字节0x45是命令码,第四字节0xBA是命令反码。0x45和0xBA互为取反,校验通过,所以这帧数据有效。查功能码表,0x45在很多方案里对应电源键,这就能解释为什么按下它电视机会关机。

5.3 单片机上的状态机解码思路

理解了协议后,在单片机上实现解码就顺理成章了。最常用的方法是:把GPIO配置为双边沿中断,每次中断读取定时器计数值,然后根据状态机判定当前收到的是引导码、逻辑0、逻辑1还是重复码。

我给出一个用C语言描述的核心逻辑:

#define T_LOW_SHORT 560 // 单位us #define T_HIGH_SHORT 560 #define T_HIGH_LONG 1690 #define T_LEADER_LOW 9000 #define T_LEADER_HIGH 4500 uint32_t frame_bits; int bit_count; enum { STATE_IDLE, STATE_LEADER, STATE_DATA, STATE_REPEAT } state; void ir_edge_isr(int level, uint32_t pulse_us) { switch (state) { case STATE_IDLE: if (level == 0 && pulse_us > 8000) { // 检测到引导码低电平段 state = STATE_LEADER; } break; case STATE_LEADER: if (level == 1 && pulse_us > 3500) { // 引导码后的高电平段 bit_count = 0; frame_bits = 0; state = STATE_DATA; } else if (level == 1 && pulse_us < 3000) { // 2.25ms高电平,说明是重复码 state = STATE_REPEAT; } else { state = STATE_IDLE; } break; case STATE_DATA: if (level == 1) { // 低电平固定是560us,高电平决定了0/1 frame_bits <<= 1; if (pulse_us > 1000) { frame_bits |= 1; // 高电平1.69ms => 1 } // 高电平560us => 0,不用额外处理 bit_count++; if (bit_count >= 32) { handle_frame(frame_bits); state = STATE_IDLE; } } break; case STATE_REPEAT: // 收到重复码,可以触发长按事件,也可以忽略 state = STATE_IDLE; break; } }

状态机的关键点在于:接收头输出反相,所以“低电平”对应的是原始帧里的载波段。D层里的9ms低电平对应引导码的载波段,4.5ms高电平对应引导码后的空闲段。如果你把逻辑搞反,整个解码永远不会成功。

单片机上用定时器捕获模式会更容易,GPIO中断里只记录电平变化的时间戳,解码逻辑放在主循环里跑。这样可以避免在中断里做太多浮点运算和时间单位换算。

5.4 一个容易被忽略的细节:结束位和字节顺序

抓完整帧的时候,32bit数据收完之后还有一个560us的低电平脉冲,这就是结束位。有些资料把结束位算进数据位,有些不算。判断一个解码器写没写对,就看你有没有正确处理这个结束位,以及是否在收满32bit后立刻停止。如果你收完32bit还在等更多边沿,状态机可能会把结束位当成下一帧的引导码导致状态错乱。

字节顺序方面,NEC发送时是低位先发还是高位先发?市面上既有MSB先发的实现,也有LSB先发的实现。严格按NEC原始规范是LSB先发,但很多厂商用了MSB顺序,解码时如果不一致,你会发现命令码整体反转,比如0x45变成0xA2。遇到这种情况,别怀疑协议错了,先检查字节序。

6. 实际调红外时最容易踩的坑

6.1 按了没反应:先怀疑物理链路,别急着改代码

“遥控器按了没反应”是我遇到最多的求助。很多人的第一反应是解码配置错了,于是疯狂改设备树、换keymap,折腾半天发现根本不是软件问题。我建议遇到这种情况,按下面的顺序排查。

第一步,用手机摄像头看遥控器发射头。手机摄像头对红外光敏感,按下按键时屏幕上能看到白色或紫色的光点闪烁。看不到光点,基本可以确定是遥控器本身的问题:电池没电、电池接触片氧化、晶振虚焊、发射管压降异常。

第二步,确认接收头供电和电平。用万用表量接收头VCC和GND之间电压是否正常,再量OUT引脚空闲时是否高电平。如果OUT空闲时是低电平,大概率是接收头的型号方向接错,或者芯片已经损坏。

第三步,用ir-keytable -t看有没有原始扫描码。有扫描码说明物理链路和内核解码都正常,问题在keymap映射;没有扫描码才需要回头查设备树和GPIO配置。很多人跳过了前两步直接改代码,效率极低。

第四步,检查是不是协议不匹配。NEC协议虽然普及,但飞利浦的RC5、RC6,索尼的SIRC,以及一些日系厂商的自家协议也很常见。ir-keytable -t能显示出“无法识别”的提示,如果你发现按键按下去没有任何输出,很可能你手头的遥控器根本不是NEC协议。

6.2 重复码带来的“一次按键、两次触发”

长按处理不当会出现一个非常烦人的问题:明明只按了一次按键,系统却收到了两次甚至多次按键事件。原因是很多遥控器在按下瞬间发送完整帧之后,会在约110ms后补一个重复码。如果你的解码器把重复码也当成一次完整按键上报,就等于一次物理按键被解码成多次逻辑按键。

解决思路是在应用层做一个简单的抖动过滤:记录上一次按键的scancode和时间,如果下一次相同scancode在100ms内到达,就认为是重复码或抖动,丢弃处理。内核RC子系统的ir-keytable其实已经内置了repeat机制,但如果你是自己写解码器,一定要把重复码和完整帧区分开,不要一上来就把所有帧都当作新按键。

还有一个相关现象是“按住音量+跳两格”。这是重复码时间间隔较短造成的。如果应用层需要“长按连续响应”,应该把repeat帧转换成“持续按住”的状态,而不是每次都当作新的按键事件。

6.3 厂商魔改:NEC并不“标准”

NEC协议历史悠久,各厂商在实现时做“微调”的情况非常多。有些厂商把引导码宽度改了,比如9ms变成13.5ms;有些把数据位的时间放宽,比如低电平从560us变成600us;还有些用了完全不同的载波频率。这些魔改版本在协议上仍然能被称为“NEC系”,但如果你用标准NEC的参数去解码,可能会失败。

最典型的是空调遥控器。空调遥控器功能极其复杂——温度、模式、风速、摆风、定时——256个命令码根本不够用。很多空调厂商在NEC基础上扩展了帧格式:有的把命令码扩成16bit,有的把一帧扩成48bit甚至64bit,有的干脆用两帧组合表示一条完整指令。这也是为什么你用电视遥控器的解码库去解空调遥控器,经常解出一堆毫无意义的数据。

碰到这种遥控器,我的建议是不要死磕标准NEC解码,而是用逻辑分析仪把原始时序完整录下来,分析它的帧结构和数据位规律,然后针对性地写解码逻辑。很多开源红外库比如IRremoteESP8266就收录了大量空调厂商的编码格式,直接参考比自己从头摸索快得多。

6.4 接收头型号与电平极性

市面上常见的一体化接收头,比如VS1838B、HS0038B、TSOP38238,电气特性基本兼容,但细节差异会影响产品表现。

第一,带宽不同。VS1838B的通带比较宽,对38kHz附近的信号都敏感,好处是兼容非标遥控器,坏处是更容易被环境光和其他红外源干扰。TSOP系列通常带通特性更尖锐,抗干扰更强,但对频率偏差大的遥控器兼容性稍差。做产品选型时,如果使用环境里红外干扰多,优先考虑TSOP系列。

第二,输出极性虽然有标准,但有些廉价接收头模块会把输出反相,或者内部已经加了下拉电阻,导致空闲电平不确定。用之前一定先拿逻辑分析仪或者万用表量一下空闲电平和触发极性。

第三,供电电压影响接收距离。接收头在3.3V下工作正常,但距离可能比5V供电时缩短20%到30%。如果你的产品对遥控距离有硬性要求,供电和接收头选型要一起考虑。发射端的驱动电路影响更大——红外LED的峰值电流直接决定发射强度,用三极管或MOS管驱动、串联限流电阻把峰值电流抬高到100mA以上,距离能有明显改善。

我自己调红外接收时还有个习惯:在接收头OUT脚上并联一个100nF的电容,能滤掉一部分高频毛刺。虽然这会略微降低信号边沿的陡峭程度,但对解码成功率影响很小,对付恶劣电源环境很管用。

最后分享一个五年来一直用的调试套路:拿到一块新板子,我不会先写解码代码,而是先把逻辑分析仪挂上,按几个按键看看有没有波形、波形长什么样、是什么协议。就像接手一个新项目前先看输入输出接口,而不是一头扎进实现细节。红外这种东西,波形不会骗人,态度诚恳地面对波形,所有疑难杂症都会变得很好查。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 5:18:35

UVCCamera stopPreview崩溃:Native层SIGSEGV与生命周期修复实践

先说结论&#xff1a;如果你在用 saki4510t 的 UVCCamera 库做 USB 摄像头预览&#xff0c;遇到了 startPreview 正常、一调 stopPreview 就崩溃闪退的情况&#xff0c;九成不是 Java 代码写错&#xff0c;而是 UVC 底层释放流程和你 Activity/Surface 的生命周期在抢时间。上周…

作者头像 李华
网站建设 2026/10/5 5:17:02

Slack+Cloudflare Workers+MCP:构建主动式AI Agent实战

1. 从一条 GitHub 热榜说起&#xff1a;company-brain 到底想解决什么问题第一次在 GitHub 趋势榜上刷到company-brain这个项目时&#xff0c;我的第一反应是&#xff1a;终于有人把“AI 主动干活”这件事从演示视频里拽出来&#xff0c;塞进了团队每天真正在用的工具里。它的定…

作者头像 李华
网站建设 2026/10/5 5:16:59

AI Agent工具调用治理实战:Dogwood如何为Agent立规矩

很多做 AI Agent 平台的朋友&#xff0c;一开始都被“工具调用”这四个字坑惨了。模型今天想调天气接口&#xff0c;明天想读写数据库&#xff0c;后天可能把删除接口当成查询接口用——你根本不知道它会怎么使唤这些工具。我搭过几套内部 Agent 平台&#xff0c;最大的体会是&…

作者头像 李华
网站建设 2026/10/5 5:15:40

安卓手机变身STM32调试上位机:USB串口通讯实战指南

把安卓手机当成STM32的调试上位机&#xff0c;这事儿听起来挺折腾&#xff0c;但实际做下来会发现&#xff0c;它比想象中简单&#xff0c;而且非常实用。尤其当你做便携式设备、野外调试&#xff0c;或者不想抱着笔记本跑来跑去的时候&#xff0c;手机通过USB串口直接跟单片机…

作者头像 李华
网站建设 2026/10/5 5:15:07

树莓派离线唤醒词引擎Snowboy安装实战与调试指南

我最初在树莓派上折腾Snowboy&#xff0c;是想给一个老旧的USB麦克风找个正经用途。树莓派装Snowboy&#xff0c;说到底就是在本地跑一个“离线唤醒词检测引擎”&#xff0c;让树莓派像智能音箱一样&#xff0c;听到特定词才响应&#xff0c;而不是连续录音上传到云端。这个项目…

作者头像 李华
网站建设 2026/10/5 5:14:48

AI编码助手、智能体平台与开源模型:2026年工程落地与避坑指南

早上刷完今天的信息流&#xff0c;我发现整个AI圈的状态和三个月前已经完全不一样了。没有那种动辄刷屏的“发布即炸场”事件&#xff0c;但编码助手、智能体平台、开源模型这三条线的动态密度反而比过去任何时候都要高&#xff1a;编码助手开始真正“干活”而不仅是“补全”&a…

作者头像 李华