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 高。
我们遥控器的按键“▽”按下时,从红外接收头端收到的波形如图:
二.实验例程
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;
}
}