STM32F103xx红外接收处理与红外技术详解
STM32F103c6t6+红外遥控+红外接收模块
前言
红外传感只占用了STM32的一个引脚(外部中断引脚)就可以实现无数的数据传输,单片机也就对不同的红外数据进行接收与程序存储的信息进行比较,再在执行主程序·····
下面是本人对红外接收的处理,以及相应的一些简单的主函数执行。
一、什么红外通信协议
1.1、NEC 协议
1.1-1,NEC 协议 位 的定义
如何判断数据的一个位是 “1” 还是 “0” :

1.1-2,红外遥控以NEC 协议数据“单发”的格式
一个正常的数据包 :
1.1-3,红外遥控以NEC 协议“连发”数据格式
连续发送数据包 :
1.1-4捕获红外信号的思路
二、红外接收程序
2.1、由外部中断接收红外信号
2.1-1、GUA_Infrared_Receiver.c 文件
处理红外接收的函数
GUA_U8 GUA_Infrared_Receiver_Process(void)
红外接收处理出错时执行
/6 else 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;
}
跳转到该子函数执行串口显示
/5 Show_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(); //初始化IO
GUA_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或<8ms
nGUA_Time_Num = GUA_Infrared_Receiver_GetLowLevelTime();
if((nGUA_Time_Num > 500) || (nGUA_Time_Num < 400))
{
//1 Serial_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
{
//2 Serial_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.40ms
nGUA_Time_Num = GUA_Infrared_Receiver_GetLowLevelTime();
if((nGUA_Time_Num > 60) || (nGUA_Time_Num < 20))
{
//3 Serial_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{
//4 Serial_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);
//5 Show_Data(); //显示红外接收到的数据
}
/*6 else 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 文件
//******************************************************************************
//name: GUA_Infrared_Receiver.h
//******************************************************************************
#ifndef _GUA_INFRARED_RECEIVER_H_
#define _GUA_INFRARED_RECEIVER_H_
#include "main.h"
.............略了一些定义
.............
//红外接收引脚
#define GUA_INFRARED_RECEIVER_PORT GPIOB
#define GUA_INFRARED_RECEIVER_PIN GPIO_Pin_5
#define GUA_INFRARED_RECEIVER_RCC RCC_APB2Periph_GPIOB
//中断
#define GUA_INFRARED_RECEIVER_EXTI_LINE EXTI_Line5
#define GUA_INFRARED_RECEIVER_PORTSOURCE GPIO_PortSourceGPIOB
#define GUA_INFRARED_RECEIVER_PINSOURCE GPIO_PinSource5
#define TRUE 0
#define FALSE 1
#define GUA_INFRARED_RECEIVER_OK 0
#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_POWER 0xA2
#define IR_MODE 0x62
#define IR_MUTE 0xE2
#define IR_START_STOP 0x22
#define IR_PREVIOUS 0x02
#define IR_NEXT 0xC2
#define IR_EQ 0xE0
#define IR_VOL_MINUS 0xA8
#define IR_VOL_ADD 0x90
#define IR_0 0x68
#define IR_RPT 0x98
#define IR_USD 0xB0
#define IR_1 0x30
#define IR_2 0x18
#define IR_3 0x7A
#define IR_4 0x10
#define IR_5 0x38
#define IR_6 0x5A
#define IR_7 0x42
#define IR_8 0x4A
#define IR_9 0x52
#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到9
Serial_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之红外接收