網頁最後修改時間:2018/12/04
![]() |
就如同上面的圖片,根據解碼出的 Philips RC6 紅外線遙控訊號原始數據進行階梯圖繪製,這個圖形可方便用來輔助學習與了解 RC6 的編碼格式,並驗證所得資料的正確性;沒有繪製成階梯圖,是很難從所得到的原始數據看出端倪的!
為了讓理論與實際能相互對照,以手邊剛好有的紅外線遙控器(NEC 和 Philip RC6 Mode 0)來說明其通訊協定格式,其他的紅外線通訊協定,可以以這篇網頁所談及的部份去了解它們。
【紅外線遙控訊號的調變】
紅外線遙控訊號,不是單純的發送 ON/OFF 的數位訊號,而是以一個固定頻率的 ON/OFF(PWM)訊號來做傳輸(通訊協定不同,頻率也不同),並依照所定義的所謂 Logic "0" 或 Logic "1" 的方式進行調變發送。
大多數紅外線遙控器使用的通訊協定裡所用的遙控訊號 Logic (邏輯)調變示意圖,如下圖所示。
位元(bit)Logic 訊號發送的過程中,mark 代表持續發送 PWM 訊號(這一堆高頻調變波的地方為載波叢發訊號《Carrier Burst》,下面稱為脈衝)、space 代表關掉 pwm 訊號發送;T1-m、T1-s、T0-m、T0-s 代表訊號發送的時間,但其值不一定相同,而且不一定就是 mark 在前、space 在後,通訊協定各自有它們自己的定義。
![]() |
紅外線遙控訊號 Logic 調變示意圖 |
脈衝的 PWM 訊號輸出頻率依所使用的通訊協定而有所不同,不過這其中未談論到的就是其佔空比(duty cycle)。
要知道,IR LED 不同於一般常用的可見光 R/G/B LED,其順向電流可以從 100mA 變化至遠遠超過 1A,為了要得到能控制到非常遠的距離,順向電流要盡量越高越好,但相對地就會增加電力消耗,因此這兩個因素必須相互權衡。
IR LED 發射時使用了 PWM 訊號驅動,因此可以在通過較高的電流值時也不會損壞,但須注意平均功耗不應超過它的最大值、電流也不可超過它的順向脈衝峰值電流限制。其中一個很重要的參數就是 PWM 佔空比(duty cycle)的選擇(佔空比是在 PWM 訊號中 ON 部分所佔的百分比,一般在紅外線 PWM 訊號常用 33.33% 或 25%;Arduino IRremote 函式庫則是選用 33.33% 作為其 PWM 訊號的佔空比),適當的選擇佔空比,可以在增加電流的同時、又不會讓 IR LED 過熱、且能增加較長的控制距離。
下面開始是關於紅外線通訊協定(NEC 和 Philips RC6)的介紹,兩者都是以實際的例子來做解釋,所得到的資料來自於使用 Arduino IRremote 函式庫裡的範例 "IRecvDumpV2",接線與前一篇相同。
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*
有購買商品的使用者,網頁中所用到的檔案與程式碼已放置於雲端硬碟,請自行下載使用!
其餘的使用者,請自行依照提供之連結下載相關資料,程式碼複製貼上使用!
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*
*********************************************************************************
紅外線發射/接收入門學習套件可至露天賣場訂購:
*********************************************************************************
【NEC 紅外線通訊協定】
套件中的紅外線遙控器使用的是 NEC 的紅外線通訊協定,而且像是市面上的 LED 燈帶、機上盒等 ...... 的遙控器也大多都是,所以使用相當廣泛!若手邊有非特定廠牌的紅外線遙控器,也可以拿來試試抓取一下訊號。
** 這一節所談論的不涉及延伸的 NEC 紅外線通訊協定
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 特點:
- 載波頻率(Carrier Frequency)38kHz;
- 8-bit 地址、8-bit 指令;
- 為了可靠性,通訊協定裡的地址與指令各發送兩次;
- 脈衝間隔調變(PDM, Pulse Distance Modulation)方式;
脈衝寬度(mark, T1-m = T0-m)不變,但脈衝間隔(space, T1-s ≠ T0-s)改變
* NEC 紅外線遙控訊號邏輯調變方式:
NEC 紅外線遙控訊號使用的載波頻率 38kHz,一個 PWM 週期花費 26.316 µs(ON=8.772 µs, OFF=17.544 µs;佔空比 33.33%)。Logic 脈衝寬度為 562 µs,意謂在此脈衝寬度下產生約 21 個 PWM 訊號。
由下圖可以看出,NEC 的 IR Logic 調變使用 PDM 方式:其脈衝寬度都是 562 µs,Logic "1" 脈衝間隔為 1687.5 µs、Logic "0" 脈衝間隔為 562.5 µs,所以稱為脈衝間隔調變。
![]() |
NEC IR 遙控訊號邏輯調變方式 |
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* NEC 紅外線通訊協定格式:
看下圖,每一次按下按鍵時,首先發送一個 9ms 的自動增益控制(AGC, Automatic Gain Control)突發脈衝訊號(它被用來設置早期 IR 接收器的增益),後面接著一個 4.5ms 的 space 訊號,再送出地址和指令(到這裡總花費時間 67.5ms),最後以一個 562.5 µs 的 mark 作結束(圖的最後有畫出,但是沒特別標上字)。
地址和指令重複傳送兩次,位元組的低位元(LSB)先送;第一次正常傳送,第二次將位元組取反後傳送,地址和指令一正一反傳送一次各是花費 27ms。
地址:0b1010 1010
指令:0b0011 0011
![]() |
NEC 紅外線通訊協定格式 |
![]() |
NEC 紅外線通訊協定格式(Repeat) |
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* NEC 紅外線遙控器按鍵值解碼原始數據與解碼值:
打開 Arduino IDE 的 Serial Monitor,拿出套件中的遙控器,隨便選擇一個按鍵值按下一秒後放掉,應該就會看到類似下面的輸出訊息。
欄位解釋如下:
- Encoding:代表紅外線遙控所使用的通訊協定為何?經辨識出來的是 NEC;
- Code:接收的紅外線訊號解碼值,由 Timing 陣列中的原始數據計算後得到;
- Timing[67]:接收的紅外線訊號解碼的原始數據。其值表示的是時間(µs)、67 表示的是原始數據的數目;
- unsigned int rawData[67]:這裡是定義上面的原始數據為一個陣列,用來作為 sendRaw() 函式的引數,以此控制被解碼的紅外線裝置;
- unsigned int data = 0xFDA857:將 Code 整理成一個可用於程式的變數定義;
Timing 下面所列出的原始數據是帶正負號的,跟 rawData[] 裡的不同,主要是用來方便識別此處的數字所代表的是屬於 mark(+) 還是 space(-)訊號,67 的由來則是 2 + (8 + 8 + 8 + 8) * 2 + 1 = 67。
![]() |
NEC 紅外線遙控器按鍵解碼輸出訊息 |
![]() |
接收紅外線遙控訊號並顯示在 OLED 上 |
【Philips RC6 紅外線通訊協定】
Philips RC6 對比於 NEC 的紅外線通訊協定比較複雜一點:
- 邏輯採用的是雙相位編碼調變方式;
- 同一個按鍵有兩種解碼值;
- 按鍵持續按下時,輸出的是當前鍵值;
- Arduino IRremote 函式庫解碼所得原始數據數量與格式應有數量不同;
** 請注意,畢竟所使用的紅外線接收模組的載波率是 38kHz,所以並不能保證所有的紅外線接收模組都能容易的接收解碼出像這一節所給出的 Philips RC6(36kHz)輸出資料。
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 特點:
- 載波頻率(Carrier Frequency)36kHz,duty cycle 介於 25% ~ 50%;
- 雙相位編碼調變(Bi-Phase Code《又稱為曼徹斯特編碼 (Machester coding)》)方式;
- 脈衝寬度(mark, T1-m = T0-m)等於脈衝間隔(space, T1-s = T0-s),但邏輯調變前後順序不同;
* Philips RC6 的時間單位(Timing Unit)t:
Philip RC6 紅外線通訊協定格式裡,主要的時間單位是 t,是載波週期的 16 倍((1/36000) * 16 = 444.4 µs),通訊協定格式中的邏輯調變以此作為時間單位。
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* Philips RC6 紅外線通訊協定格式(Philips RC6 Mode 0):
介紹 Philips RC6 邏輯調變之前,先來看看它的通訊格式。
如同小節一開始所說的,對於使用 Philips RC6 紅外線通訊協定的遙控器而言,每一個按鍵都有兩種解碼值,但是主要的變化就是在格式的第三部分(第五個邏輯處)將數據邏輯 "0" 或 "1" 的 mark 和 space 的時間加倍(每按下一次進行邏輯變換),其餘維持一樣。
如下圖,是一實際 Philips DVD Player(RC6)開關的紅外線解碼訊號格式,也是下一小節用來解釋按鍵解碼原始數據和解碼值的參考依據。
格式的每一個部分,它擴號裡面的數字表示所佔的位元(bit)數量,其中:
- Start (1+1):起頭位元,有兩個:一長(6t+2t)一短(t + t);
- Field (3):又稱為操作模式(Operating Mode),Philips DVD Player 使用的是其 Mode 0,所以三個位元都是邏輯 "0",採用數據邏輯調變(t + t);
- Toggle (1):按鍵切換的位元,只有一個,採用按鍵切換邏輯調變(2t + 2t);
- Address (8):地址,有八個位元,採用數據邏輯調變(t + t),高位元先送;
- Command (8):指令,有八個位元,採用數據邏輯調變(t + t),高位元先送;
![]() |
Philips DVD Player ON/OFF RC6 紅外線訊號(Toggle "0") |
![]() |
Philips DVD Player ON/OFF RC6 紅外線訊號(Toggle "1") |
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* Philips RC6 紅外線遙控訊號邏輯調變方式:
Philip RC6 的邏輯調變,依據格式不同部分可區分三個部分:起頭、數據和按鍵切換,分述如下:
** 起頭:
紅外線訊號的起頭位元,一長一短,所用的時間不一樣;注意 Start (+1) 其實用的是數據邏輯 "1" 的調變方式。
![]() |
Philips RC6 紅外線邏輯調變 - 起頭 |
不管目的是作為發射或是接收紅外線訊號之用,同一個按鍵會出現兩個解碼值,而唯一區分就是根據這個位元。
若根據解碼值來做辨識的話,就是有無加上了 0x10000 這個數值;例如,Philips DVD Player 開關的解碼值就會在 0x4C7 和 0x104C7 兩者之間做變化,若是持續按下的話就會一值維持同一個值,若手邊有同類型的遙控器的話可以自己試試!
![]() |
Philips RC6 紅外線邏輯調變 - 按鍵切換 |
通訊協定格式裡面 Address 和 Command 的部分,使用下面的方式進行邏輯調變。
![]() |
Philips RC6 紅外線邏輯調變 - 數據 |
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* Philips RC6 紅外線遙控器按鍵值解碼原始數據與解碼值:
打開 Arduino IDE 的 Serial Monitor,拿出套件中的遙控器,隨便選擇一個按鍵值按下一秒後放掉,應該就會看到類似下面的輸出訊息。
![]() |
Philips RC6 紅外線遙控器按鍵解碼輸出訊息 |
這就跟函式庫的撰寫有關係了,簡單說:跟邏輯調變的 mark 和 space 前後順序有關係!NEC 的邏輯調變不管是 "0" 或是 "1" 都是 mark 在前、space 在後;但 Philips RC6 的調變邏輯 "0" 則是 space 在前、mark 在後,當 "0" 緊接著 "1" 或是 "1" 緊接著 "0",就會導致 mark 或 space 的時間加倍,造成上一個 "bit" 的 mark 或 space 被算進到下一個 "bit" 的 mark 或 space 的時間裡,所以 Timing 的陣列長度才會比想像中的短。
不過沒有關係,可以把它畫出來看再進行分析,很容易就能看出其中的差異!
只有把圖形畫出來才能知道實際的格式與接收的數據間有著什麼關係,也才能真正了解格式的意義。畫出來的階梯圖配合前面所說的 t 時間和格式進行劃分切割,可得到下面的結果。
![]() |
Philips RC6, Timing[37], 0x4C7 |
![]() |
Philips RC6, Timing[35], 0x104C7 |
但這又沿生出另一個問題:那如果最後一個位元是 0 呢?
Timing 陣列長度會各多加 2。但不管 Timing 陣列長度為何,只要接收的數據是正確的,階梯圖都會符合格式的劃分!那麼接下來,就把上面談論的這兩種情形,加入到下面的階梯圖繪製小節中,影片中所用到的檔案與文件也一併提供,可自己動手畫畫。
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
*繪製原始數據階梯圖:
雖然似乎可以用 Excel 來畫,不過後來沒這樣做,因為剛好找到一個不需付費使用的數學軟體網站: Octave Online;採用的是可線上編寫類似 Matlab 語法的整合開發環境,也提供 .m 草稿檔的運行,但使用之前需要註冊一下帳號 (可以用 FB、Google等......直接註冊) ,要不無法線上儲存文件,那就無法跟影片做到相同的事囉!
成功登入之後,拖拉需要的三個文件到左方上傳:plotirsignal.m 是草稿檔,其他兩個是 RC6 Toggle "0" 和 Toggle "1" 的原始數據。
原始數據是來自於 IRrecvDumpV2.ino 的輸出 rawdata[] 陣列裡的內容,以逗號區隔的 CSV 檔案。
執行很簡單,只要輸入:
plotirsignal('rc6-raw-data-#.csv') % # 表示 0 或 1若執行出現錯誤提示,打開 plotirsignal.m 文件看看裡面是否出現額外的文字?刪除不必要的文字就可順利執行!
繪製階梯圖的 .m 檔(如下)可同時用於 Matlab 軟體和 Ovtave 網站,至於兩個 .csv 檔案都是空白檔案可自行建立。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function plotirsignal( csvfilename ) | |
% function plotirsignal( csvfilename ) | |
% ex: | |
% > plotirsignal( 'rc6-raw-data-0.csv' ); | |
% | |
x=csvread(csvfilename); % 載入紅外線原始數據 | |
x=x./1000; % 轉換為 ms | |
x=[0,x]; | |
for i=3:length(x) % 構成 X 軸標籤序列 | |
x(i) = x(i-1) + x(i); | |
end | |
y=zeros(1,length(x)); | |
for i=1:length(x) % 構成 Y 軸序列 | |
if bitand(i, 1) % index 由 1 開始 | |
y(i) = 1; | |
end | |
end | |
figure(1, 'position', [0,0,1920,1080]); | |
stairs(x, y) % 畫圖 | |
axis([ 1.5 ,23 ,-0.2 ,1.2 ]);% 設定 X, Y 軸的範圍 | |
set( gca, 'xtick', x); % 設定 X 軸格線 | |
set( gca, 'ytick', [0 1]); % 設定 Y 軸格線 | |
set( gca, 'fontname', 'time new roman'); | |
set( gca, 'fontsize', 14); | |
grid on | |
end |
下面影片中,實際演示了由紅外線訊號接收到繪製階梯圖的過程。
** 影片中切換成大階梯圖時忘記切換 Plot: Line # 為下一則圖形,導致大階梯圖都是顯示第一則的圖形,實際使用時請別忘了切換,特此說明!
在影片中,由遙控器的兩個不同按鍵所產生的四組資料 Timing 陣列的長度都不一樣(35 和 37 同一個按鍵;37、39 同一個按鍵),會導致這樣的結果是由於訊號最後一個位元(bit)是 "0" 或是 "1" 導致,輸出的原始數據如下所示:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Encoding : RC6 | |
Code : 1042C (20 bits) | |
Timing[35]: | |
+2650, - 900 + 450, - 850 + 450, - 450 + 450, - 450 | |
+1300, -1350 + 450, - 400 + 450, - 450 + 450, - 450 | |
+ 450, - 400 + 900, - 900 + 450, - 400 + 450, - 450 | |
+ 450, - 450 + 850, - 900 + 900, - 450 + 450, - 850 | |
+ 450, - 450 + 450 | |
unsigned int rawData[35] = {2650,900, 450,850, 450,450, 450,450, 1300,1350, 450,400, 450,450, 450,450, 450,400, 900,900, 450,400, 450,450, 450,450, 850,900, 900,450, 450,850, 450,450, 450}; // RC6 1042C | |
unsigned int data = 0x1042C; | |
Encoding : RC6 | |
Code : 42C (20 bits) | |
Timing[37]: | |
+2650, - 900 + 450, - 850 + 450, - 450 + 450, - 450 | |
+ 400, - 900 + 900, - 450 + 400, - 450 + 450, - 450 | |
+ 450, - 450 + 400, - 450 + 900, - 900 + 400, - 450 | |
+ 450, - 450 + 450, - 450 + 850, - 900 + 900, - 400 | |
+ 450, - 900 + 450, - 450 + 400 | |
unsigned int rawData[37] = {2650,900, 450,850, 450,450, 450,450, 400,900, 900,450, 400,450, 450,450, 450,450, 400,450, 900,900, 400,450, 450,450, 450,450, 850,900, 900,400, 450,900, 450,450, 400}; // RC6 42C | |
unsigned int data = 0x42C; | |
Encoding : RC6 | |
Code : 1047E (20 bits) | |
Timing[37]: | |
+2650, - 900 + 400, - 900 + 450, - 450 + 450, - 400 | |
+1350, -1300 + 450, - 450 + 450, - 450 + 450, - 400 | |
+ 450, - 450 + 900, - 850 + 450, - 450 + 450, - 450 | |
+ 850, - 450 + 450, - 450 + 450, - 450 + 400, - 450 | |
+ 450, - 450 + 450, - 850 + 450 | |
unsigned int rawData[37] = {2650,900, 400,900, 450,450, 450,400, 1350,1300, 450,450, 450,450, 450,400, 450,450, 900,850, 450,450, 450,450, 850,450, 450,450, 450,450, 400,450, 450,450, 450,850, 450}; // RC6 1047E | |
unsigned int data = 0x1047E; | |
Encoding : RC6 | |
Code : 47E (20 bits) | |
Timing[39]: | |
+2650, - 900 + 400, - 900 + 450, - 450 + 450, - 400 | |
+ 450, - 900 + 900, - 400 + 450, - 450 + 450, - 450 | |
+ 450, - 400 + 450, - 450 + 900, - 850 + 450, - 450 | |
+ 450, - 450 + 900, - 400 + 450, - 450 + 450, - 450 | |
+ 450, - 400 + 450, - 450 + 450, - 850 + 450 | |
unsigned int rawData[39] = {2650,900, 400,900, 450,450, 450,400, 450,900, 900,400, 450,450, 450,450, 450,400, 450,450, 900,850, 450,450, 450,450, 900,400, 450,450, 450,450, 450,400, 450,450, 450,850, 450}; // RC6 47E | |
unsigned int data = 0x47E; |
利用上面數據所繪出的四張階梯圖(原尺寸大小),如下所示(點擊看原尺寸圖):
|
| ||||
|
|
【結論】
紅外線通訊協定或許有很多種,但基本的東西都差不多,有差異、但有資料的話不難理解。
會花這麼多的篇幅與時間來寫這一篇,除了是為了瞭解紅外線通訊協定之外,主要的目的是要能在需要的時候客製化自己的紅外線通訊協定或是將其衍生。
網頁當中也提供了將 Philips RC6 Mode 0 的原始數據繪製成階梯圖的 .m 草稿檔,對於了解紅外線通訊格式很有幫助,以後遇到未知格式且能重複產生相同數據的紅外線訊號時,就可以以這檔案為基礎進行繪製。
了解了紅外線接收和通訊格式之後,接下來要做的就是紅外線發射的部分,至於到底要做什麼,想到了再說吧!
<< 部落格相關文章 >>
不好意思可以請教一下 Code(解碼值)的 16 進位值是如何從Timing轉換而來的嗎
回覆刪除仔細的將 Timing 陣列裡面的值對照到格式中,很容易就能瞭解紅外線通訊協定格式構成的原理,以及 Code(解碼值)的 16 進位值是如何而來的!
刪除