簡介
CAN總線作為工業和汽車領域最常用的通信總線,具有拓撲結構簡潔、可靠性高、傳輸距離長等優點。CAN總線的非破壞性仲裁機制依賴于幀ID,CAN2.0A和CAN2.0B分別規定了11bit-ID(短ID)的標準幀和29bit-ID(長ID)的擴展幀,另外,還有遠程幀這種數據請求機制。關于CAN總線的更多知識可以參考這個科普文章。
本庫實現了一個輕量化但完備的FPGA CAN總線控制器,特點如下:
平臺無關:純 Verilog 編寫,可以在 Altera 和 Xilinx 等各種 FPGA 上運行。
本地ID可固定配置為任意短ID。
發送: 僅支持以本地ID發送數據長度為4Byte的幀。
接收: 支持接收短ID或長ID的幀,接收幀的數據長度沒有限制 (即支持0~8Byte) 。
接收幀過濾: 可針對短ID和長ID獨立設置過濾器,只接收和過濾器匹配的數據幀。
自動響應遠程幀: 當收到的遠程幀與本地ID匹配時,自動將發送緩存中的下一個數據發送出去。若緩存為空,則重復發送上次發過的數據。
設計文件
RTL 文件夾包含3個設計代碼文件,各文件功能如下表。你只需將這3個文件包含進工程,就可以調用頂層模塊can_top.v進行CAN通信業務的開發。
| 文件名 | 功能 | 備注 |
|---|---|---|
| can_top.v | CAN控制器的頂層 | |
| can_level_packet.v | 幀級控制器,負責解析或生成幀,并實現非破壞性仲裁 | 被can_top.v調用 |
| can_level_bit.v | 位級控制器,負責收發bit,具有抗頻率偏移的下降沿對齊機制 | 被can_level_packet.v調用 |
仿真文件
仿真相關的文件都在 SIM 文件夾中,其中:
tb_can_top.v 是針對 can_top.v 的 testbench 。
tb_can_top_run_iverilog.bat 包含了運行 iverilog 仿真的命令。
tb_can_top.v 描述了4個CAN總線設備互相進行通信的場景,每個設備都是一個 can_top 的例化,圖1是每個設備的詳細屬性,各個設備互相接收的關系可以畫成左側的圖,箭頭代表了各個設備之間的接收關系。另外,每個CAN設備的驅動時鐘并不嚴格是50MHz,而是有不同的±1%的偏移,這是為了模擬更糟糕的實際情況下,CAN控制器的“自動對齊”機制能否奏效。

圖1:仿真中的4個CAN設備的詳細參數
使用 iverilog 進行仿真前,需要安裝 iverilog ,見:iverilog_usage
然后雙擊 tb_can_top_run_iverilog.bat 運行仿真。仿真運行完后,可以打開生成的 dump.vcd 文件查看波形。
頂層模塊說明
本節介紹如何使用can_top.v,它的接口如下表。
| 信號名 | 方向 | 寬度 | 功能 | 備注 |
|---|---|---|---|---|
| rstn | 輸入 | 1 | 低電平復位 | 在開始工作前需要拉低復位一下 |
| clk | 輸入 | 1 | 驅動時鐘 | 頻率需要是CAN總線波特率的10倍以上,內部分頻產生波特率 |
| can_rx | 輸入 | 1 | CAN-PHY RX | 應通過FPGA的普通IO引出,接CAN-PHY芯片 (例如TJA1050) |
| can_tx | 輸出 | 1 | CAN-PHY TX | 應通過FPGA的普通IO引出,接CAN-PHY芯片 (例如TJA1050) |
| tx_valid | 輸入 | 1 | 發送有效 | 當=1時,若發送緩存未滿(即tx_ready=1),則tx_data被送入發送緩存 |
| tx_ready | 輸出 | 1 | 發送就緒 | 當=1時,說明發送緩存未滿。與 tx_valid 構成一對握手信號 |
| tx_data | 輸入 | 32 | 發送數據 | 當tx_valid=1時需要同步給出待發送數據 tx_data |
| rx_valid | 輸出 | 1 | 接收有效 | 當=1時,rx_data上產生1字節的有效接收數據 |
| rx_last | 輸出 | 1 | 接收最后字節指示 | 當=1時,說明當前的rx_data是一個幀的最后一個數據字節 |
| rx_data | 輸出 | 8 | 接收數據 | 當rx_valid=1時,rx_data上產生1字節的有效接收數據 |
| rx_id | 輸出 | 29 | 接收ID | 指示當前接收幀的ID,若為短ID則低11bit有效 |
| rx_ide | 輸出 | 1 | 接收ID類型 | =1 說明當前接收幀是長ID,否則為短ID |
接入CAN總線
can_top.v的can_rx和can_tx接口需要引出到FPGA引腳上,并接CAN-PHY芯片(比如TJA1050),如圖2。

圖2:接入CAN總線的方式
注:這里注意一個坑,雖然FPGA的引腳(can_rx,can_tx)可以是3.3V電平的,但CAN-PHY的電源必須是5V的,否則對CAN總線的驅動力不夠。另外,CAN-PHY要和FPGA共地。
用戶發送接口
can_top.v的tx_valid,tx_ready,tx_data構成了流式輸入接口,它們都與clk的上升沿對齊,用于向發送緩存中寫入一個數據。只要發送緩沖區不為空,其中的數據會逐個被CAN控制器發送到CAN總線上。
tx_valid和tx_ready是一對握手信號,波形如下圖,只有當tx_valid和tx_ready都為1時,tx_data才被寫入緩存。tx_ready=0說明緩存已滿,此時即使tx_valid=1,也無法寫入緩存。不過,當發送頻率不高而不至于讓 CAN 總線達到飽和時,可以不用考慮緩存滿(即tx_ready=0)的情況。
下圖中,D0,D1,D2這3個數據被寫入緩存,D0寫入后,緩存已滿,導致tx_ready=0,之后的3個周期D1都沒有成功寫入,但在第4個時鐘周期tx_ready變成1,D1被寫入。之后發送方主動空閑2個時鐘周期后,D3也被寫入。
每個數據都是4Byte(32bit)的,只要FIFO不為空,該CAN控制器就自動地每次發送一個幀,每幀一個數據,幀數據長度為4Byte。
_ __ __ __ __ __ __ __ __ __ __ __ clk \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ _____________________________ _____tx_valid ___________/ \___________/ \________ _________________ ________________________________tx_ready \_________________/ _____ _______________________ _____tx_data XXXXXXXXXXXX__D0_X___________D1__________XXXXXXXXXXXXX__D3_XXXXXXXXX
用戶接收接口
can_top.v的rx_valid,rx_last,rx_data,rx_id,rx_ide構成了接收接口,它們都與clk的上升沿對齊。
當CAN總線上收到了一個與ID過濾器匹配的數據幀后,會將這一幀的字節逐個發送出來。設數據幀長為n字節,則rx_valid上會連續產生n個周期的高電平,同時rx_data上每拍時鐘會產生一個收到的數據字節,在最后一拍會讓rx_last=1,指示一幀的結束。在整個過程中,rx_id上出現該幀的ID(若為短ID,則只有低11bit有效),同時,rx_ide指示該幀為長ID還是短ID。
接收接口的波形圖舉例如下,該例中模塊先后收到了一個短ID的,數據長度為4的數據幀,和一個長ID的,數據長度為2的數據幀。
__ __ __ __ __ __ __ __ __ __ __ clk __/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \_ _______________________ ___________rx_valid _________/ \___________/ \_________ _____ _____rx_last ___________________________/ \_________________/ \_________ _____ _____ _____ _____ _____ _____rx_data XXXXXXXXXX__0__X__1__X__2__X__3__XXXXXXXXXXXXX__0__X__1__XXXXXXXXXX _______________________ ___________rx_id XXXXXXXXXX__________ID1__________XXXXXXXXXXXXX____ID2____XXXXXXXXXX _____________________rx_ide _____________________________________________/
與發送接口不同,接收接口無握手機制,只要收到數據就讓rx_valid=1來發出,不會等待接收方是否能接受。
配置本地ID
can_top.v有一個11bit的參數(parameter)叫LOCAL_ID,它決定了該模塊發送的幀的ID;同時,當can_top接收的遠程幀的ID與LOCAL_ID匹配時,就會進行響應(ACK),并自動回復一個數據幀(如果發送緩存為空,則自動重復回復上次發過的數據)。
配置ID過濾器
can_top.v的RX_ID_SHORT_FILTER和RX_ID_SHORT_MASK參數用來配置短ID過濾器。設收到的數據幀的ID是rxid(短),匹配表達式為:
rxid & RX_ID_SHORT_MASK == RX_ID_SHORT_FILTER & RX_ID_SHORT_MASK
以上表達式滿足 Verilog 或 C 語言的語法。表達式為真時,rxid與過濾器匹配,這樣的數據幀才能被模塊響應(ACK),并將其數據轉發到用戶接受接口上。表達式為假時,rxid與過濾器不匹配,該數據幀不僅不會被響應(ACK),也不會被轉發到用戶接受接口上。
同理,RX_ID_LONG_FILTER和RX_ID_LONG_MASK參數用來配置長ID過濾器。設收到的數據幀的ID是rxid(長),匹配表達式為:
rxid & RX_ID_LONG_MASK == RX_ID_LONG_FILTER & RX_ID_LONG_MASK
MASK參數可以被稱為通配掩碼,掩碼=1的位必須匹配FILTER,掩碼=0的位我們不在乎,既可以匹配1也可以匹配0。
例如你想接收 0x122, 0x123, 0x1a2, 0x1a3 這4種短ID,則可配置為:
RX_ID_SHORT_FILTER = 11'h122,RX_ID_SHORT_MASK = 11'h17e
配置時序參數
can_top.v的default_c_PTS,default_c_PBS1,default_c_PBS2這3個時序參數決定了CAN總線上的一個位的3個段(PTS段, PBS1段, PBS2段)的默認長度,這3個段的含義詳見這個科普文章。總的來說,分頻系數計算如下:
分頻系數 = default_c_PTS + default_c_PBS1 + default_c_PBS2 + 1
而CAN總線的波特率計算方法為:
CAN波特率 = clk頻率 / 分頻系數
例如,在 clk=50MHz的情況下,可以使用如下參數組合來配置出各種常見的波特率。
| 分頻系數 | 波特率 | default_c_PTS | default_c_PBS1 | default_c_PBS2 |
|---|---|---|---|---|
| 50 | 1MHz | 16'd34 | 16'd5 | 16'd10 |
| 100 | 500kHz | 16'd69 | 16'd10 | 16'd20 |
| 500 | 100kHz | 16'd349 | 16'd50 | 16'd100 |
| 5000 | 10kHz | 16'd3499 | 16'd500 | 16'd1000 |
| 10000 | 5kHz | 16'd6999 | 16'd1000 | 16'd2000 |
示例程序
FPGA 目錄里的fpga_top.v是一個調用can_top.v進行簡單的 CAN 通信的案例。要運行該案例,請建立 FPGA 工程,并加入以下源文件:
FPGA 目錄里的 fpga_top.v 、 uart_tx.v 。
RTL 目錄里的 can_top.v 、 can_level_packet.v 、 can_level_bit.v 。
fpga_top.v 是項目的頂層,其引腳連接方法如下:
module fpga_top ( // clock ,連接到 FPGA 板上晶振,頻率必須為 50MHz input wire CLK50M, // UART (TX only), 連接到電腦串口(比如通過 USB 轉 UART 模塊),不方便接 UART 可以不接 outputwire UART_TX, // CAN bus, 連接到 CAN PHY 芯片,然后 CAN PHY 連接到 CAN 總線(如圖2) input wire CAN_RX, outputwire CAN_TX);
該案例的行為是:
本地 (也就是 FPGA) 的 CAN ID 配置為 0x456 ,因此所有發出的數據幀的 ID 都為 0x456 。
ID 過濾器配置為只接收短ID=0x123或長ID=0x12345678的數據幀。
每一秒向發送緩存中送入一個遞增的數據,該數據幀并會被發送到 CAN 總線上。
將 CAN 總線上接收到的(也就是符合過濾器配置)的數據幀通過 UART 發送給電腦。
注:該案例中,CAN 的波特率為 1MHz ; UART 的格式為 115200,8,n,1

圖3:硬件連接
我在測試該例子時,將 CAN 總線與一臺USB-CAN調試器相連,如圖3。然后編譯工程并下載FPGA,打開USB-CAN調試器的配套軟件,可以看到如下現象:
軟件中每秒會收到一個 FPGA 發來的幀,數據長度DLC=4,值遞增。如圖4中沒框的部分。
在軟件中發送短ID=0x123或長ID=0x12345678的數據幀,會顯示“發送成功”,如圖4中藍框的部分,說明該幀被 FPGA 響應了。同時,如果你把FPGA的UART連接到了電腦的串口,則可以在“串口助手”或“HyperTerminal”等軟件上看到打印出的數據內容。
在軟件中發送短ID=0x456的遠程幀,FPGA 會立即響應一個數據幀,如圖4中紅框的部分。

圖4:USB-CAN調試器的配套軟件上觀察到的現象
-
FPGA
+關注
關注
1662文章
22476瀏覽量
638365 -
控制器
+關注
關注
114文章
17845瀏覽量
194744 -
CAN總線
+關注
關注
145文章
2050瀏覽量
135418 -
Xilinx
+關注
關注
73文章
2204瀏覽量
131712
原文標題:基于?FPGA?的輕量級CAN總線控制器
文章出處:【微信號:gh_9d70b445f494,微信公眾號:FPGA設計論壇】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
基于STM32和FPGA的CAN總線運動控制器的設計
基于FPGA和CAN控制器MCP2515設計慣導系統的CAN總線
基于FPGA的VME總線和CAN總線之間的傳輸轉換方案設計
基于FPGA的CAN總線控制器SJA1000軟核的設計方案解析
如何使用FPGA進行CAN控制器軟核的設計與實現
如何使用FPGA和CAN控制器MCP2515實現慣導系統的CAN總線接口的設計
基于FPGA的輕量級CAN總線控制器實現方案
評論