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;                                                            //打開總中斷
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM