从NEC协议到格力定制码:基于STM32的智能红外学习与重放系统设计
1. 红外遥控基础与NEC协议解析
第一次拆开遥控器时,你可能注意到那个小小的红外发射管。这种看不见的光线背后,其实藏着精密的通信协议。市面上80%的家电遥控器采用NEC协议,就像电子设备间的摩斯密码。我用STM32做红外解码时,发现这个协议最妙的设计在于它的脉冲距离调制(PPM)——用不同长度的高电平间隔区分0和1。
具体来说,当示波器捕捉到560μs低电平加560μs高电平的组合,这就是二进制的0;而560μs低电平接1.68ms高电平,则代表1。这种设计比单纯的高低电平更抗干扰,实测在5米距离内误码率几乎为零。完整的NEC帧就像一封信:引导码是信封(9ms低电平+4.5ms高电平),地址码是收件人(16位),命令码是信件内容(16位)。有趣的是,协议还贴心地附上反码用于校验,就像重要文件的正副本对照。
在STM32上实现时,定时器的输入捕获功能是关键。我通常将定时器时钟配置为1MHz(1μs分辨率),这样能精确测量脉冲宽度。遇到按键长按时,协议会发送简化的重复码(9ms+2.25ms+560μs),这个细节很多开源库都没处理,导致长按失灵。后来我在代码里加了状态机才解决,具体实现我们后面会展开。
2. 格力空调的长码玄机
当我把NEC解码程序用在格力空调遥控器上时,示波器显示的波形让我愣住了——根本不是标准的NEC格式!格力采用了一种被称为"长码"的私有协议,数据量是NEC的4倍多。经过两周的逆向工程,我终于摸清了规律:它的帧结构像三明治,起始码(9000μs+4500μs)作面包,中间夹着35位数据层,连接码(560μs+20000μs)当酱料,最后再铺32位数据层。
最令人头疼的是校验码部分。网上流传的校验算法在我测试的YDK-26/YLK-13等型号上全部失效。后来发现格力不同系列空调的校验策略完全不同,甚至同系列不同批次的遥控器都有差异。我的解决方案是建立"学习-比对"数据库:先记录开机、调温等基础指令的校验码,再根据相邻按键的码值变化规律推导。例如温度+键的校验码往往是温度-键的码值加3,这个经验值在80%的机型上适用。
数据段解析也有门道。前35位包含设备类型标识(第3-8位)、工作模式(第15-18位),而连接码后的32位藏着温度值(第5-12位)和风速(第21-24位)。有意思的是,部分高端机型会用第28位作为"强力模式"标志位,这个发现在后期做智能场景联动时特别有用。
3. STM32的硬件设计实战
红外系统的硬件设计就像搭积木,但每个模块都有坑等着你跳。接收端我推荐VS1838B红外接收头,价格不到1元但灵敏度极高。注意一定要在VCC并联47μF电容,否则STM32的电源噪声会导致误触发——这是我烧坏三个接收头才换来的教训。
发射电路更有讲究。普通8050三极管驱动红外LED的方案在3米外就衰减严重,后来改用MOSFET(如IRLML2502)搭配两个LED串联,传输距离立刻提升到8米。关键参数是脉冲电流要控制在100mA左右,占空比不超过1/4,否则LED寿命会急剧缩短。PCB布局时,红外发射管要远离晶振和SWD接口,我的第一版设计就因为干扰导致载波频率漂移到37.6kHz,空调完全没反应。
STM32的定时器配置是核心技能。TIM2用于输入捕获时,建议配置为从模式+触发输入,这样能自动复位计数器避免溢出。PWM输出用TIM3_CH1产生38kHz载波,记得开启预装载使能,否则修改占空比会有毛刺。分享一个调试技巧:用杜邦线把PWM输出接到示波器探头,同时用逻辑分析仪抓取解码后的数据,两个窗口并排对比能快速定位问题。
4. 从信号捕获到精准重放的软件实现
软件架构我采用分层设计:底层是硬件抽象层(HAL驱动),中间层协议解析,上层是业务逻辑。在Keil工程里创建三个关键文件:ir_rx.c负责信号捕获,ir_tx.c处理发射逻辑,ir_database.c管理码库。这种结构后期扩展其他协议特别方便,比如后来添加的RC5协议只花了2小时就集成完成。
信号捕获的难点在去抖动处理。原始波形会有约50μs的抖动,直接解码肯定出错。我的解决方案是双缓冲机制:输入捕获中断只记录边沿时间,主循环里用状态机过滤异常脉冲。对于格力长码,还要特别处理20000μs的连接码——普通定时器会溢出,需要开启定时器溢出中断并维护全局时间戳。
重放精度决定用户体验。测试发现空调对脉冲宽度误差容忍度是±50μs,但载波频率必须严格在38±0.5kHz。我的优化方案是:PWM使用72MHz时钟源,分频值设为1,自动重装载值设为1894,这样实际频率是72MHz/(1894+1)=38.005kHz。发射时先用DMA预装载波形数据,再触发定时器突发模式,这样即使主程序被中断打断也不会影响时序。
5. 构建通用红外码库的架构设计
当系统需要支持多个品牌时,码库设计就成为关键。我参考了SQLite的架构,设计出三级存储结构:FLASH最底层存储原始波形时间数据(按μs数组存储),中间层是协议特征参数(如NEC的引导码时长),最上层是业务逻辑映射表(如"格力-制冷-26℃"对应的码值)。
在STM32F103的64KB FLASH上,采用滑动窗口压缩算法后,能存储约200条格力长码或500条NEC码。对于不常用的码值,可以设计LRU缓存机制,通过上位机动态加载。特别提醒:FLASH写入前务必先擦除整个扇区,我的第一个版本因为没做擦除,导致码库随机出错,后来加入CRC校验才彻底解决。
上位机通信协议建议用JSON格式,虽然解析会多占2KB RAM,但扩展性极好。例如添加美的空调支持时,只需在上位机发送:
{ "brand": "Gree", "model": "YDK-26", "command": "power_toggle", "raw_data": [9000,4500,560,560,1680,...] }这种设计让后期维护工作量减少了70%。
6. 真实场景下的调试经验
实验室成功的系统,到现场可能完全失灵。有一次客户投诉系统无法控制客厅的格力空调,现场检测发现是吊顶射灯干扰。解决方案是在接收头前加装850nm窄带滤光片,成本增加3元但完美解决问题。另一个常见问题是日光干扰,我在固件里加入了动态阈值调整算法:持续监测环境光强度,自动调节接收灵敏度。
功耗优化也很重要。最初版本待机电流有8mA,后来通过三项改进降到200μA:1) 接收头改用3.3V供电;2) 红外LED串联10Ω电阻限流;3) 采用事件驱动架构,空闲时STM32进入STOP模式。现在用2000mAh锂电池能续航半年,比某些原装遥控器还持久。
最意外的发现是关于格力空调的"隐藏指令"。通过分析二十多款遥控器,我整理出一套扩展功能码表,比如同时按住"模式"和"风速"键5秒会进入工程模式,这个指令在长码中表现为第17位和第23位同时置1。这些秘籍后来成为我们产品的差异化卖点。
