AIR32F103(九) CAN匯流排的通訊和ID過濾機制及範例

2023-02-26 06:00:54

目錄

CAN 匯流排

CAN是 Controller Area Network 的簡稱, 最初由BOSCH公司開發, 後來成為國際標準(ISO 11898), 是當前應用最廣泛的現場匯流排之一, 是汽車控制系統和嵌入式工業控制區域網事實上的標準.

CAN 匯流排的物理連線

相對於近距離傳輸的I2C, SPI協定, 以及RS485匯流排, CAN 匯流排定義了更先進的物理層和鏈路層, 以及種類豐富的上層協定. 與I2C, SPI等基於時鐘訊號同步的通訊方式不同, CAN通訊不使用時鐘訊號進行同步, 它是一種非同步通訊, 只有 CAN_High 和 CAN_Low 兩條訊號線, 共同構成一組差分訊號線, 以差分訊號的形式進行通訊.

CAN 物理層主要分為閉環匯流排及開環匯流排網路兩種形式, 一個適合於高速通訊, 一個適合於遠距離通訊.

  • CAN閉環通訊網路是一種遵循 ISO11898 標準的高速, 短距離網路, 匯流排最大長度為40m, 通訊速度最高為1Mbps, 匯流排的兩端各要求有一個120歐的電阻做阻抗匹配, 減少回波反射.
  • CAN開環匯流排網路是遵循 ISO11519-2 標準的低速, 遠距離網路, 最大傳輸距離為1km, 最高通訊速率為125kbps, 兩根匯流排互相獨立, 不形成閉環, 要求每根匯流排上各串聯有一個 2.2KR 的電阻.

關於共地

CAN 在多個收發器之間的連線, 可以不共地, 只需要 CANH 和 CANL 兩線連線.

CAN 匯流排的通訊機制

CAN匯流排上可以掛載多個通訊節點, 節點之間的訊號經過匯流排傳輸, 實現節點間通訊. CAN通訊協定不對節點進行地址編碼, 而是對資料內容進行編碼, 所以網路中的節點個數理論上不受限制, 只要匯流排的負載足夠即可, 可以通過中繼器增強負載.

CAN通訊節點由一個CAN控制器及CAN收發器組成, 控制器與收發器之間通過 CAN_Tx及 CAN_Rx 訊號線相連, 收發器與CAN匯流排之間使用 CAN_High 及 CAN_Low 訊號線相連. 其中 CAN_Tx 及 CAN_Rx 使用普通的類似TTL邏輯訊號, 而 CAN_High 及 CAN_Low 是一對差分訊號線, 當CAN節點需要傳送資料時, 控制器把要傳送的二進位制編碼通過 CAN_Tx 線傳送到收發器, 然後由收發器把這個普通的邏輯電平訊號轉化成差分訊號, 通過差分線 CAN_High 和 CAN_Low 輸出到CAN匯流排網路. 收發器接收匯流排上的資料時則是相反的過程, 收發器把匯流排上收到的 CAN_High 及 CAN_Low 訊號轉化成普通的邏輯電平訊號, 再通過 CAN_Rx 輸出到控制器中.

差分訊號

差分訊號又稱差模訊號, 與傳統使用單根訊號線電壓表示邏輯的方式有區別, 使用差分訊號傳輸時, 需要兩根訊號線, 這兩個訊號線的振幅相等, 相位相反, 通過兩根訊號線的電壓差值來表示邏輯0和邏輯1. 相對於單訊號線傳輸的方式, 使用差分訊號傳輸具有如下優點

  • 抗干擾能力強, 當外界存在噪聲干擾時, 幾乎會同時耦合到兩條訊號線上, 而差分訊號只關心兩個訊號的差值, 所以外界的共模噪聲可以被抑制
  • 能有效抑制它對外部的電磁干擾, 由於兩根訊號的極性相反, 對外輻射的電磁場可以相互抵消, 耦合的越緊密, 洩放到外界的電磁能量越少
  • 時序定位精確, 由於差分訊號的開關變化是位於兩個訊號的交點, 而不像普通單端訊號依靠高低兩個閾值電壓判斷, 因而受工藝, 溫度的影響小, 能降低時序上的誤差, 同時也更適合於低幅度訊號的電路

由於差分訊號的這些優點, 在USB協定, 485協定, 乙太網協定及CAN協定的物理層中, 都使用了差分訊號傳輸.

CAN協定中的差分訊號

CAN協定中對它使用的 CAN_High 及 CAN_Low 表示的差分訊號做了規定. 以高速CAN協定為例, 當表示邏輯1時(隱性電平), CAN_High 和 CAN_Low 線上的電壓均為2.5V, 即它們的電壓差為 0, 而表示邏輯0時(顯性電平), CAN_High的電平為 3.5V, CAN_Low線的電平為1.5V, 電壓差為 2V.

CAN 匯流排的特點

CAN 匯流排網路是一種多主網路, 在匯流排處於空閒狀態時, 任何一個節點單元都可以申請成為主機, 向匯流排傳送訊息. 其原則是: 最先存取匯流排的節點單元可以獲得匯流排的控制權, 多個節點單元同時嘗試獲取匯流排的控制權時, 將發生仲裁事件, 具有高優先順序的節點單元將獲得匯流排控制權.

CAN 協定中, 所有的訊息都以固定的資料格式打包傳送. 兩個以上的節點單元同時傳送資訊時, 根據節點識別符號(常稱為 ID, 打包在固定的資料格式中)決定各自優先順序關係, CAN 匯流排沒有其他匯流排的地址概念, 在匯流排上增加節點單元時, 連線在匯流排的其他節點單元的軟硬體都不需要改變.

CAN 匯流排的通訊速率和匯流排長度有關, 在匯流排長度小於 40m 的場合中, 資料傳輸速率可以達到 1Mbps, 而即便匯流排長度上升至 1000m, 資料的傳輸速率仍可達到 50Kbps, 無論在速率還是傳輸距離都明顯優於常見的 RS232, RS485 和 I2C 匯流排.

對於匯流排錯誤, CAN 匯流排有錯誤檢測功能, 錯誤通知功能, 錯誤恢復功能三種應對措施, CAN 匯流排上的每個節點都可以通過判斷得出, 當前匯流排上的錯誤是暫時錯誤(如瞬間的強幹擾)還是持續錯誤(如匯流排斷裂). 當匯流排上發生持續錯誤時, 引起故障的節點單元會自動脫離匯流排.

CAN 匯流排上的節點數量在理論上沒有上限, 但在實際上收到匯流排上的時間延時及電氣負載的限制. 降低最大通訊速率可以增加節點單元的連線數, 反之減少節點單元的連線數則最大通訊速率可以提高.

CAN 匯流排通訊

CAN 資料位傳輸時間

為了實現位同步, CAN協定把每一個資料位(bit)的時序分解成SS段, PTS段, PBS1段, PBS2段, 這四段的長度加起來即為一個CAN資料位的長度.

CAN傳輸的最小的時間單位是Tq(即CAN外設的時鐘週期), 一個完整的位由8~25個Tq組成.

CAN 的資料框

CAN 匯流排的資料通訊是以資料框的格式進行的, 瞭解CAN的資料框, 可以幫助瞭解CAN的過濾機制. 下面是一個完整的CAN資料框的結構

  • SOF 應該是 start of frame, 一個bit位拉低匯流排用於開始傳輸
  • Identifier 就是資料的 ID, 11個bit
  • RTR (Remote Transmission Request) 用於宣告這是一個資料框還是一個遠端幀
  • IDE 用於宣告使用的標準ID還是擴充套件ID
  • r 是一個保留 bit
  • DLC 用於宣告後面攜帶的資料位元組數量
  • Data Field 包含的是傳送的資料, 最多不超過8個位元組
  • Checksum 和 DEL 是校驗碼和對應的分隔符
  • ACK 和 DEL 是響應位和對應的分隔符

CAN 資料框對應的程式碼

用C語言描述的CAN幀整體結構為

CAN_TxHeaderTypeDef   TxHeader;
uint8_t               TxData[8];
uint32_t              TxMailbox;
  • TxHeader 用於儲存頭資訊, 包含了 RTR, DLC 等, 在SPL中對應的型別為 CAN_TxHeaderTypeDef
  • TxData 用於儲存傳輸的資料
  • TxMailbox 用於傳送此訊息的 mailbox

幀頭的結構和幀資料

TxHeader.StdId = 0x446;
TxHeader.RTR = CAN_RTR_DATA;  // Remote or data frame
TxHeader.IDE = CAN_ID_STD;    // Standard or extended
// reserved bit
TxHeader.DLC = 2;             // Data length in bytes
  • CAN_ID_STD 表示使用了標準ID模式(非擴充套件ID)
  • 0x446 就是傳送的ID, 位寬最大為 11-bit
  • CAN_RTR_DATA 表示這個幀為資料框
  • DLC 標識後面資料的位元組長度, 因為傳送兩個位元組, 所以這裡是2
  • 隨後在 TxData 中儲存兩個位元組
TxData[0] = 50;  
TxData[1] = 0xAA;

AIR32F103 / STM32 的 CAN 外設

以下的描述適用於AIR32F103和STM32.

bxCAN 控制器 (Basic Extended CAN) 支援CAN協定2.0A和2.0B標準. 該CAN控制器支援最高的通訊速率為1Mbps, 可以自動地接收和傳送CAN報文, 支援使用標準ID和擴充套件ID的報文. 外設中具有3個傳送郵箱, 傳送報文的優先順序可以使用軟體控制, 還可以記錄傳送的時間;具有2個3級深度的接收FIFO, 可使用過濾功能只接收或不接收某些ID號的報文; 可設定成自動重發; 不支援使用DMA進行資料收發.

CAN波特率的計算

通過設定位時序暫存器CAN_BTR的TS1[3:0]及TS2[2:0]暫存器位設定BS1及BS2段的長度後, 可以確定每個CAN資料位的時間

BS1段時間

Tbs1 =Tq x (TS1[3:0] + 1)

BS2段時間

Tbs2 = Tq x (TS2[2:0] + 1)

整個資料位的時間

Tbit = 1Tq + Tbs1 +Tbs2 = 1 + (TS1[3:0] + 1)+ (TS2[2:0] + 1)

Tq 是 CAN 通訊的最小時間單元, 與 CAN 時鐘匯流排及分頻器設定有關, CAN1和CAN2外設都是掛載在APB1匯流排上的, 而位時序暫存器 CAN_BTR 中的 BRP[9:0] 暫存器位可以設定CAN外設時鐘的分頻值 , 所以

Tq = brp * Tpclk = (BRP[9:0]+1) * Tpclk

其中的PCLK指APB1時鐘, 預設值為36MHz. 可以計算出 CAN 的波特率:

BaudRate = 1 / Tbit = Fpclk / ((Tbs1 + Tbs2 + 1) * brp)

CAN 波特率的設定

CAN_InitStructure.CAN_TTCM = DISABLE; // time triggered communication mode off
CAN_InitStructure.CAN_ABOM = DISABLE; // automatic bus-off management off
CAN_InitStructure.CAN_AWUM = DISABLE; // automatic wake-up mode off, wakeup by software cleaar CAN->MCR SLEEP bit
CAN_InitStructure.CAN_NART = ENABLE;  // no-automatic retransmission mode on
CAN_InitStructure.CAN_RFLM = DISABLE; // rx FIFO Locked mode off
CAN_InitStructure.CAN_TXFP = DISABLE; // transmit FIFO priority off
CAN_InitStructure.CAN_Mode = mode;
// Set baud rate
CAN_InitStructure.CAN_SJW = tsjw;      // synchronisation_jump_width, CAN_SJW_1tq ~ CAN_SJW_4tq
CAN_InitStructure.CAN_BS1 = tbs1;      // number of time quanta in Bit Segment 1, CAN_BS2_1tq ~ CAN_BS2_8tq
CAN_InitStructure.CAN_BS2 = tbs2;      // number of time quanta in Bit Segment 2, CAN_BS1_1tq ~ CAN_BS1_16tq
CAN_InitStructure.CAN_Prescaler = brp; // clock prescaler, 1~1024
CAN_Init(CAN1, &CAN_InitStructure);

CAN 的ID過濾機制

CAN 是一種典型的廣播式網路, 在實際應用中, 如果只希望接收到特定型別的資料, 就要藉助過濾器來實現. AIR32/STM32的CAN控制器包含14個過濾器, 可以設定為 遮蔽模式列表模式 對CAN匯流排上的報文進行過濾. 當節點希望接收到一種報文時, 可以用遮蔽位元型樣進行過濾, 當節點希望接受到單一型別報文時, 應該設定為列表模式.

CAN控制器的每個過濾器都具備一個暫存器, 稱為遮蔽暫存器。其中識別符號暫存器的每一位都有遮蔽暫存器的每一位所對應.

AIR32/STM32 使用 CAN 外設內建的過濾器, 初始化程式碼為

  CAN_FilterTypeDef canfilterconfig;

  canfilterconfig.FilterActivation = CAN_FILTER_ENABLE;
  canfilterconfig.FilterBank = 18;  // 指定使用哪個過濾器
  canfilterconfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;
  canfilterconfig.FilterIdHigh = 0x103<<5;
  canfilterconfig.FilterIdLow = 0;
  canfilterconfig.FilterMaskIdHigh = 0x103<<5;
  canfilterconfig.FilterMaskIdLow = 0x0000;
  canfilterconfig.FilterMode = CAN_FILTERMODE_IDMASK;
  canfilterconfig.FilterScale = CAN_FILTERSCALE_32BIT;
  canfilterconfig.SlaveStartFilterBank = 20;  // how many filters to assign to the CAN1 (master can)

  HAL_CAN_ConfigFilter(&hcan1, &canfilterconfig);

FilterMode 用於設定過濾模式, 在STM32中有兩種過濾模式, 這裡使用的是掩碼模式

  • MASK MODE, 掩碼模式, 使用暫存器中設定的掩碼對接收到的ID中特定的位進行比較.
  • LIST MODE, 列表模式, 對於接收到的ID, 直接使用暫存器中的ID進行比較.

FilterScale 用於指定是 1)一個32bit的過濾暫存器, 還是 2)兩個16bit的過濾暫存器. 這裡使用的是一個 32 Bit 暫存器.

FilterIdHigh 用於設定 ID 暫存器的高16 Bits, 這裡的值會被用於與輸入的ID進行比較. 這裡只比較接收到的訊息的標準ID, 因此將值左移5位, STD ID 從 ID HIGH Register 的第5位開始.

FilterMaskIdHigh 是掩碼暫存器的高16 Bits, 在對接收到的訊息的ID進行比較時, 會忽略這個暫存器中bit=0的位, 僅對會對bit=1對應的位, 與ID暫存器中對應的位進行比較.

掩碼模式過濾的圖例說明

上圖中, CAN_FxR1 和 CAN_FxR2 都是32bit暫存器, 用於儲存過濾器的 ID 和 Mask 設定, 紅色框和綠色框分別對應程式碼中的 FilterIdHigh + FilterIdLow 和 FilterMaskIdHigh + FilterMaskIdLow.

  1. 這個例子中僅使用標準ID, 不使用擴充套件ID時, 低5位可以忽略
  2. 設定ID和Mask時, 都要將需要設定的ID值(0x103)左移5位, 因為低5位用於extId
  3. 與接收的ID對比時, 會根據Mask中bit值為1的位, 與ID中的對應bit進行比較, Mask中bit為0的位都會忽略. 這個例子中需要比較的位是0, 1 和 8
  4. 接收到的ID, 與ID暫存器中對應bit的值進行比較, 當這幾個bit的值都一致時, ID就是匹配的, 訊息就會被接收, 否則會被過濾

根據上面的設定

  • 如果輸入的是0x102, 根據Mask設定, 第0,1,8位元會用於比較, 而ID的第0位為1, 所以這個輸入會被忽略
  • 如果輸入的是0x107, 根據Mask設定, 第0,1,8位元會用於比較, ID設定的這三位都是1和輸入的一致, 所以這個輸入會被接收

帶擴充套件ID的例子

根據手冊 standard frames with 11-bit identifiers as well as extended frames with 29-bit identifiers, 擴充套件幀除了原有的 11 bits 標準ID外, 還帶 18 bits 的擴充套件ID. 為什麼是29 bits? 因為後面還有3個bit的功能標誌位

如果

  • 將 stdId 和 extId 合併為一個29位的 filterId, 其結構為 EXTID[17:0] | STDID[10:0] (從高bit位到低bit位)
  • 將對應的 mask 也合併為一個29位的 filterMask, 其結構為 EXTMASK[17:0] | STDMASK[10:0]

過濾條件的設定程式碼為

// 取值 STID[10:0] & EXTID[17:13], 因為 CAN_FilterIdHigh 是 16bit, 所以 filterId << 5 的高16bit會被忽略.
filter.CAN_FilterIdHigh = ((filterId << 5)  | (filterId >> (32 - 5))) & 0xFFFF;
// 取值 EXID[12:5] & 3 Reserved bits, 這裡同樣, filterId 移位後的高16bit會被忽略
filter.CAN_FilterIdLow = (filterId >> (11 - 3)) & 0xFFF8;
// 與上面同理
filter.CAN_FilterMaskIdHigh = ((filterMask << 5)  | (filterMask >> (32 - 5))) & 0xFFFF;
filter.CAN_FilterMaskIdLow = (filterMask >> (11 - 3)) & 0xFFF8;

測試範例

硬體準備

  • TJA1050 或 MCP2551 的 CAN 收發模組 x 2, 可以混合使用, TJA1050 與 MCP2551 可以互相通訊
  • 帶 AIR32F103 的開發板

因為 TJA1050 和 MCP2551 都是5V供電, 因此開發板上要有5V輸出, 否則需要單獨供電

範例程式碼

程式碼倉庫目錄 https://github.com/IOsetting/air32f103-template/tree/master/Examples/NonFreeRTOS/CAN

這個目錄下包含兩個模式的例子, 一個是 Loopback, 一個是 Normal, 從合宙官方倉庫的例子參考(抄)的.

Loopback 模式

Loopback 是測試模式, 傳送的資料不進入匯流排直接進入接收佇列, 用於檢查 MCU 與 CAN 收發器之間通訊是否正常, 以及 CAN 收發器是否正常工作. 執行後在串列埠輸入's', 會傳送8個位元組並將接收到的資料通過串列埠回顯.

Normal 模式

正常的通訊模式, 需要兩套 MCU + CAN 收發器. CAN 收發器之間通過 CANH 和 CANL 連線. 程式碼中設定過濾器時, 使用的是相同的 ID 和 Mask 值, 對兩個MCU編譯燒錄時需要將 ID_TARGET 和 ID_RECEIV 的值互換一下.

執行後, 在一側串列埠輸入's', 在另一側會通過串列埠顯示接收到的資料.

問題

在測試中, 一開始給 TJA1050 錯誤使用了 3.3V 電壓, 在 Loopback 模式工作正常, 但是在 Normal 模式工作不正常, 只有將兩邊收發器共地才能正常通訊, 如果換成 MCP2551 則完全不能通訊.

這些問題在將電壓換成 5V 後就正常了.

參考