FPGA实现CAN通信

1、CAN物理层和协议层

CAN与串口类似,都是异步通信,利用两根差分线来进行信号的传输。

在多节点进行数据传输时主要分为遵循ISO11898标准的高速短距离闭环形式和遵循ISO11519标准的低速远距离开环网络。这两种形式主要是在硬件设计时根据实际应用情况加入120欧姆或者2.2千欧姆电阻。

在CAN通信时信号逻辑和平时常用的电平表示不太一样,根据标准电平表示形式如下图:

56d37ca3a125cd6fee66e118da2ca86a.png

27b5817626f8d58743c83548d0737378.png

CAN报文类型有5种,分别是数据帧、遥控帧、错误帧、过载帧、帧间隔。而我们常用的是数据帧,数据帧分为标准数据帧和扩展数据帧两种。数据帧结构如下图:

6e688220fe1e5b5aa9b174687d45245f.png

数据帧是以一个下降沿的电平来开始界定开始的。以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通信波特率与传输距离关系如下图:

0e120d3475a0f5b464c961dd0383829f.png

3、FPGA实现思路

在进行FPGA实现时主要是实现一个完备的状态转移状态机。在设计状态机时需要对最复杂的包格式进行设计。通过对控制段进行判断来跳转到不同类型帧格式的状态,根据数据长度来完成对数据的接收和发送。由于FPGA实现完备的CAN收发驱动并不是所有项目所必须的,因此根据不同项目来进行特定包数据的收发。

通过上面描述对CAN数据格式有了一个基本上认识,下面是通过示波器抓取CAN标准数据帧波形,示波器正连接CANH端,示波器负极连接CANL端。波特率是10Kbps,有效数据长度8字节。

f1cd3c5796bb5249e6c9dc50d8d3b918.png

CAN数据收发架构设计如下图:

9126ddfbc2b7be096d8ca25bd60cea08.png

4、FPGA实现代码

在实现波特率可调的数据收发控制时需要注意的是每个波特数据的采样点。CAN数据采样时序如下图所示,一般采样点都是在数据稳当的中间点位置,因此在设计FPGA中CAN模块的时钟频率应当是数据波特率的20倍。

266911d49d4b165a1520ddb1146e1c71.png

在以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接收状态的状态机实现如下图:

8a79c08badb395338ff48dd761b0579a.png

这个状态机的实现并不复杂,主要是对帧类型进行判断。然后根据数据长度把数据解析出来。下面是实现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<bit_flag_no)&&(can_rx==1'b0)) begin

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<can_id_data_cont)) begin

state<= state_id;

one_bit_cont<= 'b0;

bit_cont<= bit_cont + 1'b1;

end else if (one_bit_cont<bit_flag_no) begin

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<can_contral_data_cont)) begin

state<= state_control;

one_bit_cont<= 'b0;

bit_cont<= bit_cont + 1'b1;

end else if (one_bit_cont<bit_flag_no) begin

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<can_data_data_cont)) begin

state<= state_data;

one_bit_cont<= 'b0;

bit_cont<= bit_cont + 1'b1;

end else if (one_bit_cont<bit_flag_no) begin

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<can_cic_data_cont)) begin

state<= state_crc;

one_bit_cont<= 'b0;

bit_cont<= bit_cont + 1'b1;

end else if (one_bit_cont<bit_flag_no) begin

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<bit_flag_no) begin

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<bit_flag_no) begin

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进行数据的抓取。

数据发送如下图所示:

6541024b75e9471a97faf07a24753ba0.png

对上述代码测试时抓取到数据波形如下图:

5024526056ac151b600130533112842e.png

从图中可以看出接收了一包完整的标准数据帧。在通过CAN调试工具进行数据的发送测试时:CAN调试工具每秒发送60包,测试了一个小时,没有出现接收数据错误。

然而在实现CAN通信时在满足一般的代码编写的情况下有两点需要特别的注意:

1、CAN2.0的协议规定,连续5个显性/隐性电平后,要填充一位隐性/显性电平。

2、在can协议中将CAN_H和CAN_L的差值为高电平时定义为显性,逻辑上表示为0,为低电平时定义为隐形,逻辑上表示为1。

e65ee355de600c3322e1c81a0bf882b7.png

点个在看你最好看

  • 9
    点赞
  • 75
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
FPGA(Field Programmable Gate Array)是一种可编程逻辑设备,可以在硬件级别上定义和重新配置数字逻辑电路。CAN(Controller Area Network)是一种常用的网络通信协议,广泛应用于汽车和工业领域。 FPGA实现CAN通信协议的过程主要分为两个步骤:硬件设计和软件开发。 在硬件设计方面,首先需要将CAN协议的控制器部分实现FPGA中。控制器负责管理发送和接收CAN消息的过程。它包括了CAN协议的帧解析、错误检测和错误处理等功能。这些功能需要通过FPGA的逻辑电路来实现。根据CAN协议的规范,可以使用状态机设计来描述控制器的不同工作状态,并通过逻辑电路来实现状态转换和相关功能逻辑。 在软件开发方面,需要编写控制器的驱动程序。驱动程序可以通过FPGA的配置界面进行编程,与硬件部分进行通信,控制CAN消息的发送和接收。驱动程序还需提供CAN消息的解析和打包的功能,以及与其他设备(如处理器或外部存储器)进行数据传输的接口。 在实现CAN通信协议时,还需要考虑到数据的实时性和可靠性。数据的实时性是指在CAN总线上传输的数据时序要符合CAN协议要求,以确保数据的及时性和准确性。数据的可靠性则是指要进行错误检测和纠正,以防止数据传输过程中的错误。 总之,FPGA实现CAN通信协议涉及硬件和软件两个方面的设计和开发工作。通过将CAN协议的控制器部分实现FPGA中,以及编写相关的驱动程序,可以实现高性能、可靠的CAN通信功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值