STM32红外遥控信号自学习与万能遥控器实现详解

📅 2026/7/15 3:20:56
STM32红外遥控信号自学习与万能遥控器实现详解
1. 红外遥控基础与STM32实现原理家里遥控器坏了找不到替换想用手机控制老式空调却苦于没有红外功能今天我们来聊聊如何用STM32打造一个能学习各种遥控信号的万能红外遥控器。这个项目不仅实用还能让你深入理解红外通信的底层原理。先说说红外遥控的基本工作原理。常见的红外遥控器比如电视、空调遥控器大多采用NEC协议它通过38kHz的载波来传输数据。当你按下遥控器按键时红外LED会以特定频率闪烁接收头如VS1838B将这些光信号转换为电信号。STM32的定时器输入捕获功能可以精确记录这些脉冲的时序而PWM输出则能完美复现原始信号。我最初尝试这个项目时用的是一块STM32F103C8T6核心板成本不到20元。实测发现市面上90%的家电遥控器都采用NEC或类似的PWM编码这为我们的万能遥控器提供了通用性保障。关键是要准确捕获两个要素脉冲间隔时间决定逻辑0/1和载波频率通常38kHz。2. 硬件搭建与CubeMX配置2.1 必备硬件组件你需要准备以下硬件STM32开发板推荐F103系列性价比高红外接收头VS1838B或类似红外发射管940nm波长220Ω限流电阻杜邦线若干接线示意图红外接收头 STM32 VCC - 3.3V GND - GND OUT - PA0(TIM2_CH1) 红外发射管 STM32 阳极 - PA6(TIM3_CH1) 阴极 - 220Ω - GND2.2 CubeMX关键配置在CubeMX中需要配置两个核心功能输入捕获用于记录遥控器信号定时器选择TIM2通道1预分频值7172MHz/(711)1MHz捕获边沿选择双边沿触发开启捕获中断PWM输出用于信号重放定时器选择TIM3通道1预分频值0自动重载值189672MHz/1896≈38kHzPWM模式1占空比50%这里有个坑要注意输入捕获的定时器时钟不能太快否则会溢出。我最初设置预分频为0导致计时器溢出无法捕获长脉冲。后来调整为711MHz计数频率后可以准确测量长达65ms的引导码。3. 信号捕获与解码实现3.1 NEC协议解析NEC协议的典型帧结构包含9ms高电平4.5ms低电平的引导码16位地址码设备标识8位命令码8位反码560us的结束脉冲逻辑0和1的区分逻辑0560us高560us低逻辑1560us高1680us低在代码中我们通过定时器捕获回调函数记录脉冲时间void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if(htim-Instance TIM2) { uint32_t pulse HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); if(current_edge RISING_EDGE) { // 记录下降沿时间 falling_time pulse; __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING); current_edge FALLING_EDGE; } else { // 计算脉冲宽度 rising_time pulse; uint32_t duration rising_time - falling_time; decode_ir_signal(duration); // 解码信号 __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING); current_edge RISING_EDGE; } } }3.2 信号存储优化为了支持多协议学习我设计了一个环形缓冲区存储信号特征typedef struct { uint8_t protocol_type; // NEC, RC5等 uint16_t pulse_count; uint16_t pulse_interval[200]; // 存储脉冲间隔 uint32_t carrier_freq; // 载波频率 } IrSignal_t; IrSignal_t ir_signals[10]; // 存储10组信号实际测试发现大多数遥控信号脉冲数在100以内所以200的数组大小足够。对于空调遥控等复杂信号可以适当增大数组尺寸。4. 信号重放与PWM调制4.1 精确时序控制信号重放的核心是复现原始脉冲时序。我们使用TIM3产生38kHz载波通过控制PWM的启停来模拟原始信号void send_ir_signal(IrSignal_t* signal) { HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1); for(int i0; isignal-pulse_count; i) { if(i%2 0) { // 高电平周期 TIM3-CCR1 948; // 50%占空比 } else { // 低电平周期 TIM3-CCR1 0; // 关闭输出 } delay_us(signal-pulse_interval[i]); } HAL_TIM_PWM_Stop(htim3, TIM_CHANNEL_1); }这里有个实用技巧在发送前先发送3-5个38kHz的载波脉冲作为前导能提高接收灵敏度。我在测试中发现加入100us的载波前导后接收距离从3米提升到了5米。4.2 多协议兼容处理不同品牌的遥控器可能使用不同协议。通过分析捕获到的信号特征可以自动识别协议类型uint8_t detect_protocol(IrSignal_t* signal) { // 检测引导码 if(signal-pulse_interval[0] 8500 signal-pulse_interval[0] 9500) { if(signal-pulse_interval[1] 4000 signal-pulse_interval[1] 5000) { return NEC_PROTOCOL; } } // 其他协议检测... return UNKNOWN_PROTOCOL; }5. 完整代码框架与优化技巧5.1 工程文件结构建议按模块化方式组织代码/Inc ir_receiver.h # 接收解码 ir_sender.h # 发送控制 ir_protocols.h # 协议定义 /Src main.c # 主逻辑 ir_receiver.c ir_sender.c5.2 低功耗优化作为便携设备功耗优化很重要接收模式使用中断唤醒平时进入STOP模式发送完成后立即关闭PWM输出使用硬件定时器代替软件延时void enter_low_power_mode() { HAL_SuspendTick(); HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); SystemClock_Config(); // 唤醒后重新配置时钟 }6. 常见问题与调试方法6.1 信号捕获不完整可能原因定时器溢出周期设置过短中断优先级配置不当接收头供电不稳定解决方法增大定时器自动重载值如0xFFFF确保捕获中断优先级最高给接收头增加10μF滤波电容6.2 发送距离短提升距离的技巧增加发射管电流减小限流电阻但不低于100Ω使用多个发射管并联优化载波占空比实测30%-50%最佳我在最终版本中使用了3个发射管并联配合150Ω电阻控制距离达到8米足以覆盖普通客厅。这个项目最让我有成就感的是用它成功控制了家里10年历史的老空调。当看到STM32发出的信号能让空调启动时那种感觉比买新设备还兴奋。如果你也遇到类似需求不妨试试这个方案成本不到50元却能解决大问题。