引言先上两张照片这个模块并不贵带上遥控器估计三四块钱吧买了也挺久大约一年了最近终于有时间了咱们一起来把他驱动起来。本文将以STM32F103C6T6最小系统板为例介绍如何驱动HX1838红外接收头解码NEC协议红外遥控器信号。重点讲解接收过程中的状态机设计、脉冲宽度测量以及NEC协议解析流程并提供关键代码实现帮助后来者理解红外解码的原理和实现。1. NEC红外协议基础NEC协议是红外遥控最常用的协议之一其数据格式简单可靠被大量消费电子设备采用。一个完整的NEC数据帧包含以下部分引导码9ms的低电平 4.5ms的高电平地址码8位通常表示设备地址地址反码8位地址码的按位取反用于校验命令码8位按键功能码命令反码8位命令码的按位取反用于校验数据位的编码采用脉冲宽度调制逻辑“0”560µs低电平 560µs高电平逻辑“1”560µs低电平 1.69ms高电平发送顺序为LSB优先最低位先发即数据位从低位到高位依次传输。如果遥控器按键一直按住不放则只发送一次完整数据帧之后每隔约110ms发送一个重复码9ms低电平 2.25ms高电平无数据位。重复码的识别可根据需要实现本文暂不处理。这里我放上几张我使用逻辑分析仪抓到的时序图方便大家理解。有一说一没想到我十几块买的逻辑分析仪还能解析NEC协议通过这几张图我们就能很清晰的看到协议内容及其构成。2. 硬件连接与CubeMX配置2.1 模块介绍红外接收头 HX1838一体化红外接收头输出TTL电平无信号时为高电平收到红外脉冲时输出反向电平低电平有效。引脚定义VCC3.3V/5V、GND、OUT。STM32F103C6T6使用PA0引脚作为外部中断输入连接HX1838的OUT端。2.2 CubeMX关键配置时钟系统时钟72MHzHSEPLLPA0GPIO_EXTI0模式GPIO_MODE_IT_RISING_FALLING双边沿触发上拉GPIO_PULLUPUSART1PA9(TX)、PA10(RX)异步模式波特率115200用于调试输出TIM2预分频器71自动重载值0xFFFF计数频率1MHz1µs用于测量脉冲宽度中断使能EXTI0中断设置优先级3. 驱动核心设计红外解码的关键是精确测量高低电平的持续时间并依据NEC协议的时序规则进行状态转移。我们采用状态机实现。3.1 状态机定义typedef enum { IR_STATE_IDLE, // 空闲等待引导码低电平 IR_STATE_START, // 已收到引导码低电平等待引导码高电平 IR_STATE_DATA, // 接收32位数据 IR_STATE_COMPLETE // 接收完成 } IR_State_t;3.2 脉冲宽度测量在外部中断中记录每次电平变化的时间计算两次中断之间的差值得到上一个电平的持续时间。使用TIM2的计数器1µs分辨率处理溢出情况。static uint32_t IR_GetTick(void) { return __HAL_TIM_GET_COUNTER(htim2); } // 在中断中计算duration if (current_time last_time) duration current_time - last_time; else duration (0xFFFF - last_time) current_time; // 处理溢出3.3 状态机核心逻辑中断处理函数根据当前状态和电平变化上升沿/下降沿进行状态转移和数据解析。void IR_HandleEXTI(void) { uint32_t current_time IR_GetTick(); uint32_t duration; uint8_t pin_state HAL_GPIO_ReadPin(IR_PORT, IR_PIN); // 计算持续时间处理溢出 // ... // 判断电平变化类型 if (last_pin_state GPIO_PIN_RESET pin_state GPIO_PIN_SET) { // 上升沿低电平结束测量低电平持续时间 switch (state) { case IR_STATE_IDLE: if (duration 8000 duration 10000) // 9ms引导码低电平 state IR_STATE_START; break; case IR_STATE_DATA: // 数据位低电平应为560µs检查异常 if (duration 300 || duration 800) state IR_STATE_IDLE; break; // ... } } else if (last_pin_state GPIO_PIN_SET pin_state GPIO_PIN_RESET) { // 下降沿高电平结束测量高电平持续时间 switch (state) { case IR_STATE_START: if (duration 4000 duration 5000) // 4.5ms引导码高电平 state IR_STATE_DATA; else state IR_STATE_IDLE; break; case IR_STATE_DATA: // 根据高电平持续时间判断数据位0或1 if (duration 1000 duration 1800) { // 1.69ms - 1 data_buffer (data_buffer 1) | 1; bit_count; } else if (duration 400 duration 800) { // 560µs - 0 data_buffer (data_buffer 1) | 0; bit_count; } else { state IR_STATE_IDLE; // 异常脉冲 break; } if (bit_count 32) { // 解析数据 uint8_t addr (data_buffer 24) 0xFF; uint8_t addr_inv (data_buffer 16) 0xFF; uint8_t cmd (data_buffer 8) 0xFF; uint8_t cmd_inv data_buffer 0xFF; if ((addr (uint8_t)(~addr_inv)) (cmd (uint8_t)(~cmd_inv))) { // 有效数据保存结果 result.raw_data data_buffer; result.address addr; result.command cmd; result.valid 1; } state IR_STATE_COMPLETE; } break; // ... } } last_pin_state pin_state; // 保存状态供下次使用 }3.4 数据读取与状态复位主循环中检测到有效数据后打印结果并清除标志重置状态机。void IR_ClearResult(void) { result.valid 0; state IR_STATE_IDLE; bit_count 0; data_buffer 0; }4. 测试与结果将红外接收头连接至PA0串口连接至PC运行程序。按下遥控器按键串口输出解码结果例如NEC解码结果 原始数据: 0x00FF45BA 地址码: 0x00 (0) 地址反码: 0xFF (255) 命令码: 0x45 (69) 命令反码: 0xBA (186) 校验: 通过每个按键对应唯一的命令码可根据命令码实现自定义功能。5. 常见问题与调试无法解码检查红外接收头输出引脚是否连接正确确认定时器计数频率为1MHz脉冲宽度阈值可根据实际波形微调。只能解码一次确保IR_ClearResult()重置了状态机为IR_STATE_IDLE。干扰误触发可在中断中添加软件去抖动或调整阈值范围。6. 总结本文介绍了基于STM32F103和HX1838红外接收头的NEC协议解码驱动通过状态机测量脉冲宽度完整解析红外遥控信号。关键点包括双边沿外部中断测量高、低电平持续时间根据NEC协议时序进行状态转移和数据位判断处理定时器溢出保证测量精度该驱动可轻松移植到其他STM32系列为嵌入式红外遥控应用提供可靠基础。完整代码已通过测试读者可根据实际需求扩展重复码识别、按键映射等功能。7. 现象展示8.代码参考通过网盘分享的文件IR_NEC_Demo_STM32F103.zip链接: https://pan.baidu.com/s/1ZTxQSrUt3kyMu9SOadnauw?pwdnthm 提取码: nthm--来自百度网盘超级会员v8的分享