下面對以下幾種比較流行和成熟的紅外解碼程序做一下研究和總結
解碼程序一:
/*-----------------------------------------------------------------------------------------
定時器0中斷處理
-----------------------------------------------------------------------------------------*/
void tim0_isr (void) interrupt 1 using 1
{
irtime++; //用于計數2個下降沿之間的時間
}
/*-----------------------------------------------------------------------------------------
外部中斷0中斷處理
-----------------------------------------------------------------------------------------*/
void EX0_ISR (void) interrupt 0 //外部中斷0服務函數
{
static unsigned char i; //接收紅外信號處理
static bit startflag; //是否開始處理標志位
if(startflag)
{
if(irtime<=54&&irtime>=50)//引導碼 TC9012的頭碼,9ms+4.5ms
{
i=0;
}
irdata[i]=irtime;//存儲每個電平的持續時間,用于以后判斷是0還是1
irtime=0;
i++;
if(i==33)
{
irok=1;
i=0;
}
}
else
{
irtime=0;
startflag=1;
}
}
先來分析一個這個程序,這個程序用了兩個中斷,一個定時器中斷,一個外斷中斷,程序的算法是讓定時器中斷不停的記數,外部中斷在下降沒來臨的時候將這個記數值并將上一次記的數值存在一個數組中,不過這個程序有一個小Bug,就是那個startflag是沒有起到關鍵性的作用的啊?!其實編這個程序的程序員想的是用startflag,這個方法可以過濾過那一個引導碼,但是如果你足夠細心就會發現他沒有做startflag清0的操作,不過詭異的事情是即使這樣也沒有影響正常的解碼程序的運行,其實只要你認真分析一下就會發現有沒有startflag清0這一個判斷的條件其實沒有多大的意義,我的意思是有沒有這一條語句都不會影響正常的解碼,首先來分析原程序,也就是沒有startflag清0這一語句的情況,第一個引導碼來的時候,進入else語句,將第一個引導碼過慮掉,進入else語句的就是后面有用的解值了,第一次會執行到if(irtime<54&&irtime>=50)這一條語句,因為引導碼的長度剛好在這個范圍內,所以會執行下會的一條i=0,也就是會把數組的下標清0,然后會把相應的32個紅外編碼存到數組中,注意這里的數組的長度是33,而不是32,因為irdata[0]是用來存放引導碼的,還有一點非常值很注意,就是這32個有效的紅外數據接收完了之后,會有一個重復碼,這個重復碼和引導碼長得很相似,這個我用示波器看過,這個重復碼可以看作是第34個波形,而且有一點非常值很注意,就是這個重復碼只要你按鍵不松手的話是一直會發的,所以如何處理這個重復碼的問題也是很關鍵的,至于這個問題我后面會有講到,現有我們先來將這個重復碼看成是第34個紅外碼,他的意義在于可以提供一個下降沿,有了這個下降沿就會將第32個紅外碼值存入data[32]中,所以最終的結果就是33個紅外編碼全部存入了容量為33的數組中,但是這時還有一個問題就是如果按鍵按下不放的話,后面還會有無數的波形啊,這時解碼程序還在不停的處理,后面的紅外編碼會存入第34,35,36……等后面的數組中,這是不希望看到的,這也會導致一個更加可怕的問題,那就是程序的溢出,那天在防真器上出現了這個問題決對就是這個原因.因為數組只有33個元素,而在接收到這33個元素之后還沒有停止對紅外碼的接收,后面的元素繼續存入只有33個元素的數組中,必定會導致數據的溢出,也就是會彈出那樣的一個溢出的界面,我還記得當天沒有接紅外接收管硬件的時候也會有這個溢出的窗口,那是因為當時沒有在中斷進入這后將中斷標志位清0,所以會一直有中斷進來,從而這33個元素的數組就會很快存滿了,然后還在進入不停的進入中斷,最后導致這33個元素的數組全部存滿了還在不停的往里面存數據,所以也會導致程序的溢出.所以以后就有了一條經驗,那就是如果程序溢出了有一種可能性就是往一個固定長度的數組中存入了大于其長度的數據.那么如何解決這個問題呢,一種可行的方法是這樣的,待33個紅外數據全部存完以后,置一個標志,后面接收到的數據只要不是引導碼就做一次函數返回,當接收到這個正確的引導碼以后才可以進行下一輪數據的接收.
上面的這個問題暫且放下(只要搖控器發出的波形只有34個脈沖就不會出現上面的問題 ,這種搖控器的特點就是只要你按下一個按鍵不論你是不松手,它都只會發出一幀數據,不會有后面的故慮),下面考慮第二幀數據的接收情況,當第二幀數據來臨的時候,由于沒有清0 startflag,程序會進入if(startflag)這一個分支,也就是會處理引導碼的情況,這里會到下面的一條語句if(irtime<63&&irtime>=33),而這時irtime是一個隨機的數,所以說這條語句有可能能被執行到也有可能不會被執行到,無論其是否能被執行到,都無所謂,等下一個下降沿到來的時候那個時候irtime存入的才是引導碼的數據,也就是直到第二個下降沿到來的時候,irdata的下標才會清0,這時才會將引導碼存入irdata的第0個格子里面.而后的數據會將先前的數據覆蓋掉,也就消除了第一次接收到的數據的不穩定因素.
如果有接收到第33個元素后將startflag清0.每次接收紅外的時候就和第一次的一樣了,還過即使是這樣也不能擺脫那個接收到重復碼的問題.下面是有startflag清0的程序.
/*-----------------------------------------------------------------------------------------
外部中斷0中斷處理
-----------------------------------------------------------------------------------------*/
void EX0_ISR (void) interrupt 0 //外部中斷0服務函數
{
static unsigned char i; //接收紅外信號處理
static bit startflag; //是否開始處理標志位
if(startflag)
{
if(irtime<=54&&irtime>=50)//引導碼 TC9012的頭碼,9ms+4.5ms
{
i=0;
}
irdata[i]=irtime;//存儲每個電平的持續時間,用于以后判斷是0還是1
irtime=0;
i++;
if(i==33)
{
irok=1;
i=0;
startflag=0;
}
}
else
{
irtime=0;
startflag=1;
}
}
下面做一個解決重復碼問題的程序:
/*-----------------------------------------------------------------------------------------
外部中斷0中斷處理
-----------------------------------------------------------------------------------------*/
void EX0_ISR (void) interrupt 0 //外部中斷0服務函數
{
static unsigned char i; //接收紅外信號處理
static bit startflag; //是否開始處理標志位
if(statflag==1)
{
if(irtime<=54&&irtime>=50)//引導碼 TC9012的頭碼,9ms+4.5ms
{
i=0;
ir_flag=0; //接收到正確的引導碼,開始接收數據
}
else
{
return; //這個是33個紅外碼后面的重復碼,過濾掉他
}
If(ir_flag==0) //如果接收到引導碼才開始解碼
{
irdata[i]=irtime;//存儲每個電平的持續時間,用于以后判斷是0還是1
irtime=0;
i++;
if(i==33)
{
irok=1;
i=0;
startflag=0;
ir_flag=1; //接收完33個數據,清0接收數據位,即不再接收數據
}
}
else
{
startflag==1
i=0;
return;
}
}
這個程序有一個關鍵的地方,那就是那個引導碼的時間,程序就是通過這個時間來確定是否開始接收一幀數據的,真到接收到33個數據后將關閉數據的接收功能,直到有第二個引導碼的到來,才開始接收第二幀數據.如果重復碼我這個引導碼很像,那么這個一點的時間的差別也是很關鍵的了,所以必須把握好這個時間,比如13.5ms,12M的晶振定時器計一次時間是256us比如把這個時間卡在13ms到14ms之間就應該是13000/256=50,14000/256=54,也就是說應該把兩個時間卡在50和54之間,這樣的精度才能保證將那些個重復碼的脈沖過濾掉,這個卡的越準越好越小越好.也就這在邏輯上也是可行完美合理的程序.邏輯上就是要讓一幀數據接收完成之后停止數據的接收功能.上面各全局變量的初始化情況為ir_flag=0;startflag=0;
想一想如果這個程序像下面這樣會有什么后果,即去掉startflag這個標置,會有什么后果?
/*-----------------------------------------------------------------------------------------
外部中斷0中斷處理
-----------------------------------------------------------------------------------------*/
void EX0_ISR (void) interrupt 0 //外部中斷0服務函數
{
static unsigned char i; //接收紅外信號處理
static bit startflag; //是否開始處理標志位
if(irtime<=54&&irtime>=50)//引導碼 TC9012的頭碼,9ms+4.5ms
{
i=0;
ir_flag=0; //接收到正確的引導碼,開始接收數據
}
else
{
return; //這個是重復碼,過濾掉他
}
If(ir_flag==0)
{
irdata[i]=irtime;//存儲每個電平的持續時間,用于以后判斷是0還是1
irtime=0;
i++;
if(i==33)
{
irok=1;
i=0;
ir_flag=1; //接收完33個數據,清0接收數據位,即不再接收數據
}
}
}
這個程序沒有用startflag這個標置,也就不能保證接收到的第一個脈沖是引導碼了,這時會有一個隨機的數存入irtime,如果這個隨機數是和引導碼的irtime相似的話,就會造成解碼的開啟,第二個碼,也就是引導碼會存入irdata[1]中,后面所有的碼就會依次往后推移一次,這樣的話第33個紅外碼就會丟失掉,也就去直接導致解碼的失敗.所以說完美的程序應該是上面的那個程序.(這段話說的不對,那個i=0的操作會把數組的下標做一下清0,也就不存在移位的問題了.)
特別糾正一下本文的錯誤之處,上面的兩個程序都是有問題的,正確的程序應該是下面的程序,看完這個程序再來說明上面程序不正確的地方.
/*-----------------------------------------------------------------------------------------
外部中斷0中斷處理
-----------------------------------------------------------------------------------------*/
void EX0_ISR (void) interrupt 0 //外部中斷0服務函數
{
static unsigned char i; //接收紅外信號處理
static bit startflag; //是否開始處理標志位
if(irtime<=54&&irtime>=50)//引導碼 TC9012的頭碼,9ms+4.5ms
{
i=0;
ir_flag=0; //接收到正確的引導碼,開始接收數據
}
if(ir_flag==1)
{
irtime=0;
return; //這個是重復碼,過濾掉他
}
If(ir_flag==0)
{
irdata[i]=irtime;//存儲每個電平的持續時間,用于以后判斷是0還是1
irtime=0;
i++;
if(i==33)
{
irok=1;
i=0;
ir_flag=1; //接收完33個數據,清0接收數據位,即不再接收數據
}
}
}
上面的那兩個程序都不能按照正常的算法實現,關鍵在于有一個原因,那個else語句沒有做irtime清零的操作,這樣就會導致一個問題,就是下一個下降沿到來的時候不能反正確的兩個下降沿之間的時間存入數中,存入的是不確定的數,所以說irtime清零這一步操作還是很有必要的,試想下一個正確的引導碼到的的時候,沒有irtime清0的操作也就不能識別能正確的引導碼,也就不能夠實現解碼的開啟,也就不能夠實現正確的解碼,不過上面的程序還是有一個問題,那就是他沒有用startflag,也就會存在上面提到的那個問題,也說是說當有一個隨機數,也就是下一幀數據開始接收時的第一個碼,那個數就是一個隨機數,如果那個數剛好是在引導碼的時間區間內,那么就是惡夢的開始,也就是說會和上面的錯誤一樣,這個隨機數會存入irdata[0],引導碼會存入irdata[1]中,下面的所有碼都依次往下移一位,也說是說會有一位碼沒有存入這33個數據碼中(包括引導碼). (這段話說的不對,那個i=0的操作會把數組的下標做一下清0,也就不存在移位的問題了.)所以說這個程序可以說是一個比效完美的程序了
下面是另一種編程的方法.
/*-----------------------------------------------------------------------------------------
外部中斷0中斷處理
-----------------------------------------------------------------------------------------*/
void EX0_ISR (void) interrupt 0 //外部中斷0服務函數
{
static unsigned char i; //接收紅外信號處理
static bit startflag; //是否開始處理標志位
if(statflag==1)
{
if(irtime<=54&&irtime>=50)//引導碼 TC9012的頭碼,9ms+4.5ms
{
i=0;
ir_flag=0; //接收到正確的引導碼,開始接收數據
}
if(ir_flag==0)
{
irtime=0;
startflag=0;
return; //這個是33個紅外碼后面的重復碼,過濾掉他
}
If(ir_flag==1) //如果接收到引導碼才開始解碼
{
irdata[i]=irtime;//存儲每個電平的持續時間,用于以后判斷是0還是1
irtime=0;
i++;
if(i==33)
{
irok=1;
i=0;
startflag=0;
ir_flag=1; //接收完33個數據,清0接收數據位,即不再接收數據
}
}
else
{
startflag==1
i=0;
return;
}
}
這個程序的算法是那些重復碼會隔一個進入解碼程序一個,進處解碼程序的那個又會被過濾掉,也說是說第一個重復碼的下降沿會進入else starflag=1……的那個語句.下一個重復碼的下降沿會被過濾掉,因為他不滿足那個引導碼時間的那個條件.所以他會被過濾掉.這就要求那個引導碼的時間要足夠的準確,范圍也要足夠的小,如果重復碼和引導碼比效相似的話那么事情就不妙了,那個條件就起不到過濾的作用了.這個嚴酷的條件也是這個程序的精髓.
本文未完下接<幾種比較流行和成熟的紅外解碼程序做一下研究和總結2>:http://www.zg4o1577.cn/mcu/4353.html