加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

红外 HS0038接收与发送

(2011-11-04 11:14:45)
标签:

杂谈

分类: 硬件

                               红外通信原理

红外遥控有发送和接收两个组成部分。发送端采用单片机将待发送的二进制信号编码调制为一系列的脉冲串信号,通过红外发射管发射红外信号。红外接收完成对红外信号的接收、放大、检波、整形,并解调出遥控编码脉冲。为了减少干扰, 采用的是价格便宜性能可靠的一体化红外接收头(HS0038,  它接收红外信号频率为38kHz,周期约26μ s)  接收红外信号,它同时对信号进行放大、检波、整形得到 TTL  电平的编码信号,再送给单片机,经单片机解码并执行去控制相关对象。具体实现过程如下:

http://s9/middle/8f6fade74b0e027275bf8&690HS0038接收与发送" TITLE="红外 HS0038接收与发送" />

(在这里特别强调:编码与解码是一对逆过程,不仅在原理上是一对逆过程,在码的发收过程也是互反的,即以前发射端原始信号是高电平,那接收头输出的就是低电平,反之亦然。因此为了保证解码过程简单方便,在编码时应该直接换算成其反码。)

http://s14/middle/8f6fade74b0e0298355cd&690HS0038接收与发送" TITLE="红外 HS0038接收与发送" />

http://s11/middle/8f6fade74b0e02d09ab2a&690HS0038接收与发送" TITLE="红外 HS0038接收与发送" />


1.红外发射部分:

下图为红外发射部分的电路拟图:

http://s16/middle/8f6fade74b0e030befd8f&690HS0038接收与发送" TITLE="红外 HS0038接收与发送" />

 

编码过程:

(1) 二进制信号的调制

二进制信号的调制由单片机来完成,它把编码后的二进制信号调制成频率为38kHz的间断脉冲串(用定时器来完成),相当于用二进制信号的编码乘以频率为38kHz的脉冲信号得到的间断脉冲串,即是调制后用于红外发射二极管发送的信号。

(2)PPM编码

这种遥控编码具有以下特征:

1遥控编码脉冲由前导码、16 位地址码(8位地址码、8 位地址码的反码)和 16 位操作码(8 位操作码、8 位操作码的反码)组成。前导码:是一个遥控码的起始部分,由一个9ms的高电平 ( 起始码 ) 和一个4. 5ms的低电平 ( 结果码 )组成,作为接受数据的准备脉冲。

16位地址码:能区别不同的红外遥控设备,防止不同机种遥控码互相干扰。

16 位操作码:用来执行不同的操作。

2采用脉宽调制的串行码,以脉宽为0.56ms、间隔0.56ms、周期为1.12ms的组合表示二进制的“0”;以脉宽为1.68ms、间隔0.56ms、周期为2.24ms的组合表示二进制的“1”。

http://s13/middle/8f6fade74b0e033acd99c&690HS0038接收与发送" TITLE="红外 HS0038接收与发送" />

 (3)发送程序

#include <AT89X51.h>

static bit OP;        //红外发射管的亮灭控制位
static unsigned int count;       //延时计数器
static unsigned int endcount; //终止延时计数
static unsigned char flag;      //红外发送标志
sbit  P3_4=P3^4;
char iraddr1;  //十六位地址的第一个字节
char iraddr2;  //十六位地址的第二个字节
void SendIRdata(char p_irdata);//发送子函数
void delay();
void main(void)
{
  count = 0;
  flag = 0;       //无载波
  OP = 0;         //不亮
  P3_4 = 0;       //在后面会发现用OP赋值的
  EA = 1; //允许CPU中断
  TMOD = 0x11; //设定时器0和1为16位模式1
  ET0 = 1; //定时器0中断允许 
  TH0 = 0xFF;
  TL0 = 0xE6; //设定时值0为38K 也就是每隔26us中断一次 
  TR0 = 1;//开始计数
  iraddr1=3;//自定义的一个地址
  iraddr2=252;//地址反码
  do{
      delay();
      SendIRdata(12);
  }while(1);
}
//定时器0中断处理
void timeint(void) interrupt 1
{
  TH0=0xFF;
  TL0=0xE6; //设定时值为38K 也就是每隔26us中断一次
  count++;
  if (flag==1)
  {OP=~OP; }//如果是待发送的有效数据flag=1,就在此产生载波(亮灭交变)
  else
  {OP = 0; }
  P3_4 = OP;
}
void SendIRdata(char p_irdata)     //发送数据子函数
{
  int i;
  char irdata=p_irdata;

  //发送9ms的起始码,并是载波模式有效
  endcount=223;
  flag=1;
  count=0;
  while(count<endcount);//等待中断,控制亮灭交变的总时间为9ms
  //发送4.5ms的结果码,并是载波模式无效
  endcount=117
  flag=0;
  count=0;
  do{}while(count<endcount);

  //发送十六位地址的前八位
  irdata=iraddr1;
  for(i=0;i<8;i++)
  {//一个周期里规定先以高电平开始,在以低电平结束。先发送0.56ms的38KHZ“1”的红外波(即编码中0.56ms的高电平)
     endcount=10;
     flag=1;
     count=0;
     do{}while(count<endcount);
//停止发送红外信号(即编码中的低电平)
   if(irdata-(irdata/2)*2)  //判断二进制数个位为1还是0
     {endcount=41; }  //1为宽的高电平1.68ms
   else
  {endcount=15;  //0为窄的高电平0.56ms
    flag=0;
    count=0;
    while(count<endcount);

    irdata=irdata>>1;//依次取位
  }
  //发送十六位地址的后八位
  irdata=iraddr2;    //此处已经是地址的反码
  for(i=0;i<8;i++)
  {
     endcount=10;
     flag=1;
     count=0;
     while(count<endcount);
     if(irdata-(irdata/2)*2)
     {endcount=41;}
     else
     {endcount=15;}
     flag=0;
     count=0;
     do{}while(count<endcount);
     irdata=irdata>>1;
  }

  //发送八位数据
  irdata=p_irdata;
  for(i=0;i<8;i++)
  {
     endcount=10;
     flag=1;
     count=0;
     while(count<endcount);
     if(irdata-(irdata/2)*2)
     { endcount=41; }
     else
     {endcount=15; }
     flag=0;
     count=0;
     do{}while(count<endcount);

     irdata=irdata>>1;
  }
  //发送八位数据的反码
  irdata=~p_irdata;      //要将数据位取反
  for(i=0;i<8;i++)
  {
     endcount=10;
     flag=1;
     count=0;
     while(count<endcount);
     if(irdata-(irdata/2)*2)
     {endcount=41; }
     else
     {endcount=15; }
     flag=0;
     count=0;
     while(count<endcount);
     irdata=irdata>>1;
  }
  endcount=10;
  flag=1;
  count=0;
  do{}while(count<endcount);
  flag=0;
}

void delay()
{
  int i,j;
  for(i=0;i<400;i++)
  {
    for(j=0;j<100;j++)
    {
    }
  }
}

一串完整的编码如下图所示

http://s10/middle/8f6fade74b0e0382cbbb9&690HS0038接收与发送" TITLE="红外 HS0038接收与发送" />

2.红外接收部分:

红外接收完成对红外信号的接收、放大、检波、整形,并解调出遥控编码脉冲。为了减少干扰,采用的是价格便宜性能可靠的一体化红外接收头(HS0038,它接收红外信号频率为38kHz,周期约26us)  接收红外信号,它同时对信号进行放大、检波、整形得到 TTL电平的编码信号,再送给单片机,经单片机解码并执行去控制相关对象。

接收部分的电路拟图为:

http://s15/middle/8f6fade74b0e03da7fe4e&690HS0038接收与发送" TITLE="红外 HS0038接收与发送" />
其应用程序为:

#include"reg52.h"            

#define uchar unsigned char    

#define uint  unsigned int

#include"reg52.h"            

#define uchar unsigned char    

#define uint  unsigned int

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;       //引导码错误则退出 ,注意与break语句的区别

   }

  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;

returned;}

main()

   IT0=1;              //设定INT0为边沿触发

   EX0=1;      //打开外部中断0

   EA=1;              //全局中断开关打开

   while(1)

   {

   switch(dis_num) 

    {

      case  0x81: num=0; break;

    case  0xcf: num=1; break;

    case  0x92: num=2; break;

    case  0x86: num=3; break;

    case  0xcc: num=4; break;

    case  0xa4: num=5; break;

    case  0xa0: num=6; break;

    case  0x8f: num=7; break;

      case  0x80: num=8; break;

      case  0x84: num=9; break;

    case  0x88: num=10;break;

    case  0xe0: num=11;break;

    case  0xb1: num=12;break;

      case  0xc2: num=13;break;

      case  0xb0: num=14;break;

case  0xb8: num=15;break;

    }

       P2=table[num];

   P1=0x01;

   delaytime(5);

   }

}

0

阅读 收藏 喜欢 打印举报/Report
前一篇:Route Discovery
后一篇:PWM模块
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有