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

A single bus line held high by a termination resistor to Vcc. Three nodes (A, B, C) hang off the bus. Each node's output stage is a diode in series with a switch to ground -- the diode points from the bus down to the switch, so closing the switch pulls the bus low through the diode but no node can drive the bus high.

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

Eight fields drawn in sequence: SOF (1 bit), ID (11 bits), RTR (1 bit), control (6 bits), data (0 -- 8 bytes), CRC (16 bits), ACK (2 bits), and EOF (7 bits).

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