ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

STM32实战:彻底搞懂红外遥控NEC协议与解码

STM32实战:彻底搞懂红外遥控NEC协议与解码 简介《史上最全的红外遥控器编码协议》是一份系统梳理红外遥控通信协议的技术文档适合嵌入式开发者、家电维修人员、万能遥控器设计者以及电子爱好者阅读可用于解决不同品牌设备之间红外编码格式识别与解码的难题。文档重点介绍了约五十种常见及冷门编码格式包括日系、欧系与索尼飞利浦等多种主流协议并逐项说明每种协议的帧结构、脉宽调制方式、载波频率、逻辑位时间长度和完整波形时序同时针对长按按键时重复发送的周期参数也给出了明确数据可直接支撑定时器解码和红外发射程序的设计。对于相似协议之间的细微差异文档也做了对照说明能有效避免编码混淆。资源包内共包含1个PDF文件大小4.96MB目录按协议编号排列便于快速定位。目前已有345人学习浏览适合在红外遥控协议选型、嵌入式解码开发、万能遥控器码库扩充等场景中作为常备参考手册。 做嵌入式这几年红外遥控器是我接触过门槛最低、水又最深的外设之一。说它门槛低是因为一个红外接收头加几行代码就能拿到按键码说它水深是因为一旦你把不同品牌的空调、电视、机顶盒遥控器凑到一起各种编码协议、载波频率、位时序差异立刻能把人绕晕。很多新手手里存着一份名为《史上最全的红外遥控器编码协议》的资料真到了用STM32去解码NEC协议的时候却连“为什么数据帧要以9ms引导码开头”都讲不清楚。这篇文章我就结合这份资料里的核心知识把红外遥控编码协议彻底拆开揉碎从协议原理到STM32实战再到排查经验一次性讲明白。这份内容适合正在做智能家居、万能遥控器、红外转发器项目的开发者也适合那些被各种协议折腾到头大的硬件爱好者。我会以NEC协议为主线顺手对比RC5和SIRC最后给出可直接落地的STM32解码方案。1. 先搞懂红外遥控的底层逻辑载波、调制与解调1.1 红外遥控到底在传输什么红外遥控的本质是用红外光脉冲传递二进制数据。遥控器上的每一个按键对应一串特定的二进制编码接收端拿到这串编码后解析出键值再执行对应操作。但这里有个物理限制红外光是不可见光容易受到环境光干扰尤其是太阳光、白炽灯里的红外分量。如果直接把数据信号变成光脉冲发出去接收端根本分不清哪个是信号、哪个是噪声。所以实际方案是先给信号加一个“载波”让数据信号去控制一个固定频率的载波是否输出接收端则只关注这个频率附近的信号把其他频率的光滤掉。绝大多数家电遥控器选择的是38kHz载波这也决定了接收头的选型——市面上最常见的VS1838B、HS0038B都是针对38kHz优化的。1.2 调制过程的生活化类比你可以把载波理解成“快递员”的专用制服数据信号则是快递包裹上的地址。街上穿同样制服的人很多但只有穿这件制服的快递员送来的包裹才是你要的。接收头就是那台“识别制服的机器”看到38kHz频率的脉冲才认为这是有效信号其他频率一律忽略。实际调制时数据为逻辑1或逻辑0不是用电压高低表示而是用“载波突发”的长度来区分。也就是说发送端在需要表示某一位时会输出一段38kHz的方波方波持续的时间长短决定了这个位是0还是1。这也是理解所有红外编码协议的关键钥匙所有时序参数都是围绕“脉冲持续时间”和“脉冲间隔时间”来定义的。2. NEC协议绕不开的第一课2.1 NEC协议的数据帧结构NEC协议是消费电子领域应用最广的红外协议之一很多国产电视、机顶盒、风扇都采用它。一份完整的NEC数据帧由引导码、地址码、地址反码、命令码、命令反码组成总共32位数据。具体结构如下引导码9ms载波脉冲 4.5ms空闲地址码8位通常表示设备地址地址反码8位用于校验命令码8位表示按键功能命令反码8位用于校验引导码的作用是告诉接收端“我要开始发数据了”相当于一句话前的“喂注意听”。后面的地址码用于区分不同设备比如同一台电视的遥控器里地址码通常固定而命令码对应不同按键。反码的存在是为了校验防止数据传输出错后电视误执行某个功能——比如把音量误识别成电源键这种低级错误在遥控场景中是绝对不能出现的。2.2 位时序0和1到底怎么区分NEC协议里逻辑0和逻辑1的区分方式是总时长而不是单纯的脉冲宽度逻辑00.56ms载波脉冲 0.56ms空闲总时长1.125ms逻辑10.56ms载波脉冲 1.69ms空闲总时长2.25ms这里有个容易混淆的细节无论是逻辑0还是逻辑1载波脉冲的宽度都是0.56ms真正不同的是后面的空闲时间长度。这意味着解码时不能只看脉冲宽度还要测量相邻脉冲之间的间隔。我第一次用逻辑分析仪抓NEC波形时就一直想不通为什么所有脉冲看起来都一样宽。后来才意识到解码的关键是测量“脉冲与脉冲之间的低电平持续时间”而不是脉冲本身。这个认知转换很重要否则后面写STM32解码程序时思路会跑偏。2.3 连发时的重复码机制按住遥控器按键不放时NEC协议并不会把完整的32位数据帧反复发送而是在第一次发送完整数据帧后每隔110ms发送一次重复码。重复码结构是9ms载波脉冲 2.25ms空闲 0.56ms载波脉冲它不携带任何数据仅仅表示“刚才那个按键继续按住”。这个机制有实际意义对于音量调节这类需要连续生效的功能接收端检测到重复码后就会持续执行上一命令同时避免了数据帧连续发送时可能因按键抖动导致的误触发。做解码的时候一定要把重复码单独处理否则按住按键时解码程序可能会把重复码误认为一条脏数据导致键值上报紊乱。3. 不只是NECRC5与SIRC协议对比3.1 RC5协议的特点RC5协议由飞利浦提出和NEC有本质差异。它采用曼彻斯特编码每一位的中间总会有电平跳变逻辑0和逻辑1由跳变方向决定。RC5的数据帧固定为14位包括2位起始位、1位翻转位、5位地址码、6位命令码。RC5没有引导码也没有重复码设计。按键连发时遥控器会重复发送完整的数据帧只是翻转位会在每次按下时翻转接收端通过翻转位变化来判断是新按键还是长按。这种方案和NEC的长按重复码思路完全不同。RC5的解码难点在于曼彻斯特编码对时序同步要求高需要考虑每一位中间的电平跳变如果采样点落在跳变沿附近很容易误判。相比之下NEC的脉冲宽度调制方式在解码容错上更友好。3.2 SIRC协议的特点SIRC协议是索尼的协议标准使用脉冲宽度调制但和NEC不同SIRC的引导码是2.4ms载波脉冲 0.6ms空闲数据位有三种长度逻辑00.6ms载波脉冲 0.6ms空闲逻辑10.6ms载波脉冲 1.2ms空闲起始位0.6ms载波脉冲 0.6ms空闲实际上SIRC的起始位是2.4ms引导码SIRC有12位、15位、20位三种帧格式分别对应不同的设备类型。解码时需要通过引导码后的数据位数量来判断是哪一种帧格式。很多做万能遥控器的人会忽略这一点导致索尼设备兼容性差。3.3 如何判断手里遥控器用哪种协议最稳妥的方法是借助逻辑分析仪抓波形测量引导码宽度和位长度然后和已知协议参数对比。比如看到9ms引导码 4.5ms空闲大概率是NEC看到2.4ms引导码可能是SIRC。如果手头没有逻辑分析仪也可以用声卡或音频输入口做简易示波器不过建议直接买个几十块钱的逻辑分析仪调试效率完全不一样。对于STM32开发者还可以写一个“协议自动识别”逻辑先测量引导码引导码在9ms附近判定为NEC在2.4ms附近判定为SIRCRC5则没有明显引导码。后面解码时再按对应协议解析。4. 基于STM32的NEC解码实操4.1 硬件接线与选型我用的是STM32F103C8T6这颗经典芯片搭配VS1838B红外接收头。VS1838B有三个引脚OUT接STM32的某个GPIOGND接地VCC接3.3V或5V。注意接收头out引脚的默认状态是高电平收到38kHz载波信号时输出低电平也就是说信号是低有效。很多新手在这里踩坑一上电发现引脚电平不对以为硬件坏了其实是没搞清楚接收头的输出极性。为了准确测量脉冲宽度我选择TIM2的输入捕获通道1GPIO配置为复用推挽输入。这里的关键是用定时器输入捕获来记录两次跳变的时间戳而不是单纯依赖外部中断计数。如果只用外部中断在中断服务函数里调用HAL_GetTick()这类函数虽然也能测时间但精确度不够而且中断服务函数里做太多事情会影响后续捕获。更好的做法是配置定时器的输入捕获功能让硬件自动记录跳变时刻。4.2 输入捕获解码思路NEC解码的核心思路是每次检测到跳变沿时记录当前定时器的计数值然后和上一次记录的计数值相减得到脉冲宽度或者空闲时长。具体做法如下配置TIM2输入捕获通道为双边沿触发开启捕获中断。在中断回调中读取CCR寄存器值计算出本次间隔时间。根据间隔时间判断当前处于什么阶段第一次间隔较长可能是引导码后续间隔对应逻辑0或逻辑1。累积满32位数据后校验地址反码和命令反码校验通过则上报键值。一个常见的边界问题如果使用单边沿触发那么相邻两次中断之间的间隔可能是脉冲宽度也可能是空闲时间需要额外判断。我建议直接使用双边沿捕获这样每次中断都是完整的一个时间间隔。4.3 核心代码实现下面给出我在项目中验证过的NEC解码关键代码片段。这个代码基于STM32标准库编写方便理解底层原理。需要注意定时器的时钟频率不同时间换算关系也不同我使用的是72MHz主频、TIM2预分频72得到1MHz计数频率即每个计数单位1微秒。#define NEC_GUIDE_PULSE_US 9000 #define NEC_GUIDE_SPACE_US 4500 #define NEC_REPEAT_SPACE_US 2250 #define NEC_BIT_PULSE_US 560 #define NEC_LOGIC_0_SPACE_US 560 #define NEC_LOGIC_1_SPACE_US 1690 volatile uint32_t last_capture 0; volatile uint32_t nec_raw_times[68]; volatile uint8_t nec_raw_index 0; volatile uint8_t nec_receiving 0; void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_CC1) ! RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_CC1); uint32_t cur TIM_GetCapture1(TIM2); uint32_t diff cur - last_capture; last_capture cur; // 超过10ms的间隔认为是空闲复位接收状态 if (diff 12000) { nec_raw_index 0; nec_receiving 0; return; } // 检测引导码9ms脉冲4.5ms空闲但这里可能拿到的是脉冲宽度或间隔宽度 // 实际处理中需要区分高电平和低电平持续时间这里简化为时间间隔窗口判断 if (!nec_receiving) { if (diff 7500 diff 10500) { nec_receiving 1; nec_raw_index 0; } return; } if (nec_raw_index 68) { nec_raw_times[nec_raw_index] diff; } if (nec_raw_index 68) { // 解析35个时间间隔1个引导码间隔 32位数据*1个间隔 结束位 // 每一位数据由一个脉冲间隔和一个空闲间隔组成实际需要68个边沿 // 这里根据业务需求调整 } } }上面代码只是一个框架实际项目中我会把“时间间隔”区分成“高电平持续时间”和“低电平持续时间”来分别处理。因为NEC的引导码由9ms高 4.5ms低组成如果不区分高低电平直接间隔判断会很难受。一个更稳的实现是以“低电平间隔”为主要依据接收头在没有信号时输出高电平收到载波时输出低电平。引导码的4.5ms空闲本质上是一个持续4.5ms的高电平而逻辑1的1.69ms空闲也是一个高电平只是长度不同。所以解码时测量高电平持续时间就能区分引导码、逻辑1、逻辑0。脉冲宽度0.56ms则是低电平时间可以作为辅助判断。4.4 编码发送的补充解码搞定了编码发送也不能忽视。STM32发送NEC信号有两种思路一种是直接用GPIO翻转产生38kHz载波另一种是用定时器的PWM输出然后在PWM使能和禁用之间切换形成脉冲发送。PWM方案更省CPU。配置定时器输出38kHz、占空比1/3的PWM信号发送数据时要发“载波脉冲”时打开PWM输出要发“空闲”时关闭PWM输出同时用另一个定时器控制每个阶段的持续时间。我做红外发射项目时就是用一个定时器生成38kHz PWM另一个定时器做阶段计时效果非常稳定。需要注意的是红外发射管驱动电流要比普通LED大通常需要三极管扩流。直接用GPIO驱动红外发射管距离往往只有十几厘米加了三极管和限流电阻后能做到七八米以上。5. 实际调试中的问题与排查记录5.1 解码数据忽对忽错最典型的症状是把遥控器靠近接收头时解码正常距离稍远就开始丢数据或出现错误键值。排查时第一个检查对象不是软件而是电源。VS1838B对电源纹波比较敏感尤其是使用电池供电或者USB供电时电机、继电器等负载产生的干扰很容易耦合到红外接收头的VCC上。处理办法是在VS1838B的VCC和GND之间加一个10uF电解电容再并联一个0.1uF陶瓷电容。这个组合能显著提升解码稳定性。另外接收头的OUT引脚到STM32的走线不要过长避免引入高频噪声。软件方面要检查定时器的计数溢出处理。我使用的是16位定时器在72MHz下预分频后计数周期是65536微秒而NEC引导码加上32位数据的总时长大约67ms接近上限。如果不在溢出中断里补偿计时就可能出现时间间隔测量错误。解决办法是让定时器工作在溢出中断里累加计数或者使用32位定时器比如TIM2在STM32F103上虽然是16位但可以通过级联或者改用更大的定时器来解决。5.2 距离短、误码率高排除了电源问题后如果距离还是上不去就要检查接收头的型号和安装位置。有些接收头中心频率不是38kHz比如36kHz或40kHz虽然也能收到38kHz信号但灵敏度会下降很多。尽量选中心频率匹配的型号。另外接收头前面如果有半透明的黑色亚克力面板要确认这个面板对红外线的透过率足够高。有些深色面板会严重衰减红外信号导致遥控距离急剧缩短。我遇到过一台设备贴了茶色装饰膜后遥控距离从8米掉到1米后来在接收头位置开了一个小孔问题立刻解决。误码率高还有一个容易忽略的原因遥控器电池电压不足。遥控器电池快没电时红外发射管发出的光强下降接收端信噪比变差解码就会出现随机错误。这时不要怀疑接收端程序先换一节新电池试试。5.3 兼容多协议的思路做万能遥控器时不能只支持NEC协议。我建议在解码流程中先做“引导码识别”把不同协议区分开特征参数NECSIRCRC5引导码脉冲宽度9ms2.4ms无引导码后空闲4.5ms0.6ms无编码方式脉冲宽度脉冲宽度曼彻斯特数据帧长度32位12/15/20位14位长按机制重复码重复帧翻转位根据引导码就可以快速圈定协议范围再按各协议的位时序解析数据。这种思路虽然会增加代码量但适用性大大提升。我在一个红外学习器项目中就是靠这种“先识别、再解析”的框架在单片机上支持了七种协议。5.4 一个容易被忽略的引脚的坑最后提一个很多人踩过的坑STM32上电时如果红外接收头的OUT引脚对应的GPIO被配置为浮空输入而接收头输出高电平这没问题但如果配置成下拉输入接收头空闲时的微弱输出可能被拉低导致一上电就产生一个假的跳变沿进入中断后接收状态错乱。建议把GPIO配置为浮空输入或带上拉的输入模式并在解码逻辑里加一个“首个信号必须是引导码长度”的判断过滤掉异常跳变。这样即使上电瞬间有一次噪声也不会影响后续正常解码。实测经验是红外解码调试时一定要准备逻辑分析仪不要凭肉眼判断。把接收头的OUT引脚同时接到逻辑分析仪上抓一段波形存下来然后对照协议时序图逐一核对基本十分钟就能定位问题。如果没有逻辑分析仪买一个几十块钱的即可对于做嵌入式的人来说这个工具是刚需。我自己调试了十几个红外相关项目最后发现90%的问题都不是代码逻辑问题而是时序测量误差、电源干扰、器件参数不匹配这些硬件细节。把这些基础问题解决好再回头看那些编码协议其实每一个都很清晰。本文还有配套的精品资源点击获取
返回列表