轉(zhuǎn)自:http://blog.sina.com.cn/s/blog_6b86b0f20101iq0q.html
前陣子幫他們做實(shí)習(xí)小車(chē)的買(mǎi)東西,看著便宜就順便買(mǎi)了個(gè)紅外的遙控器和1838的紅外接收頭子。這兩天閑著沒(méi)事,看了很多資料才終于完成了紅外解碼,的確是久了沒(méi)玩51,好多東西生疏了導(dǎo)致調(diào)試過(guò)程幾經(jīng)挫折,經(jīng)過(guò)這次復(fù)習(xí)了好多51的東西,受益匪淺。
查閱資料得知常見(jiàn)紅外編碼為PPM制式。紅外發(fā)射端以脈寬0.565ms、間隔0.56ms的脈沖信號(hào)表示二進(jìn)制“0”,以脈寬0.565ms,間隔1.65ms的脈沖信號(hào)為“1”。如下圖.
上述“0”、“1”組成的32位二進(jìn)制碼經(jīng)過(guò)38KHz的載波進(jìn)行二次調(diào)制以提高發(fā)射效率、降低電源功耗。
32位二進(jìn)制碼組中,前16位為用戶識(shí)別碼,能區(qū)別不同電氣設(shè)備防止不同機(jī)種遙控碼相互干擾。后16位為8位操作碼極其反碼。遙控器在按下后周期性的發(fā)出一種32位二進(jìn)制碼,周期108ms。這108ms由一個(gè)起始碼(9ms),一個(gè)結(jié)果碼(4.5ms),低8位地址碼、高8位地址碼、8位數(shù)據(jù)碼極其8位數(shù)據(jù)反碼組成。
接受時(shí)使用1838通用紅外接頭

紅外接頭將38K載波信號(hào)過(guò)濾掉,得到與發(fā)射代碼反向接受碼。
上述是我自己整理的資料。后經(jīng)示波器驗(yàn)證大致是這樣的,便開(kāi)始編寫(xiě)程序。
紅外解碼的關(guān)鍵就是識(shí)別“0”和“1”。以下是具體代碼,通過(guò)51單片機(jī)接受紅外信號(hào),解碼后將8位操作碼通過(guò)串口以十六進(jìn)制發(fā)送給電腦(比較粗糙,還沒(méi)來(lái)得及完善,僅供參考)
#include//單片機(jī)型號(hào)STC89C52
#define uchar unsigned char
#define uint unsigned int
sbit IR=P3^2;//位定義,使用P3^2口外部中斷0
uint lowtime,hightime;
uchar a[4],m,flag;
bit decode();//解碼程序
void sci();//串口初始化
void main()
{
// delay_ms(100);
TR0=0;
TH0=0;
TL0=0;
sci();
EA=1;
IT0=1;//設(shè)置外部中斷0為下降沿觸發(fā)方式
EX0=1;//開(kāi)外部中斷0
while(1)
{
if(flag==1) //驗(yàn)證前導(dǎo)碼正確后才開(kāi)始解碼并向串口發(fā)送數(shù)據(jù)
{
decode();
for(m=0;m<1;m++)//向串口發(fā)射a[2],及8位操作碼
{
SBUF=a[2];
while(!TI);
TI=0;
}
while(1);
}
}
}
void ext0() interrupt 0
{
EX0=0;//進(jìn)外部中斷0后立即關(guān)閉中斷0,防止二次中斷
TR0=1;//開(kāi)定時(shí)器0
while(IR==0);//一直計(jì)數(shù),直到IR由0跳變到1
TR0=0;//關(guān)閉定時(shí)器0
lowtime=256*TH0+TL0;//lowtime儲(chǔ)存引導(dǎo)碼低電平持續(xù)時(shí)間
TH0=0;
TL0=0;//清空定時(shí)器0寄存器值,以備下一次計(jì)數(shù)
TR0=1;
while(IR==1);//等待IR由1跳表到0
TR0=0;//關(guān)閉定時(shí)器0
hightime=256*TH0+TL0;//hightime存儲(chǔ)結(jié)果碼高電平持續(xù)時(shí)間
if((lowtime>7800)&&(lowtime<8800)&&(hightime>3600)&&(hightime<4700))
//9000us/1.085=8294,判斷區(qū)間8800-7800
//4500us/1.085=4100,判斷區(qū)間3600-4700
//此值為11.0592MHz晶振時(shí)的值。
flag=1;//符合判斷條件則將flag標(biāo)志置1,否則認(rèn)為是干擾信號(hào)不予解碼
}
bit decode()
{
uchar temp;
uchar i,j;
for(i=0;i<4;i++)
{
for(j=0;j<7;j++)
{
temp>>=1;
TH0=0;
TL0=0;
TR0=1;
while(IR==0);
TR0=0;
lowtime=256*TH0+TL0;
TH0=0;
TL0=0;
TR0=1;
while(IR==1);
TR0=0;
hightime=256*TH0+TL0;
if((lowtime<370)||(lowtime>640))//低電平時(shí)間長(zhǎng)度不合理
return 0;
if((hightime>420)&&(hightime<620))//高電平持續(xù)時(shí)間560us左右即位“0”
temp&=0x7f;
if((hightime>1300)&&(hightime<1800))//高電平持續(xù)時(shí)間1680us左右即位“1”
temp|=0x80;
}
a[i]=temp;
}
}
void sci()
{
SM0=0;
SM1=1;
REN=1;
TMOD=0x21;//此處注意設(shè)置定時(shí)器0的方式為工作方式1
TH1=0xfd;
TL1=0xfd;
TR1=1;
ES=1;
}

串口顯示出的十六進(jìn)制的編碼(每次單片機(jī)斷電的時(shí)候都會(huì)向電腦發(fā)送00,不知道為什么...)
這只是初步的程序,調(diào)試過(guò)程中狀況百出。顯示串口顯示問(wèn)題,SBUFS是8位寄存器,只能存儲(chǔ)8位二進(jìn)制數(shù)據(jù),故32位二進(jìn)制需要發(fā)送四次,每次到串口為1位十六進(jìn)制數(shù)據(jù)。還有定時(shí)器0的模式,之前一直沒(méi)有設(shè)置,結(jié)果郁悶了一晚上。
另外,關(guān)于紅外的用途的話,有人說(shuō)這東西接受距離不遠(yuǎn),也就幾米(有資料說(shuō)在輸出段并上1uf電容可將接收距離增加至十多米,待驗(yàn)證),最致命的是它是紅外線,必須要將發(fā)射端對(duì)準(zhǔn)接收端才能接收到信號(hào),這就是它比不上藍(lán)牙、NRF24L01等無(wú)線通信最主要的原因。不過(guò),NRF24L01之麻煩,以我的智商估計(jì)沒(méi)有半個(gè)月是做不出來(lái)的。