STM32F103c6t6+红外遥控+红外接收模块
前言一、什么红外通信协议1.1、NEC 协议1.1-1,NEC 协议 位 的定义1.1-2,红外遥控以NEC 协议数据“单发”的格式1.1-3,红外遥控以NEC 协议“连发”数据格式1.1-4捕获红外信号的思路 二、红外接收程序2.1、由外部中断接收红外信号2.1-1、GUA_Infrared_Receiver.c 文件2.1-2、GUA_Infrared_Receiver.h 文件2.1-3、main.c 文件 效果演示相关参考资料链接前言
红外传感只占用了STM32的一个引脚(外部中断引脚)就可以实现无数的数据传输,单片机也就对不同的红外数据进行接收与程序存储的信息进行比较,再在执行主程序·····
下面是本人对红外接收的处理,以及相应的一些简单的主函数执行。
一、什么红外通信协议
1.1、NEC 协议
8位地址码和8位用户码;增加8位地址反码和8位用户反码的传输(以确保可靠性);PWM脉冲位置调制,以发射红外载波的占空比代表“0”和“1”;载波频率为38Khz;1.1-1,NEC 协议 位 的定义
如何判断数据的一个位是 “1” 还是 “0” :
一个逻辑 “1” 传输需要2.25ms(560us脉冲+1680us低电平);一个逻辑 “0” 的传输需要1.125ms(560us脉冲+560us低电平);所以,每位的周期为 2.25ms 或者 1.12ms ,(分别代表“1”和“0”)
1.1-2,红外遥控以NEC 协议数据“单发”的格式
一个正常的数据包 :
开头:发送的是9ms高电平+4.5ms低电平。为引导码。紧接着是:8bit的地址码+8bit地址反码+8bit用户码+8bit用户反码。为32位的数据。
1.1-3,红外遥控以NEC 协议“连发”数据格式
连续发送数据包 :
长按红外遥控按键时,每隔 110ms 重复发送一次。但是命令只发送一次,重复发送的是 9ms 高电平+2.25ms 低电平+0.56ms 高电平+低电平
1.1-4捕获红外信号的思路
接收程序思路分析: STM32红外接收分析关于理解 NEC 协议+程序设置思路: 红外遥控+定时器计时+定时器实现解码红外NEC协议: STM32定时器实现红外接收与解码二、红外接收程序
2.1、由外部中断接收红外信号
2.1-1、GUA_Infrared_Receiver.c 文件
针对大佬的代码进行了自己的理解与修改,增加了解译红外遥控发送的32位数据(地址码+地址反码+用户码+用户反码)的函数。这里最核心的,属红外接收处理时对高低电平时间的判断,经过不断的修正电平维持时间,就可以将红外遥控发送的数据捕获到。针对不同的遥控可能因为单片机频率和红外接收模块各因素,需要修改判断电平维持时间的范围。处理红外接收的函数
GUA_U8 GUA_Infrared_Receiver_Process(void)
在红外接收处理函数处,我增加串口输出的功能,每到一个接收出错退出循环的同时将上次计时的值从串口打印出来,由此知道红外接收输入引脚上电平变化的时间间隔。对不同的遥控按键发送的协议解读有实用价值。
红外接收处理出错时执行
/6else if(GUA_Infrared_Receiver_Process() == GUA_INFRARED_RECEIVER_ERROR){//否则、串口显示已经累加的时间(uS)和 解码错误提示Serial_SendNumber(nGUA_Time_Num,4);Serial_SendString(" GUA_INFRARED_RECEIVER_ERROR \r\n");gGUA_InfraredReceiver_Data=0;}
还有,红外发送过来的键码解读成功后,将其 地址码+地址反码+用户码+用户反码 都打印在串口上,由此,可以看到用户码(命令键)的十进制,再换算成十六进制后就可以进行比较与传递。跳转到该子函数执行串口显示
/5Show_Data(); //显示红外接收到的数据
这里对于红外连续发送时的处理比较随便,但是效果还是达到了。不符合该函数判断的红外协议并不会被捕获到。
四重对电平维持时间判断关卡决定了数据的保存与否。
#include "GUA_Infrared_Receiver.h"/*********************外部变量************************/GUA_U32 gGUA_InfraredReceiver_Data = 0; //一个接收红外原始数据的变量(32位)/********************保存红外数据的变量*************/uint8_t IR_RECEIVE[5]; // 每一项保存一个字节数据int down16,up16; // 分别保存低十六位和高十六位数据uint8_t IR_State;uint8_t IR_DataFlag; //单发标志uint8_t IR_RepeatFlag; //连发标志/*********************内部函数************************/static void GUA_Infrared_Receiver_IO_Init(void);static GUA_U16 GUA_Infrared_Receiver_GetHighLevelTime(void);static GUA_U16 GUA_Infrared_Receiver_GetLowLevelTime(void);//****************************************************************************** //name: GUA_Infrared_Receiver_IO_Init //introduce: 红外接收的IO初始化 static void GUA_Infrared_Receiver_IO_Init(void){GPIO_InitTypeDef GPIO_InitStructure;//IO结构体////失能JTAG和SWD在PB3上的功能使用//GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);//GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//时钟使能RCC_APB2PeriphClockCmd(GUA_INFRARED_RECEIVER_RCC | RCC_APB2Periph_AFIO, ENABLE);//红外接收IO配置GPIO_InitStructure.GPIO_Pin = GUA_INFRARED_RECEIVER_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GUA_INFRARED_RECEIVER_PORT, &GPIO_InitStructure); GPIO_EXTILineConfig(GUA_INFRARED_RECEIVER_PORTSOURCE, GUA_INFRARED_RECEIVER_PINSOURCE); }//****************************************************************************** //name: GUA_Infrared_Receiver_Exti_Init//introduce: 红外接收的IO中断初始化 //******************************************************************************static void GUA_Infrared_Receiver_Exti_Init(void){NVIC_InitTypeDef NVIC_InitStructure;EXTI_InitTypeDef EXTI_InitStructure;//中断配置EXTI_InitStructure.EXTI_Line = GUA_INFRARED_RECEIVER_EXTI_LINE;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling ; EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);}//****************************************************************************** //name: GUA_Infrared_Receiver_Init //introduce: 红外接收初始化 //****************************************************************************** void GUA_Infrared_Receiver_Init(void){GUA_Infrared_Receiver_IO_Init(); //初始化IOGUA_Infrared_Receiver_Exti_Init(); //初始化中断配置}//****************************************************************************** //name: GUA_Infrared_Receiver_GetHighLevelTime //introduce: 红外接收获取高电平维持时间//****************************************************************************** static GUA_U16 GUA_Infrared_Receiver_GetHighLevelTime(void){GUA_U16 nGUA_Num = 0;//判断是否一直为高电平while(GPIO_ReadInputDataBit(GUA_INFRARED_RECEIVER_PORT, GUA_INFRARED_RECEIVER_PIN) == Bit_SET){//超时超时溢出if(nGUA_Num >= 250) {return nGUA_Num;}nGUA_Num++; //计延时20us的次数Delay_us(20);//该延时针对外部晶振8000的STM32C8T6核心板,对于不同类型不同外部晶振的开发板需要根据波形图调整延时,保证20us延时 }return nGUA_Num;}//****************************************************************************** //name: GUA_Infrared_Receiver_GetLowLevelTime //introduce: 红外接收获取低电平维持时间//****************************************************************************** static GUA_U16 GUA_Infrared_Receiver_GetLowLevelTime(void){GUA_U16 nGUA_Num = 0;//判断是否一直为低电平while(GPIO_ReadInputDataBit(GUA_INFRARED_RECEIVER_PORT, GUA_INFRARED_RECEIVER_PIN) == Bit_RESET){if(nGUA_Num >= 500) {return nGUA_Num;}nGUA_Num++;delay_us(20);//该延时针对外部晶振8000的STM32C8T6核心板,对于不同类型不同外部晶振的开发板需要根据波形图调整延时,保证低电平延时 }return nGUA_Num;}//****************************************************************************** //name: GUA_Infrared_Receiver_Process //introduce: 红外接收的处理函数 // 该函数是关键所在:处理红外信号看接收管OUT引脚电平维持时间,高低电平一定时间后状态变化。// 依据电平维持时间,判断:引导码、32位数据···//******************************************************************************GUA_U16 nGUA_Time_Num;GUA_U8 nGUA_Data;GUA_U8 nGUA_Byte_Num;GUA_U8 nGUA_Bit_Num;GUA_U32 ContinuousReceiver_Data; //保存上次捕获的32位原始的数据//GUA_U8 GUA_Infrared_Receiver_Process(void){nGUA_Time_Num = 0;nGUA_Data = 0;nGUA_Byte_Num = 0;nGUA_Bit_Num = 0;//接收引导码9ms的低电平,过滤无用信号>10ms或<8msnGUA_Time_Num = GUA_Infrared_Receiver_GetLowLevelTime();if((nGUA_Time_Num > 500) || (nGUA_Time_Num < 400)){//1Serial_SendString("\r\n 1: error occurred \r\n");//_________return GUA_INFRARED_RECEIVER_ERROR;} //接收引导码4.5ms的高电平,过滤无用信号>5ms或<4ms//250ms时是连续发送信号nGUA_Time_Num = GUA_Infrared_Receiver_GetHighLevelTime();if((nGUA_Time_Num > 250) || (nGUA_Time_Num < 100)) //200<= >=250{//2Serial_SendString("\r\n 2: error occurred \r\n");//_________return GUA_INFRARED_RECEIVER_ERROR;} //接收4字节数据(分别有:用户反码、用户码、地址反码、地址码)for(nGUA_Byte_Num = 0; nGUA_Byte_Num < 4; nGUA_Byte_Num++){//接收位数据(每字节8位)for(nGUA_Bit_Num = 0; nGUA_Bit_Num < 8; nGUA_Bit_Num++){//接收每bit的前0.56ms的低电平,过滤无用信号>1.2ms或<0.40msnGUA_Time_Num = GUA_Infrared_Receiver_GetLowLevelTime();if((nGUA_Time_Num > 60) || (nGUA_Time_Num < 20)){//3Serial_SendString("\r\n 3: error occurred \r\n");//_________return GUA_INFRARED_RECEIVER_ERROR;}//接收每bit的后高电平时长:高电平数据,1.68ms(1.2ms~2.0ms),低电平数据,0.56ms(0.2ms~1ms),过滤其他无用信号nGUA_Time_Num = GUA_Infrared_Receiver_GetHighLevelTime();if((nGUA_Time_Num >=60) && (nGUA_Time_Num < 100)) //25 50{nGUA_Data = 1;}else if((nGUA_Time_Num >=10) && (nGUA_Time_Num < 50)) //50 90{nGUA_Data = 0;}else{if(nGUA_Time_Num == 250) //红外连续发送情况下,退出循环直接赋予上次的数据{IR_RepeatFlag = 1;//连续收发标志置一IR_DataFlag = 0;//单发标志置零gGUA_InfraredReceiver_Data = ContinuousReceiver_Data;return GUA_INFRARED_RECEIVER_OK; }else{//4Serial_SendString("\r\n 4: error occurred \r\n");//_________return GUA_INFRARED_RECEIVER_ERROR;}}//保存数据gGUA_InfraredReceiver_Data <<= 1; gGUA_InfraredReceiver_Data |= nGUA_Data; }}IR_DataFlag = 1;//单次收发标志置一IR_RepeatFlag = 0; //连发标志置零ContinuousReceiver_Data = gGUA_InfraredReceiver_Data;return GUA_INFRARED_RECEIVER_OK; }//*******************************************************************************//》》》》》》》》》》》》》》中断处理函数《《《《《《《《《《《《《《《《《《void EXTI9_5_IRQHandler(void){if(EXTI_GetITStatus(GUA_INFRARED_RECEIVER_EXTI_LINE) != RESET){EXTI_ClearITPendingBit(GUA_INFRARED_RECEIVER_EXTI_LINE);EXTI->IMR &= ~(GUA_INFRARED_RECEIVER_EXTI_LINE);if(GUA_Infrared_Receiver_Process() == GUA_INFRARED_RECEIVER_OK) //如果捕获到有效数据{down16=gGUA_InfraredReceiver_Data;up16=gGUA_InfraredReceiver_Data>>16;//-----------从上到下依次为用户反码、用户码、地址反码、地址码IR_RECEIVE[3]=down16;IR_RECEIVE[2]=(down16>>8);IR_RECEIVE[1]=up16;IR_RECEIVE[0]=(up16>>8);//5Show_Data(); //显示红外接收到的数据}/*6else if(GUA_Infrared_Receiver_Process() == GUA_INFRARED_RECEIVER_ERROR){ //否则、串口显示已经累加的时间(uS)和 解码错误提示Serial_SendNumber(nGUA_Time_Num,4);Serial_SendString(" GUA_INFRARED_RECEIVER_ERROR \r\n");gGUA_InfraredReceiver_Data=0;}*/}EXTI->IMR|=(GUA_INFRARED_RECEIVER_EXTI_LINE);}/***********************调试键码的函数(串口输出)***********************************函数: 显示出已经捕获得到的红外数据,* 如果解码成功,可以获得红外数据(地址码+地址反码+用户码+用户反码)* (通过串口发送相关信息,使用Serial_SendNumber()可以在调试窗口文本类得到十进制格式,* 再将数据十进制换算成十六进制就可以进行比较和传递。)*参数:无**/void Show_Data(void){//Serial_SendArray(IR_RECEIVE,32);Serial_SendString("\r\n GUA_INFRARED_RECEIVER_OK ");Serial_SendString("\r\n 用户反码: ");Serial_SendNumber( IR_RECEIVE[3],4);Serial_SendString("\r\n 用户码: ");Serial_SendNumber( IR_RECEIVE[2],4);Serial_SendString("\r\n 地址反码: ");Serial_SendNumber( IR_RECEIVE[1],4);Serial_SendString("\r\n 地址码: ");Serial_SendNumber( IR_RECEIVE[0],4);}/************************************五个可获取红外相关数据的函数************************************ @brief 红外遥控获取收到数据帧标志位* @param 无* @retval 是否收到数据帧,1为收到,0为未收到*/uint8_t IR_GetDataFlag(void){if(IR_DataFlag){IR_DataFlag=0;return 1;}return 0;}/*** @brief 红外遥控获取收到连发帧标志位* @param 无* @retval 是否收到连发帧,1为收到,0为未收到*/uint8_t IR_GetRepeatFlag(void){if(IR_RepeatFlag){IR_RepeatFlag=0;return 1;}return 0;}/*** @brief 红外遥控获取收到的地址数据* @param 无* @retval 收到的地址码数据*/uint8_t IR_GetAddress(void){return IR_RECEIVE[0];}/*** @brief 红外遥控获取收到的命令数据* @param 无* @retval 收到的用户码数据*/uint8_t IR_GetUserCode(void){return IR_RECEIVE[2];}/**** @brief 为获得的红外用户码(命令)数据赋予一个数字身份* @param 无* @retval 收到的用户码的新身份*/uint8_t IR_CommandSymbol(void){unsigned char Num;switch(IR_RECEIVE[2]){case IR_0: Num=0;break; //0 代表 0键:case IR_1: Num= 1;break; //1 代表 1键:case IR_2: Num= 2;break; //2 代表 2键:case IR_3: Num= 3;break; //3 代表 3键: case IR_4: Num= 4;break; //4 代表 4键:case IR_5: Num= 5;break; //5 代表 5键:case IR_6: Num= 6;break; //6 代表 6键:case IR_7: Num= 7;break; //7 代表 7键:case IR_8: Num= 8;break; //8 代表 8键:case IR_9: Num= 9;break; //9 代表 9键:case IR_POWER: Num= 11;break; //11 代表 OnOff键:case IR_MODE: Num= 12;break; //12 代表 Mode键:case IR_MUTE: Num= 13;break; //13 代表 Mute键:case IR_START_STOP: Num= 21;break; //21 代表 Pause键:case IR_PREVIOUS: Num= 22;break; //22 代表 Left键:case IR_NEXT: Num= 23;break; //23 代表 Right键:case IR_EQ: Num= 31;break; //31 代表 EQ键:case IR_VOL_MINUS: Num= 32;break; //32 代表 VolADD键case IR_VOL_ADD: Num= 33;break; //33 代表 VolDOW键case IR_RPT: Num= 42;break; //42 代表 RPT键:case IR_USD: Num= 43;break;//43 代表 U_SD键:default :break;}return Num;}
2.1-2、GUA_Infrared_Receiver.h 文件
得到的红外遥控键码(用户码)进行了(十六进制的)宏定义。而主要利用的是,我将用户码进行了重新的命名,针对不同的按键__相应有代表数字。其由函数:uint8_t IR_CommandSymbol(void) 来实现。如何利用这些函数,请看主函数。//****************************************************************************** //name: GUA_Infrared_Receiver.h//****************************************************************************** #ifndef _GUA_INFRARED_RECEIVER_H_#define _GUA_INFRARED_RECEIVER_H_#include "main.h".............略了一些定义.............//红外接收引脚#define GUA_INFRARED_RECEIVER_PORTGPIOB#define GUA_INFRARED_RECEIVER_PINGPIO_Pin_5#define GUA_INFRARED_RECEIVER_RCCRCC_APB2Periph_GPIOB//中断#define GUA_INFRARED_RECEIVER_EXTI_LINEEXTI_Line5#define GUA_INFRARED_RECEIVER_PORTSOURCE GPIO_PortSourceGPIOB#define GUA_INFRARED_RECEIVER_PINSOURCEGPIO_PinSource5#define TRUE 0#define FALSE 1#define GUA_INFRARED_RECEIVER_OK0#define GUA_INFRARED_RECEIVER_ERROR 1/*********************外部变量************************/extern GUA_U32 gGUA_InfraredReceiver_Data;//一个接收红外原始数据的变量(32位)/********************* 函数声明 ************************/ extern void GUA_Infrared_Receiver_Init(void);extern GUA_U8 GUA_Infrared_Receiver_Process(void);void Show_Data(void);//**************************************************************//》》》》》》》》》》能在主函数里调用的函数《《《《《《《《《《《//**************************************************************uint8_t IR_GetDataFlag(void);// 是否按一次(单发)uint8_t IR_GetRepeatFlag(void); //是否为连按(连发)uint8_t IR_GetAddress(void);// 地址码uint8_t IR_GetUserCode(void);//用户码(按键命令)uint8_t IR_CommandSymbol(void); //将用户码转换出一个对应的数字身份//***********************************************************************//***********************************************************************//从“调试键码的函数”获得相应用户码,并转换位16进制后,对遥控发送的键码(用户码)进行宏定义#define IR_POWER0xA2#define IR_MODE0x62#define IR_MUTE0xE2#define IR_START_STOP0x22#define IR_PREVIOUS0x02#define IR_NEXT0xC2#define IR_EQ0xE0#define IR_VOL_MINUS0xA8#define IR_VOL_ADD0x90#define IR_00x68#define IR_RPT0x98#define IR_USD0xB0#define IR_10x30#define IR_20x18#define IR_30x7A#define IR_40x10#define IR_50x38#define IR_60x5A#define IR_70x42#define IR_80x4A#define IR_90x52#endif
2.1-3、main.c 文件
很显然,IR_GetDataFlag()和 IR_GetRepeatFlag()两个函数是判断是否有接收到红外信号。IR_GetAddress()和IR_GetUserCode()两个函数就是分别获得红外数据中“地址码”和“用户码”。IR_CommandSymbol()函数是根据遥控器上按键对“用户码”进行了重新命名定义,就方便于使用与记忆。#include "main.h"uint8_t Num;uint8_t Address;uint8_t Command;uint8_t One_More =1,Command_early;uint8_t Information;void Click_Button_(void);void Continuous_Button_(void);int main(void){OLED_Init();LED_Init();Serial_Init();GUA_Infrared_Receiver_Init(); //红外接收初始化Serial_SendString("\r\n Is OK \r\n");OLED_ShowString(1, 1,"User Add CMD ");OLED_ShowString(2, 1,"0000 0000 00 ");LED2_OFF();LED2_OFF();while (1){if(IR_GetDataFlag()) //若红外单发{LED2_Turn();OLED_ShowNum(2,1,IR_GetUserCode(),4);OLED_ShowNum(2,6,IR_GetAddress(),4);OLED_ShowNum(2,12,IR_CommandSymbol(),2);if(((int )IR_CommandSymbol() >= 0) && ((int )IR_CommandSymbol() <= 9) ){Serial_SendString("\r\n 你按下了——键 "); // 打印按键0到9Serial_SendNumber(IR_CommandSymbol(),1);}else if(IR_CommandSymbol() == 6) //当然该判断是不会执行了,因为已经满足了前面一个的判断条件{Serial_SendString("\r\n 你按下的是键“6”");}else if(IR_CommandSymbol() == 11){Serial_SendString("\r\n 你按了开关键");}else if(IR_CommandSymbol() == 12){OLED_ShowString(3,1," Happy Happy");OLED_ShowString(4,2,"Happy every day");}else if(IR_CommandSymbol() == 22){Serial_SendString("\r\n 上一曲");}else if(IR_CommandSymbol() == 23){Serial_SendString("\r\n 下一曲");}}if(IR_GetDataFlag() || IR_GetRepeatFlag()) //若红外单发或连发{if(IR_CommandSymbol() == 32){Serial_SendString("\r\n 音量- -");}else if(IR_CommandSymbol() == 33){Serial_SendString("\r\n 音量+ +");}}}}
效果演示
红外接收视频演示
相关参考资料链接
1、典型的 NEC 协议传输格式:红外遥控原理
3、该参考程序的原版来源: STM32之红外接收
如果觉得《【STM32f103xx+红外接收处理+红外】》对你有帮助,请点赞、收藏,并留下你的观点哦!