CAN 是一種典型的廣播式網路, 在實際應用中, 如果只希望接收到特定類型的數據, 就要藉助過濾器來實現. AIR32/STM32的CAN控制器包含14個過濾器, 可以設置為 屏蔽模式 或 列表模式 對CAN匯流排上的報文進行過濾. 當節點希望接收到一種報文時, 可以用屏蔽位模式進行過濾, ...
目錄
- AIR32F103(一) 合宙AIR32F103CBT6開發板上手報告
- AIR32F103(二) Linux環境和LibOpenCM3項目模板
- AIR32F103(三) Linux環境基於標準外設庫的項目模板
- AIR32F103(四) 27倍頻216MHz,CoreMark跑分測試
- AIR32F103(五) FreeRTOSv202112核心庫的集成和示例代碼
- AIR32F103(六) ADC,I2S,DMA和ADPCM實現的錄音播放功能
- AIR32F103(七) AIR32F103CBT6/CCT6啟用96K記憶體
- AIR32F103(八) 集成Helix MP3解碼庫播放MP3
- AIR32F103(九) CAN匯流排的通信和ID過濾機制及實例
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.
- 這個例子中僅使用標準ID, 不使用擴展ID時, 低5位可以忽略
- 設置ID和Mask時, 都要將需要設置的ID值(0x103)左移5位, 因為低5位用於extId
- 與接收的ID對比時, 會根據Mask中bit值為1的位, 與ID中的對應bit進行比較, Mask中bit為0的位都會忽略. 這個例子中需要比較的位是0, 1 和 8
- 接收到的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 後就正常了.
參考
- CAN Protocol in STM32 https://controllerstech.com/can-protocol-in-stm32/
- Does The CAN Bus Need a Common Ground? https://www.edn.com/does-the-can-bus-need-a-common-ground/
- STM32 CAN Communication || NORMAL Mode https://www.youtube.com/watch?v=KHNRftBa1Vc&t=104s