CAN是 Controller Area Network 的簡稱, 最初由BOSCH公司開發, 後來成為國際標準(ISO 11898), 是當前應用最廣泛的現場匯流排之一, 是汽車控制系統和嵌入式工業控制區域網事實上的標準.
相對於近距離傳輸的I2C, SPI協定, 以及RS485匯流排, CAN 匯流排定義了更先進的物理層和鏈路層, 以及種類豐富的上層協定. 與I2C, SPI等基於時鐘訊號同步的通訊方式不同, CAN通訊不使用時鐘訊號進行同步, 它是一種非同步通訊, 只有 CAN_High 和 CAN_Low 兩條訊號線, 共同構成一組差分訊號線, 以差分訊號的形式進行通訊.
CAN 物理層主要分為閉環匯流排及開環匯流排網路兩種形式, 一個適合於高速通訊, 一個適合於遠距離通訊.
CAN 在多個收發器之間的連線, 可以不共地, 只需要 CANH 和 CANL 兩線連線.
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_High 及 CAN_Low 表示的差分訊號做了規定. 以高速CAN協定為例, 當表示邏輯1時(隱性電平), CAN_High 和 CAN_Low 線上的電壓均為2.5V, 即它們的電壓差為 0, 而表示邏輯0時(顯性電平), CAN_High的電平為 3.5V, CAN_Low線的電平為1.5V, 電壓差為 2V.
CAN 匯流排網路是一種多主網路, 在匯流排處於空閒狀態時, 任何一個節點單元都可以申請成為主機, 向匯流排傳送訊息. 其原則是: 最先存取匯流排的節點單元可以獲得匯流排的控制權, 多個節點單元同時嘗試獲取匯流排的控制權時, 將發生仲裁事件, 具有高優先順序的節點單元將獲得匯流排控制權.
CAN 協定中, 所有的訊息都以固定的資料格式打包傳送. 兩個以上的節點單元同時傳送資訊時, 根據節點識別符號(常稱為 ID, 打包在固定的資料格式中)決定各自優先順序關係, CAN 匯流排沒有其他匯流排的地址概念, 在匯流排上增加節點單元時, 連線在匯流排的其他節點單元的軟硬體都不需要改變.
CAN 匯流排的通訊速率和匯流排長度有關, 在匯流排長度小於 40m 的場合中, 資料傳輸速率可以達到 1Mbps, 而即便匯流排長度上升至 1000m, 資料的傳輸速率仍可達到 50Kbps, 無論在速率還是傳輸距離都明顯優於常見的 RS232, RS485 和 I2C 匯流排.
對於匯流排錯誤, CAN 匯流排有錯誤檢測功能, 錯誤通知功能, 錯誤恢復功能三種應對措施, CAN 匯流排上的每個節點都可以通過判斷得出, 當前匯流排上的錯誤是暫時錯誤(如瞬間的強幹擾)還是持續錯誤(如匯流排斷裂). 當匯流排上發生持續錯誤時, 引起故障的節點單元會自動脫離匯流排.
CAN 匯流排上的節點數量在理論上沒有上限, 但在實際上收到匯流排上的時間延時及電氣負載的限制. 降低最大通訊速率可以增加節點單元的連線數, 反之減少節點單元的連線數則最大通訊速率可以提高.
為了實現位同步, CAN協定把每一個資料位(bit)的時序分解成SS段, PTS段, PBS1段, PBS2段, 這四段的長度加起來即為一個CAN資料位的長度.
CAN傳輸的最小的時間單位是Tq(即CAN外設的時鐘週期), 一個完整的位由8~25個Tq組成.
CAN 匯流排的資料通訊是以資料框的格式進行的, 瞭解CAN的資料框, 可以幫助瞭解CAN的過濾機制. 下面是一個完整的CAN資料框的結構
用C語言描述的CAN幀整體結構為
CAN_TxHeaderTypeDef TxHeader;
uint8_t TxData[8];
uint32_t TxMailbox;
幀頭的結構和幀資料
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
TxData[0] = 50;
TxData[1] = 0xAA;
以下的描述適用於AIR32F103和STM32.
bxCAN 控制器 (Basic Extended CAN) 支援CAN協定2.0A和2.0B標準. 該CAN控制器支援最高的通訊速率為1Mbps, 可以自動地接收和傳送CAN報文, 支援使用標準ID和擴充套件ID的報文. 外設中具有3個傳送郵箱, 傳送報文的優先順序可以使用軟體控制, 還可以記錄傳送的時間;具有2個3級深度的接收FIFO, 可使用過濾功能只接收或不接收某些ID號的報文; 可設定成自動重發; 不支援使用DMA進行資料收發.
通過設定位時序暫存器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_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 是一種典型的廣播式網路, 在實際應用中, 如果只希望接收到特定型別的資料, 就要藉助過濾器來實現. 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中有兩種過濾模式, 這裡使用的是掩碼模式
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.
根據上面的設定
根據手冊 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的功能標誌位
如果
過濾條件的設定程式碼為
// 取值 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 都是5V供電, 因此開發板上要有5V輸出, 否則需要單獨供電
程式碼倉庫目錄 https://github.com/IOsetting/air32f103-template/tree/master/Examples/NonFreeRTOS/CAN
這個目錄下包含兩個模式的例子, 一個是 Loopback, 一個是 Normal, 從合宙官方倉庫的例子參考(抄)的.
Loopback 是測試模式, 傳送的資料不進入匯流排直接進入接收佇列, 用於檢查 MCU 與 CAN 收發器之間通訊是否正常, 以及 CAN 收發器是否正常工作. 執行後在串列埠輸入's', 會傳送8個位元組並將接收到的資料通過串列埠回顯.
正常的通訊模式, 需要兩套 MCU + CAN 收發器. CAN 收發器之間通過 CANH 和 CANL 連線. 程式碼中設定過濾器時, 使用的是相同的 ID 和 Mask 值, 對兩個MCU編譯燒錄時需要將 ID_TARGET 和 ID_RECEIV 的值互換一下.
執行後, 在一側串列埠輸入's', 在另一側會通過串列埠顯示接收到的資料.
在測試中, 一開始給 TJA1050 錯誤使用了 3.3V 電壓, 在 Loopback 模式工作正常, 但是在 Normal 模式工作不正常, 只有將兩邊收發器共地才能正常通訊, 如果換成 MCP2551 則完全不能通訊.
這些問題在將電壓換成 5V 後就正常了.