51单片机之红外通信

1.红外遥控简介

          红外遥控是一种无线、非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等显著优点。 由于红外线为不可见光,因此对环境影响很小,再由红外光波动波长远小于无线电波的波长,所以红外线遥控不会影响其他家用电器,也不会影 响临近的无线电设备。

         红外线是波长在760nm~1mm之间的非可见光。红外通信装置由红外发射管和红外接受管组成,红外发射管是能发射出红外线的发光二极管,发射强度随着电流的增大而增大;红外接受管是一个具有红外光敏感特征的PN节的光敏二极管,只对红外线有反应,产生光电流。

2.信号调制原理

    基带信号:从信号源发出没有经过调制的原始信号,特点是频率较低,信号频率从0开始,频谱较宽。
    调制:就是用待传送信号去控制某个高频信号的幅度、相位、频率等参量变化的过程,即用一个信号去装载另一个信号。

    红外遥控器使用38KB的载波对原始信号进行解调,原理如下:

调制后产生一定频段的高低电平,但红外接收头接受到的信号和调制后的信号电平相反。

3.NEC协议

  • 8 位地址和 8 位指令长度;
  • 地址和命令 2 次传输(确保可靠性)
  • PWM 脉冲位置调制,以发射红外载波的占空比代表“0”和“1”;
  • 载波频率为 38Khz
  • 位时间为 1.125ms 2.25ms

(1)NEC 码的位定义:一个脉冲对应 560us 的连续载波;逻辑“1”:560us脉冲+1.68ms低电平; 逻辑“0”:560us脉冲+560us低电平。而遥控接收头在收到脉冲的时候为低电平,在没有脉冲的时候为高电平,这样,我们在接收头端收到 的信号为:逻辑 1 应该是 560us 低+1680us 高,逻辑 0 应该是 560us 低+560us 高

(2)NEC 遥控指令的数据格式为:同步码头、地址码、地址反码、控制码、控制反码。 接收端的同步码由一个 9ms 的低电平和一个 4.5ms 的高电平组成 ,地址码、地址反码、控制码、控制反码均是 8 位数据格式。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性(可 用于校验)。
比如接收到0100 1000的数据,因为低位在前,高位在后,所以要转化成0001 0010

我们遥控器的按键“▽”按下时,从红外接收头端收到的波形如图:

从上图可以看到,其地址码为 0 ,地址反码为255,控制码为 168,控制反码为87
可以看到在 100ms 之后,我们还收到了几个脉冲,这是 NEC 码规定的连发码 ( 9ms 低电平 +2.5m 高电平 +0.56ms 低电平 +97.94ms 高电平组成 )。如果在一帧数据发送完毕之后,按键仍然没有放开,则发射重复码,即发送的是以110ms为周期的重复码 ,可以通过统计连发码的次数来标记按键按下的长短/次数。
(3)发射端的重复码由9ms高电平和2.25ms的低电平以及560us的高电平组成。

二.实验例程
1.实验原理

产生下降沿,进入外部中断0的中断函数,延时一下之后检IO口是否还是低电平,是就等待9ms的低电平过去。等待完9ms低电平过去,再去等待4.5ms的高电平过去。接着开始接收传送的4组数据先等待560us的低电平过去检测高电平的持续时间,如果超过1.12ms那么是高电平(高电平的的持续时间为1680us,低电平的持续时间为565us。)检测接收到的数据和数据的反码进行比较,是否等到的数据是一样的。

红外遥控键值表:

2.实验程序

方法1:使用外部中断(普中科技代码)

备注:不要在定时中断里面使用延时函!此程序只做参考

/**************************************************************************************
*		              红外通信实验												  *
实现现象:下载程序后,数码管显示红外遥控键值数据
注意事项:	红外遥控器内的电池绝缘片一定要抽掉																			  
***************************************************************************************/

#include "reg52.h"			 //此文件中定义了单片机的一些特殊功能寄存器
	

typedef unsigned int u16;	  //对数据类型进行声明定义
typedef unsigned char u8;

sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;

sbit IRIN=P3^2;

u8 IrValue[6];
u8 Time;

u8 DisplayData[8];
u8 code smgduan[17]={
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0X76};
//0、1、2、3、4、5、6、7、8、9、A、b、C、d、E、F、H的显示码

/*******************************************************************************
* 函 数 名         : delay
* 函数功能		   : 延时函数,i=1时,大约延时10us
*******************************************************************************/
void delay(u16 i)
{
	while(i--);	
}


/*******************************************************************************
* 函数名         :DigDisplay()
* 函数功能		 :数码管显示函数
* 输入           : 无
* 输出         	 : 无
*******************************************************************************/
void DigDisplay()
{
	u8 i;
	for(i=0;i<3;i++)
	{
		switch(i)	 //位选,选择点亮的数码管,
		{
			case(0):
				LSA=0;LSB=0;LSC=0; break;//显示第0位
			case(1):
				LSA=1;LSB=0;LSC=0; break;//显示第1位
			case(2):
				LSA=0;LSB=1;LSC=0; break;//显示第2位	
		}
		P0=DisplayData[2-i];//发送数据
		delay(100); //间隔一段时间扫描	
		P0=0x00;//消隐
	}		
}


/*******************************************************************************
* 函数名         : IrInit()
* 函数功能		   : 初始化红外线接收
* 输入           : 无
* 输出         	 : 无
*******************************************************************************/

void IrInit()
{
	EA=1;	//打开总中断
	EX0=1;//打开中断0允许
	IT0=1;//下降沿触发

	IRIN=1;//初始化端口
}


/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{	
	IrInit();
	while(1)
	{	
		DisplayData[0] = smgduan[IrValue[2]/16];
		DisplayData[1] = smgduan[IrValue[2]%16];
		DisplayData[2] = smgduan[16];
	    DigDisplay();		
	}		
}

/*******************************************************************************
* 函数名         : ReadIr()
* 函数功能		   : 读取红外数值的中断函数
* 输入           : 无
* 输出         	 : 无
*******************************************************************************/

void ReadIr() interrupt 0
{
	u8 j,k;
	u16 err;
	Time=0;					 
	delay(700);	//7ms
	if(IRIN==0)		//确认是否真的接收到正确的信号
	{	 
		
		err=1000;				//1000*10us=10ms,超过说明接收到错误的信号
		/*当两个条件都为真是循环,如果有一个条件为假的时候跳出循环,免得程序出错的时
		侯,程序死在这里*/	
		while((IRIN==0)&&(err>0))	//等待前面9ms的低电平过去  		
		{			
			delay(1);
			err--;
		} 
		if(IRIN==1)			//如果正确等到9ms低电平
		{
			err=500;
			while((IRIN==1)&&(err>0))		 //等待4.5ms的起始高电平过去
			{
				delay(1);
				err--;
			}
			for(k=0;k<4;k++)		//共有4组数据
			{				
				for(j=0;j<8;j++)	//接收一组数据
				{

					err=60;		
					while((IRIN==0)&&(err>0))//等待信号前面的560us低电平过去
					{
						delay(1);
						err--;
					}
					err=500;
					while((IRIN==1)&&(err>0))	 //计算高电平的时间长度。
					{
						delay(10);	 //0.1ms
						Time++;
						err--;
						if(Time>30)
						{
							return;
						}
					}
					IrValue[k]>>=1;	 //k表示第几组数据
					if(Time>=8)			//如果高电平出现大于565us,那么是1
					{
						IrValue[k]|=0x80;     //接收的第1个数为高电平;在第二个for循环中,数据会向右移8次
					}
					Time=0;		//用完时间要重新赋值							
				}
			}
		}
		if(IrValue[2]!=~IrValue[3])
		{
			return;
		}
	}			
}


方法2:不使用外部中断,直接用定时中断(建议采用,需自己理解)

#include "IR.h"

#include "extern.h"


#define  P_IR  	 (PT2_DIN&0x08)		//红外

//-------10ms----------------------
void SBR_IR_PRO()
{
	  
	  if(bfirstover_NUM>=C_IR_up_time)
	  {
		 bfirstover_NUM=0;
		 f_ir_keyupping=0;
		 	
		 IR_DEAL_INIT();

	  }
	  else
	  {
		 IR_DEAL_PRO1();

	  }

}

//----------10ms-------------
void IR_DEAL_PRO1()
{
	  if(P_IR)		//高电平
	  {
		 
		 if(!f_ir_old)
		 {
			 f_ir_old=1;
			 //-------------上升沿 0-1--------------------
			  bfirstover_NUM=0;
			  if(f_ir_head)		  //地址码的第一个低电平后f_ir_head为1
			  {
				  bIrCnt=0;		  //定时器125us加1
				  
			  }
			  else
			  {
				  if(bIrCnt<C_IR_head_min||bIrCnt>C_IR_head_max)	//低电平时间
				  {
					   IR_DEAL_INIT();	  //错误信号
				  }
				  else
				  {
					  	
						bIrCnt=0;
						f_ir_head_one=1;
				  }
			  }
		 }
		 
	  }
	  else			//低电平
	  {
		   if(f_ir_old)			//f_ir_old初始时为1;经过高电平为1
		   {
			  f_ir_old=0;
			  //-------------下降沿 1-0------------------
			  bfirstover_NUM=0;
			  if(f_ir_head)		 //初始时为0;地址码的第一个低电平后为1
			  {
				 IR_DATA_COM_PRO1();

			  }
			  else
			  {
				 //---------引导码----0--------------
				 if(f_ir_head_one)	 //初始时为0;经过开始信号后为1
				 {
					 
					if((bIrCnt>=16)&&(bIrCnt<=20))	//重复码
		 			{
						 IR_REPEAT_CNT++;	   //记录重组码次数
		 			}
					else
					{
						f_ir_head=1;
					}

				 }
				 bIrCnt=0;

			  }
		   }


	  }
}


//-------10ms--------初始状态-----------
void  IR_DEAL_INIT()
{
	   birread_num=0;
	   f_ir_head=0;
	   f_ir_old=1;
	   F_IRfrist_over=0;
	   f_ir_head_one=0;
	   IR_REPEAT_CNT=0;

}

//--------下降沿--用所得的高电平时间决定逻辑“1”和“0”------------------
void  IR_DATA_COM_PRO1()
{
	 if(!f_ir_keyupping)	//初始值为0
	 {
		 if((bIrCnt<=1)||(bIrCnt>=20))	//这时的bIrCnt是高电平时间
		 {
			IR_DEAL_INIT();	  //错误信号
		 }
		 else
		 {
			 birread_num++;				//记录7次移位
			 if(birread_num>=250)
					  birread_num=250;   //设置一个值 防止按键一直按着  变量溢出
			 if(birread_num==1)
			 {
			   IR_CODE_VALUE1=0;   //控制反码
			   IR_CODE_VALUE2=0;   //控制码
			   IR_CODE_VALUE3=0;  //地址反码
			   IR_CODE_VALUE4=0;  //地址码
			   
			 }
			 else
			 {

				IR_DATA_COM_PRO2();
				
			 }

		 }

	 }
}

void  IR_DATA_COM_PRO2()
{	
	if(birread_num<=C_readover)
	{    
		if(birread_num<=8)
			IR_CODE_VALUE4>>=1;		//移位7次	
		else if(birread_num<=16)
			IR_CODE_VALUE3>>=1;		
		else if(birread_num<=24)
			IR_CODE_VALUE2>>=1;				
		else						
			IR_CODE_VALUE1>>=1;	
		
		if(bIrCnt>=C_l_length)	//大于9*125us 就判断为逻辑“1”
		{
				if(birread_num<=8)
					IR_CODE_VALUE4|=0x80;	
				else if(birread_num<=16)
					IR_CODE_VALUE3|=0x80;			
				else if(birread_num<=24)
					IR_CODE_VALUE2|=0x80;							
				else						
					IR_CODE_VALUE1|=0x80;
		}
	  		
	}						
						
	bIrCnt=0;
	if(birread_num==C_readover)
	{
	   //*************接收完毕后*********
	   f_ir_keyupping=1;
	   if(!F_IRfrist_over)
	   {
		  F_IRfrist_over=1;
		  
		  SBR_IR_DATA_OUT();
		 //----------------------
		   f_ir_head=0;
		   f_ir_head_one=0;		
	   }
	}

}


//--------------数据输出---------------------
void  SBR_IR_DATA_OUT()
{
	 if((IR_CODE_VALUE4==C_user_value)&&(IR_CODE_VALUE3==C_user_value_1))
	   {}
	   else
	 	 return;  //退出
	   //-----------------
	   if((IR_CODE_VALUE2==C_IRDATA_KEY1)&&(IR_CODE_VALUE1==(~C_IRDATA_KEY1)))
	   {
	   //-------ON
	   	  PT24DO=1;
	   
	   }
	   if((IR_CODE_VALUE2==C_IRDATA_KEY2)&&(IR_CODE_VALUE1==(~C_IRDATA_KEY2)))
	   {
	   //-------OFF
	   	  PT24DO=0;
	   
	   }


}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值