news 2026/10/5 5:18:35

NEC红外协议精讲:从时序原理到RK3576解码实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
NEC红外协议精讲:从时序原理到RK3576解码实战

前阵子帮朋友调一块RK3576开发板上的红外遥控,板子明明收到了红外接收头吐出来的波形,键值却怎么都对不上。折腾了一下午,最后发现不是驱动问题,而是对NEC协议的时序理解出了偏差——他把遥控器按键抬起时的重复码当成了一帧完整数据去解析。后来我把波形导出来一条一条比对,问题一下就清晰了。这种事在玩IR的人里太常见了。

红外协议里,NEC协议可以说是最普及的一种编码规则。电视、机顶盒、空调、风扇、各种小家电的遥控器,十台里至少七八台用的是它或它的变体。做嵌入式、搞单片机、做智能家居,几乎都会跟它打交道。这篇文章我尽量用大白话把NEC协议讲透,从载波、时序、帧结构到实际抓波形、写解码代码,再到RK3576这类Linux平台怎么适配红外遥控,全部过一遍。不管你是刚接触红外遥控的新手,还是被某个诡异问题卡住的老手,应该都能找到点有用的东西。

1. 认识NEC协议:满大街遥控器背后的“通用语言”

1.1 NEC协议解决了什么问题

红外遥控的本质很简单:发射端用红外LED发出光脉冲,接收端把光信号转回电信号,然后从脉冲的宽窄、长短里读出0和1。但问题在于,如果发射端只是简单地把数据位用“亮”和“灭”来表示,接收端很难区分一段很长的低电平到底是“一个0”还是“两个0”,也容易受到环境光、荧光灯等干扰源的误触发。

NEC协议做的第一件事,就是把每一位数据用固定节奏的“载波脉冲+静默间隔”来表达,而且每一位的起始都是相同的560µs载波脉冲,后面的静默长度决定这一位是0还是1。这样接收端只需要精确测量“静默”时间,就能可靠地判断每一位,不需要依赖绝对电平持续时间的测量,鲁棒性一下就上来了。

第二件事是它定义了一套完整的帧结构:引导码、地址码、地址反码、命令码、命令反码。引导码让接收端知道“下面开始发正式数据了”,地址码用来区分不同设备,命令码放按键功能,反码用于自动校验。这套结构既解决了同步问题,又解决了误码问题和多设备共存的寻址问题。可以说NEC协议把一次小巧的红外传输需要的所有要素都考虑全了。

所以你会发现,NEC协议虽然叫“协议”,但它不是一套复杂的软件栈,更像是一份约定好的“波形方言”。只要收发双方都按同一套时间规则说话,就能稳定通信。

1.2 为什么偏偏是38kHz

NEC协议最常用的载波频率是38kHz,也就是红外LED每秒闪烁38000次。你可能会问,为什么不直接用直流亮灭来传数据?因为环境里有大量红外干扰源,阳光、白炽灯、节能灯都会发出红外成分。如果用纯直流电平,接收端很难分辨哪些是遥控器的信号,哪些是背景噪声。

38kHz载波配合接收头里的带通滤波器,就能把干扰基本滤掉。一体化红外接收头(比如VS1838、TSOP38238这类)内部集成了光敏二极管、放大电路、AGC电路和解调电路。它们只对38kHz左右的调制光敏感,其他频率和直流背景光都会被抑制掉。这就像收音机调台,载波就是那个“台”,数据是台里播的内容。

38kHz的载波周期大约是26.3µs,典型占空比1/3左右,也就是每周期里高电平约8.8µs,低电平约17.5µs。选1/3占空比是功耗和发射距离的折中:占空比太低,平均光功率不够,传不远;太高,LED持续大电流发热,驱动电路压力也大。实际选LED限流电阻时,可以按平均电流来算,而不是按峰值电流,这是很多人容易忽略的点。

2. NEC协议的时序拆解:把波形当作句子来读

2.1 逻辑0和逻辑1:两个不同长度的“音节”

NEC协议里每一位数据都由一个低电平脉冲加一个高电平静默组成,但“低”和“高”要看从哪端看。发射端输出的是载波调制信号,有载波就是“发送”,无载波就是“空闲”。一体化接收头输出的是数字电平,且逻辑是反相的:收到载波时输出低电平,没收到载波时输出高电平。所以从接收头看,有载波段是低电平,静默段是高电平。后面讲时序,我都默认从接收头输出的角度来说,这也是大家用逻辑分析仪抓波形时实际看到的形态。

逻辑0的波形是“低电平560µs + 高电平560µs”,总时长约1.12ms。逻辑1的波形是“低电平560µs + 高电平1690µs”,总时长约2.25ms。也就是说,每一位的开头都有一个560µs的低电平同步头,中间那个高电平的长度是区分0和1的关键:短的是0,长的是1。

这个设计非常巧妙。接收端解码时只需要找“低电平脉冲”,然后测它后面的高电平有多长。如果高电平在1.1ms左右,判为0;如果在2.2ms左右,判为1。因为每一位都有固定的起始低脉冲,即使数据连续发送,接收端也能一个接一个地切分位,不会错位。

2.2 引导码、数据帧、重复码:一段完整“句子”的结构

一帧正常的NEC数据,开头先是一个引导码:9ms的低电平加4.5ms的高电平。9ms这个长度远远长于单个数据位,接收端一看就知道“新的一帧来了”,然后开始准备接收后续的32位数据。

引导码之后依次是:8位地址码、8位地址反码、8位命令码、8位命令反码。共32位,每一位都按低位在前(LSB first)的顺序发送。地址反码就是地址码按位取反,比如地址是0x00,反码就是0xFF;命令码是0x45,反码就是0xBA。接收端可以把收到的码和反码做一个异或校验,如果结果不是0xFF,就认为这一帧有误码,直接丢弃。这个机制的容错价值在实测中非常明显,我后来写解码驱动时基本都靠它过滤垃圾数据。

数据帧发完之后,会有一个560µs的结束低脉冲,表示这一帧说话完毕。

还有一种情况,就是按键一直按住不放。NEC协议不会用数据帧无限重发来表现“长按”,而是每大约110ms发送一次重复码。重复码的结构比较简单:9ms低电平 + 2.25ms高电平 + 560µs低电平。也就是说,它的引导码部分与正常帧几乎一样,只是那个4.5ms的高电平变成了2.25ms。接收端在已经收到过完整数据帧后,如果再遇到9ms+2.25ms的重复码,就知道还是同一个键被按着,不需要重新解析命令。

2.3 地址码和反码:设备如何避免串扰

为什么NEC协议要把8位地址扩成16位(地址码+反码)来发?直接发8位不更节省时间吗?这里藏着两层考虑。

第一层是可靠性。前面说了,反码可以让接收端做异或校验。一次电平传输受干扰、被环境光误触发,都可能导致某一位翻转。如果不校验,一个误码就可能让电视把“音量+”当成“音量-”来执行。加了反码后,命令码和命令反码必须严格互补,否则直接丢弃,大大降低误动作概率。

第二层是设备区分。虽然没有遥控器的地址码是0x00,电视可能是0x04,DVD可能是0x08,设备地址不同,就能避免一个遥控器同时控制多台设备。当然实际中很多廉价遥控器根本不区分地址码,全都用0x00或0xFF,这时候地址反码就只是个校验位。

我在实际开发中还发现一个有意思的现象:很多变种协议会把第二个字节(标准NEC中的地址反码)直接用作地址高字节,这样就有16位地址空间。遇到这种遥控器,用标准NEC解码会得到一堆奇怪的地址,对照不了键值表。所以做解码驱动时,最好把“是否是严格反码”作为一个判断条件,进入不同的解析分支,兼容性会好很多。

3. 实操:从抓波形到完成解码

3.1 硬件与测试环境准备

要真正吃透NEC协议,光看书不够,必须自己抓一次波形。需要的硬件很简单:一个遥控器、一个一体化红外接收头(38kHz)、一个逻辑分析仪(几块钱的USB逻辑分析仪就够用)、几根杜邦线。

一体化接收头一般有三个引脚:VCC、GND、OUT。OUT引脚是集电极开路或推挽输出,通常需要接一个上拉电阻到VCC,阻值4.7k到10k都可以。通电后,在没有红外信号时,OUT输出高电平;一旦检测到38kHz载波,OUT被拉低。接逻辑分析仪时,把通道夹在OUT上,地线接到GND,采样率设置为2MHz或更高,触发方式设置为下降沿触发,然后按下遥控器的一个按键,就能抓到完整的发射过程。

我建议准备两个不同品牌的遥控器,因为不同厂商对NEC时序有微小差异。有的引导码高电平可能是4.6ms而不是4.5ms,有的数据位高电平是1.7ms而不是1.69ms。多抓几组数据,心里就有底了。

3.2 用逻辑分析仪抓下一段NEC波形

连接好之后,按一下遥控器上的“音量+”键,逻辑分析仪上会看到一段明显的波形,开头是一个很宽的低电平脉冲(约9ms),接着是一个稍宽的高电平(约4.5ms),再往后是密密麻麻的一串窄脉冲,最后以一个560µs低电平收尾。

对照前面讲的时序,你可以按住“音量+”不放,这时每隔约110ms会出现一个重复码。重复码的波形和数据帧很像,但引导码后面的高电平只有2.25ms,而且后面没有再跟32位数据,直接就是560µs的结束位。这个区别在波形上肉眼很容易看出来。

如果你的逻辑分析仪软件支持协议解析(比如Saleae Logic的NEC协议解码插件),它可以自动标出每一位是0还是1。不过我更喜欢关掉自动解析,自己用光标量几个关键时间点。量完后你会发现,NEC协议的实际参数和理想参数非常接近,但也有一点小偏差,这就是为什么解码时最好用时间窗口判断而不是固定等于某个值。

3.3 单片机端的解码状态机

抓完波形,就可以在单片机上写解码程序了。我的常用思路是用一个定时器做输入捕获,测量相邻两次边沿的时间差。这里给一个简化的状态机框架,以STM32为例,假设红外接收头的OUT引脚接到某个支持输入捕获的TIM通道上,解码逻辑如下:

// 定时器捕获频率1MHz,也就是1个计数=1us // 每次捕获中断里,读取两次边沿之间的间隔delta_us // 用一个变量记录当前接收状态 #define TOLERANCE_US 300 // 时间宽容度 #define GUIDE_LOW_MIN 8000 // 引导码低电平范围 #define GUIDE_LOW_MAX 11000 #define GUIDE_HIGH_MIN 4000 // 引导码高电平范围(正常帧) #define GUIDE_HIGH_MAX 5000 #define REPEAT_HIGH_MIN 1800 // 重复码高电平范围 #define REPEAT_HIGH_MAX 2800 #define BIT_LOW_MIN 400 // 数据位低电平范围 #define BIT_LOW_MAX 700 #define BIT_ZERO_MIN 800 // 数据位高电平,短为0,长为1 #define BIT_ZERO_MAX 1300 #define BIT_ONE_MIN 1400 #define BIT_ONE_MAX 2100 static uint16_t bit_count; static uint32_t code; // 暂存32位数据 static uint8_t state; // 0=等待引导码, 1=接收数据, 2=等待结束 void TIM_IRQHandler(void) { uint32_t delta = get_capture_delta_us(); // 检测到低电平宽度在8~11ms范围内,可能是引导码 if (state == 0 && delta >= GUIDE_LOW_MIN && delta <= GUIDE_LOW_MAX) { state = 1; // 等下一个高电平宽度,判断是数据帧还是重复码 code = 0; bit_count = 0; return; } if (state == 1) { // 第一个高电平宽度:区分4.5ms数据帧和2.25ms重复码 if (bit_count == 0) { if (delta >= GUIDE_HIGH_MIN && delta <= GUIDE_HIGH_MAX) { bit_count = 1; // 正式数据帧开始 } else if (delta >= REPEAT_HIGH_MIN && delta <= REPEAT_HIGH_MAX) { state = 0; // 重复码,维持上一次按键值 repeat_event(); } return; } // 后续每一位:先遇到低电平,再遇到高电平,高电平宽度决定0/1 if (delta >= BIT_LOW_MIN && delta <= BIT_LOW_MAX) { return; // 数据位的起始低脉冲,不用处理 } if (delta >= BIT_ZERO_MIN && delta <= BIT_ZERO_MAX) { // 这一位是0,直接累加 } else if (delta >= BIT_ONE_MIN && delta <= BIT_ONE_MAX) { code |= (1UL << (bit_count - 1)); // LSB在前 } else { state = 0; // 时间不在合理范围,丢弃重来 return; } bit_count++; if (bit_count == 33) { // 32位数据 + 结束低脉冲 checksum_and_emit(code); state = 0; } } }

这段代码的核心思路就是前文说的:每一位都由一个560µs左右的低电平开头,紧接着的高电平宽度决定0或1。因为每位都从低电平开始,所以用边沿捕获seq的delta值可以很自然地切分位边界。实际工程里,建议把所有阈值都做成宏,方便按抓到的真实波形微调。

3.4 RK3576这类Linux平台怎么适配红外遥控

单片机上的解码思路搞明白后,Linux平台上适配红外遥控就顺理成章了。以RK3576开发板为例,一般有两种方案。

第一种是内核里已有红外接收驱动,把红外接收头的OUT脚接到SoC支持IR输入的GPIO或专用红外接收控制器引脚。设备树里配置好引脚复用和中断,然后在用户态用ir-keytable查看和修改键值映射表。现代内核大多用input子系统上报键值,修改键值映射可以用ir-keytable -c清空默认表,再导入自己的配置文件。

第二种方案是板子上没有现成的IR控制器,那就用一个GPIO模拟。把GPIO配置为中断输入,中断里记录电平跳变的时间戳,然后把时间序列交给内核的LIRC子系统或者用户态的lircd处理。用户态解析NEC协议后,通过input子系统上报按键事件。调试时配合evtest工具,可以看到每次按键上报的scancode和keycode。

经常有人问:“我按下了遥控器,dmesg没报错,evtest也没有任何事件,怎么办?”我一般先建议检查设备树里的GPIO号对不对,再用gpioinfo或/sys/class/gpio看看引脚能不能正常读到电平跳变。如果引脚没反应,八成是接线或者引脚复用的问题;如果引脚有反应但没上报事件,那就是解码层没识别到NEC帧,回去抓波形最直接。

4. 常见问题与排查技巧实录

4.1 波形正常但解出的码全乱?(位序和反码校验)

这是我被问得最多的问题。逻辑分析仪抓出来的波形明明和手册上很像,但解析出来的地址码、命令码就是不对。最常见的原因是位序搞反了。NEC协议是LSB first,也就是每一位字节中最低位先发送。比如命令码0x45,二进制是01000101,在波形上先看到的应该是10100010这一串的顺序。如果用MSB方式解析,出来的值自然完全不是那么回事。

第二个常见原因是把地址反码也当成有效地址用了。前面说过,标准NEC在地址码后面会跟一个地址反码,很多新手直接把32位数据按两个字节取出来用,结果看到0xFF、0x00之类的一脸懵。正确做法是先判断地址码和地址反码是否互为取反,是则只取地址码和命令码,如果不是则走扩展NEC或其他变体的解析分支。

第三个原因是把重复码和数据帧的前半段混淆。重复码的引导码是9ms低+2.25ms高,数据帧是9ms低+4.5ms高。如果代码里只判断了9ms低电平就开始收集数据,等到2.25ms高电平这个位置,就会把后续的560µs结束位当作第一位数据,导致整个帧错位。这就是我开头说的那个RK3576项目的坑。

4.2 遥控距离变近、接收失灵?(载波匹配与AGC)

如果你发现遥控器必须贴得很近才能触发,第一个要怀疑的是载波频率匹配。一体化接收头有频率选择,常见的是38kHz,但也有36kHz、40kHz、56kHz的。如果遥控器用的发射频率是40kHz,接收头是38kHz的,灵敏度会明显下降,距离大幅缩短。用示波器或逻辑分析仪看发射端的载波频率是最直接的确认方式。

第二个嫌疑是接收头的AGC(自动增益控制)在搞鬼。一体化接收头内部有AGC电路,用来抑制连续的红外干扰。如果环境光里含有较强红外成分,接收头会自动降低增益,这时候正常的遥控信号也可能被当成噪声滤掉。遇到这种情况,先遮挡环境光试试,或者把接收头前加一个滤光片。

第三个嫌疑是供电问题。接收头对电源噪声比较敏感,如果供电纹波大,输出波形就会出现抖动,影响解码。我遇到过在电机驱动的板子上红外解码不稳的情况,最后在接收头VCC和GND之间加了一个10µF电容加0.1µF陶瓷电容,问题就解决了。

4.3 按键连发、一次按出多个键?(重复码误判)

正常NEC遥控器按下按键时,先发一帧数据,然后每约110ms发一次重复码,直到按键抬起。如果你在按一次键时收到了多个不同键值,多半是解码逻辑没有正确处理重复码,导致上一帧数据的尾巴被当成了新一帧的引导码。

我的建议是:把“收到完整数据帧并校验通过”和“收到重复码”当成两个不同事件。完整数据帧到来时,更新当前键值并上报;重复码到来时,只重复上报上一次键值,不重新解析。如果重复码到来时还没有任何数据帧,要直接丢弃。这样就能避免按键重复触发混乱。

4.4 命名陷阱:IR不一定指红外(打印机能耗软故障排查参考)

排查红外问题的时候,经常会在搜索资料时被“IR”这个词误导。比如佳能imageRUNNER系列打印机的驱动安装报错信息里,会有“IR C3226”这样的型号字样,很多人一看到IR就想到了红外。这个IR其实是imageRUNNER的缩写,跟红外八竿子打不着。我在给客户调红外设备时就遇到过类似的事,对方查了一下午红外协议,结果问题只是打印机驱动安装路径不对。

所以做红外项目时,搜索关键词要尽量具体,比如“NEC协议 时序”“38kHz 红外接收头”,不要只用IR一个词。否则很容易被打印机、热像仪之类的其他含义带走。顺带提一句,FLIR的ResearchIR这类热像仪分析软件也是“IR”的另一个含义,我在调试红外发射管时偶尔会用热像仪看发射管的发热分布,来判断驱动电流是否正常。这些场景和遥控用的红外协议完全是两码事,但名字上都挂着IR,容易混淆。

4.5 通用功能码速查表

NEC协议的命令码并没有强制统一,不同厂商的设备完全可能把同一个键定义成不同码值。但公版遥控器和不少消费电子产品会沿用一套常见定义。以下是我手头一款公版NEC遥控器实测抓出来的码,仅供参考,不代表所有设备,实际以你自己抓包结果为准。

按键功能命令码(十六进制)地址码(十六进制)备注
电源0x450x00最常见的电源键码之一
菜单0x470x00不少电视、盒子沿用
音量+0x150x00部分遥控器用0x16
音量-0x090x00部分遥控器用0x19
频道+0x180x00公版常用
频道-0x080x00公版常用
确认/OK0x140x00有的设备是0x40
返回0x430x00有的设备是0x0D

如果你在做万能遥控器或者学习型遥控器,建议直接用逻辑分析仪把自己的遥控器按键全抓一遍,生成一张键值表,存成配置文件。比在网上盲目找码表靠谱得多。

5. 我的一些经验和建议

做红外遥控项目多了,我给自己的几条规定:第一,新项目用到不熟悉的红外协议时,先花半小时抓波形,不凭记忆写解码代码。NEC协议再常见,不同品牌遥控器的细微偏差也足够让你踩坑。第二,解码代码里所有时间阈值都用范围判断,不要用精确等于,因为实际硬件的时钟偏差、接收头响应延迟都会让时序有几百微秒的变化。第三,校验必须做,反码校验是白给的可靠性,不要省。

另外一个容易被忽略的小技巧是:接收头的输出引脚上拉电阻不是随便选的。上拉太小,功耗高,且输出低电平可能拉不彻底;上拉太大,边沿变缓,影响时序测量。我习惯用4.7k到10k之间的值。如果MCU的GPIO内部有上拉且外部没有其他负载,用内部上拉其实也够用。

NEC协议看起来简单,但真要在产品上跑稳定,还是有不少细节需要打磨。希望这篇文章能帮你少走一些弯路。如果你在调试中遇到其他奇怪的时序问题,别急着怀疑协议文档,把波形抓出来,一条一条量,问题往往就自己浮出来了。

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

Ponytail动效:轻量级动态进度指示器实现指南

1. 项目概述&#xff1a;从“ponytail”这个词出发&#xff0c;我们到底在聊什么&#xff1f;“ponytail”这个词最近在社交平台和内容社区里反复出现&#xff0c;但它的语义正在悄然发生偏移——它不再只是教科书里那个“马尾辫”的基础释义。我翻了近三个月的主流平台热榜、小…

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

实时数据智能:AI应用生产落地的核心分水岭

AI 应用进入生产&#xff0c;开始拼「实时数据智能」。这句话在我最近大半年参与的几个项目里&#xff0c;几乎是每天都在被验证。两年前我们聊 AI 落地&#xff0c;大家关心的是模型精度、训练数据量、排行榜名次&#xff1b;今年再聊&#xff0c;所有人开口闭口都是延迟、吞吐…

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

电磁场矢量分析入门:梯度、散度、旋度与麦克斯韦方程组推导

简介&#xff1a;这份《电磁场与电磁波矢量分析》PPT课件源自电子科技大学编写、高等教育出版社与高等教育电子音像出版社2005年出版的教材&#xff0c;面向电子信息、通信工程等专业本科生及考研复习者&#xff0c;用于夯实电磁场理论的数学基础。课件系统梳理矢量代数、三种常…

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

AI智能体构建实战:从ReAct循环到生产级并发与审计

简介&#xff1a;这份PDF报告面向AI应用开发者、技术负责人与希望深入理解智能体架构的进阶学习者&#xff0c;系统梳理了构建高效AI智能体的设计理念与工程实践。内容围绕控制权分配这一核心命题&#xff0c;对比AI工作流与AI智能体的双重范式&#xff0c;并给出何时选用工作流…

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

RAG进阶实战:从可诊断、可归因到可修复的工程化落地

1. 这不是又一个RAG入门教程&#xff1a;为什么“进阶实战”四个字必须拆开理解“RAG进阶实战”——这六个字在2024年中后期的技术内容生态里&#xff0c;已经快被刷屏到产生视觉疲劳。但真正翻完市面上90%标着“RAG实战”的文章后&#xff0c;你会发现一个尴尬的事实&#xff…

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

AI英语学习App开发实战:从语音评测到自适应路径

很多人以为把大模型的接口接进去&#xff0c;就能做出一个AI英语学习App。我早期也这么干过&#xff0c;结果用户进来玩几句就走了&#xff0c;留存惨不忍睹。后来才想明白一件事&#xff1a;AI在这里不是炫技引擎&#xff0c;而是一个能陪练、会批改、懂规划的私教助理。你如果…

作者头像 李华