我使用的模块是stm32F103R8T6,配合STM32CudeMX实现的红外遥控串口通讯
一、 红外编码
1、实物设备
发射管(在遥控器上面):
接收管(和主控芯片具有定时器输入捕获功能的I/O引脚连接在一起):
遥控器:
2、基本原理
调制:将数据能够发送的更远,并且数据的损耗在合理范围,大致过程如下的1–》2–》3。
解调:将接收到的已调数据重新恢复并获取出来,大致过程如下的3–》2–》1。
红外通信是利用950nm近红外波段的红外线作为传递信息的媒体, 即通信信道。
发送端采用脉时调制(PPM) 方式, 将二进制数字信号调制成某一频率的脉冲序列, 并驱动红外发射管以光脉冲的形式发送出去;
接收端将接收到的光脉转换成电信号, 再经过放大、 滤波等处理后送给解调电路进行解调, 还原为二进制数字信号后输出。
简而言之, 红外通信的实质就是对二进制数字信号进行调制与解调, 以便利用红外信道进行传输, 红外通信接口就是针对红外信道的调制解调器。
3、红外编码原理
NEC编码原理(Philips RC5协议)
NEC编码的一帧(通常按一下遥控器按钮所发送的数据) 由引导码、 地址码及数据码组成,如下图所示, 把地址码及数据码取反的作用是加强数据的正确性。
NEC 协议通过脉冲串之间的时间间隔来实现信号的调制(英文简写PPM)。逻辑“0”是由0.56ms的38KHZ载波和0.560ms的无 载波间隔组成;逻辑“1”是由0.56ms的38KHZ载波和1.68ms的无载波间隔组成;结束位是0.56ms的38K载波。
二、红外遥控模块程序
//红外遥控模块头文件
#ifndef __IR_H
#define __IR_H
#include "main.h"
#define IR_TIM TIM2
#define MAX_DATALEN 5
typedef struct{
uint8_t mode;
uint8_t Ir_Data[MAX_DATALEN];
uint16_t Ir_Length;
}IR_DATA_TypeDef;
extern uint8_t IR_code;
uint8_t Ir_RecvAnalysis(void);
#endif
//红外遥控模块函数实现代码
#include "ir.h"
#define RCKeyNum 17
#define RCIRLength 4
uint8_t IR_key[17] ="123456789*0#+LOR-";
uint8_t RemoteControl_Table[RCKeyNum][RCIRLength] = {
{0x00,0xFF,0x45,0xBA},
{0x00,0xFF,0x46,0xB9},
{0x00,0xFF,0x47,0xB8},
{0x00,0xFF,0x44,0xBB},
{0x00,0xFF,0x40,0xBF},
{0x00,0xFF,0x43,0xBC},
{0x00,0xFF,0x07,0xF8},
{0x00,0xFF,0x15,0xEA},
{0x00,0xFF,0x09,0xF6},
{0x00,0xFF,0x16,0xE9},
{0x00,0xFF,0x19,0xE6},
{0x00,0xFF,0x0D,0xF2},
{0x00,0xFF,0x18,0xE7},
{0x00,0xFF,0x08,0xF7},
{0x00,0xFF,0x1C,0xE3},
{0x00,0xFF,0x5A,0xA5},
{0x00,0xFF,0x52,0xAD},
};
uint16_t ir_buf[1024] = {0};
uint16_t ir_count = 0;
uint16_t ir_flag = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim==&htim2)
{
ir_flag = 1;
Ir_RecvAnalysis(); //接收红外信号
}
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2 )
{
if(ir_flag == 0)
{
ir_buf[ir_count++] = IR_TIM->CCR2;
__HAL_TIM_SET_COUNTER(&htim2,0);
IR_TIM->CCER ^= (1<<5);
}
}
}
//判断数据范围函数
//数据在给定的范围之内:返回1
//数据在给定的范围之外:返回0
//time1:待判断时间
//time2:标准时间
uint8_t Time_Range(uint16_t time1, uint16_t time2, uint16_t range1, uint16_t range2)
{
if((time1 > (time2-range1)) && (time1 < (time2+range2)))
return 1;
else
return 0;
}
//校验遥控器按键
//正确,返回遥控器按键所对应的数组行号
//错误 -1 按键地址错误 不是这个遥控器
//错误 -2 地址正确,但是当前版本遥控器没有这个按键值
int Check_RemoteControlKey(uint8_t *buff)
{
uint8_t i=0;
int retn;
// if(buff[0] != RemoteControl_Table[0][0] || buff[1] != RemoteControl_Table[0][1])
if((buff[0]!=0x00) || (buff[1]!=0xFF))
retn = -1;//地址错误
for(i=0; i<RCKeyNum; i++)
{
if((buff[2]==RemoteControl_Table[i][2]) && (buff[3]==RemoteControl_Table[i][3]))
{
retn = i;
break;
}
}
if(i>=RCKeyNum) retn = -2;
return retn;
}
//返回值
//0 解析成功
//1 没有接收完成
//2 引导码错误
//3 前半段数据错误
//4 后半段数据错误
IR_DATA_TypeDef ir = {0};
uint8_t IR_code = 0XFF;
uint8_t Ir_RecvAnalysis(void)
{
uint16_t i = 0;
uint8_t err;
if(ir_flag == 0) return 1;
// for(i=0; i<ir_count; i++)
// printf("%d\r\n",ir_buf[i]);
if(Time_Range(ir_buf[1],9000,1000,1000) == 0){
err = 2;
goto error;
}
if(Time_Range(ir_buf[2],4500,500,500) == 0){
err = 2;
goto error;
}
for(i=3; i<ir_count-1; i++)
{
if(Time_Range(ir_buf[i],560,200,200))
{
i++;
if(Time_Range(ir_buf[i],560,200,200))
{
//数据0 0-7 data[0] 8-15 data[1]
//先收到低位保存到低位,也可以反着
ir.Ir_Data[ir.Ir_Length/8] &= ~(1<<(ir.Ir_Length%8));
ir.Ir_Length++;
}
else if(Time_Range(ir_buf[i],1690,200,200)==1)
{
//数据1
ir.Ir_Data[ir.Ir_Length/8] |= (1<<(ir.Ir_Length%8));
ir.Ir_Length++;
}
else{
err = 4;
goto error;
}
}
else{
err = 3;
goto error;
}
}
// for(i=0; i<ir.Ir_Length/8; i++)
//
// printf("%02X\t",ir.Ir_Data[i]);
// printf("\r\n");
if(Check_RemoteControlKey(ir.Ir_Data) < 17 && Check_RemoteControlKey(ir.Ir_Data) >=0)
{
IR_code = IR_key[Check_RemoteControlKey(ir.Ir_Data)];
memset(ir_buf,0,sizeof(ir_buf));
memset(ir.Ir_Data,0,sizeof(ir.Ir_Data));
ir.Ir_Length = 0;
ir_count = 0;
ir_flag = 0;
return 0;
}
error:
memset(ir_buf,0,sizeof(ir_buf));
memset(ir.Ir_Data,0,sizeof(ir.Ir_Data));
ir.Ir_Length = 0;
ir_count = 0;
ir_flag = 0;
// TIM_Cmd(TIM2,ENABLE);
HAL_TIM_Base_Start_IT(&htim2);
if(err) return err;
else return 0;
}