51單片機紅外遙控信號解碼

簡介

紅外遙控作為一種遙控手段應用是非常廣泛的,並且紅外線遙控裝置具有體積小、 功耗低、 功能強、 成本低這些特點,在錄音機、空調、音響等小型電器大多是採用紅外遙控的方式進行控制。這篇文章我就來詳細講解一下紅外遙控信號的解碼方法,對於紅外信號的收發原理在這裡只做簡單瞭解。紅外遙控系統要有發射端和接收端,發射端將信號經芯片調製後由紅外發光二極管將信號以紅外光的形式發送出去。接收端通過紅外監測二極管接收到紅外信號後再經過放大、濾波、解調等操作輸出高低電平,這樣就完成了紅外信號的收發。本次教程我選擇下圖這種紅外遙控器進行紅外信號的發射

51單片機紅外遙控信號解碼

選擇一體化紅外接收頭作為紅外信號的接收端,這是一種特殊的紅外接收電路,它把紅外接收管和放大電路集成到了一塊,從外形上看只有一個三極管大小,如圖

51單片機紅外遙控信號解碼

一體化紅外接收頭共有三個引腳,將有凸起的一面對準自己後從左到右依次是信號輸出、GND、VCC,它的工作電壓為5V,可以選擇一體化紅外接收模塊,我們在使用的時候只需要給它接上5V電源,接地,然後將信號輸出腳連接到單片機就可以使用了。本篇文章我使用51單片機進行試驗,51單片機用來解碼紅外信號是足夠的並且易於理解,文章主要是講解解碼原理。

硬件連接

第一步是硬件連接,用到的硬件非常簡單,只有一體化紅外接收頭和單片機最小系統。首先,我們給單片機最小系統和紅外接收頭接上電源,注意共地,然後將紅外接收頭的信號輸出腳接到單片機的外部中斷引腳上,這樣就完成了硬件的連接。之所以將信號輸出腳接到單片機的外部中斷引腳是因為前面說過紅外接收頭在接收到紅外信號後會輸出高低電平,接在外部中斷引腳後單片機可以在有信號的時候快速做出反應,後面的程序也是靠中斷程序實現的。

NEC協議

前面講過紅外接收頭在接收到紅外信號後會輸出高低電平,然後就可以通過我們的程序分析電平的變化來識別接收到的信號的內容。要將接收端的信號解碼我們就要知道發送端的數據是如何編碼的,在本文中用到的紅外遙控器所使用的是NEC編碼協議,所以我們首先要講解一下NEC協議的相關信息。NEC協議是用於紅外通信中的一種協議,通過NEC協議發送信息的格式如下圖:

51單片機紅外遙控信號解碼

信息的開始是9ms高電平加4.5ms低電平的引導碼,作用就是告訴設備通訊開始,有信號發送過來了。之後接的是8位地址碼和8位地址反碼,地址碼的作用就是區分不同的接收器件,實現單獨控制某個器件。最後是8位數據碼和8位數據反碼,數據碼就是功能碼,我們可以給不同數據碼設定不同的功能,反碼的作用就是檢驗接收到的數據是否正確。在上面的這條信息裡,‘0’是用560us高電平加560us低電平表示的,‘1’是用560us高電平加1680us低電平表示。要注意的是上面的波形圖是發射端的波形,我們紅外接收端收到的信號的高低電平是與上圖相反的,還要注意發射端在發射數據是是從最低位開始發射,所以我們接收到的信號也是從最低位開始的,另外當我們持續按住同一個按鍵時,發射端並不會重複發送地址碼和數據碼而是在發送完一次後每隔一段時間發送一次引導碼(重複),間隔時間大約是108ms。下圖為接收端引導碼、引導碼(重複)、‘0’、‘1’的波形:

51單片機紅外遙控信號解碼

在瞭解了這些關於NEC協議的信息後我們就可以開始寫程序控制單片機來解碼紅外信號了。首先我來講解一下解碼程序的思路,之前我們將紅外接收頭的信號輸出腳接在了單片機的外部中斷引腳上,我們將外部中斷的觸發方式設置為跳變沿觸發,當接收頭接收到信號後就會輸出一個低電平,低電平觸發外部中斷,我們在中斷程序中啟動定時器,這樣當下一次低電平觸發中斷前,在這期間定時器所定的時間我們可以進行比較從而確定脈衝的寬度,確定數據為‘0’還是‘1’。接下來我根據具體代碼進行介紹,

<code>#include"reg52.h"#define uint unsigned int #define uchar unsigned charuchar IRtime;        //儲存檢測紅外高低電平持續時間uchar IRcord[4];    //儲存解碼後的4個字節數據uchar IRdata[33];    //包含起始碼在內的33位數據bit IRpro_ok;        //解碼後4個數據接收完成標誌位bit IRok;            //33位數據接收完成標誌位/<code>

在上面這段函數中我們添加上相應的頭文件和一些宏定義,之後我們定義一些變量和數組作為一些標誌位以及一些數據的保存,在每個定義後面都有註釋來解釋相應的作用,在這裡就不再贅述。

<code>void init(){    TMOD |= 0x02;    //設置定時器0工作模式2    TL0 = TH0 = 0;    //初始化定時器0寄存器    EA = 1;    ET0 = 1;    TR0 = 1;    IT0 = 1;    EX0 = 1;    TMOD |= 0x20;    //設置定時器1工作模式2    TL1 = TH1 = 0xfd;    //比特率9600    SM1 = 1;    //設置串口工作模式1,10位異步收發    TR1 = 1;    //啟動定時器1    }/<code>

然後我們定義一個初始化函數用於定時器和中斷的初始化設置,我們將定時器0用於計算紅外信號高低電平的時間,將定時器1用於串口發送數據。

<code>void T0_ISR(void) interrupt 1    //定時器0中斷一次277.76us{    IRtime++;}/<code>

上面這段是定時器0的程序,在裡面只是用來計算時間,定時器0中斷一次是277.76us,我們通過IRtime就可以計算出紅外信號的持續時間。

<code>void int0(void) interrupt 0{    static uchar i;        //靜態變量用於存入33次數據計數    static bit startflag;    //開始存儲脈寬標誌位    if(startflag)    {        if((IRtime < 53) && (IRtime >= 32))        //判斷是否為引導碼 若為引導碼則從起始碼開始存        {i = 0;}        IRdata[i] = IRtime;        //以T0溢出的次數來計算脈寬把這個時間存放在數組中            IRtime = 0;        //計數清零        i++;    //計數脈寬存入次數自加        if(i == 30)        {            IRok = 1;    //脈寬檢查完成            i = 0;        //把脈寬計數清零準備下次存入        }    }    else    {        IRtime = 0;        //定時器0計數清零        startflag = 1;        //開始處理標誌位置1    }}/<code> 

上面這段程序是外部中斷的處理程序,當紅外接收頭接收到紅外信號後會輸出相應的高低電平變化,引導碼和“1”、“0”的波形在前面的接收端波形圖已經畫出,這樣當接收端為高電平的時候,就會觸發外部中斷,這是會查看IRtime的值,根據這個值來判斷是否是9ms的引導碼,如果是引導碼則先將IRtime清零,然後將以後的脈衝時間都保存下來,如果不是則把IRtime清零,這樣紅外接收頭接收到的33次脈衝的寬度就會保存到IRdata[]數組裡。

<code>void IRcordpro(){    uchar i;    //用於計數處理4個字節    uchar j;    //用於計數處理1個字節的8位數據    uchar k;    //用於計數處理33次脈寬    k = 1;        //從第一位開始處理 丟掉起始碼    for(i = 0;i < 4;i++)    {        for(j = 0;j <8;j++)        {            if(IRdata[k] > 5)    //如果脈寬大於數據0標準的1125us就判定為數據1            {                IRcord[i] |= 0x80;        //置於最高位            }            if(j < 7)    //從最高位移位置低位            {                IRcord[i] >>= 1;            }            k++;    //處理下一次脈寬        }    }    IRpro_ok = 1;    //解碼完成}/<code>

上面這個函數是用來處理我們之前保存的33次脈衝寬度,並且轉化為4字節的數據保存在IRcord[]數組裡,具體的操作就是根據脈衝的時間來判斷是“1”還是“0”,大家可以根據程序後面的註釋來理解函數的內容。

<code>void main(){    uchar i;    //計數串口發送字節數    init();        //初始化    while(1)    {        if(IRok)    //判斷33次脈寬是否提取完成        {            IRcordpro();    //根據脈寬解碼出4字節數據            IRok = 0;        //清零脈寬檢查完成標誌位等待下一次脈寬檢查            if(IRpro_ok)    //判斷解碼是否完成            {                for(i = 0;i < 4;i++)                {                    SBUF = IRcord[i];                    while(!TI);                    TI = 0;                }                           IRpro_ok = 0;        //清零解碼標誌位            }        }    }}/<code> 

最後我們在主函數部分調用之前的函數,並且在等待解碼完成後將解碼出來的4字節數據通過串口發送到電腦。最後我們看一下實際的效果,首先打開串口助手,波特率設置為9600,接收設置為HEX模式,這樣當按下紅外遙控器的按鍵時,就會返回遙控器的鍵值了,

51單片機紅外遙控信號解碼

好了,本期教程到此就結束了,感謝各位的觀看。

更多幹貨內容只需要你關注電子芯吧客微信公眾號


分享到:


相關文章: