
这次我们来看一个很典型的嵌入式入门项目基于 STM32 的红外遥控多路开关控制系统。它解决的问题非常直接——用普通家用红外遥控器远程控制一路或多路电器开关比如控制灯、风扇、继电器、插座等。项目采用常见 STM32F103 系列 MCU配合红外接收头和继电器模块核心逻辑是红外解码、按键映射、状态输出。最值得关注的是这套系统从硬件到软件都可以自己搭成本低、代码可移植性强、调试链路清晰非常适合课程设计、毕业设计、智能家居入门改造也适合刚学完 GPIO、定时器、外部中断的开发者做综合练习。整体来看项目的硬件门槛不高一块 STM32 最小系统板加红外接收头就能跑通解码加上继电器和 LED 即可扩展成多路控制器。软件部分基本是 HAL 库加外部中断加定时器计时的方式实现 NEC 协议解码逻辑不复杂重点在于时序测量和状态机设计。环境准备主要是 Keil MDK 5、STM32CubeMX、ST-LINK 驱动和串口调试助手。这篇文章会带你把整体架构、硬件连接、工程创建、解码代码、控制逻辑、串口指令协议、问题排查和扩展方向全部过一遍读完可以直接照着做自己的红外多路开关项目。1. 核心能力速览能力项说明项目类型STM32 嵌入式控制器红外遥控多路开关核心 MCUSTM32F103C8T6也可移植到其他 STM32 系列红外协议NEC 协议兼容常见 38kHz 红外遥控器控制通道以 4 路或 8 路为例GPIO 可扩展到更多输出方式继电器模块、LED 指示灯、蜂鸣器提示状态显示串口日志、I2C OLED 屏幕可选调试接口USART 串口可打印解码键值和开关状态开发工具Keil MDK5、STM32CubeMX、ST-LINK、串口助手开源程度代码、接线图、设计思路可整理为完整开源工程适用场景课程设计、毕设、智能家居 DIY、家电遥控改造成本预估视具体模块而定常规 DIY 成本很低扩展方向WiFi 联网、语音控制、手机 App、RTOS、学习型遥控从使用方式看这个项目不是跑 AI 模型或深度学习框架而是典型的 MCU 裸机控制程序。它不要求高算力也不要求大内存重点是硬件接口和中断响应的实时性。只要 STM32 能跑 72MHz 主频Flash 大于 32KBRAM 大于 10KB就足够完成红外解码和多路控制。2. 项目整体架构与工作原理2.1 系统组成整个系统分为四个部分红外遥控器发送 NEC 协议红外信号是系统的输入设备。红外接收头把红外光信号转换为电信号常见型号为 VS1838B、HX1838B工作频率 38kHz。STM32 主控完成信号解码、按键识别、状态机控制和输出控制。执行与反馈模块继电器控制强电设备LED 显示开关状态蜂鸣器提供按键反馈OLED 显示当前系统状态。系统架构是一条单向数据流红外遥控器 - 红外接收头 - STM32 外部中断 定时器解码 - 按键映射 - GPIO 控制继电器/LED - 状态输出红外接收头数据脚连接到 STM32 的某个外部中断引脚接收头在收到红外信号后数据脚会输出一串 TTL 电平变化。STM32 通过外部中断检测边沿变化再用定时器或微秒级延时测量高电平和低电平持续时间从而还原出按键编码。2.2 NEC 协议基础NEC 协议是红外遥控器最常见的编码协议之一。一帧数据由引导码、地址码、地址反码、命令码、命令反码组成总共 32 位数据。关键时序参数信号高电平时间低电平时间说明引导码9ms4.5ms表示一帧数据开始逻辑 0560us560us高电平和低电平时间相同逻辑 1560us1690us低电平时间明显更长重复码9ms2.25ms长按按键时的重复信号红外接收头输出的是反向电平信号也就是空闲时数据脚为高电平收到载波时输出低电平。因此解码时通常检测下降沿通过两次下降沿之间的时间间隔来判断逻辑 0 和逻辑 1这是理解后续代码的关键。NEC 协议在使用上还有一个特点每个按键命令码都会同时发送反码用于校验这样解码程序可以很容易判断数据是否出错有效提高可靠性。2.3 多路开关的控制链路解码得到 32 位键值后程序会把它与预存的按键映射表做比较。每个遥控器按键可以对应一个开关动作例如按键 1控制第一路继电器开/关切换。按键 2控制第二路继电器开/关切换。按键 3控制第三路继电器。按键 4全部继电器开启。按键 5全部继电器关闭。按键 6蜂鸣器提示音测试。按键 7OLED 显示当前各通道状态。控制链路的实现并不复杂本质就是建立一张“键值到动作函数”的映射表。动作函数可以是单一开关切换也可以是组合控制比如一键全开、一键全关、场景联动。3. 适用场景与使用边界3.1 适合什么场景这个项目最适合的读者有几类第一种是正在学 STM32 的开发者。外部中断、定时器、GPIO 输出、串口调试这几个知识点是嵌入式开发的核心基础。通过一个红外遥控开关项目能把分散的知识点串成完整的闭环。第二种是需要做课程设计或毕业设计的学生。红外遥控多路开关是一个经典题目难度适中功能直观材料容易采购代码完整度较高答辩时也容易讲清楚系统原理。第三种是想改造传统家电的 DIY 爱好者。比如把普通台灯、电风扇、LED 灯带改成遥控控制不需要改变电器内部电路只需要在电源线上串接继电器模块再由 STM32 控制继电器通断即可。3.2 不适合什么场景需要强调这个项目不适合直接用于高可靠工业控制。裸机程序没有实时操作系统也没有完整故障保护机制继电器动作只是简单的 GPIO 电平控制。如果用于工业现场还需要考虑电路隔离、电磁兼容、看门狗、状态冗余等问题。另外控制 220V 交流设备时必须通过正规继电器模块或接触器中转强电和弱电必须隔离不能把 STM32 直接接到强电回路。接线、焊接、通电测试都要遵守电气安全规范最好在断开电源的情况下完成接线。3.3 安全与合规边界涉及电器控制时需要注意以下边界用户自行扩展控制对象时必须确认设备功率在继电器允许范围内防止过载烧毁继电器。红外遥控只是控制信号不改变设备原有保护机制设备本身短路、过温、过流保护仍然依赖原有电路。如果项目要发布到开源平台建议在 README 中明确提示强电操作安全并对控制对象做功率说明。不要用红外遥控系统控制没有安全认证的私接设备避免引发用电事故。4. 开发环境与硬件准备4.1 硬件清单开始之前建议准备以下物料硬件数量说明STM32F103C8T6 最小系统板1 块蓝板或自制最小系统均可红外遥控器1 个任选 NEC 协议遥控器常见 50 键款即可红外接收头 VS1838B1 个数据脚输出 TTL 电平继电器模块4-8 路根据实际控制对象选择LED 限流电阻若干用于测试 GPIO 输出蜂鸣器模块1 个可选用于按键提示OLED 0.96 寸 I2C1 个可选显示当前通道状态ST-LINK V2 下载器1 个烧录和调试程序USB-TTL 串口模块1 个调试日志输出杜邦线、面包板、电源模块若干搭建测试电路从成本角度看STM32F103C8T6 最小系统板加上红外接收头、遥控器、LED 等基础物料就可以跑通红外解码功能后续再按需增加继电器和显示模块。4.2 软件工具软件部分用到的主要工具Keil MDK 5STM32 开发最常用的 IDE用于代码编写和编译。STM32CubeMX图形化初始化配置工具可以快速生成 GPIO、定时器、串口、外部中断的初始化代码。ST-LINK Utility用于烧录和读取芯片 Flash也可以查看是否成功识别目标芯片。串口调试助手例如 SSCOM、PuTTY用于查看 STM32 发送的调试信息。驱动软件ST-LINK 驱动、串口芯片驱动根据手头的硬件选择安装。如果本机已经用 Keil 开发过 STM32可以直接复用现有环境不必重复安装。需要注意 Keil MDK 5 需要安装对应芯片的 Device Pack例如 STM32F1xx 的 Keil 支持包否则工程编译会提示找不到芯片型号。4.3 标准库还是 HAL 库目前 STM32F103 常见开发方式有两种标准库代码直接入门教程多新老项目资料丰富适合学习寄存器背后的逻辑。HAL 库STM32CubeMX 原生支持生成代码结构清晰外设抽象统一方便以后迁移到其他 STM32 系列。对这个项目来说两种方案都可以实现。如果追求快速搭建工程推荐使用 STM32CubeMX 生成 HAL 库工程再用 Keil 编写业务逻辑。如果参考的老教材是标准库也完全可行。关键在于选一种方案并保持一致不要混用。5. 硬件连接与工程搭建5.1 引脚分配这里给出一种常用的引脚分配方案实际接线可以按自己板子调整功能STM32 引脚说明红外接收头 OUTPA0配置为外部中断输入下降沿触发第一路继电器PB0推挽输出高电平吸合第二路继电器PB1推挽输出第三路继电器PB10推挽输出第四路继电器PB11推挽输出蜂鸣器PB12推挽输出低电平触发或高电平触发视模块而定OLED SCLPB8I2C 时钟线OLED SDAPB9I2C 数据线调试串口 TXPA9USART1_TX接 USB-TTL RX调试串口 RXPA10USART1_RX接 USB-TTL TXSWD 下载PA13 / PA14SWDIO / SWCLK接 ST-LINK建议把红外接收头放在距离 STM32 主控较近的位置避免长线引入干扰。继电器模块与主控之间最好采用光耦隔离的继电器模块不要直接在 GPIO 上驱动大功率负载。5.2 使用 STM32CubeMX 创建工程打开 STM32CubeMX新建工程并选择 STM32F103C8Tx 芯片按以下步骤配置时钟配置选择外部晶振 HSE系统时钟 SYSCLK 配置为 72MHzAPB1 总线时钟 36MHzAPB2 总线时钟 72MHz。GPIO 配置PA0 设置为 GPIO_EXTI0外部中断模式下降沿触发。PB0、PB1、PB10、PB11 设置为 GPIO_Output输出电平默认低电平。PB12 设置为 GPIO_Output。PA9 设置为 USART1_TXPA10 设置为 USART1_RX。USART1 配置异步模式波特率 115200数据位 8无校验停止位 1。I2C1 配置可选配置为快速模式用于连接 OLED 屏幕。定时器配置可选如果使用定时器做微秒级计时可以配置一个定时器为 1MHz 计数频率例如 TIM2计数周期 0xFFFF用于测量红外信号高低电平时间。在 Project Manager 中设置工程名称、保存路径和 Toolchain 为 MDK-ARM然后点击 GENERATE CODE 生成初始工程。生成的代码中GPIO、串口、定时器的初始化函数已经写好业务逻辑在用户代码区添加。5.3 编写代码时的工程结构推荐按模块拆分源文件保持可维护性项目根目录 ├── Core │ ├── Inc │ │ ├── main.h │ │ ├── ir.h │ │ ├── switch_control.h │ │ └── debug_uart.h │ └── Src │ ├── main.c │ ├── ir.c │ ├── switch_control.c │ └── debug_uart.c ├── Drivers ├── MDK-ARM └── 其他生成目录ir.c 负责红外解码switch_control.c 负责继电器开关逻辑debug_uart.c 负责串口打印。这样即使以后增加 OLED 功能、WiFi 功能也不会让 main.c 变成一个大杂烩。6. 核心代码实现与设计思路6.1 NEC 红外解码模块红外解码是这个项目的核心。实现方案有两种第一种是外部中断加延时查询。在外部中断回调函数中读取引脚电平用延时函数测量电平持续时间。这种方法代码简单但延时函数会阻塞主循环不适合处理长按重复码和多任务场景。第二种是外部中断加定时器计时。外部中断每触发一次程序读取定时器的计数值通过两次中断的时间差计算脉冲宽度。这种方式不阻塞主循环解码精度高推荐使用。下面的代码是第二种方案的简化示意核心思路是在下降沿中断中计算与上一次下降沿的时间间隔然后进入状态机判断引导码、逻辑 0、逻辑 1 和重复码。// ir.h 中定义解码状态 typedef enum { IR_STATE_IDLE 0, IR_STATE_AGC, IR_STATE_DATA, IR_STATE_REPEAT } IR_State_t; extern volatile uint8_t ir_rx_flag; extern volatile uint32_t ir_key_code; extern volatile uint8_t ir_repeat_flag;// ir.c 中实现中断回调 // 注意使用定时器 TIM2 计数系统时钟 72MHzPSC 配为 71得到 1MHz 计数频率 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin IR_RX_PIN) { uint16_t cnt __HAL_TIM_GET_COUNTER(htim2); __HAL_TIM_SET_COUNTER(htim2, 0); // cnt 减去上一次中断后的计数增量得到两次下降沿之间的时间差 // 这里按 1MHz 计数cnt 的数值就是微秒数 uint32_t diff_us cnt; // 按 NEC 协议时间参数判断 if (diff_us 12000) { // 超过 12ms认为空闲后重新开始复位状态机 ir_state IR_STATE_AGC; ir_bit_count 0; ir_key_code 0; } else if (ir_state IR_STATE_AGC) { // 引导码后的间隔较大约 4.5ms 左右 // 实际进入这里说明引导码已接收准备接收 32 位数据 if (diff_us 9000) { ir_state IR_STATE_DATA; } } else if (ir_state IR_STATE_DATA) { // 数据位判断普通位间隔约 1.12ms 对应逻辑 0 // 约 2.25ms 对应逻辑 1 if (diff_us 2000) { // 认为逻辑 1 ir_key_code 1; ir_key_code | 1; } else if (diff_us 1000) { // 认为逻辑 0 ir_key_code 1; } else { // 时间太短无效数据 ir_state IR_STATE_IDLE; } ir_bit_count; // 完整接收 32 位注意细节引导码后第一个判断是数据位 // 需要根据实际中断次数调整计数逻辑这里仅表达状态机思路 if (ir_bit_count 32) { ir_rx_flag 1; ir_state IR_STATE_IDLE; } } else if (ir_state IR_STATE_REPEAT) { ir_repeat_flag 1; ir_state IR_STATE_IDLE; } } }上面代码更偏向状态机示意实际调通时要注意边界处理。解码完毕后主循环中检测 ir_rx_flag读取 ir_key_code 并做按键映射随后清标志位。如果使用 HAL 库的 HAL_GetTick 做时间测量1ms 分辨率比较紧张因为 NEC 的数据位时间差只有微秒级所以这里使用定时器计数的做法更可靠。6.2 按键映射与多路开关控制解码出键值后接下来就是按键映射。由于不同遥控器键码不同代码中需要维护一个键值表调试时用串口打印实际键值再把键值填入表中。// switch_control.c 中的按键映射示例 typedef struct { uint32_t key_code; void (*action)(void); } IR_ActionMap_t; void CH1_Toggle(void); void CH2_Toggle(void); void CH3_Toggle(void); void CH4_Toggle(void); void All_On(void); void All_Off(void); const IR_ActionMap_t ir_action_map[] { { 0xFF00FF00, CH1_Toggle }, { 0xFF807F00, CH2_Toggle }, { 0xFF40BF00, CH3_Toggle }, { 0xFFC03F00, CH4_Toggle }, { 0xFF00FF00, All_On }, { 0xFF58A700, All_Off }, };按键动作函数可以这样实现void CH1_Toggle(void) { HAL_GPIO_TogglePin(CH1_GPIO_Port, CH1_Pin); printf(CH1 toggle, state %d\r\n, HAL_GPIO_ReadPin(CH1_GPIO_Port, CH1_Pin)); }在 main 主循环中处理按键映射if (ir_rx_flag) { ir_rx_flag 0; for (int i 0; i sizeof(ir_action_map) / sizeof(ir_action_map[0]); i) { if (ir_action_map[i].key_code ir_key_code) { ir_action_map[i].action(); break; } } }注意上面表格里 0xFF00FF00 等键值只是常见的 NEC 键值之一具体键值必须以实际遥控器通过串口打印出来的数据为准。设计按键映射表时建议做两遍校验地址码和命令码反码是否匹配防止误触发。6.3 状态指示与串口调试串口调试在这个项目中非常关键。第一次调试时先不接继电器只接红外接收头把解码得到的键值打印到串口确认每个按键对应的键值再填入映射表。#include stdio.h // 重定向 printf 到 USART1HAL 库中在 main.c 添加 // int fputc(int ch, FILE *f) 的重定向或使用 GCC 的 __io_putchar在解码头文件或主循环中打印键值printf(IR Key Code 0x%08X\r\n, ir_key_code);OLED 显示状态是可选功能。如果接入了 I2C OLED可以在每次开关动作后刷新屏幕显示当前各通道是开还是关。显示刷新频率不需要太高因为人眼对状态变化实时性要求不高切键后刷新一次即可。7. 功能测试与效果验证7.1 分步骤验证思路建议按照“先解码、后控制、再整机”的顺序进行测试。第一次上电不要直接接强电设备先使用 LED 模拟继电器输出。第一步烧录一个最简单的程序让 STM32 通过串口循环打印系统启动信息验证烧录和串口通信链路正常。第二步实现红外解码后把遥控器对准红外接收头按下按键观察串口输出的键值。第三步按键值表填好后使用 LED 代替继电器测试各通道切换功能。LED 能正常亮灭说明 GPIO 输出逻辑正确可以接入继电器模块。第四步接入继电器模块测试空载情况下的吸合与释放确认控制逻辑无误后再接入实际负载。7.2 红外解码验证测试条件红外接收头 OUT 接 PA0串口波特率 115200STM32 与调试电脑之间通过 USB-TTL 连接。操作步骤烧录带串口打印功能的红外解码程序。打开串口调试助手波特率设为 115200。遥控器距离接收头 30cm 到 1m 左右按下按键。观察串口是否输出了 32 位键值。判断标准按下遥控器按键“1”串口输出一个固定键值按下按键“2”输出另一个固定键值连续长按能识别出重复码或持续按键事件。如果一直没有输出优先排查红外接收头供电电压是否为 3.3V 或 5V数据脚是否接对。外部中断是否配置为下降沿触发引脚是否被其他复用功能占用。遥控器电池是否有电是否真的是 NEC 协议遥控器。周围环境光线是否过强阳光或节能灯干扰可能导致接收头无法正常工作。7.3 多路开关功能验证在 GPIO 输出引脚上接 LED 和限流电阻。按下遥控器对应按键观察 LED 状态是否切换。测试按键预期动作验证方法按键 1第一路切换CH1 LED 亮/灭按键 2第二路切换CH2 LED 亮/灭按键 3第三路切换CH3 LED 亮/灭按键 4第四路切换CH4 LED 亮/灭全开键所有通道开启所有 LED 点亮全关键所有通道关闭所有 LED 熄灭如果 LED 状态不稳定比如按下一次切换多次很可能是因为长按产生了重复码。此时需要在重复码逻辑里做处理要么完全忽略重复码要么在重复码执行和前一次相同动作时加一个去抖延时。7.4 距离与角度测试红外遥控角度测试是一个容易被忽略的环节。NEC 协议的红外接收头通常有接收角度限制测试时遥控器不要正对接收头而是稍微偏转一定角度观察解码成功率。如果遥控距离远低于正常水平可能不是程序问题而是硬件布局问题。接收头安装位置避免被遮挡接收窗口前面不要放置金属或深色塑料。若在强光环境下使用可以考虑加装红外滤光片或者在代码里做多次解码确认减少单次误码影响。8. 串口指令协议与扩展方向8.1 串口指令协议设计在没有 WiFi 和上位机的情况下串口本身就可以作为系统的控制接口。可以设计一套简单的指令协议在调试电控逻辑时直接通过串口发送命令控制多路开关。这种设计相当于给系统加了一个可编程控制接口后续接入上位机或配合自动化测试非常方便。协议可以设计为帧头、指令、数据、校验四段模式// 串口指令帧格式AA 01 00 55 // 帧头 AA指令字数据校验和例如定义指令AA 01 01 00开启第一路。AA 01 02 00关闭第一路。AA 01 03 00切换第一路。AA 02 01 00开启第二路。在串口中断接收回调中做解析typedef enum { FRAME_IDLE 0, FRAME_HEAD, FRAME_CMD, FRAME_DATA, FRAME_CHECK } FrameParse_State_t; void UART_RxCpltCallback(UART_HandleTypeDef *huart) { // 通过 HAL_UART_Receive_IT 接收单字节并进入状态机 }使用串口指令的好处是可以在不操作遥控器的情况下快速验证各通道控制逻辑。如果后续加上 ESP8266 或 ESP32 模块这套串口协议可以直接复用变成网络控制指令的转发通道。8.2 扩展 WiFi 或以太网控制这个项目留出的扩展空间很大。常见做法是 STM32 串口连接 ESP8266通过 MQTT 或 HTTP 与上位机通信。远程控制手机 App、网页或智能音箱通过 WiFi 模块发送指令给 STM32。状态回传STM32 把当前各通道状态通过 WiFi 模块发送到云端或局域网。定时控制系统增加 RTC 时钟模块实现定时开关。联动控制接入温湿度传感器、人体红外传感器实现自适应场景。扩展时注意红外遥控和 WiFi 控制是两套输入源需要设计统一的控制入口。可以增加一个“控制源优先级”或“最后生效原则”避免红外遥控和 App 同时操作控制逻辑冲突。8.3 学习型遥控与多协议支持NEC 协议之外很多空调遥控器、电视遥控器使用的协议并不相同。如果想支持更多遥控器可以升级为学习型红外遥控器方案首次使用时记录按键波形之后重新发送。学习型遥控的难点在于波形存储和发送精度。STM32F103 的低功耗定时器可以精确输出 38kHz 载波用 PWM 加 DMA 可以模拟红外发射时序。不过这个功能会明显增加代码复杂度建议先跑通 NEC 协议再逐步扩展。8.4 批量控制与场景联动红外遥控器也可以设计场景联动。例如一键“回家模式”可以同时打开客厅灯、电视电源、空调。一键“离家模式”可以关闭全部通道。这种场景联动不改变底层控制逻辑只需要在按键映射表中增加组合动作函数void Scene_Home(void) { CH1_On(); CH2_On(); CH3_Off(); CH4_Off(); }场景模式需要在功能表中预留按键位调试时注意区分单键控制和场景控制避免按一下场景键把不该动作的通道也切了。9. 资源占用与性能观察9.1 Flash 与 RAM 占用STM32F103C8T6 内置 64KB Flash 和 20KB RAM。基础工程在编译后占用的 Flash 取决于是否使用 HAL 库、启用多少外设以及是否开启串口重定向。这个项目涉及的模块不多主循环逻辑简单从工程经验看占用不会太大。但具体数值要以自己工程编译后的 Map 文件为准。建议在项目后期做一次编译输出检查观察 Flash 剩余容量如果功能扩展较多可以考虑使用 STM32F103RCT6 等 Flash 更大的型号。9.2 中断实时性与主循环负载红外解码对外部中断的响应实时性要求较高。如果主循环中执行了较长时间的阻塞操作比如 OLED 刷屏、延时等待、串口大数据量打印可能导致中断回调延迟进而影响解码精度。更稳妥的做法是解码状态机只做时间测量和标志位记录不直接执行开关动作。主循环中通过查询标志位的方式处理按键映射。串口打印的数据量不要过大调试完成后可以降低打印频率或加条件编译开关。9.3 功耗与抗干扰整个系统在待机状态下功耗主要来自 STM32 最小系统板、红外接收头、继电器线圈等。如果要低功耗运行可以考虑以下措施使用 STM32 的睡眠模式或停机模式由外部中断唤醒。红外接收头在空闲时依然会消耗电流可以在待机时关闭接收头供电。继电器使用低功耗锁存继电器动作完成后自动断电保持而不是持续给线圈供电。抗干扰方面继电器吸合和释放瞬间会产生较大的电流变化有可能通过电源线干扰 STM32。解决办法是主控电源和继电器电源分开供电继电器模块与主控之间保持足够距离必要时增加光耦隔离。10. 常见问题与排查方法问题现象可能原因排查方式解决方案串口没有任何打印ST-LINK 下载成功但串口接线错误或波特率不对检查 USB-TTL 的 TX/RX 是否交叉连接串口助手设置 115200 8N1正确接线更换串口助手或 USB 口红外接收头无输出供电电压不对、数据脚未接对、遥控器电池没电用万用表测量接收头 VCC 引脚电压示波器观察数据脚电平更换接收头或遥控器检查 3.3V/5V 供电选择有电平变化但解码不稳定外部中断与定时器时间基准不准确用示波器测量引导码时间是否为 9ms 加 4.5ms检查系统时钟配置使用定时器微秒计数替代阻塞延时按键值偶尔错误红外信号反射、环境光干扰、采样时序边界问题不同距离和角度反复测试记录错误键值分布增加数据校验对地址码反码和命令码反码做二次验证按下一次开关切换两次长按产生重复码重复码被当作新按键事件处理串口打印确认重复码标志位对重复码做去抖或者忽略重复码继电器模块不动作GPIO 输出高电平驱动能力不足或模块供电不足先用 LED 测试 GPIO 是否正常输出测量继电器模块输入脚电压使用光耦隔离模块引入独立电源必要时加三极管驱动Keil 编译报找不到芯片Keil 缺少 STM32F1 系列 Device Pack在 Pack Installer 中搜索 STM32F1 并安装安装对应支持包后重新编译ST-LINK 无法识别芯片接线错误、驱动未装、芯片被锁检查 SWDIO/SWCLK/GND 连接ST-LINK Utility 尝试连接重装驱动如果芯片被锁可用 ST-LINK Utility 执行整片擦除11. 最佳实践与使用建议11.1 先做最小功能验证不要一开始就连接八路继电器和 OLED整个系统越复杂越难定位问题。先跑通红外解码加串口打印确认按键值然后加一路 LED 开关再扩展到多路和继电器。一步一步验证每一步都确定无误后再向下推进。11.2 保留一张可复用的按键映射表不同红外遥控器的键值差别很大建议把按键映射表独立成单独结构体数组并写成可以统一修改的配置文件。这样换遥控器时只需要修改键值表不需要动红外解码逻辑。11.3 模块间使用状态标志解耦解码模块、按键映射模块、继电器控制模块之间尽量通过全局标志位或回调函数交互不要在中断回调中直接操作继电器。这样既保证解码实时性也方便以后移植到 RTOS 环境。11.4 强电控制的安全细节如果控制对象是 220V 设备必须注意继电器模块选型时确认触点额定电流大于负载工作电流。控制火线而不是零线且保证断电状态下接线。强电区域与弱电区域物理隔离避免裸露接线。外壳使用绝缘材料安装完成后做一次整体绝缘检查。11.5 开源发布时注意整理资料如果要把这个项目开源建议整理好以下内容README 中的功能说明、硬件清单、接线图、效果演示。原理图和 PCB 工程文件如果没有可以直接给接线图。编译好的 Keil 工程和烧录说明。按键映射表的说明文档标注不同遥控器键值如何填写。License 选择建议如果允许自由使用可以选择 MIT 或 Apache-2.0如果需要保护硬件设计可以选择相应的硬件开源许可。开源项目不只是放代码一个清晰的接线图和使用说明往往比代码本身更能帮助你获得反馈和 star 数量。12. 总结与下一步这个项目值得一试的地方在于它把嵌入式开发的高频知识点完整串了一遍GPIO 输出、外部中断、定时器计时、串口调试、状态机设计。完成它之后你不只是会调一个红外遥控开关而是掌握了“传感器输入 MCU 处理 执行器输出”这一类典型系统的开发方法。最先要验证的功能一定是红外解码。用串口打印键值确认每个按键数据稳定后再做多路开关控制。最容易踩的坑有两个一个是红外接收头接线和供电一个是用 1ms 级别的延时去量 NEC 微秒级脉冲。前者属于硬件问题后者属于方案问题换用定时器计时后就能解决。后续扩展方向很多可以接 ESP8266 做手机遥控可以加 OLED 做可视化控制面板可以把裸机代码移植到 FreeRTOS 上跑多任务还可以做学习型红外遥控支持更多设备。路径越走越宽建议先把手上的版本稳定跑通再挑一个方向深入下去。