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

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

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

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

圖4:USB-CAN調(diào)試器的配套軟件上觀察到的現(xiàn)象
-
FPGA
+關(guān)注
關(guān)注
1662文章
22472瀏覽量
638244 -
控制器
+關(guān)注
關(guān)注
114文章
17838瀏覽量
194709 -
CAN總線
+關(guān)注
關(guān)注
145文章
2050瀏覽量
135385 -
Xilinx
+關(guān)注
關(guān)注
73文章
2204瀏覽量
131696
原文標(biāo)題:基于?FPGA?的輕量級CAN總線控制器
文章出處:【微信號:gh_9d70b445f494,微信公眾號:FPGA設(shè)計(jì)論壇】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
基于CAN總線的家庭控制器的設(shè)計(jì)與實(shí)現(xiàn)
基于STM32和FPGA的CAN總線運(yùn)動(dòng)控制器的設(shè)計(jì)
FPGA實(shí)現(xiàn)CAN總線控制器源碼
基于FPGA和CAN控制器MCP2515設(shè)計(jì)慣導(dǎo)系統(tǒng)的CAN總線
基于FPGA的VME總線和CAN總線之間的傳輸轉(zhuǎn)換方案設(shè)計(jì)
基于FPGA的CAN總線控制器SJA1000軟核的設(shè)計(jì)方案解析
如何使用FPGA進(jìn)行CAN控制器軟核的設(shè)計(jì)與實(shí)現(xiàn)
采用CAN總線控制器SJA1000實(shí)現(xiàn)控制電路接口的設(shè)計(jì)
如何使用FPGA和CAN控制器MCP2515實(shí)現(xiàn)慣導(dǎo)系統(tǒng)的CAN總線接口的設(shè)計(jì)
如何用FPGA實(shí)現(xiàn)CAN總線通信控制器
基于FPGA的CAN總線通信節(jié)點(diǎn)設(shè)計(jì)
基于FPGA的輕量級CAN總線控制器
基于FPGA的輕量級CAN總線控制器實(shí)現(xiàn)方案
評論