NEC 協(xié)議紅外遙控器
家電遙控器通信距離往往要求不高,而紅外的成本比其它無線設(shè)備要低的多,所以家電遙控器應(yīng)用中紅外始終占據(jù)著一席之地。遙控器的基帶通信協(xié)議很多,大概有幾十種,常用的就有 ITT 協(xié)議、NEC 協(xié)議、Sharp 協(xié)議、Philips RC-5 協(xié)議、Sony SIRC 協(xié)議等。用的最多的就是 NEC 協(xié)議了,因此我們 KST-51 開發(fā)板配套的遙控器直接采用 NEC 協(xié)議,我們這節(jié)課也以 NEC 協(xié)議標(biāo)準(zhǔn)來講解一下。
NEC 協(xié)議的數(shù)據(jù)格式包括了引導(dǎo)碼、用戶碼、用戶碼(或者用戶碼反碼)、按鍵鍵碼和鍵碼反碼,最后一個(gè)停止位。停止位主要起隔離作用,一般不進(jìn)行判斷,編程時(shí)我們也不予理會。其中數(shù)據(jù)編碼總共是4個(gè)字節(jié)32位,如圖16-7所示。第一個(gè)字節(jié)是用戶碼,第二個(gè)字節(jié)可能也是用戶碼,或者是用戶碼的反碼,具體由生產(chǎn)商決定,第三個(gè)字節(jié)就是當(dāng)前按鍵的鍵數(shù)據(jù)碼,而第四個(gè)字節(jié)是鍵數(shù)據(jù)碼的反碼,可用于對數(shù)據(jù)的糾錯(cuò)。
圖16-7 NEC 協(xié)議數(shù)據(jù)格式
這個(gè) NEC 協(xié)議,表示數(shù)據(jù)的方式不像我們之前學(xué)過的比如 UART 那樣直觀,而是每一位數(shù)據(jù)本身也需要進(jìn)行編碼,編碼后再進(jìn)行載波調(diào)制。
引導(dǎo)碼:9 ms 的載波 +4.5 ms 的空閑。
比特值“0”:560 us 的載波 +560 us 的空閑。
比特值“1”:560 us 的載波 +1.68 ms 的空閑。
結(jié)合圖16-7我們就能看明白了,最前面黑乎乎的一段,是引導(dǎo)碼的 9 ms 載波,緊接著是引導(dǎo)碼的 4.5 ms 的空閑,而后邊的數(shù)據(jù)碼,是眾多載波和空閑交叉,它們的長短就由其要傳遞的具體數(shù)據(jù)來決定。HS0038B 這個(gè)紅外一體化接收頭,當(dāng)收到有載波的信號的時(shí)候,會輸出一個(gè)低電平,空閑的時(shí)候會輸出高電平,我們用邏輯分析儀抓出來一個(gè)紅外按鍵通過HS0038B 解碼后的圖形來了解一下,如圖16-8所示。
圖16-8 紅外遙控器按鍵編碼
從圖上可以看出,先是 9 ms 載波加 4.5 ms 空閑的起始碼,數(shù)據(jù)碼是低位在前,高位在后,數(shù)據(jù)碼第一個(gè)字節(jié)是8組 560 us 的載波加 560 us 的空閑,也就是 0x00,第二個(gè)字節(jié)是8組 560 us的載波加 1.68 ms 的空閑,可以看出來是 0xFF,這兩個(gè)字節(jié)就是用戶碼和用戶碼的反碼。按鍵的鍵碼二進(jìn)制是 0x0C,反碼就是 0xF3,最后跟了一個(gè) 560 us 載波停止位。對于我們的遙控器來說,不同的按鍵,就是鍵碼和鍵碼反碼的區(qū)分,用戶碼是一樣的。這樣我們就可以通過單片機(jī)的程序,把當(dāng)前的按鍵的鍵碼給解析出來。
我們前邊學(xué)習(xí)中斷的時(shí)候,學(xué)到51單片機(jī)有外部中斷0和外部中斷1這兩個(gè)外部中斷。我們的紅外接收引腳接到了 P3.3 引腳上,這個(gè)引腳的第二功能就是外部中斷1。在寄存器TCON 中的 bit3 和 bit2 這兩位,是和外部中斷1相關(guān)的兩位。其中 IE1 是外部中斷標(biāo)志位,當(dāng)外部中斷發(fā)生后,這一位被自動置1,和定時(shí)器中斷標(biāo)志位 TF 相似,進(jìn)入中斷后會自動清零,也可以軟件清零。bit2 是設(shè)置外部中斷類型的,如果 bit2 為0,那么只要 P3.3 為低電平就可以觸發(fā)中斷,如果 bit2 為1,那么 P3.3 從高電平到低電平的下降沿發(fā)生才可以觸發(fā)中斷。此外,外部中斷1使能位是 EX1。那下面我們就把程序?qū)懗鰜?,使用?shù)碼管把遙控器的用戶碼和鍵碼顯示出來。
Infrared.c 文件主要是用來檢測紅外通信的,當(dāng)發(fā)生外部中斷后,進(jìn)入外部中斷,通過定時(shí)器1定時(shí),首先對引導(dǎo)碼判斷,而后對數(shù)據(jù)碼的每個(gè)位逐位獲取高低電平的時(shí)間,從而得知每一位是0還是1,最終把數(shù)據(jù)碼解出來。雖然最終實(shí)現(xiàn)的功能很簡單,但因?yàn)榫幋a本身的復(fù)雜性,使得紅外接收的中斷程序在邏輯上顯得就比較復(fù)雜,那么我們首先提供出中斷函數(shù)的程序流程圖,大家可以對照流程圖來理解程序代碼,如圖16-9所示。
圖16-9 紅外接收程序流程圖
/***************************Infrared.c文件程序源代碼*****************************/#includesbitIR_INPUT=P3^3;//紅外接收引腳bitirflag=0;//紅外接收標(biāo)志,收到一幀正確數(shù)據(jù)后置1unsignedcharircode[4];//紅外代碼接收緩沖區(qū)/*初始化紅外接收功能*/voidInitInfrared(){IR_INPUT=1;//確保紅外接收引腳被釋放TMOD&=0x0F;//清零T1的控制位TMOD"=0x10;//配置T1為模式1TR1=0;//停止T1計(jì)數(shù)ET1=0;//禁止T1中斷IT1=1;//設(shè)置INT1為負(fù)邊沿觸發(fā)EX1=1;//使能INT1中斷}/*獲取當(dāng)前高電平的持續(xù)時(shí)間*/unsignedintGetHighTime(){TH1=0;//清零T1計(jì)數(shù)初值TL1=0;TR1=1;//啟動T1計(jì)數(shù)while(IR_INPUT){//紅外輸入引腳為1時(shí)循環(huán)檢測等待,變?yōu)?時(shí)則結(jié)束本循環(huán)//當(dāng)T1計(jì)數(shù)值大于0x4000,即高電平持續(xù)時(shí)間超過約18ms時(shí),//強(qiáng)制退出循環(huán),是為了避免信號異常時(shí),程序假死在這里。if(TH1>=0x40){break;}}TR1=0;//停止T1計(jì)數(shù)return(TH1*256+TL1);//T1計(jì)數(shù)值合成為16bit整型數(shù),并返回該數(shù)}/*獲取當(dāng)前低電平的持續(xù)時(shí)間*/unsignedintGetLowTime(){TH1=0;//清零T1計(jì)數(shù)初值TL1=0;TR1=1;//啟動T1計(jì)數(shù)while(!IR_INPUT){//紅外輸入引腳為0時(shí)循環(huán)檢測等待,變?yōu)?時(shí)則結(jié)束本循環(huán)//當(dāng)T1計(jì)數(shù)值大于0x4000,即低電平持續(xù)時(shí)間超過約18ms時(shí),//強(qiáng)制退出循環(huán),是為了避免信號異常時(shí),程序假死在這里。if(TH1>=0x40){break;}}TR1=0;//停止T1計(jì)數(shù)return(TH1*256+TL1);//T1計(jì)數(shù)值合成為16bit整型數(shù),并返回該數(shù)}/*INT1中斷服務(wù)函數(shù),執(zhí)行紅外接收及解碼*/voidEXINT1_ISR()interrupt2{unsignedchari,j;unsignedcharbyt;unsignedinttime;//接收并判定引導(dǎo)碼的9ms低電平time=GetLowTime();//時(shí)間判定范圍為8.5~9.5ms,//超過此范圍則說明為誤碼,直接退出if((time<7833)||(time>8755)){IE1=0;//退出前清零INT1中斷標(biāo)志return;}//接收并判定引導(dǎo)碼的4.5ms高電平time=GetHighTime();//時(shí)間判定范圍為4.0~5.0ms,//超過此范圍則說明為誤碼,直接退出if((time<3686)||(time>4608)){IE1=0;return;}//接收并判定后續(xù)的4字節(jié)數(shù)據(jù)for(i=0;i<4;i++){//循環(huán)接收4個(gè)字節(jié)for(j=0;j<8;j++){//循環(huán)接收判定每字節(jié)的8個(gè)bit//接收判定每bit的560us低電平time=GetLowTime();//時(shí)間判定范圍為340~780us,//超過此范圍則說明為誤碼,直接退出if((time<313)||(time>718)){IE1=0;return;}//接收每bit高電平時(shí)間,判定該bit的值time=GetHighTime();//時(shí)間判定范圍為340~780us,//在此范圍內(nèi)說明該bit值為0if((time>313)&&(time<718)){byt>>=1;//因低位在先,所以數(shù)據(jù)右移,高位為0//時(shí)間判定范圍為1460~1900us,//在此范圍內(nèi)說明該bit值為1}elseif((time>1345)&&(time<1751)){byt>>=1;//因低位在先,所以數(shù)據(jù)右移,byt|=0x80;//高位置1}else{//不在上述范圍內(nèi)則說明為誤碼,直接退出IE1=0;return;}}ircode[i]=byt;//接收完一個(gè)字節(jié)后保存到緩沖區(qū)}irflag=1;//接收完畢后設(shè)置標(biāo)志IE1=0;//退出前清零INT1中斷標(biāo)志}