从零构建STM32红外遥控器:硬件选型、代码解析与实战应用
1. 项目背景与核心功能红外遥控器是我们日常生活中最常见的电子设备控制方式之一。从电视机到空调几乎所有的家电都配备了红外遥控功能。但每次使用不同设备都需要切换遥控器确实有些麻烦。这就是为什么我想到了开发一个基于STM32的万能红外遥控器。这个项目的核心功能其实很简单学习、存储和发射红外信号。具体来说它可以做到以下几点学习标准NEC格式的红外信号大多数机顶盒和部分电视使用这种格式学习非标准格式的红外信号比如空调、风扇等设备使用的特殊编码将学习到的信号存储在STM32的Flash中根据需要重新发射这些信号来控制设备我选择STM32F103RCT6作为主控芯片有几个原因首先它价格亲民其次它有足够的Flash空间256KB来存储大量红外编码数据。实测下来这个容量可以存储上百个不同的遥控指令。2. 硬件选型与电路设计2.1 核心组件清单做这个项目你需要准备以下硬件组件主控芯片STM32F103RCT6最小系统板其他F103系列也可以但Flash最好≥256KB显示模块0.96寸OLED屏IIC接口128×64分辨率红外接收头VS1838B或类似通用红外接收模块红外发射管940nm波长的红外LED控制输入可以用现成的红外遥控器作为输入设备2.2 关键电路设计要点红外接收电路很简单只需要将接收头的输出端接到STM32的任意GPIO我用的PB9。但红外发射部分有几个需要注意的地方驱动能力单个GPIO的输出电流有限通常8mA左右直接驱动红外LED亮度不够。我建议使用一个NPN三极管如8050来放大电流。载波频率大多数红外遥控使用38kHz载波所以我们需要用PWM来生成这个频率。发射距离为了提高发射距离可以使用2-3个红外LED串联配合适当的限流电阻。具体接线方式如下OLED屏SCL → PB6SDA → PB7VCC → 3.3VGND → GND红外接收头OUT → PB9VCC → 3.3VGND → GND红外发射电路LED阳极 → 3.3V通过限流电阻LED阴极 → 三极管集电极三极管基极 → PA0通过基极电阻三极管发射极 → GND控制端 → PC2用于整体开关控制3. 软件架构与核心代码3.1 工程目录结构整个项目的代码结构是这样的Template ├── USER │ └── main.c ├── SYSTEM │ └── delay.c └── HARDWARE ├── oled.c ├── remote.c ├── pwm.c ├── irsend.c ├── stmflash.c └── remote_save.c3.2 主程序逻辑主程序的流程很直观初始化所有外设OLED、红外接收、PWM等显示欢迎界面进入主菜单循环主菜单有四个功能选项设置分区实现按键复用学习/发射模式删除存储的数据显示红外数据信息while (1) { OLED_Fill_picture(0x00); OLED_ShowStr(0, 0, 1.SetPart( )); OLED_ShowNum(10, 0, GetPart(), 1); OLED_ShowStr(0, 1, 2.Send/Learn); OLED_ShowStr(0, 2, 3.Delete); OLED_ShowStr(0, 3, 4.ShowData); switch (Remote_Num()) { case 1: SetPart(); break; case 2: SendLearn(); break; case 3: Delete(); break; case 4: ShowData(); break; } }3.3 红外信号处理红外信号的处理是这个项目的核心难点主要分为接收解码和发射编码两部分。接收解码部分对于标准NEC协议一个完整的信号包含9ms的起始高电平4.5ms的起始低电平32位数据地址码命令码560μs的脉冲间隔我们通过外部中断捕获这些时间间隔然后解码出具体的键值。u32 Remote_GetData(void) { u32 data 0; // 捕获起始信号 while(!RDATA); // 等待高电平 delay_us(9000); // 检测9ms高电平 // 捕获4.5ms低电平 // ...省略具体解码过程... return data; }发射编码部分发射时需要重新生成这些时间序列同时还要调制38kHz载波。这里我们使用TIM2的PWM功能来生成载波。void TIM2_PWM_Init(u16 arr, u16 psc) { // 初始化TIM2 PWM输出 // 频率计算72MHz/(18951) ≈ 38kHz TIM_TimeBaseInitStructure.TIM_Period arr; TIM_TimeBaseInitStructure.TIM_Prescaler psc; // ...其他初始化代码... }4. 数据存储方案设计4.1 Flash存储结构为了高效利用有限的Flash空间我设计了一个专门的数据存储格式。每个红外指令占用351个16位空间结构如下地址范围用途0-9数据大小1-35010-11保留12-14数据类型0NEC1模拟编码15数据有效标志16-366实际数据存储空间这种设计有几个优点统一了标准和非标准格式的存储通过分区管理实现了按键复用可以快速查询存储状态4.2 关键存储函数int SaveData(int i, u8 type, u16 *pBuffer, u16 lenth) { // 检查参数有效性 if(i DATA_NUMBER || lenth 350) return 0; // 准备状态字 u16 sta 1 15; // 有效标志 sta | (type 0x07) 12; // 类型 sta | lenth 0x03FF; // 长度 // 写入Flash STMFLASH_Write(FLASH_SAVE_ADDR i*MAX_LENTH, sta, 1); STMFLASH_Write(FLASH_SAVE_ADDR i*MAX_LENTH 1, pBuffer, lenth); return 1; }读取函数也很简单先读取状态字判断数据是否有效然后再读取实际数据。5. 实际应用与调试技巧5.1 常见家电的红外编码不同品牌的设备使用不同的红外编码方案NEC编码最常用占空比1:338kHz载波格力空调使用长码格式脉冲间隔编码索尼设备通常使用SIRC协议风扇类设备很多使用简单的脉冲宽度编码在调试时建议先用逻辑分析仪或示波器观察原始遥控器的信号特征然后再针对性地实现解码算法。5.2 提高发射距离的技巧如果发现红外信号发射距离不够可以尝试以下方法增加红外LED的数量2-3个串联调整限流电阻值适当增大电流确保LED指向正确方向使用反射面增强信号5.3 调试中的常见问题接收不灵敏检查供电电压3.3V最佳确保接收头没有被强光直射尝试不同品牌的接收头发射信号不稳定检查PWM频率是否准确38kHz测量LED两端电压是否正常确保三极管工作在饱和区Flash写入失败检查写入地址是否擦除过确保写入地址对齐注意Flash的写入寿命约1万次6. 项目优化与扩展方向这个基础版本已经实现了核心功能但还有很大的优化空间增加学习模式自动识别编码格式支持更多协议RC5、SIRC等改进用户界面增加图形化菜单支持设备命名和图标添加无线功能通过蓝牙或WiFi远程控制手机APP控制界面云端同步将学习到的编码上传到云端从云端下载常见设备的编码库低功耗优化使用STM32的低功耗模式增加动唤醒功能在实际使用中我发现分区管理确实大大提高了按键的利用率。通过将不同设备的遥控指令存储在不同分区可以用相同的按键控制多个设备。比如按键1在分区1中是电视的开关在分区2中就变成了空调的温度。红外信号的发射距离经过优化后在5米范围内都能稳定工作这对于家庭使用已经足够了。存储方面256KB的Flash可以存储大约180条不同的红外指令覆盖家中所有红外设备绰绰有余。