红外原理

参考:
http://blog.csdn.net/mhjerry/article/details/6600414
http://blog.csdn.net/techelefeng/article/details/45972583
1. 原理
红外遥控器是利用一个红外发光二极管,以红外光为载体来将按键信息传递给接收端的设备。红外光对于人眼是不可见的, 因此使用红外遥控器不会影响人的视觉(可以打开手机摄像头,遥控器对着摄像头按,可以看到遥控器发出的红外光)。

首先,必须要了解一些基本原理。其实按下遥控器的某一个键,遥控器会发出一连串经过调制后的信号,这个信号经过红外一体化模块接收后,输出解调后的数字脉冲,每个按键对应不同的脉冲,故识别出不同的脉冲就能识别出不同的按键。

上图就是很常见的车载MP3遥控器,比较小巧,很好用。下面是红外发射和接受原理:

到此读者可能会有疑惑,那么不同的调制解调方法那么出来的脉冲规则是不一样的?是的,的确如此。

遥控发射器专用芯片很多,根据编码格式可以分成两大类,这里我们以运用比较广泛,解码比较容易的一类来加以说明,现以日本NEC的uPD6121G组成发射电路为例说明编码原理(一般家庭用的DVD、VCD、音响都使用这种编码方式)。当发射器按键按下后,即有遥控码发出,所按的键不同遥控编码也不同。这种遥控码具有以下特征:

2. 调制
日常生活环境中有很多红外光源,太阳、蜡烛火光、白炽灯、甚至是我们的身体。这些红外光源都可能会对我们的接收 设备 产生干扰,为了屏蔽干扰,只接收有效信息,我们就需要用到调制。 调制是我们使需要的信号区别于噪音方法。通过调制我们可以使红外光以特定的频率闪烁。红外接收 器会适配这个频率, 其它的噪音信号都将被忽略。 你可以认为这种闪烁是引起接收器“注意”的方法,正如我们人类特别容易被黄色的灯光 引起注意一样, 甚至在白天。

3. 协议

NEC协议
  • 8 位地址码, 8 位命令码
  • 完整发射两次地址码和命令码,以提高可靠性
  • 脉冲时间长短调制方式
  • 38KHz 载波频率
  • 位时间 1.12ms 或 2.25ms

引导吗:

下图为一个引导码的调制波形:


引导码 由“9ms mark + 4.5ms space”构成,表示一组 键码 的开始。


逻辑0和1:

下图为逻辑0和逻辑1的调制波形:


逻辑“1”由“560us mark + 1690 space”组成,symbol period 为2.25ms;逻辑“0”由“560us mark + 560us space”组成,symbol period 为 1.12ms。


完整的一组键码:
下图位NEC 协议的典型脉冲链:

协议规定低位首先发送,如上图所示的情况,发送的地 址码为“59”,命令码为“16”,总的码值为 0x59A616E9。


重复码:

图为重复码的调制波形:


重复码由 “9ms mark + 2.25ms space”组成,symbol period 为 11.25ms,重复码表示一个重复按键,当按键按着不松时,会先发一个完整的键码,接着每 110ms 发送一个重复码,直到松开按键,如下面的波形图所示:


接收端输出:
在接收端,一个 mark 对应一个低电平输出,一个 space 对应一个高电平输出,因此起始码、逻辑0 1、重复码等在接收端的输出波形如下图所示:


请看下图,来自网络:



当一个键按下超过36ms,振荡器使芯片激活,将发射一组108ms的编码脉冲,这108ms发射代码由一个引导码(9ms),一个结果码(4.5ms),低8位地址码(9ms~18ms),高8位地址码(9ms~18ms),8位数据码(9ms~18ms)和这8位数据的反码(9ms~18ms)组成。如果键按下超过108ms仍未松开,接下来发射的代码(连发码)将仅由起始码(9ms)和结束码(2.25ms)组成。(实际上人手的动作是很慢的,即使你快速的按下按键,可能对于芯片来说还是超过108ms,所以如何处理连发码是很关键的)

遥控器在按键按下后,周期性地发出同一种32位二进制码,周期约为108ms。一组码本身的持续时间随它包含的二进制“0”和“1”的个数不同而不同,大约在45~63ms之间,图为发射波形图。

 

下面是我写的代码,按键编码通过串口发送到电脑端:

由于时间关系,代码注释不多。

其中START_Judge()函数是判断9ms低电平,既是判断有无遥控信号。

BOOT_REPEATING_CODE_Judge()是判断是引导码还是连发码,引导码则进入接受数据环节,连发码表明数据已经接受结束。

H_L_LEVEL_Judge()是接受数据时判断高低电平。

/*------------------------------------------------------------*-
  红外收发.C
  ------------------------------------------------------------
  遥控器测试
-*------------------------------------------------------------*/

#include <reg52.h>

// --- 红外接收一体化输出口 ----------------------------------
sbit IR_Out = P3^2;

bit START_Flag = 0;
bit BOOT_REPEATING_CODE_Flag = 0;
unsigned char DATA[4] = {0};
bdata unsigned char TEMP_BIT;

sbit B0 = TEMP_BIT^0;
sbit B1 = TEMP_BIT^1;
sbit B2 = TEMP_BIT^2;
sbit B3 = TEMP_BIT^3;
sbit B4 = TEMP_BIT^4;
sbit B5 = TEMP_BIT^5;
sbit B6 = TEMP_BIT^6;
sbit B7 = TEMP_BIT^7;

// --- 有无遥控信号判断函数 ----------------------------------
bit START_Judge();

// --- 连发码判断函数 ----------------------------------------
bit BOOT_REPEATING_CODE_Judge();

// --- "0"和"1"识别 ------------------------------------------
bit H_L_LEVEL_Judge();

// --- 串口初始化 --------------------------------------------
void UART_Initial();

void DELAY_Us(unsigned int Us)
{
	unsigned int x;
	for(x = 0; x <= (Us/200-1); x++);
}
void DELAY_Ms(unsigned int Ms)
{
	unsigned int x,y;
	for(x = 0; x <= (Ms-1); x++)
	{
		for(y = 0; y <= 120; y++);
	}
}

void main()
{
	unsigned char i;
	UART_Initial();
	IR_Out = 1;
	while(1)
	{		
		START_Flag = START_Judge();
		BOOT_REPEATING_CODE_Flag = BOOT_REPEATING_CODE_Judge();
		if ( START_Flag && !BOOT_REPEATING_CODE_Flag )
		{			
			for(i =0;i <4; i++)
			{				
				B0 = H_L_LEVEL_Judge();
				B1 = H_L_LEVEL_Judge();
				B2 = H_L_LEVEL_Judge();
				B3 = H_L_LEVEL_Judge();
				B4 = H_L_LEVEL_Judge();
				B5 = H_L_LEVEL_Judge();
				B6 = H_L_LEVEL_Judge();
				B7 = H_L_LEVEL_Judge();								
				DATA[i] = TEMP_BIT;
			}
			for(i =0;i <4; i++)
			{
				SBUF = DATA[i];
				while( TI == 0 );
				TI = 0;
			}
		}
	}
}

void UART_Initial()
{
	SCON = 0x50; 			// SCON: 模式 1, 8-bit UART, 使能接收

	TMOD |= 0x20; 			// TMOD: timer 1, mode 2, 8-bit reload

	TH1 = 0xFD; 			// TH1: reload value for 9600 baud @
							// 11.0592MHz 
	TR1 = 1; 				// TR1: timer 1 run

	EA = 0; 				// 关闭总中断
	ES = 0; 				// 关闭串口中断
}
	

bit START_Judge()
{
	bit TEMP_Flag = 1;
	unsigned char i = 0;

    //在正常无遥控信号时,一体化红外接收头输出是高电平,程序一直在循环。
    while ( IR_Out == 1);

	//重复10次,目的是检测在6876~8352微秒内如果出现高电平就退出解码程序
	for(i =0;i <9; i++)
	{
		DELAY_Us(800);		// 测试实际延时约为764~928us
		if ( IR_Out == 1 )
		{
			TEMP_Flag = 0;
			break;
		}
	}
	
	return TEMP_Flag;
}

bit BOOT_REPEATING_CODE_Judge()
{
	bit TEMP_Flag = 1;
	while( IR_Out == 0 ) ;	// 等待高电平避开9毫秒低电平引导脉冲

	DELAY_Ms(1);			// 测试实际延时约为1.007ms 
	DELAY_Ms(1);			// 测试实际延时约为1.007ms 
	DELAY_Us(200);			// 0.086ms
	DELAY_Us(200);			// 0.086ms	
	DELAY_Us(200);			// 0.086ms
							// 共计2.272ms	

	if( IR_Out == 0 )
	{
		TEMP_Flag = 1;		// 是连发码
	}
	else
	{
		TEMP_Flag = 0;		// 不是连发码,而是引导码
	}
	return TEMP_Flag;
}
bit H_L_LEVEL_Judge()
{
	while( IR_Out == 0 );	// 等待地址码第一位的高电平信号
	DELAY_Us(800);			// 测试实际延时约为764~928us
	if ( IR_Out == 1)
	{
		DELAY_Ms(1);		// 测试实际延时约为1.007ms 
		return 1;
	}
	else
	{
		return 0;
	}
}

 

编辑如下:

01 FE 8B 74 --- 01 FE 8D 72 --- 01 FE 8F 70

01 FE 89 76 --- 01 FE 81 7E --- 01 FE 87 78

01 FE 0F F0 --- 01 FE 2B D4 --- 01 FE 13 EC

01 FE 2D D2 --- 01 FE 33 CC --- 01 FE 1B E4

01 FE 19 E6 --- 01 FE 31 CE --- 01 FE BD 42

01 FE 11 EE --- 01 FE 39 C6 --- 01 FE B5 4A
以上为对应按键的编码。

过程中存在问题:

一是如何有效的识别引导码和连发码,因为这个能直接影响到长时间按键,单片机的响应与否。这个问题,貌似我以解决,就是长时间按键后,单片机识别一次按键后,如果还是同一按键,就不与理睬。

还有一个问题就是,如果连续按下两次按键,该程序能够识别出,但是如果间隔很短,第二下按键的编码容易出错,容易变成这样:

03 FE 8B 74.。。。就是第一个字节出现误差,这个问题现在还未来得及解决。

还有就是本程序对于延时函数的精度要求很高,因为本身处理的脉冲就是MS级别的。所以需要严格的测试延时函数的实际延时时间:

 

以上的代码,可以看出许多问题,软件延时不准确,大量的“while( IR_Out == 0 ) ;”代码,抗干扰能力弱,容易进入死循环。

下面介绍的这种解码方法,利用外部中断触发程序,定时器定时(但没有设置定时中断程序,即判断TF的值确定定时结束),在代码过程中,开头的一个7.93ms延时,足以滤掉不合法的红外信号。应该说效率质量更高的。

代码注释很详细,在此不在细述:

/*------------------------------------------------------------*-
  IR_Decoder.C (v1.00)
  ------------------------------------------------------------
  名称:遥控器红外解码,PO口接LED,显示功能码以供查看
  编写:mhjerry
  日期:20011.7
  内容:按遥控器上的按键,会在PO口LED上显示
-*------------------------------------------------------------*/
#include "reg52.h"

// 此口为红外信号输入MCU口
sbit IR_Out = P3^2;	

// 主程序运行标志位,运行主程序时LED灭,运行中断程序时LED亮	
sbit IR_Flag = P3^1;

// LED显示口
#define LED_Port P1

// 用于存放按键码值,初始化为0000 0000这样接受数据时可以只考虑1了	
unsigned char dat[4] = {0,0,0,0};


/*............................................................*/
void main()
{
	IR_Out = 1;		// 此口为MCU输入口,故需要置1
	IR_Flag = 1;	// 灭LED灯
	TMOD = 0x01;	// 定时器0,方式1
	IT0 = 1;		// 外部中断0,下降沿触发
	EX0 = 1;		// 准许外部中断
	EA = 1;			// CPU准许中断

	while(1)
	{
		IR_Flag = 1;// 执行主程序时,LED灯灭
	}
}
/*------------------------------------------------------------*- 
  函数名称:Int0()
  函数输入:无(容许中断时,外部触发)
  函数输出:无
  函数说明:外部中断0中断处理
-*------------------------------------------------------------*/
void Int0() interrupt 0
{
	unsigned char i,j;
	EX0 = 0;			// 关闭外部中断0
	IR_Flag = 0;		// 执行中断程序时,LED灯亮
	i = 10;				// 0.793ms延时,运行10次
	while( --i )
	{
		// 定时0.793ms,延时0.793ms*10=7.93ms
		TH0 = 0xfc;
		TL0 = 0xe7;
		TR0 = 1;
		while( !TF0 );
		TF0 = 0;
		TR0 = 0;
		
		// 这7.93ms期间只要IR_Out变高电平,就非合法的红外信号,跳出
		if( IR_Out )
		{
			EX0 = 1;	// 准许中断
			return ;
		}
	}
	
	// 程序进行到这里,表明是合法的红外信号(利用9ms判断)
	while( !IR_Out );	// 等待9ms低电平过去
	
	// 程序进行到这里,表明经过9ms低电平
	TH0 = 0xf6;
    TL0 = 0xff;
	TR0 = 1;
	while( !TF0 );
	TF0 = 0;
	TR0 = 0;	  		// 延时2.305ms
	
	// IR_Out 为低表明是连发码,不予理睬,跳出
	if( !IR_Out )
	{
		EX0=1;
		return;
	}

	// 程序进行到这里,表明是引导码,等待4.5ms高电平的过去
	while( IR_Out );	
	
	// 开始接收用户码
	for(i=0; i<4; i++)
 	{
		for(j=0; j<8; j++)
		{
			while( !IR_Out );	// 等待低电平过去
   	 		dat[i] >>= 1;		// 把上次的数据位右移一位

			TH0 = 0xfc;
		    TL0 = 0xe7;
			TR0 = 1;
	        while( !TF0 );
	        TR0=0;
	        TF0=0; 				//延时0.793ms	
			
			// 若为数据"1",则延时后IR_Out为高电平
	  		if( IR_Out ) 
	   		{
	     		dat[i] |= 0x80;		// 所有数据位1放最高位
	     		while( IR_Out );	// 等待高电平过去
	    	}
		}
  	}	
	LED_Port = dat[2];
 	EX0=1;		// 开中断
    return;
}
/*------------------------------------------------------------*-
  ---- END OF FILE -------------------------------------------
-*------------------------------------------------------------*/

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值