51单片机红外遥控信号解码
简介
红外遥控作为一种遥控手段应用是非常广泛的,并且红外线遥控装置具有体积小、 功耗低、 功能强、 成本低这些特点,在录音机、空调、音响等小型电器大多是采用红外遥控的方式进行控制。这篇文章我就来详细讲解一下红外遥控信号的解码方法,对于红外信号的收发原理在这里只做简单了解。红外遥控系统要有发射端和接收端,发射端将信号经芯片调制后由红外发光二极管将信号以红外光的形式发送出去。接收端通过红外监测二极管接收到红外信号后再经过放大、滤波、解调等操作输出高低电平,这样就完成了红外信号的收发。本次教程我选择下图这种红外遥控器进行红外信号的发射
选择一体化红外接收头作为红外信号的接收端,这是一种特殊的红外接收电路,它把红外接收管和放大电路集成到了一块,从外形上看只有一个三极管大小,如图
一体化红外接收头共有三个引脚,将有凸起的一面对准自己后从左到右依次是信号输出、GND、VCC,它的工作电压为5V,可以选择一体化红外接收模块,我们在使用的时候只需要给它接上5V电源,接地,然后将信号输出脚连接到单片机就可以使用了。本篇文章我使用51单片机进行试验,51单片机用来解码红外信号是足够的并且易于理解,文章主要是讲解解码原理。
硬件连接
第一步是硬件连接,用到的硬件非常简单,只有一体化红外接收头和单片机最小系统。首先,我们给单片机最小系统和红外接收头接上电源,注意共地,然后将红外接收头的信号输出脚接到单片机的外部中断引脚上,这样就完成了硬件的连接。之所以将信号输出脚接到单片机的外部中断引脚是因为前面说过红外接收头在接收到红外信号后会输出高低电平,接在外部中断引脚后单片机可以在有信号的时候快速做出反应,后面的程序也是靠中断程序实现的。
NEC协议
前面讲过红外接收头在接收到红外信号后会输出高低电平,然后就可以通过我们的程序分析电平的变化来识别接收到的信号的内容。要将接收端的信号解码我们就要知道发送端的数据是如何编码的,在本文中用到的红外遥控器所使用的是NEC编码协议,所以我们首先要讲解一下NEC协议的相关信息。NEC协议是用于红外通信中的一种协议,通过NEC协议发送信息的格式如下图:
信息的开始是9ms高电平加4.5ms低电平的引导码,作用就是告诉设备通讯开始,有信号发送过来了。之后接的是8位地址码和8位地址反码,地址码的作用就是区分不同的接收器件,实现单独控制某个器件。最后是8位数据码和8位数据反码,数据码就是功能码,我们可以给不同数据码设定不同的功能,反码的作用就是检验接收到的数据是否正确。在上面的这条信息里,‘0’是用560us高电平加560us低电平表示的,‘1’是用560us高电平加1680us低电平表示。要注意的是上面的波形图是发射端的波形,我们红外接收端收到的信号的高低电平是与上图相反的,还要注意发射端在发射数据是是从最低位开始发射,所以我们接收到的信号也是从最低位开始的,另外当我们持续按住同一个按键时,发射端并不会重复发送地址码和数据码而是在发送完一次后每隔一段时间发送一次引导码(重复),间隔时间大约是108ms。下图为接收端引导码、引导码(重复)、‘0’、‘1’的波形:
在了解了这些关于NEC协议的信息后我们就可以开始写程序控制单片机来解码红外信号了。首先我来讲解一下解码程序的思路,之前我们将红外接收头的信号输出脚接在了单片机的外部中断引脚上,我们将外部中断的触发方式设置为跳变沿触发,当接收头接收到信号后就会输出一个低电平,低电平触发外部中断,我们在中断程序中启动定时器,这样当下一次低电平触发中断前,在这期间定时器所定的时间我们可以进行比较从而确定脉冲的宽度,确定数据为‘0’还是‘1’。接下来我根据具体代码进行介绍,
#include"reg52.h"#define uint unsigned int #define uchar unsigned charuchar IRtime; //储存检测红外高低电平持续时间uchar IRcord[4]; //储存解码后的4个字节数据uchar IRdata[33]; //包含起始码在内的33位数据bit IRpro_ok; //解码后4个数据接收完成标志位bit IRok; //33位数据接收完成标志位
在上面这段函数中我们添加上相应的头文件和一些宏定义,之后我们定义一些变量和数组作为一些标志位以及一些数据的保存,在每个定义后面都有注释来解释相应的作用,在这里就不再赘述。
void init(){ TMOD |= 0x02; //设置定时器0工作模式2 TL0 = TH0 = 0; //初始化定时器0寄存器 EA = 1; ET0 = 1; TR0 = 1; IT0 = 1; EX0 = 1; TMOD |= 0x20; //设置定时器1工作模式2 TL1 = TH1 = 0xfd; //比特率9600 SM1 = 1; //设置串口工作模式1,10位异步收发 TR1 = 1; //启动定时器1 }
然后我们定义一个初始化函数用于定时器和中断的初始化设置,我们将定时器0用于计算红外信号高低电平的时间,将定时器1用于串口发送数据。
void T0_ISR(void) interrupt 1 //定时器0中断一次277.76us{ IRtime++;}
上面这段是定时器0的程序,在里面只是用来计算时间,定时器0中断一次是277.76us,我们通过IRtime就可以计算出红外信号的持续时间。
void int0(void) interrupt 0{ static uchar i; //静态变量用于存入33次数据计数 static bit startflag; //开始存储脉宽标志位 if(startflag) { if((IRtime < 53) && (IRtime >= 32)) //判断是否为引导码 若为引导码则从起始码开始存 {i = 0;} IRdata[i] = IRtime; //以T0溢出的次数来计算脉宽把这个时间存放在数组中 IRtime = 0; //计数清零 i++; //计数脉宽存入次数自加 if(i == 30) { IRok = 1; //脉宽检查完成 i = 0; //把脉宽计数清零准备下次存入 } } else { IRtime = 0; //定时器0计数清零 startflag = 1; //开始处理标志位置1 }}
上面这段程序是外部中断的处理程序,当红外接收头接收到红外信号后会输出相应的高低电平变化,引导码和“1”、“0”的波形在前面的接收端波形图已经画出,这样当接收端为高电平的时候,就会触发外部中断,这是会查看IRtime的值,根据这个值来判断是否是9ms的引导码,如果是引导码则先将IRtime清零,然后将以后的脉冲时间都保存下来,如果不是则把IRtime清零,这样红外接收头接收到的33次脉冲的宽度就会保存到IRdata[]数组里。
void IRcordpro(){ uchar i; //用于计数处理4个字节 uchar j; //用于计数处理1个字节的8位数据 uchar k; //用于计数处理33次脉宽 k = 1; //从第一位开始处理 丢掉起始码 for(i = 0;i < 4;i++) { for(j = 0;j <8;j++) { if(IRdata[k] > 5) //如果脉宽大于数据0标准的1125us就判定为数据1 { IRcord[i] |= 0x80; //置于最高位 } if(j < 7) //从最高位移位置低位 { IRcord[i] >>= 1; } k++; //处理下一次脉宽 } } IRpro_ok = 1; //解码完成}
上面这个函数是用来处理我们之前保存的33次脉冲宽度,并且转化为4字节的数据保存在IRcord[]数组里,具体的操作就是根据脉冲的时间来判断是“1”还是“0”,大家可以根据程序后面的注释来理解函数的内容。
void main(){ uchar i; //计数串口发送字节数 init(); //初始化 while(1) { if(IRok) //判断33次脉宽是否提取完成 { IRcordpro(); //根据脉宽解码出4字节数据 IRok = 0; //清零脉宽检查完成标志位等待下一次脉宽检查 if(IRpro_ok) //判断解码是否完成 { for(i = 0;i < 4;i++) { SBUF = IRcord[i]; while(!TI); TI = 0; } IRpro_ok = 0; //清零解码标志位 } } }}
最后我们在主函数部分调用之前的函数,并且在等待解码完成后将解码出来的4字节数据通过串口发送到电脑。最后我们看一下实际的效果,首先打开串口助手,波特率设置为9600,接收设置为HEX模式,这样当按下红外遥控器的按键时,就会返回遥控器的键值了,
好了,本期教程到此就结束了,感谢各位的观看。
更多干货内容只需要你关注电子芯吧客微信公众号
单片机实现遥控器-红外数据传输——红外编解码原理
一、红外通信原理
红外遥控有发送和接收两个组成部分。发送端采用单片机将待发送的二进制信号编码调制为一系列的脉冲串信号,通过红外发射管发射红外信号。红外接收完成对红外信号的接收、放大、检波、整形,并解调出遥控编码脉冲。为了减少干扰,采用的是价格便宜性能可靠的一体化红外接收头(HS0038, 它接收红外信号频率为38kHz,周期约26μs) 接收红外信号,它同时对信号进行放大、检波、整形得到TTL 电平的编码信号,再送给单片机,经单片机解码并执行去控制相关对象。如图1 所示:
红外发送部分由51单片机、键盘、红外发光二极管和7段数码管组成。键盘用于输入指令, 51单片机检测键盘上按键的状态,并对红外信号进行调制,发光二极管产生红外线,数码管用来显示发送的键值。图2红外发射电路
红外接收部分由51单片机、一体化红外接收头HS0038和7段数码管组成。51单片机检测HS0038,并对HS0038接收到的数据解码,通过数码管显示接收到的键值。图 3红外接收电路
二、编码、解码
(1) 二进制信号的调制
二进制信号的调制由单片机来完成,它把编码后的二进制信号调制成频率为38kHz 的间断脉冲串,相当于用二进制信号的编码乘以频率为38kHz 的脉冲信号得到的间断脉冲串,即是调制后用于红外发射二极管发送的信号如图4 二进制码的调制所示
(2) 红外接收需先进行解调,解调的过程是通过红外接收管进行接收的。其基本工作过程为:当接收到调制信号时,输出高电平,否则输出为低电平,是调制的逆过程(图5 解调)。HS0038是一体化集成的红外接收器件,直接就可以输出解调后的高低电平信号;红外接收器HS0038的应用电路(图6)。
(3)红外遥控发射芯片采用 PPM 编码方式,当发射器按键按下后 ,将发射一组 108ms 的编码脉冲。遥控编码脉冲由前导码、16位地址码(8 位地址码、 8 位地址码的反码)和16位操作码(8 位操作码、 8 位操作码的反码)组成。通过对用户码的检验,每个遥控器只能控制一个设备动作,这样可以有效地防止多个设备之间的干扰。编码后面还要有编码的反码,用来检验编码接收的正确性,防止误操作,增强系统的可靠性。前导码是一个遥控码的起始部分,由一个 9ms 的高电平 ( 起始码 ) 和一个 4. 5ms 的低电平 ( 结果码 ) 组成,作为接受数据的准备脉冲。以脉宽为 0. 56ms 、周期为 1. 12ms 的组合表示二进制的 “0” ;以脉宽为 1. 68ms 、周期为 2. 24ms 的组合表示二进制的 “1” 。
(4)单片机采用外部中断 INT0 管脚和红外接收头的信号线相连,中断方式为边沿触发方式。计算中断的间隔时间,来区分前导码、二进制的 “1” 、 “0” 码。并将 8 位操作码提取出来在数码管上显示。
红外接收头输出的原始遥控数据信号,正好和发射端倒向.也就是以前发射端原始信号是高电平,那接收头输出的就是低电平,反之.
软件原理:
开始时发射一个特定的同步码头,对于接收端而言就是一个9ms的低电平,和一个4.5ms的高电平,这个同步码头可以使程序知道从这个同步码头以后可以开始接收数据。
采用脉宽调制的串行码,以脉宽为0.565ms、间隔0.56ms、周期为1.125ms的组合表示二进制的“0”;以脉
宽为0.565ms、间隔1.685ms、周期为2.25ms 的组合表示二进制的“1”。
解码的关键是如何识别“0”和“1”,从位的定义我们可以发现“0”、“1”均以0.56ms的高电平开始,不同的是低电平的宽度不同,“0”为0.56ms,“1”为1.685ms,所以必须根据高电平的宽度区别“0”和“1”。如果从0.56ms低电平过后,开始延时,0.56ms以后,若读到的电平为低,说明该位为“0”,反之则为“1”,为了可靠起见,延时必须比0.56ms长些,但又不能超过1.12ms,否则如果该位为“0”,读到的已是下一位的高电平,因此取(1.12ms+0.56ms)/2=0.84ms最为可靠,一般取0.84ms 左右即可。根据红外编码的格式,程序应该等待9ms的起始码和4.5ms的结果码完成后才能读码。
HS0038红外接收器,接收红外遥控器发射的信号,输出DATA口和单片机的外部中断0P3.2口相连。当有红外信号时,触发中断查询中断时间,并和红外起始码,“0”、“1”、终止码的时间进行比较。从而检测红外的操作码。
51可参考的程序
(1)发送程序
#include
static bit OP; //红外发射管的亮灭
static unsigned int count; //延时计数器
static unsigned int endcount; //终止延时计数
static unsigned int temp; //按键
static unsigned char flag; //红外发送标志
static unsigned char num;
sbit ir_in=P3^4;
char iraddr1; //十六位地址的第一个字节
char iraddr2; //十六位地址的第二个字节
unsigned char code table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e}; //共阳数码管 0~~f
void SendIRdata(char p_irdata);
void delay(unsigned int);
void keyscan();
void main(void)
{
num=0;
P2=0x3f;
count = 0;
flag = 0;
OP = 0;
ir_in= 0;
EA = 1; //允许CPU中断
TMOD = 0x11; //设定时器0和1为16位模式1
ET0 = 1; //定时器0中断允许
TH0 = 0xFF;
TL0 = 0xE6; //设定时值0为38K 也就是每隔26us中断一次
TR0 = 1;//开始计数
iraddr1=3;//00000011
iraddr2=252;//11111100
do{keyscan();
}while(1);
}
void timeint(void) interrupt 1
{
TH0=0xFF;
TL0=0xE6; //设定时值为38K 也就是每隔26us中断一次
count++;
if (flag==1)
{
OP=~OP;
}
else
{
OP = 0;
}
ir_in= OP;
}
void SendIRdata(char p_irdata)
{
int i;
char irdata=p_irdata;
//发送9ms的起始码
endcount=223;
flag=1;
count=0;
do{}while(count
endcount=117;
flag=0;
count=0;
do{}while(count
irdata=iraddr1;
for(i=0;i<8;i++)
{
endcount=10;
flag=1;
count=0;
do{}while(count
if(irdata-(irdata/2)*2) //判断二进制数个位为1还是0
{
endcount=41; //1为宽的高电平
}
else
{
endcount=15; //0为窄的高电平
}
flag=0;
count=0;
do{}while(count
irdata=irdata>>1;
}
irdata=iraddr2;
for(i=0;i<8;i++)
{
endcount=10;
flag=1;
count=0;
do{}while(count
if(irdata-(irdata/2)*2)
{
endcount=41;
}
else
{endcount=15;
}
flag=0;
count=0;
do{}while(count
irdata=irdata>>1;
}
irdata=p_irdata;
for(i=0;i<8;i++)
{
endcount=10;
flag=1;
count=0;
do{}while(count
if(irdata-(irdata/2)*2)
{
endcount=41;
}
else
{
endcount=15;
}
flag=0;
count=0;
do{}while(count
irdata=irdata>>1;
}
irdata=~p_irdata;
for(i=0;i<8;i++)
{
endcount=10;
flag=1;
count=0;
do{}while(count
if(irdata-(irdata/2)*2)
{
endcount=41;
}
else
{
endcount=15;
}
flag=0;
count=0;
do{}while(count
irdata=irdata>>1;
}
endcount=10;
flag=1;
count=0;
do{}while(count
flag=0;
}
void delay(unsigned int z)
{
unsigned char x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
(2)接收程序
#include"reg52.h"
#define uchar unsigned char
#define uint unsigned int
uchar dis_num,num,num1,num2,num3;
sbit led=P1^0;
unsigned char code table[]={
0xc0,0xf9,0xa4,0xb0,
0x99,0x92,0x82,0xf8,
0x80,0x90,0x88,0x83,
0xc6,0xa1,0x86,0x8e}; //共阳数码管 0~~f
sbit prem =P3^2; //定义遥控头的接收脚
uchar ram[4]={0,0,0,0};//存放接受到的4个数据 地址码16位+按键码8位+按键码取反的8位
void delaytime(uint time) //延迟90uS
{
uchar a,b;
for(a=time;a>0;a--)
{
for(b=40;b>0;b--);
}
}
void rem()interrupt 0 //中断函数
{
uchar ramc=0; //定义接收了4个字节的变量
uchar count=0; //定义现在接收第几位变量
uint i=0; //此处变量用来在下面配合连续监测9MS 内是否有高电平
prem=1;
for(i=0;i<1100;i++) //以下FOR语句执行时间为8MS左右
{
if(prem) //进入遥控接收程序首先进入引导码的前半部判断,即:是否有9MS左右的低电平
return; //引导码错误则退出
}
while(prem!=1); //等待引导码的后半部 4.5 MS 高电平开始的到来。
delaytime(50); //延时大于4.5MS时间,跨过引导码的后半部分,来到真正遥控数据32位中
//第一位数据的0.56MS开始脉冲
for(ramc=0;ramc<4;ramc++)//循环4次接收4个字节
{ for(count=0;count<8;count++) //循环8次接收8位(一个字节)
{
while(prem!=1); //开始判断现在接收到的数据是0或者1 ,首先在这行本句话时,
//保已经进入数据的0.56MS 低电平阶段
//等待本次接受数据的高电平的到来。
delaytime(9);//高电平到来后,数据0 高电平最多延续0.56MS,而数据1,高电平可
//延续1.66MS大于0.8MS 后我们可以再判断遥控接收脚的电平,
if(prem) //如果这时高电平仍然在继续那么接收到的数据是1的编码
{
ram[ramc]=(ram[ramc]<<1)+1;//将目前接收到的数据位1放到对应的字节中
delaytime(11); //如果本次接受到的数据是1,那么要继续延迟1MS,这样才能跨入//下个位编码的低电平中(即是开始的0.56MS中)
}
else //否则目前接收到的是数据0的编码
ram[ramc]=ram[ramc]<<1; //将目前接收到的数据位0放到对应的字节中
} //本次接收结束,进行下次位接收,此接收动作进行32次,正好完成4个字节的接收
}
if(ram[2]!=(~(ram[3]&0x7f))) //本次接收码的判断
{
for(i=0;i<4;i++) //没有此对应关系则表明接收失败,清除接受到的数据
ram[i]=0;
return ;
}
dis_num=ram[2]; //将接收到的按键数据赋给显示变量
}
相关问答
热释电 红外 传感器 单片机 ?你应该没搞清楚热释电红外传感器的原理吧!它的工作原理是通过将红外线的能量变化转化为电压信号的,这个的难点在你处理信号的电路上,你要采集热释电红外传感器...
求HS0038 红外 接收探头工作原理,就是怎么接收 红外 线后转换到作用于51 单片机 ?从型号上可以看出这个红外接收头是38HKZ工作频率的,就是说在接收到这个频率的时候,接收头会自动把信号解码输出低电平,也就是说没信号时是高电平,有信号时低...
人体 红外 传感器模块与 单片机 结合怎么用?人体红外传感器模块一般集成人体感应和光线感应,输出控制的负载一般是照明灯电力设备。一般在光线黑暗的情况下,如果检测到范围内有人则吸合继电器,打开照明负...
单片机 与手机之间进行通信,什么方式比较好 - 懂得1.串口对串口(不过一般普通用户不太容易连接到手机的串口)2.蓝牙:单片机+蓝牙模块-》手机蓝牙模块-》手机上应用程序3.红外:单片机+红外模块-》手机...
如何用 单片机 来驱动 红外 对管?这个检测模块是不需要用单片机来驱动的,循迹为什么要寻黑线,因为当有黑线的时候红外线被吸收,接收头收不到。这样就可以通过接收端的输出来判断是不是在线上...
51 单片机 和 红外 遥控是怎么连接的?红外传感器和单片机之间的连接方法:1、红外传感器绝大部分都是通过测引脚电压换算成距离。所以一般红外都有三根引脚,VCC和GND肯定会接噻,然后信号线接到你开...
单片机 和 红外 传感器怎么连接?单片机与红外传感器的连接:连接单片机和红外传感器时,通常需要使用一个电阻器。将传感器的输出引脚连接到单片机的一个输入/输出引脚,并在该引脚和单片机的电...
51 单片机红外 报警器怎么报警?1.通过红外传感器检测到目标物体后,51单片机会触发报警器进行报警。2.红外传感器可以检测到目标物体的红外辐射,当目标物体进入传感器的检测范围内时,传感器...
51 单片机 怎么样接 红外 传感器模块?51单片机可以通过接口电路来连接红外传感器模块。一般来说,红外传感器模块有三个引脚:VCC、GND和OUT。其中,VCC和GND分别连接到单片机的电源正负极,OUT则连接...
红外 发射能不能不用按键,改为 单片机 控制按键信号?《按下按键就发出信号,持续按按键持续发光,松开按键就停止发光》按照你所说的这些,并不需要单片机,直接用按键,来控制发光管,即可。《这组信号用红外接收头...