ARTICLE DETAIL

资讯详情

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

STM32万能红外遥控器DIY:红外学习、NEC协议解析与码库存储实战

STM32万能红外遥控器DIY:红外学习、NEC协议解析与码库存储实战 简介这是一份基于STM32设计的万能红外遥控器完整项目文档面向嵌入式开发者、STM32学习者和智能家居DIY玩家针对传统家电遥控器兼容性差、无法统一控制等问题给出了一套硬件加软件的系统级解决方案。文档详细介绍了项目开发背景、设计思路、系统框架图、原理图与实物图并对ESP8266 Wi-Fi模块、红外线学习模块、矩阵键盘模块、语音识别模块等关键硬件做了深入讲解。同时覆盖Qt上位机开发、STM32主循环程序设计、语音识别固件定制与烧录流程内容从硬件选型到代码实现环环相扣读者可据此独立复现一套支持红外控制、红外学习、语音命令、无线远程控制及LCD状态显示的多功能遥控器。资源为单个PDF文件大小约46.43MB目录结构完整目前已有79人学习下载适合需要系统了解红外遥控与智能家居项目的读者参考。1. 项目概述与需求拆解1.1 为什么需要万能红外遥控器先说说做这个项目的动机。我家里有个抽屉专门用来堆放各种遥控器空调的、电视的、机顶盒的、风扇的还有不知道哪台设备留下的。每次想换个设备操作都得在一堆遥控器里翻找实在是耽误事。其实市面上早就有万能遥控器卖但问题在于一是便宜的型号学习能力很弱很多新协议的设备根本匹配不上二是无法自定义按键逻辑用起来别扭。既然一直在玩STM32干脆自己动手做一个把红外发射、接收、学习、存储全部集成在一起彻底告别遥控器堆积的问题。这个项目解决的核心痛点很明确用一套硬件兼容市面上绝大多数红外遥控协议。STM32在这件事上的优势远不是普通51单片机或者Arduino能比的——定时器精度高、中断响应快、Flash容量足以存储多组码库关键是开发资源和调试工具非常成熟。我选的是STM32F103C8T6也就是大家常说的蓝板最小系统板某宝十几块钱一片对DIY项目来说性能完全够用。1.2 系统整体架构与模块划分整个项目的功能可以概括为两个核心模式学习模式和发射模式。学习模式下设备通过红外接收头采集目标遥控器的信号解析出脉冲时序后存入Flash发射模式下用户按键选择对应的码型STM32驱动红外发射管复现该信号完成对家电的控制。从硬件结构上看系统可以拆成五个部分模块核心器件作用主控STM32F103C8T6时序捕获、PWM生成、存储管理红外接收VS1838B一体化接收头学习模式下采集遥控器信号红外发射红外发射管 NPN三极管驱动发射模式下输出载波信号人机交互4x4矩阵键盘 OLED显示屏按键选择功能、显示当前状态存储STM32内部Flash或外接24C02保存学习到的红外码型可能有人会问为什么不用现成的红外学习模块比如那种集成了学习发射的一体化模块外围电路几乎不用自己画。我的回答是自己做才能理解红外通信的底层机制而且后期想加功能比如手机蓝牙配码、语音控制时自定义方案的扩展空间是模块化方案给不了的。另外自己掌控接收和发射电路之后遇到信号弱、误码率高的问题也有动手优化的余地。2. 硬件设计思路与电路细节2.1 红外通信原理38kHz载波与脉冲编码在做硬件之前必须先把红外通信的基本原理交代清楚。红外遥控的本质是用红外光脉冲承载控制信息但并不是简单地亮一下表示1、暗一下表示0。为了提高抗干扰能力发射端会把要传输的数据信号调制在一个固定频率的载波上最常用的载波频率是38kHz。打个比方来解释载波相当于快递的货车数据信号是车上的货货车本身没有意义但货物必须装在车上才能安全运输。38kHz的载波让接收端可以过滤掉环境光中大量无规律的杂散红外信号只响应这个频率附近的有效信号。这就是为什么你在阳光很强的窗边遥控电视偶尔会失灵——阳光里含有的红外成分干扰了接收端对有效信号的判别。接收端用的是VS1838B这类一体化红外接收头它的内部集成了光敏二极管、放大电路、解调电路和输出驱动电路。在无信号时输出高电平收到38kHz载波信号时输出低电平。也就是说接收头输出的是已经去掉载波、还原出来的数字电平信号这一下就把问题简化成了传统的电平时序测量——只需要测高、低电平各自持续的时间就能反推出遥控器发出的数据。2.2 红外发射电路设计与驱动参数计算发射电路比接收电路简单但有一个容易踩坑的地方红外发射管的驱动电流不能直接接在GPIO上拉。STM32的GPIO典型输出电流在20mA左右而红外发射管要达到有效遥控距离瞬时脉冲电流往往需要50mA到100mA。拿GPIO直接驱动要么距离很近一两米就失灵要么长期使用影响单片机端口寿命。这里我用的方案是GPIO控制一个NPN三极管的基极通过集电极串联红外发射管到3.3V电源。三极管选的是最常见的S8050基极串联1kΩ限流电阻集电极串联10Ω电阻限制发射管电流。实测在3.3V供电下发射管峰值电流能到100mA左右遥控距离大约8-10米家用环境下绰绰有余。注意红外发射管是有极性的长引脚为正极接集电极短引脚为负极接地。装反了不会烧管子但永远不会发光容易白白排查半天。接收头部分的电路相对简单VS1838B有三只脚从左到右分别是OUT、GND、VCCOUT接STM32的一个GPIO我用的PA6配合TIM3的输入捕获功能VCC接3.3VGND接地。需要注意的是VS1838B的供电要加一个10μF左右的电解电容做滤波不然在开关电源供电时容易产生误触发。这个细节我是实际掉进坑里才发现的——一开始没加电容LED灯一开关接收端就莫名其妙收到一堆乱码。3. 红外编码协议详解如何听懂不同遥控器3.1 NEC协议最普及的红外编码格式在拆解协议之前先了解一下市面上最主流的编码格式。NEC协议是日系厂商NEC、松下等电视遥控器最早采用的格式如今几乎成为红外遥控的事实标准。它的时序特征特别明显非常适合作为第一个学习和解析的对象。NEC协议的一帧数据结构是这样的先是9ms的引导码AGC脉冲然后是4.5ms的间隔低电平紧接着是8位地址码 8位地址反码 8位命令码 8位命令反码。最后一个反码字段是用来做校验的接收端解码后把地址码和地址反码相加如果不是0xFF就判定为传输错误。0和1的区分不在于脉冲宽度本身而在于脉冲之后低电平的持续时间。逻辑0由560μs的载波脉冲560μs的低电平组成周期1.12ms逻辑1由560μs的载波脉冲1.69ms的低电平组成周期2.25ms。相比占空比调制的方式这种脉宽调制在抗噪声方面更稳定协议实现也简单。3.2 常见协议对比与通用解码策略NEC再普及也架不住其他厂商另起炉灶。索尼用SIRC协议12位或15位短帧格式飞利浦用RC5协议曼彻斯特编码还有大量国产空调品牌使用各家私有的格式甚至同一品牌不同型号的空调协议都可能不同。如果给每类协议都写一套解析代码工作量会非常庞大而且永远追不完。所以我在实际工程里换了一个思路不区分协议全部按高低电平的时长序列来存储和复现。原理其实很简单无论哪种协议本质都是一串高电平和低电平交替出现的脉冲高电平是38kHz载波低电平是静默各种协议的区别只是高/低电平持续时间不同。既然是万能遥控器最通用的做法就是如实记录原样复现——我需要关心的只是每一段高/低电平持续了多长时间而不用理解这些时间代表什么业务含义。这就引出软件实现的两个核心任务一是测量接收到的波形中每段电平的持续时间二是按测得的时长序列反向驱动发射管生成相同的波形。后文我会详细展开这两部分的实现。4. 软件实现从录码到发射的完整流程4.1 发射模块PWM与定时器的配合红外发射的核心有两点一是38kHz载波的产生二是让载波在特定时间段出现或停止也就是脉冲调制的过程。STM32的定时器PWM输出可以一次性解决这两个问题。我用的是TIM2的CH1PA0配置成PWM模式输出频率38kHz占空比1/3。你没看错不是常规的50%——红外发射管在脉冲瞬间电流很大占空比太高不仅费电还容易让管子过热。1/3占空比在保证有效辐射强度的前提下能降低管子的平均功耗。实测下来距离和3/4占空比没什么差别但管子长时间工作不发烫。载波有了之后怎么控制它断续呢我的做法是GPIO控制三极管通断的思路调整一下直接利用PWM输出通道的使能开关。需要用载波时打开TIM2的PWM输出不需要时关闭。关闭状态下引脚输出低电平三极管截止发射管不发光。具体的发射时序用定时器中断来控制。我把要发送的电平序列放在一个数组里数组元素是微秒级的延时值奇数位代表高电平载波持续时间偶数位代表低电平静默持续时间。发送时启动定时器在中断服务函数里切换GPIO状态并更新下一个延时值直到数组遍历完毕。// 红外发射电平序列结构体定义 typedef struct { uint16_t LevelTime[128]; // 每段电平的持续时间单位us uint8_t Len; // 有效段数 } IR_Packet; // 发送一帧红外信号 void IR_SendPacket(IR_Packet *pkt) { IR_Index 0; IR_State 1; // 先输出高电平 HAL_GPIO_WritePin(IR_TX_GPIO_Port, IR_TX_Pin, GPIO_PIN_SET); HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_1); // 开启38kHz载波 TIM3-ARR pkt-LevelTime[0]; // 设置第一段持续时长 __HAL_TIM_SET_COUNTER(htim3, 0); HAL_TIM_Base_Start_IT(htim3); // 启动定时中断 }4.2 学习模块输入捕获与脉宽测量学习模式是整个项目最有技术含量的部分——因为解码的准确性直接取决于对脉宽的测量是否精准。这里我用的是TIM3的输入捕获功能把VS1838B的输出接到PA6上。输入捕获的思路是这样的定时器在自由计数每当引脚发生电平跳变上升沿或下降沿硬件自动把当前计数值锁存到捕获寄存器里同时触发中断。我在中断里读取两次捕获值的差值再换算成时间就得到了一段电平的持续时间。这种方式不需要CPU轮询等待引脚变化精度能达到微秒级是STM32处理此类任务的推荐做法。void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM3) { uint32_t now HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); uint32_t diff (now IR_LastCount) ? (now - IR_LastCount) : (0xFFFF - IR_LastCount now); IR_LastCount now; uint32_t duration_us diff * 1000000 / 72000000; // 72MHz时钟换算 if (IR_LearnIndex MAX_LEVELS) { IR_LearnBuffer[IR_LearnIndex] duration_us; } // 切换到下一个捕获沿 TIM_RESET_CAPTUREPOLARITY(htim3, TIM_CHANNEL_1); TIM_SET_CAPTUREPOLARITY(htim3, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING); // 省略根据当前状态切换上升/下降沿捕获 } }实际采集一段NEC信号后缓冲区里大概会记录60~70组电平数据。这里有三个关键点需要说明一下第一采完一段后要设置一个超时判定比如200ms内没有新的电平跳变就认为一帧信号接收完毕因为引导码之后后面的数据位间隔最长的也不会超过几十毫秒第二采集到的数据要做毛刺过滤如果某段电平时间小于100μs大概率是干扰噪声直接丢弃第三学习过程中会误触发两次——第一次是接收头从空闲状态刚进入输出时可能产生一个极短的毛刺脉冲实现时可以加个状态机只有正确接收完引导码后才认为有效帧开始。4.3 数据存储Flash扇区规划与码型管理学习功能完成后数据结构是一组时间序列数组必须想办法持久化保存否则一断电录制的码就全丢了。STM32F103C8T6有64KB的Flash存放几百组码型绰绰有余。Flash的写入有几个硬性约束必须按扇区擦除F103每扇区1KB擦除后所有位变成0xFF然后才能写入数据写入时只能把1改成0不能反向操作。所以存储策略必须做好扇区规划。我的方案是在Flash末尾划出4个扇区共4KB作为码库区每个扇区存32组码型用循环覆盖的方式管理。每组的存储格式如下偏移地址含义0状态标志0xA5表示有效1码型编号2电平段数最大1284~511每2字节存一个电平时长为什么要加状态标志因为Flash只能从1改成0删除一条记录时只需要把标志位改成0即可下次写入直接覆盖不用频繁擦除整个扇区。这个小技巧能显著延长Flash寿命也简化了删除逻辑。关于电平段数最大128这个设计我是在实际测试后定下来的。NEC协议完整帧大约68段电平SIRC协议15位格式大概40段而一些空调私有协议可能长一些但也没超过100段。128段是留了余量的足以覆盖市面上绝大多数红外遥控协议。5. 实操调试与踩坑记录5.1 常见故障速查表做这个项目的过程中我踩过不少坑有些还花了不少时间去排查。把高频问题整理成一张表给后来人做个参考现象常见原因排查与解决方案学习模式采集不到信号VS1838B引脚接错或供电不足检查OUT/GND/VCC接线加10μF滤波电容测量接收头输出端静态电压应为3.3V采集到一堆乱码、长度不对供电纹波干扰或环境光干扰接收头供电加RC滤波测试时远离节能灯和阳光直射发射后家电无反应载波频率不对或驱动电流不足用示波器/逻辑分析仪测量38kHz输出频率检查三极管基极偏置和集电极限流电阻部分设备能控、部分不能码型采集时漏掉了引导码后的某段电平加强超时判定逻辑确认接收数据的首段是否为正常的引导码脉冲关机重开后码库丢失Flash写入时序或地址错误检查是否在擦除后写入写入期间是否发生了复位用ST-Link Utility读取Flash内容核对5.2 解码失败时的排查思路解码失败是这类项目里最闹心的问题因为红外信号看不见摸不着。我的建议是先让逻辑分析仪或者示波器接管。在接收头输出端挂上逻辑分析仪按下被学习的遥控器按键观察输出的波形。这个波形就是移植到单片机前的原始真相——如果你能看到清晰的引导码和重复码说明硬件链路正常问题出在软件过滤或时序换算上如果波形本身就是一片模糊那就要回查电路和供电。实际调试中我发现一个很容易忽略的点STM32F103的输入捕获定时器分频不要设得太大。如果把定时器时钟分频成1MHz1μs计数一次理论上够用但捕获中断的处理时间几十个CPU周期和代码执行时间会带来微小的累计误差。实测72MHz直接计数分辨率约13.9ns再在软件里换算成微秒误差可以控制在±20μs以内对红外解码完全够用。如果用1MHz分频累计误差可能到±100μsNEC协议里560μs的最小脉冲就可能被误判。5.3 发射距离与稳定性优化成品做好之后还要解决遥控距离短和按了没反应的问题。第一次做完测试时在3米外遥控空调时而有效时而无效简直崩溃。后来排查出来是发射管工作电流不够稳定导致的。3.3V供电下S8050的饱和压降再加上10Ω限流电阻的压降实际流过发射管的电流没有理论计算值那么高而且电池电压下降后电流也跟着下降距离就明显缩短。优化方案有两个方向如果硬件已经定型可以通过降低限流电阻阻值到4.7Ω、把发射管单独用5V电源供电来提升峰值电流如果硬件可以调整直接把供电改成5V用NPN三极管做开关效果会更好。我最后改成5V供电后室内10米直线遥控毫无压力。还有一个细节是发射管的朝向和外壳遮挡。如果设备装进不透光的盒子遥控距离会断崖式下降。发射管尽量靠近外壳开孔处或者在外壳上留一个透明窗否则信号被壳子吃掉大半。6. 项目扩展与个性化改造建议6.1 增加OLED显示与交互优化原始版本的交互是矩阵键盘数码管信息量有限。把按键换成OLED显示 旋转编码器的组合后体验会提升不少。OLED可以实时显示当前选中的设备类型、码库总数、电池电量还可以在录制模式里显示实时的脉宽数据——这功能在调试协议时特别实用能直接在屏上看到NEC引导码是不是9ms。编码器还有一个好处按键数量大幅减少UI可以设计成菜单层级结构操作逻辑和市售万能遥控器类似。实际上STM32的生态里OLED驱动库比如U8g2移植成本极低几乎零门槛。6.2 用HC-05蓝牙模块实现手机配码如果想把万能贯彻到底可以在串口上挂一个HC-05蓝牙模块配合手机App使用。手机端做一份码库云端共享的概念验证使用者扫描空调铭牌上的型号App从服务器拉取对应码型的时序参数通过蓝牙发给STM32存储。这样一来新空调不用对着学习键一顿乱按直接下载就完事。6.3 从红外到射频多协议融合的想象空间红外遥控终究是视线范围内的工作方式如果想要更极致的体验可以考虑外挂一个315MHz/433MHz射频模块。不少智能家居设备如射频遥控插座、电动窗帘都工作在433MHz频段。硬件上只需在STM32的串口上挂一个超外差收发模块软件上参考红外电平序列的思路同样处理射频编码就能实现红外射频全兼容的万能遥控中心。我在实际使用中发现这类项目最有意思的点在于适合反复拆改。最初做成一个最简单的录码-发射工具后来加OLED、加编码器、加蓝牙每一轮改动都会逼着你更深入地理解协议解析和状态设计。比直接买一个成品万能遥控器有意思得多。如果你也想做一个练手建议第一版不要贪多先把NEC协议学透、发射电路调通再慢慢往上面叠功能。这样即使后面出了问题排查范围也小不容易劝退。本文还有配套的精品资源点击获取
返回列表