前言
最近學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的中斷響應 }
按鍵發送數據,同時添加了一個按鍵做清空緩存數組用,程序如下:

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); } } } }
上位機紅外波形分析
錄制完波形后,波形數據通過串口發送到上位機,得到類似下圖的十六進制數據,進行數據處理后就可以進行分析解碼了。
為方便分析,我用C#簡單寫了個小程序,可以很方便的繪制波形,並將每幀的2字節數據直接轉換為時間長度,方便對照各種紅外協議分析。如下圖,該段紅外信號已9220us的高電平開始,緊隨一個4484us的低電平,與NEC協議中“9ms高電平+4.5ms低電平”的引導碼格式相符,分析其后面的電平持續時間,可知這段紅外信號為NEC格式信號。
歡迎關注本人的個人博客YoungCoding.top