基於STC12系列單片機的通用紅外遙控信號分析程序(一)


前言

  最近學51單片機學習到紅外遙控解碼與發送部分,開發板的相關教程只有NEC協議的解碼,基本的解碼套路是1838接收頭輸出管腳接單片機外部中斷0,當接收到紅外信號時產生下降沿觸發中斷,在中斷函數中先延時9ms判斷電平再延時4.5ms判斷電平,從而跳過引導碼;再分別延時560us、1690us左右不等的時間判斷電平來解碼“0”或“1”,直到結束;紅外發送思路就是根據NEC協議及紅外碼值的二進制碼分別控制高低電平,並延時相應的時間。但存在這么幾個問題:

  1. 解碼邏輯寫死在中斷處理函數中,不方便擴展、移植;

  2. 只能解碼NEC協議的紅外遙控信號,如果拿一款別的遙控器來,編碼協議未知,整個程序就無能為力了;

  3. 接收到信號時實時解碼,沒有保存紅外波形信息,不能輸出波形進行分析;

  4. 只能發送NEC協議的紅外遙控信號。

  恰逢外地出差,帶了開發板卻沒帶NEC的紅外遙控器,手邊只有空調、電視遙控器和一個帶紅外遙控功能的手機,於是就想利用51單片機做一個通用的紅外遙控信號錄波、解碼、發送為一體的程序,直接錄制紅外波形,發送時也是直接按原波形發送,這樣就做到了萬能紅外信號的學習與發送。同時將錄制的波形數據發送到上位機進行顯示、分析,這樣就算拿到一款未知紅外協議的遙控器,也可以做它的協議分析了。PS:本人沒有示波器、邏輯分析儀,有這些裝備的同學請隨便看看。

紅外遙控基本原理

  紅外發射和接收的原理就不細說了,網上很多,也可以參見這篇文章《全面了解紅外遙控(中文版)》,這是一個歪果仁寫的,網友翻譯,講了基本原理,也介紹了各種常見的協議。

  需注意的是通常的介紹協議時說的表示邏輯“0”或“1”的高低電平是針對發射端的,而常用的一體化紅外接收頭如HS0038、VX1838等在無紅外信號是輸出高電平,有紅外信號時輸出低電平,也就是與發射端時相反的——發射端高電平發射紅外線,接收端接收后產生低電平。這在解碼時必須注意。

紅外遙控錄波硬件系統

  為了盡可能的提高錄波時的分辨率,采用了1T模式的STC12C5A60S2單片機,之所以用STC12系列而沒用更快的STC15系列時因為12系列DIP40封裝與傳統8051完全兼容,直接插51開發板上就能用。紅外遙控接收頭為HS0038,輸出管腳接P3.2口(原理圖中紅外接收頭只是隨便找了個相近的元件做示意)。采用LCD1602做簡單顯示。原理圖如下:

 

  

紅外遙控錄波程序實現

  本文章內只貼出關鍵程序,完整程序請點擊下載,編譯環境Keil4。

  原理:HS0038輸出管腳接INT0中斷,下降沿觸發。當接收到紅外信號后,HS0038輸出管腳為低電平,進入中斷處理函數,立即啟動定時器0,等待紅外輸出管腳變為高電平,記錄低電平時間;然后重置定時器0,等待紅外輸出管腳變為低電平,記錄高電平時間;如此往復,直到某次等待超時或記錄時間的數組已用完。

  錄制的波形數據保存到一個unsigned char數組中,兩兩一組,以低電平開始(針對接收端而言),交替表示低電平、高電平的持續時間。格式為:

0x04, 0x24,    //低字節在前,實際數據為0x2404,低電平持續時間的計數值
0x84, 0x11,    //低字節在前,實際數據為0x1184,高電平持續時間的計數值
...

  該段程序不僅可以錄制紅外波形,還可以做簡易的邏輯分析儀使用。錄制波形時定時器0的計時時間為1us,所以該段程序的錄波理論最小分辨率為1us,但由於中間計算過程等耗時會產生誤差,所以最好用來錄制電平持續時間大於10us的脈沖波形。

  錄波的流程圖如下:

 

//硬件
//@單片機          :    STC12C5A60S2
//@晶振            :    12.0MHz

 

void InitTimer0()    //定時器0初始化
{
    ET0 = 1;
    AUXR &= 0x7f;        //定時器時鍾12T模式,1us
    TMOD &= 0xF0;        //設置定時器模式
    TMOD |= 0x01;        //設置16位定時器
    TL0 = 0x00;            //設置定時器初值
    TH0 = 0x00;            //設置定時器初值
    TF0 = 0;            //清除TF0標志
    TR0 = 0;            //暫不開啟定時器0計時
    PT0 = 1;            //高優先級,必須,否則在外部中斷0中就不能執行定時器0是否超時溢出
}

void Timer0Interrupt(void) interrupt 1 using 1
{
    timer0Overflow = true;    //超時溢出標志
}

void InitINT0()
{
    EX0 = 1;    //打開中斷0
    IT0 = 1;    //1——下降沿觸發;0——低電平觸發
}

void INT0Interrupt() interrupt 0 using 2
{
    UINT8 i;
    UINT8  cH, cL;
    TR0 = 1;     //定時器0開始計數
    EX0 = 0;    //關閉外部中斷0的中斷響應

    usedLength = 1;    //如果沒有接收到有效信號,串口發送1次共4字節數據,用來跟串口收發失敗的情況區分
    for (i = 0; i < MAX_BUFFER_LENGTH / 4; i++)
    {
        while (!IR_In)
        {
            if (timer0Overflow)
            {
                usedLength = i;
                goto endfor; //65ms,超時,跳出循環
            }
        }
        TR0 = 0;
        cL = TL0;    //取定時數據
        cH = TH0;
        TL0 = 0x00;    //初始化
        TH0 = 0x00;
        timer0Overflow = false;
        TR0 = 1;     //定時器0開始計數

        waveData[4 * i + 0] = cL;
        waveData[4 * i + 1] = cH;

        while (IR_In)
        {
            if (timer0Overflow)
            {
                usedLength = i;
                goto endfor; //65ms,超時,跳出循環
            }
        }
        TR0 = 0;
        cL = TL0;    //取定時數據
        cH = TH0;
        TL0 = 0x00;    //初始化
        TH0 = 0x00;
        timer0Overflow = false;
        TR0 = 1;     //定時器0開始計數

        waveData[4 * i + 2] = cL;
        waveData[4 * i + 3] = cH;

        usedLength = i;
    }
endfor:
    TR0 = 0;    //關閉定時器0
    timer0Overflow = false;
    TL0 = 0x00;
    TH0 = 0x00;

    if (usedLength > 2)    //至少錄制了一組有效數據,顯示錄制的數據長度
    {
        Lcd1602Clear();

        setPos(0, 0);
        writeData('L');
        writeData(':');
        writeData((usedLength + 1) * 4 / 100 + '0');
        writeData((usedLength + 1) * 4 / 10 % 10 + '0');
        writeData((usedLength + 1) * 4 % 10 + '0');
    }

    IE0 = 0;    //若接收信號過程中產生了下降沿,IE0則為1,此處需清除外部中斷0的中斷標志
    EX0 = 1;    //打開外部中斷0的中斷響應
}
View Code

   按鍵發送數據,同時添加了一個按鍵做清空緩存數組用,程序如下:

void main()
{
    UINT16 n;

    InitSys();

    while (1)
    {
        Key_Send = 1;
        if ( Key_Send != 1)
        {
            DelayX10ms(1);
            Key_Send = 1;
            if (Key_Send != 1)
            {
                for (n = 0; n < usedLength; n++)
                {
                    //將波形數據串口發送到上位機
                    UartSendByte(waveData[4 * n + 0]);
                    UartSendByte(waveData[4 * n + 1]);
                    UartSendByte(waveData[4 * n + 2]);
                    UartSendByte(waveData[4 * n + 3]);
                }

                while (!Key_Send);    //等待彈起
            }
        }

        Key_Clear = 1;
        if ( Key_Clear != 1)
        {
            DelayX10ms(1);
            Key_Clear = 1;
            if (Key_Clear != 1)
            {
                //清空波形緩存數組
                for (n = 0; n < MAX_BUFFER_LENGTH; n++)
                {
                    waveData[n] = 0;
                }
                SystemReady();
                while (!Key_Send);
            }
        }
    }
}
View Code

 

上位機紅外波形分析

  錄制完波形后,波形數據通過串口發送到上位機,得到類似下圖的十六進制數據,進行數據處理后就可以進行分析解碼了。

  為方便分析,我用C#簡單寫了個小程序,可以很方便的繪制波形,並將每幀的2字節數據直接轉換為時間長度,方便對照各種紅外協議分析。如下圖,該段紅外信號已9220us的高電平開始,緊隨一個4484us的低電平,與NEC協議中“9ms高電平+4.5ms低電平”的引導碼格式相符,分析其后面的電平持續時間,可知這段紅外信號為NEC格式信號。

上位機程序及源碼下載

 

 

 歡迎關注本人的個人博客YoungCoding.top


免責聲明!

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



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