38KHz,NEC红外模拟发送和接收程序

/*************************************************************************************************/
//38k NEC 编码接收和模拟发射
//完整的信号构成:引导码+8位的客户码+8位客户码的补码+8位的按键值+8位按键值的补码+结束码
//接收使用外部中断0,发射管低电平触发
//STC15F104W@24MHz
//
//为了尽可能的简化代码量,没有引入按键,程序运行起来为每250ms一个周期,每250ms通过红外发射管发射一次数据,同
//时若检测到接收管接收到了完整的数据则把这个数据通过串口发出
//
//由于发送和接收数据肯定无法同时在一片单片机上完成,所以若需测试代码的完整功能应准备两个单片机或者其他设备。
//
//网上能找到的模拟红外发射的代码多为通过定时器实现,本代码则统统使用软件延时实现。一来使用定时器太浪费资源,二
//来对于1T单片机来说,用软件延时马马虎虎输出38K的波形还是挺简单的(这都0202年了,估计以后很难由机会再用到12T
//的51单片机了吧)
/*************************************************************************************************/

#include    "config.h"
#include    "delay.h"
#include    "suart.h"

sbit IRIN =P3^2;    //红外接收端口
sbit IROUT=P3^3;    //红外发射端口
unsigned char IrValue[4];            //红外键值.四个数据分别为用户码、用户补码、键值、键值补码
bit flag_Ir;                         //红外接收完成标志位
void Ir_Init(void);                                 //红外初始化(外部中断0和发送接收端口初始化)
void Ir_Out(unsigned char u, unsigned char x);      //发送数据,两个参数分别为用户码和键值
void Ir_Out_Frame(unsigned char y);                 //发送一帧数据,由 Ir_Out() 函数调用
void Delay7us();        //7us延时 @24.000MHz_STC-Y5
void Delay19us();        //19us延时@24.000MHz_STC-Y5
void Delay100us();      //100us延时@24.000MHz_STC-Y5

/**************************************************************************************************
* 主函数
*/
void main(void)
{
    Ir_Init();                    //红外初始化
    SUART_INIT();                 //串口初始化
    TxString("38KHz NEC 红外接收测试程序\r\n");
    while (1)
    {
        delay_ms(250);        //每250ms通过红外发送一个数据
        Ir_Out(0x42,0x5A);    //参数为用户码、键值
        
        if(flag_Ir)                //如果接收到完整数据,则把数据通过串口打印出来
        {
            flag_Ir=0;             //必须清空接收完成标志
            TxByte(IrValue[0]);
            TxByte(IrValue[1]);
            TxByte(IrValue[2]);
            TxByte(IrValue[3]);
        }
    }
}

/**************************************************************************************************
* 红外接收初始化
*/
void Ir_Init(void)
{
  IT0  = 1;  //下降沿触发
  EX0  = 1;    //打开中断0允许
  EA   = 1;    //打开总中断
  IRIN  = 1;    //初始化端口
  IROUT  = 1;
  flag_Ir  = 0;    //初始化接收完成标志位
}

/**************************************************************************************************
* 延时7us @ 24MHz STC-Y5
* 生成38k波使用,占空比在1/3时效果最好。i的取值供参考,使用时视波形表现调整。
*/
void Delay7us()        //@24.000MHz
{
  unsigned char i;
  _nop_();
  _nop_();
  i = 38;
  while (--i);
}
/**************************************************************************************************
* 延时19us @ 24MHz STC-Y5
* 生成38k波使用,占空比在1/3时效果最好。i的取值供参考,使用时视波形表现调整。
*/
void Delay19us()        //@24.000MHz
{
  unsigned char i;
  _nop_();
  _nop_();
  i = 105;
  while (--i);
}
/**************************************************************************************************
* 延时100us @ 24MHz STC-Y5
*/
void Delay100us()        //@24.000MHz
{
  unsigned char i, j;
  i = 3;
  j = 82;
  do
  {
    while (--j);
  } while (--i);
}

/**************************************************************************************************
* 红外发射程序
*/
void Ir_Out(unsigned char u, unsigned char x)
{
  int tt;
  EA=0;   //关闭所有中断
    
  /*发送引导码*/
  tt=350;    //输出9ms   1(有38k信号输出表示 1. 38k每个周期约26us,9ms大概tt个周期,视实际情况调整)
  do {IROUT=0; Delay19us(); IROUT=1; Delay7us();} while(--tt);
  tt=45;    //输出4.5ms 0 (无38k信号输出表示 0. 同样,tt的取值看实际情况调整)
    do {IROUT=1; Delay100us();} while(--tt);
    
  /*发送用户码和用户补码*/
  Ir_Out_Frame(u);
  Ir_Out_Frame(~u);

  /*发送键值和键值补码*/
  Ir_Out_Frame(x);
  Ir_Out_Frame(~x);

  /*发送结束码*/
  tt=25;    //输出0.65ms  1
  do {IROUT=0; Delay19us(); IROUT=1; Delay7us();} while(--tt);
    tt=5;        //输出40ms            0
  do {IROUT=1; Delay100us();} while(--tt);

  /*重复码*/
  //9ms            1
  //2.25ms    0
  //0.56ms    1
  //40ms        0
  //56ms        0

  EA=1;   // 打开中断
}
/**************************************************************************************************
* 红外单帧发射程序
*/
void Ir_Out_Frame(unsigned char y)
{
  char num;
  int    tt;
  for (num=0; num<8; num++)            //循环8次移位
  {
    tt=25;            //输出0.65ms  1                                    
    do {IROUT=0; Delay19us(); IROUT=1; Delay7us();} while(--tt);
    if(y&0x01)    //if为1
    {
      tt=16;        //输出1.65ms 0
      do {IROUT=1; Delay100us();} while(--tt);
    }
    else                //否则
    {
      tt=5;            //输出0.56ms 0
      do {IROUT=1; Delay100us();} while(--tt);
    }
    y >>= 1;                                        //右移一位
  }
}

/**************************************************************************************************
* 读取红发外值
* 外部中断的服务程序
*/
void ReadIr() interrupt 0
{
    unsigned int err;
    unsigned int tt;
    unsigned char Time;
  unsigned char j, k;
    EA=0;                                                            //关闭总中断
  Time=0;
  tt=80;                                                         //延时8ms去干扰,引导码为9ms的低电平和4.5ms的高电平
    do { Delay100us(); } while(--tt);
  if(IRIN==0)                                                //确认是否真的接收到正确的信号
  {
    err=500;                                                //超时判断
    while((IRIN==0)&&(err>0))                //等待高电平,最多等待50ms
    {
      Delay100us();
      err--;
    }
    if(IRIN==1)                                            //收到高电平
    {
      err=500;                                            //超市判断
      while((IRIN==1)&&(err>0))            //等待4.5ms的高电平过去
      {
        Delay100us();
        err--;
      }
      for(k=0; k<4; k++)                        //开始接收数据,共有4个数据
      {
        for(j=0; j<8; j++)                    //每个8位
        {
          err=60;
          while((IRIN==0)&&(err>0))    //等待信号前面的560us低电平过去
          {
            Delay100us();
            err--;
          }
          err=500;
          while((IRIN==1)&&(err>0))    //计算高电平的时间长度。
          {
            Delay100us();
            Time++;
            err--;
            if(Time>50)                            //超过5ms说明接收到重复码。重复码:9ms高电平+2.25ms低电平+560us高电平
                        {
                            EA=1;                                    //打开总中断
              return;                                //退出函数
                        }
          }
          IrValue[k]>>=1;                         //k表示第几组数据
          if(Time>=8)                                //若高电平出现大于0.8ms,为1(1.65ms为1,0.56ms为0,这里取中间值)
            IrValue[k]|=0x80;
          Time=0;                                        //用完时间要重新赋值
        }
      }
    }
    if(IrValue[2]=~IrValue[3])            //这里一般只判断键值而不判断用户码,因为对于一些用户码是0的遥控无论怎样都是0
    {
      flag_Ir = 1;                                 //如果按键值与按键值的补码的取反相同,则接收完成标志置位
    }
  }
    EA=1;                                                            //打开总中断
}

 

posted @ 2020-02-18 21:48  路安达  阅读(3697)  评论(6编辑  收藏  举报