51单片机STC89C52RC——17.1 红外线遥控器

avatar
作者
筋斗云
阅读量:2

目的/效果

LCD1602显示红外遥控按键值

一,STC单片机模块

二,红外线遥控器

2.1 简介

人的眼睛能看到的可见光按波长从长到短排列,依次为红、橙、黄、绿、青、蓝、紫。

光的波长和频率如下图

红外遥控是利用红外光进行通信的设备,由红外LED将调制后的信号发出,由专用的红外接收头进行解调输出。

通信方式:单工,异步

红外LED波长940nm

通信协议NEC标准

通常我们说的红外遥控器是由键盘电路、红外编码电 路、电源电路和红外发射电路组成。

2.2 电路 

发送模块电路图

接收模块电路图

2.3 NEC 协议

NEC 码的位定义

一个脉冲对应 560us 的连续载波,

一个逻辑 1 传输需要 2.25ms(560us 脉冲+1680us 低电平)

一个逻辑 0 的传输需要 1.125ms(560us 脉冲+560us 低电平),为逻辑“1”的一半。 

NEC协议采用PPM(Pulse Position Modulation,脉冲位置调制)的形式进行编码,数据的每一位(Bit)脉冲长度为560us,由38KHz的载波脉冲 (carrier burst) 进行调制,推荐的载波占空比为 1/3至 1/4。有载波脉冲的地方,其宽度都为 560us,而载波脉冲的间隔时间是不同的。

每次信息都是按照引导码 (9ms载波脉冲+4.5ms 空闲信号)地址码、地址反码、控制码和控制反码的格式进行传输,因此,单次信息传输的时间是固定不变的

当按键被一直按下时,红外遥控器只会发送一次完整的信息,其后会每隔 110ms 发送一次重复码(连发码)。重复码的数据格式比较简单,同样是由 9ms的载波脉冲开始,紧接着是2.25ms的空闲信号,随后是560us的载波脉冲。 

红外接收头通常被厂家集成在一个元件中,成为一体化红外接收头。红外接收头内部的三极管电路具有信号反向的功能,也就是将1变为0,0变为1,即数据0是0.56ms的低电平和0.56ms的高电平,数据1是0.5ms的低电平和1.69ms的高电平,9ms是高电平变为低电平。

总结:

空闲状态:红外LED不亮,接收头输出高电平

发送低电平:红外LED以38KHz频率闪烁发光,接收头输出低电平

发送高电平:红外LED不亮,接收头输出高电平 

2.4 代码构建逻辑

2.4.1 外部中断配置

由于红外线信号可能转瞬即逝,我们利用外部中断下降沿触发 ,计时用。有关中断和定时器的详细介绍详请参考51单片机STC89C52RC——6.1 中断系统》《51单片机STC89C52RC——6.2 定时器,这里我们不再做细述。

	IT0=1;	//打开定时器中断  下降沿触发 	IE0=0;	//中断请求标识位 	EX0=1;	//允许中断进入 	EA=1;	//打开总中断 	PX0=0;	//中断优先级 设置到最高

 

/**  * 函    数:定时器0初始化  * 参    数:无  * 返 回 值:无  */ void Timer0_Init(void) { 	TMOD &= 0xF0;		//设置定时器模式 	TMOD |= 0x01;		//设置定时器模式 	TL0 = 0;		//设置定时初值 	TH0 = 0;		//设置定时初值 	TF0 = 0;		//清除TF0标志 	TR0 = 0;		//定时器0不计数 }

2.4.2 数据接收

接收头内部已经调制解调过滤掉38KHz,所以可NEC直接解析数据。

注意:低位在前,高位在后

只需要按照时长解析外部中断引脚的电平。两个下降沿之间的时长。

当中断进入时开始计时,到下一次中断进入时停止计时。数据流程如下

红外信号中断解码

 /**  * 函    数:外部中断0中断函数,下降沿触发执行  * 参    数:无  * 返 回 值:无  */ void Int0_Routine(void) interrupt 0 { 	if(IR_State==0)				//状态0,空闲状态 	{ 		Timer0_SetCounter(0);	//定时计数器清0 		Timer0_Run(1);			//定时器启动 		IR_State=1;				//置状态为1 	} 	else if(IR_State==1)		//状态1,等待Start信号或Repeat信号 	{ 		IR_Time=Timer0_GetCounter();	//获取上一次中断到此次中断的时间 		Timer0_SetCounter(0);	//定时计数器清0 		//如果计时为13.5ms,则接收到了Start信号(判定值在12MHz晶振下为13500,在11.0592MHz晶振下为12442) 		if(IR_Time>12442-500 && IR_Time<12442+500) 		{ 			IR_State=2;			//置状态为2 		} 		//如果计时为11.25ms,则接收到了Repeat信号(判定值在12MHz晶振下为11250,在11.0592MHz晶振下为10368) 		else if(IR_Time>10368-500 && IR_Time<10368+500) 		{ 			IR_RepeatFlag=1;	//置收到连发帧标志位为1 			Timer0_Run(0);		//定时器停止 			IR_State=0;			//置状态为0 		} 		else					//接收出错 		{ 			IR_State=1;			//置状态为1 		} 	} 	else if(IR_State==2)		//状态2,接收数据 	{ 		IR_Time=Timer0_GetCounter();	//获取上一次中断到此次中断的时间 		Timer0_SetCounter(0);	//定时计数器清0 		//如果计时为1120us,则接收到了数据0(判定值在12MHz晶振下为1120,在11.0592MHz晶振下为1032) 		if(IR_Time>1032-500 && IR_Time<1032+500) 		{ 			IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8));	//数据对应位清0 			IR_pData++;			//数据位置指针自增 		} 		//如果计时为2250us,则接收到了数据1(判定值在12MHz晶振下为2250,在11.0592MHz晶振下为2074) 		else if(IR_Time>2074-500 && IR_Time<2074+500) 		{ 			IR_Data[IR_pData/8]|=(0x01<<(IR_pData%8));	//数据对应位置1 			IR_pData++;			//数据位置指针自增 		} 		else					//接收出错 		{ 			IR_pData=0;			//数据位置指针清0 			IR_State=1;			//置状态为1 		} 		if(IR_pData>=32)		//如果接收到了32位数据 		{ 			IR_pData=0;			//数据位置指针清0 			if((IR_Data[0]==~IR_Data[1]) && (IR_Data[2]==~IR_Data[3]))	//数据验证 			{ 				IR_Address=IR_Data[0];	//转存数据 				IR_Command=IR_Data[2]; 				IR_DataFlag=1;	//置收到连发帧标志位为1 			} 			Timer0_Run(0);		//定时器停止 			IR_State=0;			//置状态为0 		} 	} }

三,创建Keil项目

详细参考:51单片机STC89C52RC——创建Keil项目-CSDN博客

四,代码 

完整代码参考《https://gitee.com/oopxiajun/STC89C52

#include <REGX52.H> #include "Delay.h" #include "LCD1602.h" #include "IR.h"   unsigned char Address; unsigned char Command;  void main() { 	LCD_Init(); 	LCD_ShowString(1,1,"Addr  Cmd  "); 	LCD_ShowString(2,1,"00    00   "); 	 	IR_Init(); 	 	while(1) 	{ 		if(IR_GetDataFlag() || IR_GetRepeatFlag())	//如果收到数据帧或者收到连发帧 		{ 			Address=IR_GetAddress();		//获取遥控器地址码 			Command=IR_GetCommand();		//获取遥控器命令码 			 			LCD_ShowHexNum(2,1,Address,2);	//显示遥控器地址码 			LCD_ShowHexNum(2,7,Command,2);	//显示遥控器命令码 		} 	} }  

五,代码编译、下载到51单片机

代码编译请参考

51单片机STC89C52RC——代码编译-CSDN博客

代码下载请参考

《51单片机STC89C52RC——STCAI-ISP代码下载-CSDN博客

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!