• 歡迎光臨東莞市飛江電子科技有限公司官網!
    全國咨詢熱線

    13926563901

    18925580829

    飛江淘寶店鋪

    首頁>技術資料>其它單片機

    STC的51單片機紅外遙控器讀碼、發射程序,已試成功

    發布時間:2018-04-09   瀏覽量:

     

    STC的51單片機紅外遙控器讀碼、發射程序,已試成功
    wxleasyland@sina.com
    2014.11.21

    有一臺DVD機沒有遙控器,正好別的遙控器有的鍵可以用,但功能不一樣。

    于是開展本工程,程序原為網上摘的,經過修改均已全部成功。

    采用STC的51單片機,STC12C5A60S2,可以直接串口編程,而且是1T的,非常方便。

    一、紅外遙控器讀碼
    讀碼程序沒怎么修改就成功了。
    注意:這里的延時程序是STC12C5A60S2的,如果用別的單片機,需要修改。

    #include <STC\STC12C5A60S2.H>
    #include <INTRINS.h>

    //采用1T周期的STC12C5A60S2單片機,11.0592MHZ
    //WXL:一體化接收頭默認是輸出高電平,有信號時輸出低電平;接P3.2腳。
    //WXL:這里按“低位在先”

    /******************************************************************/
    /* 本程序的藍本從網上搜集,經修改并注釋,萬能遙控器解碼成功 */
    /* 晶振:11.0592MHz */
    /* 整理與測試:單片機教程網  胡琴 2012.5.15 */
    /************************* 說 明 *********************************/
    /* 以一個9ms的低電平和4.5ms的高電平為引導碼,后跟32位二進制代碼 */
    /* 前16位為8位用戶碼及其反碼,后16位為8位的操作碼及其反碼 */
    /* 以脈寬為低電平0.565ms、間隔高電平0.56ms、周期為1.125ms的組合表示"0"; */
    /* 以脈寬為低電平0.565ms、間隔高電平1.685ms、周期為2.25ms的組合表示"1"。 */
    /* 注意:接收碼的脈寬與間隔是對發射碼取反的,即間隔是0.565ms */
    /* 解碼后共有四個十六進制碼,本程序取第三個作為識別碼 */
    /*******************************************************************/

    #define uchar unsigned char
    uchar data IRcode[4]; //定義一個4字節的數組用來存儲代碼
    uchar CodeTemp; //編碼字節緩存變量
    uchar i,j,k; //延時用的循環變量
    sbit IRsignal=P3^2; //HS0038接收頭OUT端直接連P3.2(INT0)
    sbit P0_0=P0^0; //P0連接到 LED 上
    sbit P0_1=P0^1;
    sbit P0_2=P0^2;



    /**************************延時0.6ms子程序**********************/
    void Delay0_6ms(void) //@11.0592MHz
    {
    unsigned char i, j;

    _nop_();
    _nop_();
    i = 7;
    j = 112;
    do
    {
    while (--j);
    } while (--i);

    }


    /**************************延時0.9ms子程序**********************/
    void Delay0_9ms(void) //@11.0592MHz
    {
    unsigned char i, j;
    _nop_();
    _nop_();
    _nop_();
    i = 10;
    j = 170;
    do
    {
    while (--j);
    } while (--i);
    }




    /***************************延時1ms子程序**********************/
    void Delay1ms(void)
    {
    unsigned char i, j;

    _nop_();
    i = 11;
    j = 190;
    do
    {
    while (--j);
    } while (--i);
    }

    /***************************延時4ms子程序**********************/
    void Delay4ms(void)
    {
    unsigned char i, j;

    _nop_();
    _nop_();
    _nop_();
    i = 44;
    j = 3;
    do
    {
    while (--j);
    } while (--i);
    }

    /**************************** 延時子程序 ************************/
    void Delay(void)
    {
    uchar i,j,k;
    for(i=200;i>0;i--)
    for(j=200;j>0;j--)
    for(k=3;k>0;k--)  ;
    }

    /******************** 中斷0解碼服務子程序 ********************/
    void int0(void) interrupt 0 using 2
    {
    EA = 0; //??? 可以這樣,跳入中斷,但仍可對P3.2(INT0)進行電平變化的讀取
    for(k=0;k<10;k++)
    {
    Delay0_9ms();
    if (IRsignal==1)        //如果0.9ms后IRsignal=1,說明不是引導碼,退出中斷
    {
    k=10;
    break;
    }

    else if(k==9)         //如果 持續了10×0.9ms=9ms的低電平,說明是引導碼。WXL:一定是從引導碼開始
    {
    while(IRsignal==0);      // WXL:因為紅外頭默認輸出是高電平,故用while(IRsignal==0)很安全,而用while(IRsignal==1)則可能會進入死循環
    Delay4ms();       //跳過持續4.5ms的高電平     WXL:要超過4.5ms更好
    Delay0_6ms();

    for(i=0;i<4;i++)     //分別讀取4個字節
    {
    for(j=1;j<=8;j++)    //每個字節8個bit的判斷
    {
    while(IRsignal==0); //等待上升沿,此處用得很好:因為0.56ms的低電平(接收時)是代碼0與1的相同部分
    Delay0_9ms(); //從上升沿那一時刻開始延時0.9ms(因為0.9介于0.56(=1.125-0.56)與1.69(=2.25-0.56)之間),再判斷IRsignal
    if(IRsignal==1) //  如果IRsignal是"1",高位置"1",并向右移一位
    {
    Delay1ms(); //為什么要延時1ms呢?因為要使IRsignal跳至低電平(即0.56ms的0與1相同部分上)
    CodeTemp=CodeTemp | 0x80; //此處的算法很好
    if(j<8)  CodeTemp=CodeTemp>>1;
    }
    else       //  如果IRsignal是"0",高位置"0",并向右移一位
    if(j<8) CodeTemp=CodeTemp>>1;   //如果IRsignal是"0",則直接向右移一位,自動補"0"
    }
    IRcode=CodeTemp;
    CodeTemp=0;
    }   //end for

    for(i=0;i<4;i++) //通過串口將代碼發出
    {
    SBUF=IRcode;
    while(!TI); //等待一個字節發送完畢
    TI=0;
    }

    Delay();
    }    //end else
    }   //END for
    EA = 1;
    }

    /***********************串口初始化程序*********************/
    void initUart(void)
    {
    TMOD |= 0x20; //
    SCON = 0x50; //
    PCON |= 0x80; //
    TH1 = 250; // 9600 bps @ 11.0592MHz
    TL1 = 250;
    TR1 = 1;
    }

    /**************************主程序*************************/
    void main()
    {
    //P0=0XFF;
    initUart();
    IT0 = 1; //INT0為負邊沿觸發, (1:負邊沿觸發,0:低電平觸發)
    EX0 = 1; //外部中斷INT0開, (1:開, 0:關 )
    EA = 1; //開所有中斷

    CodeTemp = 0; //初始化紅外編碼字節緩存變量
    Delay();
    while(1)
    {

    }
    }






    二、紅外遙控發射

    網上的程序是http://gudeng614.blog.163.com/blog/static/818017420101545648734/

    做發射程序費了很大波折,因為網上的程序不好用。

    后來不得不用計算機的并口采集了發射數據,發現數據有異常,終于找到了問題所在。

    原因是count變量是int的,對其賦值或比較時,匯編語句一句完不成,會被中斷服務程序中斷,造成count變量賦值或比較出現問題。
    解決方法是必須在操作時屏蔽中斷。而flag變量是bit的,一句匯編即可完成賦值,故不會有問題。

    其間還發現別的遙控器會在起始碼前加一個前脈沖,以為是這個問題,其實不是。

    注意:由于13us會中斷一次,這里是采用1T的單片機。如果采用普通的51單片機,由于是12T的,不知道能不能成功。

    //程序從網上修改而來
    //由于中斷需要13us中斷一次,即中斷要在幾us處理完,因此需要單片機速度比較快,用24MHZ晶振才能保證正常
    //但24MHZ晶振,用串口不方便
    //這里采用1T周期的STC12C5A60S2單片機,11.0592MHZ,可以兼顧。
    //STC12C5A60S2  引腳可灌入20mA電流,直接從正電源→紅外LED→串1K電阻→P0.0腳。
    //串口1默認選T1作為波特率發生器
    //TO用于中斷
    //發送時,低比特位優先

    #include <STC\STC12C5A60S2.H>
    #include <INTRINS.h>

    sbit  P0_0 = P0^0;

    static bit g_OP;            //紅外發射管的亮滅
    static unsigned int g_count;       //延時計數器
    static unsigned int g_endcount;    //終止延時計數
    static bit g_flag;       //紅外發送標志
    unsigned char g_iraddr1;     //十六位地址的第一個字節
    unsigned char g_iraddr2;     //十六位地址的第二個字節


    //定時器0中斷處理
    void timeint(void) interrupt 1
    {
      g_count++;
      if (g_flag)    g_OP=~g_OP;
      else    g_OP = 1;    //LED不點亮
      P0_0 = g_OP;
    }


    /////////////////////////////////////////////////////
    void SendIRdata_38KHZ(unsigned int temp1, bit temp2)
    {
      g_endcount=temp1;
      g_flag=temp2;
      EA=0; g_count=0; EA=1;  //避免中斷影響count置數
      while(1)
      {
       EA=0;
       if( g_count < g_endcount ) EA=1;  //避免中斷影響count比較
       else
       {
       EA=1;
       break;
       }  
      }  
    }

    /////////////////////////////////////////////////////
    void SendIRdata_BYTE(unsigned char irdata)
    {
      unsigned char i;
      for(i=0;i<8;i++)
      {
         //先發送0.56ms的38KHZ紅外波(即編碼中0.56ms的高電平)
    SendIRdata_38KHZ(43, 1);   //13.02*43=0.56ms

        //停止發送紅外信號(即編碼中的低電平)
         if(irdata & 1)  //判斷最低位為1還是0。   低位先發送??!
           SendIRdata_38KHZ(130, 0);         //1為寬電平,13.02*130=1.693ms
        else      SendIRdata_38KHZ(43, 0);   //0為窄電平,13.02*43=0.560ms

        irdata=irdata>>1;
      }
    }

    /////////////////////////////////////////////////////
    void SendIRdata(unsigned char p_irdata)
    {
      //有的遙控器會發一個前脈沖,如果不靈,可試試加上前脈沖
      //發送起始碼前脈沖,高電平有38KHZ載波
      //SendIRdata_38KHZ(18, 1);
      //發送起始碼前脈沖,低電平無38KHZ載波
      //SendIRdata_38KHZ(18, 0);

      //發送9ms的起始碼,高電平有38KHZ載波
      SendIRdata_38KHZ(692, 1); //13.02*692=9.010ms

      //發送4.5ms的結果碼,低電平無38KHZ載波
      SendIRdata_38KHZ(346, 0);    //13.02*346=4.505ms

      //發送十六位地址的前八位
      SendIRdata_BYTE(g_iraddr1);

      //發送十六位地址的后八位
      SendIRdata_BYTE(g_iraddr2);

      //發送八位數據
      SendIRdata_BYTE(p_irdata);

      //發送八位數據的反碼
      SendIRdata_BYTE(~p_irdata);  

      //發送總的結束位1bit
      SendIRdata_38KHZ(43, 1);     //13.02*43=0.56ms


    /*  //后面這些可以不用發
      g_endcount=1766;
      g_flag=0;
      EA=0; g_count=0; EA=1;
      while(1){EA=0; if(g_count<g_endcount) EA=1; else { EA=1; break; }  }   

      //發送9ms的起始碼,高電平有38KHZ載波
      g_endcount=692;   //13.02*692=9.010ms
      g_flag=1;
      EA=0; g_count=0; EA=1;
      while(1){EA=0; if(g_count<g_endcount) EA=1; else { EA=1; break; }  }   

      //發送4.5ms的結果碼,低電平無38KHZ載波
      g_endcount=346;    //13.02*346=4.505ms
      g_flag=0;
      EA=0; g_count=0; EA=1;
      while(1){EA=0; if(g_count<g_endcount) EA=1; else { EA=1; break; }  }   

    //發送總的結束位1bit
      g_endcount=43;    //13.02*43=0.56ms
      g_flag=1;
      EA=0; g_count=0; EA=1;
      while(1){EA=0; if(g_count<g_endcount) EA=1; else { EA=1; break; }  }   

    */

      g_flag=0;


    }



    ///////////////////////////////////////////////////////////
    void main(void)
    {
    unsigned char com_data;    //數據字節

      g_count = 0;
      g_flag = 0;
      g_OP = 1;
      P0_0 = g_OP;   //LED接電源正極,不點亮

    SCON=0x50;  //串口方式1    01 0 1 0 0 00  模式1,非多機,允許接收,無數據位8,清中斷標識TI和RI

      TMOD = 0x22; //(定時器0和1:方式2,自動重裝,8位)
    TH1=253;  //11.0592MHZ,9600bps。沒有設置SMOD,故波特率沒有加倍。即:11.0592/12/3/32=9600bps
    TL1=253;
    TR1=1;    //啟動定時器

      TH0 = 244;
      TL0 = 244; //(WXL:即計數12次中斷一次,即11.0592MHZ晶振,機器周期是1.085us,12次*1.085=13.02us,這樣達38KHZ。  13us一次中斷,時間太短了,所以單片機要快)
      ET0 = 1;   //定時器0中斷允許
      EA = 1;    //允許CPU中斷
      TR0 = 1;   //開始計數

      g_iraddr1=0;       //地址碼
      g_iraddr2=255;     //地址反碼

    RI=0;
    while(1)
    {
    if(RI==1)
    {
    com_data =SBUF;
    RI=0;       //要人工清RI
    SendIRdata(com_data);   //發送紅外數據
    TI=0;
    SBUF = com_data;   //輸出字符
    while(!TI) ;     //空語句判斷字符是否發完,TI=1表示發完
    TI = 0;         //要人工清TI
    }
    }

    }

    黑人粗大无码AV人妻一区