单片机红外遥控初学和极为简单的C51红外遥控程序

        近来想学习单片机红外遥控,在网上找了N多资料,基本完成学习,分享出去,希望和我一样的初学者可以借鉴,大神能够指点一二。

一、简介

    根据使用的编码芯片不同,红外遥控编码的格式也不同,较普遍的有NEC标准和PHILIPS标准。最常用的应该是NEC标准吧,我手上也就是几个遥控器(单买的、学习板带的)都是这个标准,后面也以此为例。

    不管使用什么标准,接收头收到的都是一样的。常用的接收头HS0038 VS838等 功能大致相同,只是引脚封装不同。

二、NEC编码格式

EC标准:遥控载波的频率为38KHz(占空比1:3)当某个键按下时,发射端首先发射一个完整的全码,如果按键超过108ms仍未松开,接下来发射的代码(连发代码)将由起始码(9ms)和结束码(2.5ms)组成,并每隔108ms重复。

    完整的全码包括:

1、引导码为系统起始工作标志;

2、用户码8位 、用户反码8位(或称为用户码低8位、高8位)为用户识别码,以区别不同的红外遥控设备,防止互相干扰,学习常用的用户码一般都为0x00ff,两个码位与位相反,用于校验(如果需要);

3、按键码8位、按键码反码8位,用于区分不同的按键键值,两个码位与位相反,用于校验(如果需要);

4、结束码代表1个完整全码的结束;


NEC标准下的发射码为:

引导码高电平9ms,低电平4.5ms;

数据(用户码、反码、按键码、反码)0时用”0.56ms高电平 + 0.565ms低电平 = 1.125ms”表示;1时用”高电平0.56ms + 1.69ms低电平 = 2.25ms”表示。

结束码为1个高电平(好多资料没提起)大约1ms吧,然后持续低电平,直到松开按键或108ms后发送连发代码。

下图分别为发送和接收头数据码波形:

这里要特别注意:接收头收到38kHz红外信号时并传输给单片机端口时,一体化接收头输出的波形和发射波形是反相的(包括引导码)。

      也就是说红外遥控数据的传输和普通芯片不同,普通芯片数据传输是按照时序图,不同时间电平分别为1和0来区分,而红外遥控发射数据(接收相反)时不管是0还是1,都先拉高=1并保持0.56ms,然后拉低=0持续保持0.565ms(共1.125ms)则表示发送的是0,拉底=0后持续保持1.69ms(共2.25ms)则表示发送的是1;接受头输出波形正相反,先拉底=0,然后拉高=1,根据拉高=1保持的时间长短分别为0和1;

三、操作实验:

    上面说的略微了解即可,动手实验能够增加映象。因为每个遥控按键的按下数据每个“位”的传输都是以负跳变(接收头输出波形)开始的,所以用外部中断INT0(P3.2口)或INT1(P3.3口)作为触发方式;而且每个按键数据传输完整时间高达58.5~76.5ms,不能为了等待按键,占用系统时间,所以系统使用计时器定时来判断状态和读取数据。下面是逐步的学习实验:

1、接收管接P3.2(外部中断0端口)或P3.3(外部中断1端口),写程序调用外部中断,触发方式为下降沿(IT1=1),设置变量a,外部中断服务函数写a=5,主函数设置显示设备显示变量a;

实验结果:按下遥控器任意键,显示“5”,红外遥控系统正常,接收管收到红外信号并触发外部中断。

2、接上面程序,设置变量b,外部中断服务函数写b++(累加),主函数设置显示设备显示变量b的值;

实验结果:显示数据最少为34,大多显示36,如果松开慢一些显示值更高,说明红外遥控器按键按下1次,最少触发34次外部中断。

3、使用示波器观察,可以明显看到波形有引导码1位,2个8位的地址码(大多是0x00和0xff),2个8位的按键码,1个结束码。所有都是电平先0后1(接收头输出波形),除引导码和结束码外,电平为高=1的时间长短决定数据是0还是1。结果和实验2中测试的相同,完整的全码为34个数据位。


四、程序思路:

1、鉴别引导码、数据码和连发代码

    引导码为1个下降沿(负跳变)后13.5ms跟着第1位数据码下降沿;

    数据码根据发送的0和1不同,在1个下降沿之后1.125ms(数据0)或2.25ms(数据1)后是下1位数据下降沿;

   连发代码是在1个结束码下降沿后108ms之后产生;

   所有的判断都是基于下降沿(触发外部中断)开始到下一个下降沿之间的时间长短,这个时间是判断所有传输数据类型的唯一条件。

2、数据位的读取:设定变量a和b,分别在计时器中断和外部中断中进行累加,在每1个下降沿触发外部中断都重置计时器累加变量(a=0),同时在计时器中断中判断a累加计时是否超过1.125ms,而小于2.25ms,如果有则对应序号为b的数据位=1;

3、判断计时器中断中累加计时a,计时时间超过15ms(引导码时长13.5ms)表示错误或数据以及传输完,重置a和b;

五C51代码:

#define XTAL_T 129                        //XTAL_T=(计时器溢出时间*晶振频率)/12=(140us*11.0592MHz)/12
sbit IR=P3^3;                                 //红外接收头数据端口
unsigned char num_INT1;               //外部中断INT1累加变量
unsigned char num_T0;                    //计时器中断T0累加变量

unsigned char tmp_code[34];        //1个完整码的34位数据都取了

void init_T0()             //计时器中断T0子函数
{    
    ET0=1;                     
    TMOD=0x02;    
    TH0=(256-129);       
    TL0=(256-129);       
    TR0=1;                     
}

void init_INT1()            //外部中断子函数
{        
    EX1=1;                      
    IT1=1;                     
}

void main()                    //主函数
{
    EA=1;
    init_T0();
    init_INT1();
    while(1)
    {
    }
}
void int1() interrupt 2        
{
    num_T0=0;  
    num_INT1++;
}
void t0() interrupt 1
{
    num_T0++;
    if((num_T0>=10)&&(num_INT1>=1)&&(num_INT1<=34))
        tmp_code[num_INT1-2]=1;
    if(num_T0==120)
    {
        num_INT1=0;
        num_T0=0;
    }

}

在数组tmp_code[34]中读取了1个完整码的34位数据,分别为:tmp_code[0]=1位引导码;tmp_code[1-8]=8位用户码;tmp_code[9-16]=8位用户反码;tmp_code[17-24]=8位键值码;tmp_code[25-32]=8位键值反码;tmp_code[33]=1位结束码;

需要的可以自己写显示函数验证;

或者直接输出按键编码值:

unsigned char dat_IR;

void t0() interrupt 1
{
    num_T0++;
    if((num_T0>=10)&&(num_INT1>=18)&&(num_INT1<=25))
        dat_IR=dat_IR|(0x80>>(num_INT1-18));
    if(num_T0==120)
    {
        num_INT1=0;
        num_T0=0;
    }
}



猜你喜欢

转载自blog.csdn.net/zhb2004xp/article/details/80951011