本文是武汉市海联天下物联网有限公司技术团队内部学习笔记,将详细讲解红外遥控的基本原理以及51单片机如何利用外部中断和定时器0对红外信号进行解码。——技术部 张傲
现象描述
使用红外遥控器对准HL-STC51开发板红外接收头,按下不同的按键,在开发板数码管上将显示对应按键的数字。
红外线简介
在光谱中波长自760nm至400um的电磁波称为红外线,它是一种不可见光。目前所有的视频和音频设备都可以通过红外遥控的方式进行遥控,比如电视机、空调、影碟机等,都可以见到红外遥控的影子。
红外遥控系统一般由红外发射装置和红外接收设备两大部分组成。红外发射装置可由键盘电路、红外编码芯片、电源和红外发射电路组成。红外接收设备可由红外接收电路、红外解码芯片、电源和应用电路组成。通常为了使信号能更好的被传输发送端将基带二进制信号调制为脉冲串信号,通过红外发射管发射。常用的有通过脉冲宽度来实现信号调制的脉宽调制(PWM)和通过脉冲串之间的时间间隔来实现信号调制的脉时调制(PPM)两种方法。
红外通信特征(NEC编码)
- 8位地址码,8位命令码 ;
- 完整发射两次地址码和命令码,以提高可靠性;
- 脉冲时间长短调制方式;
- 38KHz载波频率;
- 位时间1.12ms(逻辑0)或2.25ms(逻辑0);
NEC协议根据脉冲时间长短解码。每个脉冲为560us长的38KHz载波(约32个载波周期)。逻辑“1”脉冲时间为2.25ms,逻辑“0”脉冲时间为1.12ms。推荐的载波周期为1/4或者1/3。
NEC协议规定低位首先发送,如上图所示的情况,发送的地址码为“59”,命令码为“16”。每次发送的信息首先是用于调整红外接收器增益的9ms AGC(自动增益控制)高电平脉冲,接着是4.5ms的低电平,接下来便是地址码和命令码。地址码和命令码发送两次,第二次发送的是反码(如:1111 0000的反码是0000 1111),用于验证接收的信息的准确性。
电路分析
sbit IR = P3 ^ 2;
sbit smg_duan = P3 ^ 4;
IR与数码管段显示可根据原理图来定义:
红外接收管的输入引脚与单片机P32引脚相连,而P32又是单片机外部中断0引脚。使用数码管第一位显示解码的提示信息,数码管第一位的使能脚与单片机P34引脚相连,查看原理图定义引脚后,便于理解接下来的代码。
定时器0计数时间计算
使用定时器0作为红外接收端脉冲宽度检测计数器,将定时器0初始化为工作方式2,8位自动重载。定时器初值为0,则256个计数周期产生溢出中断,定时器每计数1次的时间为=1S/(SYSCLK/12)=1.085us;因此每次溢出的时间为=1.085us*256=0.277MS。
void TIM0init(void) //定时器0初始化
{
TMOD = 0x02; //定时器0工作方式2,TH0是重装值,TL0是初值
TH0 = 0x00; //重载值
TL0 = 0x00; //初始化值
ET0 = 1; //开中断
TR0 = 1;
}
定时器0中断服务函数,中断服务函数中对全局变量irtime进行++操作,由上面的分析可知,irtime每加1表示的时间长度为0.277ms,即irtime*0.277则表示测量到的时间长度。
void tim0_isr (void) interrupt 1 using 1
{
irtime++; //用于计数2个下降沿之间的时间
}
因此,对于红外引导码的检测可以使用如下函数进行:
if (irtime < 63 && irtime >= 33) //引导码 TC9012的头码,9ms+4.5ms
...
在下图中可以观察到红外每次发送会有9ms+4.5ms的头码,if语句中的作用就是判断是否接收到引导码,irtime>=33等价于定时时长=33*0.28ms=9.24ms,同理irtime<17.64ms。
注意:红外发射信号与红外接收信号极性是相反的,单片机解码的信号时红外接收头接收到的信号!
外部中断0检测红外接收信号
一体化红外接收头与单片机P32引脚(外部中断引脚)相连。外部中断初始化为下降沿触发。红外接收头默认为高电平,在检测到38K红外遥控载波信号时输出低电平,单片机外部中断引脚加测到外部有高到低的变化(下降沿)则触发中断,使进入中断处理函数。
外部中断0初始化函数
void EX0init(void)
{
IT0 = 1; //指定外部中断0下降沿触发INT0 (P3.2)
EX0 = 1; //外部中断使能(外部中断0开关)
EA = 1; //开总中断
}
外部中断服务函数
void EX0_ISR (void) interrupt 0 //外部中断0 服务函数
{
static uchar i; //接收红外信号处理
static bit startflag ; //是否开始处理标志
if (startflag)
{
if (irtime < 63 && irtime >= 33) //引导码TC9012的头码,9ms+4.5ms
i = 0;
IRdata[i]=irtime; //存储每个电平的持续时间,用于判断0还是1
irtime = 0; //初始化irtime,开始接收地址位
i++;
if (i == 33) //4字节每字节8位,32位加上一个引导码
{
IR_ok = 1;
i = 0;
startflag = 0; //初始化标志位,便于下一次接收
}
}
else
{
irtime = 0;
startflag = 1;
}
}
第一次触发外部中断后,startflag初始值为0,进入else语句,初始化irtime的值并将startflag置1,引导码结束时下降沿触发第二次中断,因为startflag已经置1,则直接进入if条件句,此时的irtime刚好为引导码的时间(引导码的实际时间存在偏差),如果irtime的值介于33和63之间,则把i置0,并将irtime的值写入IRdata数组中的第一位,此时正式开始接收地址码和命令码,地址码和命令码的下降沿引发中断,每次中断会将irtime置0,且将i+1,依次把地址码和命令码填入Irdata数组中,直到填满33个,33个是由引导码加上地址码、地址码反码、命令码、命令码反码构成的,地址码和命令码每个字节含有8位。当i==33时,表示接收完,单片机进入数据处理。
void Ircordpro(void) //红外码值处理函数
{
uchar i, j, k;
uchar cord, value;
k = 1;
for (i = 0;i < 4;i++) //处理4个字节
{
for (j = 1;j <= 8;j++) //处理一个字节的8位
{
cord = IRdata[k];
if(cord>6) //2.25/0.28=8=irtime,大于6时为1
value |= 0x80; // 1xxx xxxx,当cord>6时为1,所以把value高位置1
else
value &= 0x7f;
if (j < 8) //前面7位读出来时需要为下一位空出位置
{
value >>= 1; // 01xx xxxx,将读出来的位数右移
}
k++; //循环8次得到一个完整的字节
}
IRcord[i] = value; //将读到的完整字节赋值给IRcord数组
value = 0;
}
IRpro_ok = 1; //处理完毕标志位置1
}
数组Ircord中Ircord[2]是接收到的命令码,根据红外遥控器的键码值判断按下的是哪个键,从而控制数码管进行显示:
void Ir_work(void) //红外键值转数码管显示
{
switch (IRcord[2]) //判断第三个数码值
{
case 0x0c:
{
DataPort = dofly_DuanMa[1]; //1显示相应的按键值
}
break;
case 0x18:
{
DataPort = dofly_DuanMa[2]; //2显示相应的按键值
}
break;
case 0x5e:
{
DataPort = dofly_DuanMa[3]; //3显示相应的按键值 }
break;
case 0x08:
{
DataPort = dofly_DuanMa[4]; //4显示相应的按键值 }
break;
case 0x1c:
{
DataPort = dofly_DuanMa[5]; //5显示相应的按键值
}
break;
case 0x5a:
{
DataPort = dofly_DuanMa[6]; //6显示相应的按键值
}
break;
case 0x42:
{
DataPort = dofly_DuanMa[7]; //7显示相应的按键值
}
break;
case 0x52:
{
DataPort = dofly_DuanMa[8]; //8显示相应的按键值
}
break;
case 0x4a:
{
DataPort = dofly_DuanMa[9]; //9显示相应的按键值
}
break;
default:
break;
}
IRpro_ok = 0; //处理完成标志
}