基于天空星STM32F407的NEC红外编解码模块串口通信实战

发布时间:2026/5/20 0:22:59

基于天空星STM32F407的NEC红外编解码模块串口通信实战 基于天空星STM32F407的NEC红外编解码模块串口通信实战最近在做一个智能家居的小项目需要用到红外遥控功能。自己从头写红外编解码协议太费时间而且对时序要求很严格。后来发现市面上有现成的红外编解码模块通过串口就能控制特别方便。今天我就用立创·天空星STM32F407开发板带大家一步步实现红外信号的接收和发射。这个教程适合正在学习STM32或者想做物联网、智能家居遥控项目的朋友。我会从模块介绍、硬件连接、代码移植到实际测试手把手教你怎么用。即使你是嵌入式新手跟着做也能搞定。1. 认识红外编解码模块咱们先来看看今天要用到的核心部件——红外编解码模块。这个模块其实是个“翻译官”它把复杂的红外信号处理工作都做好了我们只需要通过简单的串口命令就能控制它。1.1 模块是什么能干什么这个模块集成了MCU、红外发射头和红外接收头。它的工作方式很简单发射时你通过串口发送指令模块就把指令转换成NEC格式的红外信号发射出去接收时模块的红外接收头收到信号后自动解码并通过串口把数据发给你注意NEC格式是红外遥控中最常用的协议之一市面上99%的电视、机顶盒、DVD、电风扇等家电都用这个格式。模块的核心特点工作电压5V需要外部供电供电电流100mA电流不小供电要充足控制距离6-10米实际距离受环境光线和发射功率影响控制方式串口通信TTL电平接口4Pin排针2.54mm间距1.2 模块引脚说明模块只有4个引脚接线特别简单引脚功能说明VCC电源正极接5V电源GND电源负极接地TXD串口发送接MCU的RX引脚RXD串口接收接MCU的TX引脚1.3 资料获取在开始之前你需要准备好模块的资料模块购买链接可以在淘宝搜索“红外解码模块 编码模块 红外无线通信 NEC码”资料下载百度网盘链接提取码n8ud资料里最重要的是数据手册里面详细说明了模块的通信协议。我刚开始用的时候没仔细看协议调了半天才发现指令格式不对这个坑大家要避开。2. 通信协议解析模块通过特定的串口协议工作理解这个协议是成功的关键。协议其实不复杂我给大家拆解一下。2.1 数据帧格式模块收发的都是16进制数据。比如A1要写成0XA1。一个完整的指令帧包含帧头通信地址默认是0XA1操作位告诉模块要做什么数据位具体的数据内容提示帧头地址是可以修改的。如果你忘记了自定义的地址可以用通用地址0XFA来修改。2.2 操作位定义操作位就像指令的类型码不同值代表不同功能操作位功能说明0XF1发送红外信号0XF2修改通信地址0XF3修改串口波特率2.3 反馈机制模块很贴心每次执行指令后都会给个反馈反馈数据说明0XF1发送成功0XF2串口地址修改成功0XF3波特率设置成功无返回指令接收错误或操作不成功举个例子发送FA F1 E0 FD FD后如果返回F1说明红外信号发送成功发送FA F3 02 00 00后如果返回F3说明波特率修改成功2.4 红外数据格式模块解码后输出的数据格式很简单就是三个字节用户码1用户码2命令码比如我用美的空调遥控器测试按下开机键后模块通过串口输出E0 FD FDE0是用户码1FD是用户码2FD是命令码开机发射时也只需要发送这三个字节的数据。3. 硬件连接与配置3.1 天空星开发板引脚选择天空星STM32F407有多个串口我们选择USART2因为它对应的引脚PA2和PA3正好是开发板引出的接线方便。引脚对应关系模块TXD → 天空星PA3USART2_RX模块RXD → 天空星PA2USART2_TX模块VCC → 5V电源模块GND → GND注意模块需要5V供电天空星开发板上有5V输出引脚可以直接使用。3.2 串口参数配置模块默认的串口参数是波特率9600数据位8位停止位1位校验位无这些参数在代码初始化时要保持一致。4. 代码移植与实现现在进入实战环节我会带你一步步把驱动代码移植到天空星开发板上。4.1 创建驱动文件首先在你的工程中创建两个文件bsp_infrared.c- 驱动源文件bsp_infrared.h- 驱动头文件4.2 头文件配置bsp_infrared.h头文件里主要定义引脚和函数声明#ifndef _BSP_INFRARED_H_ #define _BSP_INFRARED_H_ #include stm32f4xx.h // 引脚定义 #define BSP_INFRARED_TX_RCC RCC_AHB1Periph_GPIOA // TX端口时钟 #define BSP_INFRARED_RX_RCC RCC_AHB1Periph_GPIOA // RX端口时钟 #define BSP_INFRARED_RCC RCC_APB1Periph_USART2 // 串口2时钟 #define BSP_INFRARED_TX_PORT GPIOA // TX端口 #define BSP_INFRARED_RX_PORT GPIOA // RX端口 #define BSP_INFRARED_AF GPIO_AF_USART2 // 复用功能 #define BSP_INFRARED_TX_PIN GPIO_Pin_2 // TX引脚 #define BSP_INFRARED_TX_SOURCE GPIO_PinSource2 // TX引脚源 #define BSP_INFRARED_RX_PIN GPIO_Pin_3 // RX引脚 #define BSP_INFRARED_RX_SOURCE GPIO_PinSource3 // RX引脚源 #define BSP_INFRARED USART2 // 使用串口2 #define BSP_INFRARED_IRQ USART2_IRQn // 串口2中断 #define BSP_INFRARED_IRQHandler USART2_IRQHandler // 中断服务函数 #define USART2_RECEIVE_LENGTH 1024 // 串口接收缓冲区大小 // 全局变量声明 extern unsigned char infrared_recv_buff[USART2_RECEIVE_LENGTH]; extern uint16_t infrared_recv_length; extern unsigned char infrared_recv_flag; // 函数声明 void Infrared_GPIO_Init(uint32_t band_rate); void infrared_receive_clear(void); char Infrared_emission_cmd(unsigned char* Infrared_buff, char len); char modified_addr_cmd(unsigned int addr_value); char modified_baud_cmd(unsigned int baud_value); #endif4.3 初始化函数实现初始化函数要配置GPIO和串口参数我加了详细注释void Infrared_GPIO_Init(uint32_t band_rate) { GPIO_InitTypeDef GPIO_InitStructure; // 1. 使能GPIO时钟 RCC_AHB1PeriphClockCmd(BSP_INFRARED_TX_RCC, ENABLE); RCC_AHB1PeriphClockCmd(BSP_INFRARED_RX_RCC, ENABLE); // 2. 配置引脚复用为串口功能 GPIO_PinAFConfig(BSP_INFRARED_TX_PORT, BSP_INFRARED_TX_SOURCE, BSP_INFRARED_AF); GPIO_PinAFConfig(BSP_INFRARED_RX_PORT, BSP_INFRARED_RX_SOURCE, BSP_INFRARED_AF); // 3. 配置TX引脚PA2 GPIO_StructInit(GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin BSP_INFRARED_TX_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF; // 复用模式 GPIO_InitStructure.GPIO_Speed GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_OType GPIO_OType_PP; // 推挽输出 GPIO_InitStructure.GPIO_PuPd GPIO_PuPd_UP; // 上拉 GPIO_Init(BSP_INFRARED_TX_PORT, GPIO_InitStructure); // 4. 配置RX引脚PA3 GPIO_StructInit(GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin BSP_INFRARED_RX_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_OType GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd GPIO_PuPd_UP; GPIO_Init(BSP_INFRARED_RX_PORT, GPIO_InitStructure); // 5. 配置串口参数 USART_InitTypeDef USART_InitStructure; RCC_APB1PeriphClockCmd(BSP_INFRARED_RCC, ENABLE); // 使能串口2时钟 USART_DeInit(BSP_INFRARED); // 复位串口2 USART_StructInit(USART_InitStructure); USART_InitStructure.USART_BaudRate band_rate; // 波特率 USART_InitStructure.USART_WordLength USART_WordLength_8b; // 8位数据 USART_InitStructure.USART_StopBits USART_StopBits_1; // 1位停止位 USART_InitStructure.USART_Parity USART_Parity_No; // 无校验 USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx; // 收发模式 USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; // 无硬件流控 USART_Init(BSP_INFRARED, USART_InitStructure); // 6. 清除接收标志位 USART_ClearFlag(BSP_INFRARED, USART_FLAG_RXNE); // 7. 使能接收中断 USART_ITConfig(BSP_INFRARED, USART_IT_RXNE, ENABLE); // 8. 使能串口 USART_Cmd(BSP_INFRARED, ENABLE); // 9. 配置中断优先级 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel BSP_INFRARED_IRQ; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; // 主优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; // 子优先级 NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); }4.4 核心功能函数4.4.1 发送红外信号函数这个函数是最常用的我把它写成了可以直接调用的接口char Infrared_emission_cmd(unsigned char* Infrared_buff, char len) { unsigned char send_data[5] {0}; // 必须初始化 unsigned int time_out 1000; // 超时时间1000ms // 检查数据长度必须是3个字节用户码1用户码2命令码 if(len ! 3) return 100; // 长度错误 // 构建发送数据帧 send_data[0] 0XA1; // 设备地址默认 send_data[1] 0XF1; // 操作位发射红外信号 send_data[2] Infrared_buff[0]; // 用户码1 send_data[3] Infrared_buff[1]; // 用户码2 send_data[4] Infrared_buff[2]; // 命令码 // 清除之前的接收数据 infrared_receive_clear(); // 发送数据 infrared_send_hex(send_data, 5); // 等待模块回应带超时机制 while(infrared_recv_flag ! 1 time_out 0) { time_out--; delay_ms(1); // 延时1ms } // 处理响应 if(time_out 0) // 没有超时 { infrared_recv_flag 0; // 清除标志位 // 检查是否收到发送成功的响应 if(infrared_recv_buff[0] 0XF1) return 1; // 发送成功 else return 2; // 接收的数据不对 } return 0; // 接收超时 }注意这里有个重要的细节——发送前要先清除接收缓冲区。因为模块可能会残留之前的数据不清除的话会影响判断。4.4.2 串口接收中断服务函数模块的反馈数据是通过中断接收的void BSP_INFRARED_IRQHandler(void) { // 检查是否是接收中断 if(USART_GetITStatus(BSP_INFRARED, USART_IT_RXNE) SET) { // 读取接收到的数据 infrared_recv_buff[infrared_recv_length] USART_ReceiveData(BSP_INFRARED); // 添加字符串结束符方便调试打印 infrared_recv_buff[infrared_recv_length-1] \0; // 设置接收完成标志 infrared_recv_flag 1; // 清除中断标志 USART_ClearITPendingBit(BSP_INFRARED, USART_IT_RXNE); } }4.4.3 辅助函数还有一些基础的发送和清除函数// 发送一个字节 void infrared_send_byte(uint8_t ucch) { USART_SendData(BSP_INFRARED, ucch); while(RESET USART_GetFlagStatus(BSP_INFRARED, USART_FLAG_TXE)) {} } // 发送多个字节 void infrared_send_hex(uint8_t *ch, int len) { while(len--) { infrared_send_byte(*ch); } } // 清除接收缓冲区 void infrared_receive_clear(void) { unsigned int i 0; for(i 0; i USART2_RECEIVE_LENGTH; i) { infrared_recv_buff[i] 0; } infrared_recv_length 0; infrared_recv_flag 0; }5. 实际测试与应用5.1 主函数测试代码移植完成后我们来写个简单的测试程序#include board.h #include bsp_uart.h #include stdio.h #include bsp_infrared.h // 美的空调开机红外码实测数据 unsigned char Midea_Open[3] {0xE0, 0xFD, 0xFD}; int main(void) { // 1. 开发板初始化 board_init(); // 2. 初始化调试串口用于打印信息 uart1_init(9600); // 3. 初始化红外模块串口 Infrared_GPIO_Init(9600); printf(红外模块测试开始\r\n); while(1) { // 4. 发送红外信号 char result Infrared_emission_cmd(Midea_Open, 3); // 5. 打印发送结果 printf(发送结果: ); switch(result) { case 0: printf(超时未响应\r\n); break; case 1: printf(发送成功\r\n); break; case 2: printf(响应数据错误\r\n); break; case 100: printf(数据长度错误\r\n); break; default: printf(未知错误\r\n); break; } // 延时2秒再发送 delay_ms(2000); } }5.2 测试方法我建议用两种方法测试方法一双模块测试推荐模块A接天空星开发板用于发射模块B接USB转TTL连到电脑串口助手天空星发送红外信号在电脑上查看模块B接收到的数据方法二实际设备测试找一个支持NEC协议的红外设备如电视、空调先用模块学习设备的遥控器编码再用天空星控制模块发射该编码看设备是否有响应5.3 常见问题排查在实际使用中我遇到过这些问题大家可以参考模块无响应检查电源模块需要5V供电电流要足够检查接线TX/RX是否接反了检查波特率确保是9600发送成功但设备没反应确认红外码是否正确先用模块学习原遥控器的码值检查发射距离不要太远中间不要有遮挡确认设备支持NEC协议接收数据乱码检查地线是否接好检查串口参数设置尝试降低波特率测试6. 进阶应用掌握了基础功能后你可以尝试这些进阶应用6.1 红外学习功能你可以做一个红外学习器让模块进入学习模式用各种遥控器对着模块按键把学习到的码值保存到Flash或EEPROM需要时再调出发射6.2 多设备控制通过修改设备地址你可以控制多个红外模块// 修改模块地址为0xA2 char result modified_addr_cmd(0xA2); if(result 1) { printf(地址修改成功\r\n); }6.3 波特率调整如果通信距离远或有干扰可以调整波特率// 修改波特率为19200 char result modified_baud_cmd(19200); if(result 1) { printf(波特率修改成功需要重新初始化串口\r\n); Infrared_GPIO_Init(19200); // 重新初始化 }注意修改波特率后模块和MCU的串口都要重新配置为相同的波特率。这个红外模块用起来真的很方便省去了自己写红外协议的麻烦。我在智能家居项目里用它控制空调、电视、灯光稳定性很好。关键是代码结构清晰移植到其他STM32平台也很容易。如果你在移植过程中遇到问题重点检查串口配置和协议格式这两个地方最容易出错。

相关新闻