基于FPGA实现CAN通信的设计方案

时间:2025-07-25  作者:Diven  阅读:0
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 段(DataLength 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 )beginif(rst_n==1'b0) begincan_div<= 'b0;end else if (can_div==249) begincan_div<= 'b0;endelse  begin can_div<= can_div + 1'b1;endend

 

基于FPGA实现CAN通信的设计方案

always @(posedge clk_100m or negedge rst_n )beginif(rst_n==1'b0) begincan_clk<= 'b0;end else if (can_div==249) begincan_clk<= can_clk + 1'b1;endend //时钟经过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 )beginif(rst_n==1'b0) begincan_rx_t<= 'b0;end else  begincan_rx_t<= can_rx;endend   parameterstate_idle = 9'b000000000;//状态机初始化parameterstate_start = 9'b000000001;//监测到开始标志parameterstate_sof = 9'b000000010;           //开始帧头第一位SOFparameterstate_id = 9'b000000100;           //包IDparameterstate_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 )beginif(rst_n==1'b0) beginstate<= '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))beginstate<= state_sof;one_bit_cont<= 'b0;can_rx_en_flag              <= 'b1;end else beginstate<= 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))beginstate<= state_id;id_en_flag<= 'b1;one_bit_cont<= 'b0;end else if ((one_bit_contstate<= 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))beginstate<= 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_contstate<= state_id;one_bit_cont<= 'b0;bit_cont<= bit_cont + 1'b1;end else if (one_bit_contstate<= state_id;one_bit_cont<= one_bit_cont + 1'b1;end else beginstate<= state_idle;end end state_control:begin if ((one_bit_cont==bit_flag_no)&&(bit_cont==can_contral_data_cont))beginstate<= 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_contstate<= state_control;one_bit_cont<= 'b0;bit_cont<= bit_cont + 1'b1;end else if (one_bit_contstate<= state_control;one_bit_cont<= one_bit_cont + 1'b1;end else beginstate<= state_idle;end end state_data:begin if ((one_bit_cont==bit_flag_no)&&(bit_cont==can_data_data_cont))beginstate<= 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_contstate<= state_data;one_bit_cont<= 'b0;bit_cont<= bit_cont + 1'b1;end else if (one_bit_contstate<= state_data;one_bit_cont<= one_bit_cont + 1'b1;end else beginstate<= state_idle;end end state_crc:begin if ((one_bit_cont==bit_flag_no)&&(bit_cont==can_cic_data_cont))beginstate<= 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_contstate<= state_crc;one_bit_cont<= 'b0;bit_cont<= bit_cont + 1'b1;end else if (one_bit_contstate<= state_crc;one_bit_cont<= one_bit_cont + 1'b1;end else beginstate<= state_idle;end end state_ack:begin if ((one_bit_cont==bit_flag_no)&&(bit_cont==1))beginstate<= 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)) beginstate<= state_ack;one_bit_cont<= 'b0;bit_cont<= bit_cont + 1'b1;end else if (one_bit_contstate<= state_ack;one_bit_cont<= one_bit_cont + 1'b1;end else beginstate<= state_idle;end end state_eof:begin if ((one_bit_cont==bit_flag_no)&&(bit_cont==5))beginstate<= state_end;one_bit_cont<= 'b0;bit_cont<= 'b0;end else if ((one_bit_cont==bit_flag_no)&&(bit_cont<5)) beginstate<= state_eof;one_bit_cont<= 'b0;bit_cont<= bit_cont + 1'b1;end else if (one_bit_contstate<= state_eof;one_bit_cont<= one_bit_cont + 1'b1;end else beginstate<= 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  endcaseend  //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 )beginif(rst_n==1'b0) begincan_continuity_data<= 5'b11111;can_continuity_data_flag<= 'b0;end else if ((one_bit_cont==9)&&(can_rx_en_flag==1)) begincan_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)) begincan_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)) begincan_continuity_data_flag<= 'b1;end else if (can_rx_en_flag==0) begincan_continuity_data<= 5'b11111;can_continuity_data_flag<= 'b0;end else begincan_continuity_data_flag<= 'b0;endend    always @(posedge can_clk or negedge rst_n )beginif(rst_n==1'b0) begincan_rx_unen_flag<= 'b0;end else if ((can_rx_en_flag==1)&&(can_continuity_data_flag==1)&&(cic_en_flag==0)) begincan_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)) begincan_rx_unen_flag<= 1'b1;end else if (one_bit_cont==11) begincan_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 )beginif(rst_n==1'b0) begincan_id_data_cont<= 'd10;end else if ((id_en_flag==1)&&(can_continuity_data_flag==1)) begincan_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 )beginif(rst_n==1'b0) begincan_contral_data_cont<= 'd6;end else if ((contral_flag==1)&&(can_continuity_data_flag==1)) begincan_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 )beginif(rst_n==1'b0) begincan_data_data_cont<= 'd63;end else if ((data_en_flag==1)&&(can_continuity_data_flag==1)) begincan_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 )beginif(rst_n==1'b0) begincan_cic_data_cont<= 'd15;end else if ((cic_en_flag==1)&&(can_continuity_data_flag==1)) begincan_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 )beginif(rst_n==1'b0) begincan_id_out<= 'b0;end else if ((one_bit_cont==9)&&(id_en_flag==1)&&(can_rx_unen_flag==0)) begincan_id_out<= {can_id_out[9:0],can_rx};endend  always @(posedge can_clk or negedge rst_n )beginif(rst_n==1'b0) begincan_id_out_en<= 'b0;end else if ((one_bit_cont==9)&&(bit_cont==can_id_data_cont)&&(id_en_flag==1)) begincan_id_out_en<= 1'b1;endelse can_id_out_en<= 'b0;end   always @(posedge can_clk or negedge rst_n )beginif(rst_n==1'b0) begincan_data_out<= 'b0;end else if ((one_bit_cont==9)&&(data_en_flag==1)&&(can_rx_unen_flag==0)) begincan_data_out<= {can_data_out[62:0],can_rx};endend  always @(posedge can_clk or negedge rst_n )beginif(rst_n==1'b0) begincan_data_out_en<= 'b0;end else if ((bit_cont==can_data_data_cont)&&(one_bit_cont==9)&&(data_en_flag==1)) begincan_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。编辑:黄飞
猜您喜欢

电子学中,二极管是重要的半导体器件,具有单向导电的特性。近年来,"二极管"这一词在网络和社交媒体中逐渐被赋予了新的含义,特别是在形容某些人的...
2025-04-01 11:00:03

贴片电阻上通常印有3位或4位数字代码,用来表示阻值。解读这些代码需要用到EIA-96标准码表。三位数代码: 前两位数字表示有效数字,第三位数字表示10的幂次方。...
2024-11-29 10:26:02

试管是常见的实验室器具,应用于化学、生物等领域。通常由玻璃或塑料制成,具有圆柱形的结构,底部封闭,便于盛装液体和进行实验操作。试管的容量多样,从几毫升到数百毫升...
2009-06-20 00:00:00

薄膜电阻作为关键的电子元件,应用于各种电路设计和电子设备中。SEI(世达柏科技)作为一家专业的薄膜电阻制造商,高品质的产品和技术创新赢得了业界的认可。市场上薄膜...
2014-08-31 16:29:13

PTC热敏电阻作为重要的温度保护元件,受到了越来越多的关注。紫泰荆作为国内知名的PTC热敏电阻制造品牌,其产品以性能稳定、质量可靠,应用于家电、通讯、电源等领域...
2019-07-30 22:07:30

光敏电阻作为重要的光电元件,应用于各种智能设备和自动化系统中。四川永星作为一家专业生产光敏电阻的企业,其产品以品质优良和技术先进闻名。那么,四川永星光敏电阻具体...
2023-11-19 01:32:20

贴片电阻1206 2.2R±5%的丝印通常遵循EIA-96标准。1206表示电阻的尺寸,即长1.2英寸,宽0.6英寸(实际尺寸约为3.2mm x 1.6mm)。...
2025-04-14 15:04:01

DSBGA9是新兴的技术,近年来在多个行业中引起了广泛关注。它不仅在电子产品的设计和制造中发挥着重要作用,还在软件开发、数据处理等领域展现出其独特的优势。本文将...
2025-02-21 14:03:38

贴片排阻作为电子元器件中的重要组成部分,应用于各种电子设备中。SEI(世达柏科技)作为知名的电子元器件供应商,提供多种类型的贴片排阻产品,满足不同客户的需求。本...
2016-12-01 05:56:30

现在我们对比一下CPLD和FPGA的主要特性,提供一个简单参考,帮助确定某一设计适合采用哪类器件。首先,对比一下硬件,如表1.2所示。表1.2 FPGA与CPL...
2018-04-17 17:08:00