3.25. CAN 匯流排基礎¶
CAN(Controller Area Network,控制器區域網路)最初由 Bosch 於 1980 年代設計,用來把汽車中所有的電子控制單元連接到一條短的共用匯流排上。它至今仍主宰著汽車的線束,但其同樣的堅固性、即時行為與多主控(multi-master)設計,使它成為各類自動化、機器人、農業與工業設備中預設的現場匯流排。
CAN 並不像 SPI 與 I2C 那樣具有單一的「控制器」與一組「周邊裝置」。匯流排上的每個節點都是對等的,任何節點都可以在匯流排閒置時隨時傳輸。讓這種架構得以擴展的,正是匯流排的「廣播加仲裁」設計。
3.25.1. 具優先權仲裁的廣播¶
CAN 匯流排上的每則訊息都帶有一個識別碼(identifier)——傳統的標準訊框為 11 位元,擴充版本則為 29 位元。識別碼並不是位址;它標示這則訊息關於什麼(引擎轉速、煞車踏板位置、電池電壓等)。當某個節點要傳送一個數值時,它會以對應的識別碼標記後廣播出去,而匯流排上的每個節點都會看到這則廣播。每個接收端都會以硬體過濾匯流排,只挑出它所關心的 ID。
如果兩個節點試圖同時傳輸,匯流排的電氣設計會讓識別碼數值較低的訊息勝出,而不會遺失任何位元。
其關鍵在於匯流排的兩種電氣狀態:顯性(dominant,邏輯 0)與隱性(recessive,邏輯 1)。當沒有節點在通訊時,CAN 線會被終端電阻拉至高電位(隱性);任何節點都可以透過其收發器(transceiver)灌入電流,把線拉低(顯性)。其結果是一個線接 AND(wired-AND):只要有任何一個節點將匯流排驅動為顯性,線路就讀為顯性;只有當每個節點都釋放它時,才會讀為隱性。顯性永遠勝出。(有些參考資料將同樣的配置稱為線接 OR(wired-OR),把「顯性」視為被宣告的訊號,而非隱性位元的 AND——無論哪種說法,其物理行為都完全相同。)
概念形式的線接 AND。真正的 CAN 是在一對差動線(CAN_H / CAN_L)上執行相同的邏輯,收發器與終端電阻分布在兩條線上,但匯流排上的規則是一樣的:任何節點都可以拉為顯性,只有當每個節點都釋放後,它才會讀為隱性。¶
仲裁直接利用了這種不對稱性。每個傳輸中的節點都以 MSB 優先、一次一個位元的方式送出其 ID,並在傳輸的同時監看匯流排。一個在線上放出隱性位元卻讀回顯性的節點,便知道另一個 ID 較低的節點正在同一時刻傳輸,並已贏得該位元位置。它會立即停止驅動匯流排並進入聆聽。與此同時,勝出的節點看到自己的位元原封不動地送出——從它的角度看,沒有發生任何異常。ID 數值較小者勝出,是因為它的顯性位元覆蓋了 ID 數值較大者在相同位置原本會送出的隱性位元。
落敗者隨後會等待匯流排回到閒置狀態,並自動重試。沒有碰撞、沒有遺失的訊息——只有確定性的優先權排序。
這就是讓 CAN 能大規模運作的發布/訂閱(publish/subscribe)風格:任何人都可以發言,任何人都可以聆聽,而 ID 讓分派得以隱含完成。
3.25.2. 實體匯流排¶
MCU 的 CAN 控制器並不直接驅動匯流排。它只對外提供兩支 3.3 V CMOS 接腳——TX(它想送出的位元)與 RX(它在匯流排上看到的位元)——而這些接腳會連接到一顆稱為 CAN 收發器(transceiver)或 PHY 的獨立晶片。收發器位於控制器與實際的 CAN 線之間;它才是知道如何與匯流排通訊的角色。
匯流排本身是一對 5 V 的差動線:
CAN_H —— 這對線中的「高」線。
CAN_L —— 「低」線。
在隱性狀態下,兩條線都大約位於 2.5 V(匯流排 5 V 電源的中點)。在顯性狀態下,收發器會把 CAN_H 拉高至約 3.5 V、把 CAN_L 拉低至約 1.5 V,使這對線之間產生約 2 V 的差動電壓。接收端取樣的是兩條線之間的差值,這讓訊號得以免疫於長距離纜線傳輸所拾取的共模雜訊。
收發器的雙向任務:
在 TX 端,它讀取控制器的 3.3 V 單端 TX 接腳,並在顯性時把 CAN_H 與 CAN_L 拉開,或在隱性時釋放兩者。
在 RX 端,它讀取差動的 CAN_H / CAN_L 線對,並在其 RX 接腳上回報一個單端的 3.3 V 電位給控制器。
兩個 120 歐姆的終端電阻,分別位於纜線的每一個實體端點,在沒有節點驅動時將匯流排維持在隱性中點,並抑制原本會在長距離傳輸時破壞差動訊號的反射。
3.25.3. 資料訊框¶
一個標準的 CAN 資料訊框在線上看起來像這樣:
一個標準的 CAN 資料訊框:SOF、ID、RTR、control、data、CRC、ACK 與 EOF 欄位。¶
每個欄位都有特定的職責:
SOF(start of frame,訊框起始)。一個顯性位元,宣告一個新訊框正要開始,並同步每個節點的位元時脈。
ID(identifier,識別碼)。匯流排用來仲裁的 11 位元識別碼。數值越低 = 優先權越高。
RTR(remote transmission request,遠端傳輸請求)。在請求資料而非遞送資料時設定;具有相符 ID 的接收端會以自行送出資料來回應。
Control。 一個 6 位元欄位,編碼資料長度(
DLC)以及幾個內務用的位元。Data。 0 至 8 位元組的酬載(CAN Classic;CAN FD 將其擴充至 64 位元組)。
CRC。 共 16 位元:對前述欄位計算的 15 位元 CRC,再加上 1 位元的 CRC 分隔符。
ACK。 共 2 位元。在第一個位元——ACK 槽——傳輸端會釋放線路,任何正確接收到該訊框的節點都會把它拉低;第二個位元是隱性的分隔符。缺少 ACK 表示沒有節點聽到該訊框,傳輸端應再試一次。
EOF(end of frame,訊框結束)。七個隱性位元,用以結束訊框。
上述這一切都由 CAN 控制器以硬體產生與解碼;軟體只看到 ID、資料以及少數幾個旗標。
3.25.4. CAN 的定位¶
CAN 的設計取捨界定了它的利基:
堅固。 在雙絞線上的差動訊號傳輸、內建的錯誤偵測、確定性的優先權仲裁,以及自動重試,使 CAN 能在電氣雜訊嚴重的環境中存活,而 UART 與 SPI 在這類環境下會損毀資料。
多主控。 任何節點都可以隨時通訊。不需要中央控制器,也沒有任何節點是單一故障點。
頻寬有限。 傳統 CAN 的上限約為
1 Mbit/s;CAN FD 對此有所擴充。當連結是跨電路板或模組之間,常常需橫越數公尺的纜線,且以可靠性為優先時,CAN 就是正確的答案。上層協定。 裸的 CAN 匯流排並未規定某個 ID 代表什麼意義,也未規定如何把一則長訊息切成片段。應用層協定——CANopen、J1939、ISO-TP/UDS、NMEA 2000——把這些規則疊加在其上。另一個值得認識的項目是 DBC 檔:一種廠商專屬的文字格式,記錄某一特定車輛或系統上,哪些 ID 攜帶哪些位元層級的訊號。DBC 檔是描述某一匯流排訊息布局的中介資料格式,本身並不是一種協定。
程式碼中的 CAN 匯流排 中的驅動程式處理線層級的 CAN 訊框;把 ID 對應到意義則是應用程式的工作。