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——無論哪種說法,其物理行為都完全相同。)

一條被連接至 Vcc 的終端電阻拉至高電位 的匯流排。三個節點(A、B、C)掛在這條 匯流排上。每個節點的輸出級是一個二極體 串聯一個對地的開關——二極體從匯流排指向 下方的開關,因此閉合開關會透過二極體把 匯流排拉低,但沒有任何節點能把匯流排 驅動為高電位。

概念形式的線接 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 資料訊框在線上看起來像這樣:

依序繪出的八個欄位:SOF(1 位元)、ID (11 位元)、RTR(1 位元)、control(6 位元)、data (0 -- 8 位元組)、CRC(16 位元)、ACK(2 位元)以及 EOF(7 位元)。

一個標準的 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 對應到意義則是應用程式的工作。