1、CAN物理層和協議層
CAN與串口類似,都是異步通信,利用兩根差分線來進行信號的傳輸。
在多節點進行數據傳輸時主要分為遵循ISO11898標準的高速短距離閉環形式和遵循ISO11519標準的低速遠距離開環網絡。這兩種形式主要是在硬件設計時根據實際應用情況加入120歐姆或者2.2千歐姆電阻。
在CAN通信時信號邏輯和平時常用的電平表示不太一樣,根據標準電平表示形式如下圖:
CAN報文類型有5種,分別是數據幀、遙控幀、錯誤幀、過載幀、幀間隔。而我們常用的是數據幀,數據幀分為標準數據幀和擴展數據幀兩種。數據幀結構如下圖:
數據幀是以一個下降沿的電平來開始界定開始的。以7個連續隱性電平結束的。數據幀中間包含幀起始、仲裁段、控制段、數據段、CRC段、ACK段和幀結束段。
幀起始SOF段(Start Of Frame),幀起始信號只有一個數據位,是一個顯性電平,它用于監測數據傳輸的開始,通過電平跳變沿來進行數據起始位的確定。
仲裁段:內容是數據幀的ID信息,標準幀ID長度是11位,擴展幀為18位。CAN 協議不對掛載在它之上的節點分配優先級和地址,對總線的占有權是由幀的ID決定的,對于重要信息給與一個優先級較高的ID,這樣數據就能及時的發送出去。而ID優先級的仲裁原則是由物理層決定的,總線狀態總是顯性電平掩蓋隱形電平,因此顯性ID優先級較高。
仲裁段還包括(1)RTR 位(Remote Transmission Request Bit),譯作遠程傳輸請求位,它是用于區分數據幀和遙控幀的,當它為顯性電平時表示數據幀,隱性電平時表示遙控幀。
(2) IDE 位(Identifier Extension Bit),譯作標識符擴展位,它是用于區分標準格式與擴展格式,當它為顯性電平時表示標準格式,隱性電平時表示擴展格式。
(3) SRR 位(Substitute Remote Request Bit),只存在于擴展格式,它用于替代標準格式中的
RTR 位。由于擴展幀中的SRR 位為隱性位,RTR 在數據幀為顯性位,所以在兩個ID相同的標準格式報文與擴展格式報文中,標準格式的優先級較高。
控制段:在控制段中的r1 和r0 為保留位,默認設置為顯性位。它最主要的是DLC 段(Data
Length Code),譯為數據長度碼,它由4 個數據位組成,用于表示本報文中的數據段含有多
少個字節,DLC 段表示的數字為0~8。
數據段:數據段為數據幀的核心內容,它是節點要發送的原始信息,由0~8 個字節組成,MSB先行。
CRC 段:為了保證報文的正確傳輸,CAN 的報文包含了一段15 位的CRC 校驗碼,一旦接收節點算出的CRC 碼跟接收到的CRC 碼不同,則它會向發送節點反饋出錯信息,利用錯誤幀請求它重新發送。CRC 部分的計算一般由CAN 控制器硬件完成,出錯時的處理則由軟件控制最大重發數。
ACK 段:ACK 段包括一個ACK 槽位,和ACK 界定符位。類似I2C 總線,在ACK 槽位中,發送節點發送的是隱性位,而接收節點則在這一位中發送顯性位以示應答。在ACK 槽和幀結束之間由ACK 界定符間隔開。
幀結束EOF 段(End Of Frame),幀結束段由發送節點發送的7 個隱性位表示結束。
2、傳輸的波特率
CAN由于是異步通信,通信波特率與串口波特率定義類似,波特率的定義有每個子的長度來確定的。CAN的通信距離與波特率存在負相關關系,波特率越高,傳輸距離越短。CAN通信波特率與傳輸距離關系如下圖:
3、FPGA實現思路
在進行FPGA實現時主要是實現一個完備的狀態轉移狀態機。在設計狀態機時需要對最復雜的包格式進行設計。通過對控制段進行判斷來跳轉到不同類型幀格式的狀態,根據數據長度來完成對數據的接收和發送。由于FPGA實現完備的CAN收發驅動并不是所有項目所必須的,因此根據不同項目來進行特定包數據的收發。
通過上面描述對CAN數據格式有了一個基本上認識,下面是通過示波器抓取CAN標準數據幀波形,示波器正連接CANH端,示波器負極連接CANL端。波特率是10Kbps,有效數據長度8字節。
CAN數據收發架構設計如下圖:
4、FPGA實現代碼
在實現波特率可調的數據收發控制時需要注意的是每個波特數據的采樣點。CAN數據采樣時序如下圖所示,一般采樣點都是在數據穩當的中間點位置,因此在設計FPGA中CAN模塊的時鐘頻率應當是數據波特率的20倍。
在以10Kbps采樣率位例,CAN收發模塊時鐘設計如下:
//
//數據速率位10Kbps,一個數據位時間為10us.數據采集在5us時刻。
CAN波特率設置
reg[7:0]can_div;//500倍分頻,一個數據位分為20份
regcan_clk;
always @(posedge clk_100m or negedge rst_n )begin
if(rst_n==1'b0) begin
can_div<= 'b0;
end else if (can_div==249) begin
can_div<= 'b0;
endelse begin
can_div<= can_div + 1'b1;
end
end
always @(posedge clk_100m or negedge rst_n )begin
if(rst_n==1'b0) begin
can_clk<= 'b0;
end else if (can_div==249) begin
can_clk<= can_clk + 1'b1;
end
end
//時鐘經過BUFG緩沖
wirecan_clk_o;
BUFG can_clk_obuf (.I(can_clk), .O(can_clk_o));
數據的收發需要根據實際的項目情況進行組包控制,這里就不進行細致描述了。
CAN實現數據的收發兩個過程中對應FPGA來說由于接收相對復雜,就以接收模塊進行描述。
數據的接收過程就按照一般的狀態機進行設計就行,需要注意的是不同類型幀的跳轉是在控制段進行了,因此在控制段會發生狀態的跳轉。CAN接收狀態的狀態機實現如下圖:
這個狀態機的實現并不復雜,主要是對幀類型進行判斷。然后根據數據長度把數據解析出來。下面是實現CAN數據接收代碼:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date:
// Design Name:
// Module Name: can_rx
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module can_rx(
input wire can_clk ,
input wire rst_n ,
inputwirecan_rx,
outputregcan_ack_out_low,
outputregcan_id_out_en,
outputreg[10:0]can_id_out,
outputregcan_data_out_en,
outputreg[63:0]can_data_out
);
reg[8:0]state;
regcan_rx_t;
regerror_state;
reg[9:0]error_data;
reg[4:0]one_bit_cont;
reg[10:0]can_id;
reg[6:0]bit_cont;
regid_en_flag;
regcontral_flag;
regdata_en_flag;
regcic_en_flag;
regcan_rx_en_flag;
regcan_rx_unen_flag;
reg[4:0]can_continuity_data;
regcan_continuity_data_flag;
reg[4:0]can_id_data_cont;
reg[3:0]can_contral_data_cont;
reg[6:0]can_data_data_cont;
reg[4:0]can_cic_data_cont;
always @(posedge can_clk or negedge rst_n )begin
if(rst_n==1'b0) begin
can_rx_t<= 'b0;
end else begin
can_rx_t<= can_rx;
end
end
parameterstate_idle = 9'b000000000;//狀態機初始化
parameterstate_start = 9'b000000001;//監測到開始標志
parameterstate_sof = 9'b000000010; //開始幀頭第一位SOF
parameterstate_id = 9'b000000100; //包ID
parameterstate_control = 9'b000001000; //標準幀控制段
parameterstate_data = 9'b000010000; //數據段
parameterstate_crc = 9'b000100000; //CRC段
parameterstate_ack = 9'b001000000; //ACK段
parameterstate_eof = 9'b010000000; //幀結束段
parameterstate_end = 9'b100000000; //狀態機結束狀態
parameterbit_flag_no = 5'b10011;
always @(posedge can_clk or negedge rst_n )begin
if(rst_n==1'b0) begin
state<= 'b0;
one_bit_cont<= 'b0;
bit_cont<= 'b0;
id_en_flag<= 'b0;
contral_flag<= 'b0;
data_en_flag <= 'b0;
cic_en_flag<= 'b0;
can_rx_en_flag <= 'b0;
can_ack_out_low<= 'b1;
end else case(state)
state_idle:begin if ((can_rx_t==1'b1)&&(can_rx==1'b0))begin
state<= state_sof;
one_bit_cont<= 'b0;
can_rx_en_flag <= 'b1;
end else begin
state<= state_idle;
one_bit_cont<= 'b0;
bit_cont<= 'b0;
id_en_flag<= 'b0;
contral_flag <= 'b0;
data_en_flag<= 'b0;
cic_en_flag <= 'b0;
can_rx_en_flag <= 'b0;
can_ack_out_low<= 'b1;
end
end
state_sof:begin if ((one_bit_cont==bit_flag_no)&&(can_rx==1'b0))begin
state<= state_id;
id_en_flag<= 'b1;
one_bit_cont<= 'b0;
end else if ((one_bit_cont
state<= state_sof;
one_bit_cont<= one_bit_cont + 1'b1;
end
end
state_id:begin if ((one_bit_cont==bit_flag_no)&&(bit_cont==can_id_data_cont))begin
state<= state_control;
id_en_flag<= 'b0;
contral_flag<= 'b1;
one_bit_cont<= 'b0;
bit_cont<= 'b0;
end else if ((one_bit_cont==bit_flag_no)&&(bit_cont
state<= state_id;
one_bit_cont<= 'b0;
bit_cont<= bit_cont + 1'b1;
end else if (one_bit_cont
state<= state_id;
one_bit_cont<= one_bit_cont + 1'b1;
end else begin
state<= state_idle;
end
end
state_control:begin if ((one_bit_cont==bit_flag_no)&&(bit_cont==can_contral_data_cont))begin
state<= state_data;
contral_flag<= 'b0;
one_bit_cont<= 'b0;
bit_cont<= 'b0;
data_en_flag <= 'b1;
end else if ((one_bit_cont==bit_flag_no)&&(bit_cont
state<= state_control;
one_bit_cont<= 'b0;
bit_cont<= bit_cont + 1'b1;
end else if (one_bit_cont
state<= state_control;
one_bit_cont<= one_bit_cont + 1'b1;
end else begin
state<= state_idle;
end
end
state_data:begin if ((one_bit_cont==bit_flag_no)&&(bit_cont==can_data_data_cont))begin
state<= state_crc;
one_bit_cont<= 'b0;
bit_cont<= 'b0;
data_en_flag <= 'b0;
cic_en_flag<= 'b1;
end else if ((one_bit_cont==bit_flag_no)&&(bit_cont
state<= state_data;
one_bit_cont<= 'b0;
bit_cont<= bit_cont + 1'b1;
end else if (one_bit_cont
state<= state_data;
one_bit_cont<= one_bit_cont + 1'b1;
end else begin
state<= state_idle;
end
end
state_crc:begin if ((one_bit_cont==bit_flag_no)&&(bit_cont==can_cic_data_cont))begin
state<= state_ack;
can_ack_out_low<= 'b0;
one_bit_cont<= 'b0;
bit_cont<= 'b0;
cic_en_flag<= 'b0;
end else if ((one_bit_cont==bit_flag_no)&&(bit_cont
state<= state_crc;
one_bit_cont<= 'b0;
bit_cont<= bit_cont + 1'b1;
end else if (one_bit_cont
state<= state_crc;
one_bit_cont<= one_bit_cont + 1'b1;
end else begin
state<= state_idle;
end
end
state_ack:begin if ((one_bit_cont==bit_flag_no)&&(bit_cont==1))begin
state<= state_eof;
can_ack_out_low<= 'b1;
one_bit_cont<= 'b0;
bit_cont<= 'b0;
end else if ((one_bit_cont==bit_flag_no)&&(bit_cont<1)) begin
state<= state_ack;
one_bit_cont<= 'b0;
bit_cont<= bit_cont + 1'b1;
end else if (one_bit_cont
state<= state_ack;
one_bit_cont<= one_bit_cont + 1'b1;
end else begin
state<= state_idle;
end
end
state_eof:begin if ((one_bit_cont==bit_flag_no)&&(bit_cont==5))begin
state<= state_end;
one_bit_cont<= 'b0;
bit_cont<= 'b0;
end else if ((one_bit_cont==bit_flag_no)&&(bit_cont<5)) begin
state<= state_eof;
one_bit_cont<= 'b0;
bit_cont<= bit_cont + 1'b1;
end else if (one_bit_cont
state<= state_eof;
one_bit_cont<= one_bit_cont + 1'b1;
end else begin
state<= state_idle;
end
end
state_end:begin
state<= state_idle;
one_bit_cont<= 'b0;
bit_cont<= 'b0;
id_en_flag<= 'b0;
contral_flag <= 'b0;
data_en_flag<= 'b0;
cic_en_flag <= 'b0;
can_rx_en_flag <= 'b0;
can_ack_out_low<= 'b1;
end
default:begin
state<= state_idle;
one_bit_cont<= 'b0;
bit_cont<= 'b0;
id_en_flag<= 'b0;
contral_flag <= 'b0;
data_en_flag<= 'b0;
cic_en_flag <= 'b0;
can_rx_en_flag <= 'b0;
can_ack_out_low<= 'b1;
end
endcase
end
//always @(posedge can_clk or negedge rst_n )begin
//if(rst_n==1'b0) begin
//error_data<= 'b0;
//end else if (can_rx_en_flag==1) begin
//error_data<= {error_data[9:0],can_rx};
//endelse if (one_bit_cont==11) begin
//can_rx_unen_flag<= 1'b0;
//end else if (can_rx_en_flag==0)
//can_rx_unen_flag<= 'b0;
//end
always @(posedge can_clk or negedge rst_n )begin
if(rst_n==1'b0) begin
can_continuity_data<= 5'b11111;
can_continuity_data_flag<= 'b0;
end else if ((one_bit_cont==9)&&(can_rx_en_flag==1)) begin
can_continuity_data<= {can_continuity_data[3:0],can_rx};
can_continuity_data_flag<= 'b0;
endelse if (((can_continuity_data==0)||(can_continuity_data==5'b11111))&&(one_bit_cont==10)&&(cic_en_flag==0)) begin
can_continuity_data_flag<= 'b1;
end else if (((can_continuity_data==0)||(can_continuity_data==5'b11111))&&(one_bit_cont==10)&&(cic_en_flag==1)&&(bit_cont<14)) begin
can_continuity_data_flag<= 'b1;
end else if (can_rx_en_flag==0) begin
can_continuity_data<= 5'b11111;
can_continuity_data_flag<= 'b0;
end else begin
can_continuity_data_flag<= 'b0;
end
end
always @(posedge can_clk or negedge rst_n )begin
if(rst_n==1'b0) begin
can_rx_unen_flag<= 'b0;
end else if ((can_rx_en_flag==1)&&(can_continuity_data_flag==1)&&(cic_en_flag==0)) begin
can_rx_unen_flag<= 1'b1;
end else if ((can_rx_en_flag==1)&&(can_continuity_data_flag==1)&&(cic_en_flag==1)&&(bit_cont<14)) begin
can_rx_unen_flag<= 1'b1;
end else if (one_bit_cont==11) begin
can_rx_unen_flag<= 1'b0;
end else if (can_rx_en_flag==0)
can_rx_unen_flag<= 'b0;
end
always @(posedge can_clk or negedge rst_n )begin
if(rst_n==1'b0) begin
can_id_data_cont<= 'd10;
end else if ((id_en_flag==1)&&(can_continuity_data_flag==1)) begin
can_id_data_cont<= can_id_data_cont+ 1'b1;
endelse if (id_en_flag==0)
can_id_data_cont<= 'd10;
end
always @(posedge can_clk or negedge rst_n )begin
if(rst_n==1'b0) begin
can_contral_data_cont<= 'd6;
end else if ((contral_flag==1)&&(can_continuity_data_flag==1)) begin
can_contral_data_cont<= can_contral_data_cont+ 1'b1;
endelse if (contral_flag==0)
can_contral_data_cont<= 'd6;
end
always @(posedge can_clk or negedge rst_n )begin
if(rst_n==1'b0) begin
can_data_data_cont<= 'd63;
end else if ((data_en_flag==1)&&(can_continuity_data_flag==1)) begin
can_data_data_cont<= can_data_data_cont+ 1'b1;
endelse if (data_en_flag==0)
can_data_data_cont<= 'd63;
end
always @(posedge can_clk or negedge rst_n )begin
if(rst_n==1'b0) begin
can_cic_data_cont<= 'd15;
end else if ((cic_en_flag==1)&&(can_continuity_data_flag==1)) begin
can_cic_data_cont<= can_cic_data_cont+ 1'b1;
end else if (cic_en_flag==0)
can_cic_data_cont<= 'd15;
end
///
always @(posedge can_clk or negedge rst_n )begin
if(rst_n==1'b0) begin
can_id_out<= 'b0;
end else if ((one_bit_cont==9)&&(id_en_flag==1)&&(can_rx_unen_flag==0)) begin
can_id_out<= {can_id_out[9:0],can_rx};
end
end
always @(posedge can_clk or negedge rst_n )begin
if(rst_n==1'b0) begin
can_id_out_en<= 'b0;
end else if ((one_bit_cont==9)&&(bit_cont==can_id_data_cont)&&(id_en_flag==1)) begin
can_id_out_en<= 1'b1;
endelse
can_id_out_en<= 'b0;
end
always @(posedge can_clk or negedge rst_n )begin
if(rst_n==1'b0) begin
can_data_out<= 'b0;
end else if ((one_bit_cont==9)&&(data_en_flag==1)&&(can_rx_unen_flag==0)) begin
can_data_out<= {can_data_out[62:0],can_rx};
end
end
always @(posedge can_clk or negedge rst_n )begin
if(rst_n==1'b0) begin
can_data_out_en<= 'b0;
end else if ((bit_cont==can_data_data_cont)&&(one_bit_cont==9)&&(data_en_flag==1)) begin
can_data_out_en<= 1'b1;
endelse
can_data_out_en<= 'b0;
end
/
endmodule
在完成上述代碼編寫后通過CAN收發器發送數據然后利用chipscop進行數據的抓取。
數據發送如下圖所示:
對上述代碼測試時抓取到數據波形如下圖:
從圖中可以看出接收了一包完整的標準數據幀。在通過CAN調試工具進行數據的發送測試時:CAN調試工具每秒發送60包,測試了一個小時,沒有出現接收數據錯誤。
然而在實現CAN通信時在滿足一般的代碼編寫的情況下有兩點需要特別的注意:
1、CAN2.0的協議規定,連續5個顯性/隱性電平后,要填充一位隱性/顯性電平。
2、在can協議中將CAN_H和CAN_L的差值為高電平時定義為顯性,邏輯上表示為0,為低電平時定義為隱形,邏輯上表示為1。
原文標題:FPGA實現CAN通信
文章出處:【微信公眾號:FPGA設計論壇】歡迎添加關注!文章轉載請注明出處。
-
FPGA
+關注
關注
1630文章
21802瀏覽量
606384 -
CAN
+關注
關注
57文章
2774瀏覽量
464469 -
物理層
+關注
關注
1文章
152瀏覽量
34564
原文標題:FPGA實現CAN通信
文章出處:【微信號:gh_9d70b445f494,微信公眾號:FPGA設計論壇】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
Labview用在通信物理層可以做那些事?
CAN總線不同的物理層
如何對CAN物理層進行調試?
從物理層和協議層兩方面來了解I2C總線
CSA7000系列通信信號分析儀在網絡通信物理層測試領域中的
WLAN物理層關鍵通信技術
![WLAN<b class='flag-5'>物理層</b>關鍵<b class='flag-5'>通信</b>技術](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
評論