当前位置:文档之家› 红外解码程序详解

红外解码程序详解

红外遥控解码程序设计——————基于uPD6121红外编码制式红外传感系统是目前应用最为广泛的遥控系统,一个红外遥控系统可分为发射和接收两部分组成,发射端称之为红外遥控器,一般由矩阵键盘,红外编码调制芯片和红外发射管组成;接收端用一体化红外接收头即可,这个东东内置光电放大器和解调部分,信号接收之后一般很微弱须放大后才可解码,为有效发射出去得先托付在载波上所以需经历调制、解调的过程,其实对于发射部分主要工作在于编码,而对于编码方式只有几种主流方式,而目前国内大部分均为uPD6121编码方式(日本NEC公司搞出来的。

),所以我们只须弄清楚这种编码的时序,即可写出万能的红外解码程序,只要是基于这种编码方式的遥控器(家里的电视、空调、电扇遥控器)都可以用该程序来解码(这点也充分证明了C语言的高移植性啊。

)这种编码的格式其实很简单,开头是一个引导码,人家芯片在编码时将其设计成9ms的高电平和4.5ms的低电平,也就是说你必须跳过这段引导码之后才会接收到数据,第一个问题来了:为什么要加这段引导码?因为红外传感是非常容易受到干扰的,如果直接传送数据很可能并非发送端的信号,很可能来自其他辐射,后面设计程序时会遇到这个问题。

所以我们在写程序时在引导码时可以加入检测代码,如果是引导码则继续接收,否则跳出。

第二个问题就是:接收数据时我们用外部中断接收,这是考虑到CPU 的执行效率,如果你在主函数里接收数据,就好比CPU一直在问:你接收到数据没?你接收到没?..很明显不靠谱,和串口通信一样,接收数据用中断这是经验,有利于单片机的执行效率。

第三个要注意的就是红外接收端和编码发送的数据是反向的!这点很重要,我看很多资料没有写明这点,让很多童鞋疑惑不解,也就是说引导码编码时确实是9ms高电平和4.5ms 的低电平,但是到了接收端是9ms的低电平和4.5ms的高电平,所以我们在解码时就得注意引导码高电平出现的顺序。

对于编码格式,引导码后接了4个字节的数据,前两个字节为用户码和用户反码,简单点说就是器件地址;后两字节为操作码和操作反码,就是我们真正需要的数据。

图为发送端编码格式,注意接收到的已反向!说到这里,可能会想到既然第三个字节才是我们真正的操作数据,那么我设置一个4字节数组ircode[4] 把四字节数据依次装进去,那么ircode[2]就是数据码,ircode[3]就是其反码,这样我们可以通过判断ircode[2]是否等于ircode[3]的求反?如果是,则说明接收的数据是对的!这样可以作为接收是否正确的检测代码。

最关键的问题来了:如何判断接收的是‘1’还是‘0’? uPD6121编码中说明:一个逻辑单位前0.565ms 为低电平,接着为高电平,如果高电平持续时间为0.565Ms则说明为逻辑‘1’,如果高电平持续超过0.565ms 则说明为逻辑‘0’(接收端反向后的结果,实际编码时相反)。

图为编码格式,解码应反向。

所以理清红外解码的整体流程如下:跳过引导码9ms————跳过引导码4.5ms——循环接收四字节数据(每个字节八位,每位都须判断高还是低电平)好了,思路有了,开始写程序,代码如下:#include<reg52.h>#include"delay.h"#include"lcd.h"#define uint unsigned int#define uchar unsigned charsbit irin=P3^2;sbit BEEP=P1^5;sbit lcdrw=P2^5;sbit lcdrs=P2^6;sbit lcden=P2^7;uchar ircode[4];uchar send_da;uchar table[]="IR_test:";uint k,i,j;void ir_ini();void write_da(uchar da);void write_comd(uchar comd);void beep();void delay(unsigned char x) //x*0.14MS{unsigned char i;while(x--){for (i = 0; i<13; i++) {;}}}void main(){uint i;ir_ini();serial_ini();lcd_ini();for(i=0;i<8;i++){write_da(table[i]);msdelay(1);}while(1);}void ir_ini(){EA=1;EX0=1;IT0=1;}void ir() interrupt 0{EX0=0; //关闭外部中断while(!irin) //跳过9ms低电平{delay(1);}while(irin) //跳过4.5ms高电平{delay(1);}for(i=0;i<4;i++) //4个字节的数据,前16位为用户码和其反码,后16位为操作码和其反码{for(j=0;j<8;j++) //每字节八位{while(!irin); //等待irin出现高电平msdelay(1); //短暂延时一下再判断if(irin) //irin为logic '1'{msdelay(1);ircode[i]>>=1; //右移,高位补零ircode[i]|=0x80; //最高位置位}else if(!irin) //irin为logic '0'{ircode[i]>>=1; //右移,高位补零ircode[i]&=0x7f;}}EX0=1;打开外部中断}程序写到这里,发现还没有程序能说明是否收到了数据。

于是我们加入串口通信代码,把接收到的数据发送给PC机,在调试助手上观察那四个字节的数据到底是什么东东,这样我才能知道遥控器上每个按键对应的操作码是多少,才能进行后续按键功能的设置。

串口通信初始化函数:void serial_ini(){TMOD=0x20;TH1=0xe6; //1200bpsTL1=0xe6;SM0=0;SM1=1;REN=1;TR1=1;}串口发送数据函数:for(i=0;i<4;i++){send_da=ircode[i];SBUF=send_da;while(!TI);TI=0;}加入后我们编译下载到单片机,然后打开串口调试助手观察:发现每按一个键,电脑上接收了4个字节的16进制数据,这说明确实接收成功了!因为编码格式就是一次红外发射数据中包含4个字节的数据,第三个位操作码!所以我们把每个操作码对应的十六进制数记录下来就知道这个遥控器按键在设计时对应的操作码是多少了。

(用串口的方式发送数据到电脑显示是我想到的一个方法,当然也可以发送到数码管或LCD显示,目的都是为了找到你的遥控器对应按键的操作码)从上面可以发现四个字节数据对应的第三个分别是0x45,0x46,0x47,0x44,0x40.。

(注意勾上hex选项,意思是以16进制显示)这样就好办了,我每个按键都对应一个操作码,如果我用一个选择语句,岂不是就可以让它们执行对应的功能吧,太好了,为了简单,我们现在只让他们对应显示在LCD上(知道方法后可以让它们实现你想要的功能),于是加入“lcd.h”头文件,这是我写好的LCD文件,包括其初始化,写指令和写数据函数,这些代码我在以前的日志已经写过,现在只是将其封装在一个头文件里,方便调用(详见“C51模块化编程方法”)我的遥控器上一共21个键,用两个数组建两个按键表,其实只是取了个名字,方便LCD显示:uchar code jianma2[]="0 /1 /2 /3 /4 /5 /6 /7 /8 /9 ";uchar code jianma1[]="switch/mode /quiet /start /< /> /eq /vol- /vol+ /rpt /u/sd ";然后加入选择结构,让不同按键在LCD显示出对应的按键名称。

switch(ircode[2]){ // 显示遥控上的字符按键case 0x45:write_comd(0x80+0x48); //移动数据指针for(i=0;i<=5;i++)write_da(jianma1[i]); //写入数据显示break;case 0x46:write_comd(0x80+0x48);for(i=7;i<=12;i++)write_da(jianma1[i]);break;case 0x47:write_comd(0x80+0x48);for(i=14;i<=19;i++)write_da(jianma1[i]);break;case 0x44:write_comd(0x80+0x48);for(i=21;i<=26;i++)write_da(jianma1[i]);break;case 0x40:write_comd(0x80+0x48);for(i=28;i<=33;i++)write_da(jianma1[i]);break;case 0x43:write_comd(0x80+0x48);for(i=35;i<=40;i++)write_da(jianma1[i]);break;case 0x07:write_comd(0x80+0x48);for(i=42;i<=47;i++)write_da(jianma1[i]);break;case 0x15:write_comd(0x80+0x48);for(i=49;i<=54;i++)write_da(jianma1[i]);break;case 0x09:write_comd(0x80+0x48);for(i=56;i<=61;i++)write_da(jianma1[i]);break;case 0x19:write_comd(0x80+0x48);for(i=63;i<=68;i++)write_da(jianma1[i]);break;case 0x0d:write_comd(0x80+0x48);write_da(jianma1[i]);break;case 0x16: //显示遥控上得数字write_comd(0x80+0x48);for(i=0;i<=5;i++)write_da(jianma2[i]);break;case 0x0c:write_comd(0x80+0x48);for(i=7;i<=12;i++)write_da(jianma2[i]);break;case 0x18:write_comd(0x80+0x48);for(i=14;i<=19;i++)write_da(jianma2[i]);break;case 0x5e:write_comd(0x80+0x48);for(i=21;i<=26;i++)write_da(jianma2[i]);break;case 0x08:write_comd(0x80+0x48);write_da(jianma2[i]);break;case 0x1c:write_comd(0x80+0x48);for(i=35;i<=40;i++)write_da(jianma2[i]);break;case 0x5a:write_comd(0x80+0x48);for(i=42;i<=47;i++)write_da(jianma2[i]);break;case 0x42:write_comd(0x80+0x48);for(i=49;i<=54;i++)write_da(jianma2[i]);break;case 0x52:write_comd(0x80+0x48);write_da(jianma2[i]);break;case 0x4a:write_comd(0x80+0x48);for(i=63;i<=68;i++)write_da(jianma2[i]);break;}到这里红外解码后的效果就可以在LCD上看到了,我们这里都只是让各个按键显示应的名字,如果想让某个按键实现某种特定功能也非常方便了,比如按下第三个键(对应操作码0x47)时想让蜂鸣器发声只须将一个case分支改成蜂鸣器发声函数就可以了!似乎代码写到这里我们已经成功了大半,但是实际测试时会发现一个非常重要的问题,抗干扰能力!几乎我一上电复位它就会接收到数据,其实是假码,甚至我电脑开个机都能影响到它(- -.) ,因为这些设备在启动或关闭时都会辐射一定的红外线,而我们的红外接收头又是相当灵敏,所以必然被干扰信号扰乱。

相关主题