0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

CAN通信物理层和协议层简介

FPGA设计论坛 来源:CSDN技术社区 作者:撕裂的牛仔裤 2022-06-09 16:27 次阅读

1、CAN物理层和协议层

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

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

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

b722c414-e7a9-11ec-ba43-dac502259ad0.png

b7434810-e7a9-11ec-ba43-dac502259ad0.png

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

b76ab81e-e7a9-11ec-ba43-dac502259ad0.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通信波特率与传输距离关系如下图:

b79623c8-e7a9-11ec-ba43-dac502259ad0.png

3、FPGA实现思路

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

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

b7b8da26-e7a9-11ec-ba43-dac502259ad0.png

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

b8069f2c-e7a9-11ec-ba43-dac502259ad0.png

4、FPGA实现代码

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

b8529d50-e7a9-11ec-ba43-dac502259ad0.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接收状态的状态机实现如下图:

b8804cd2-e7a9-11ec-ba43-dac502259ad0.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

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

数据发送如下图所示:

b8a0f680-e7a9-11ec-ba43-dac502259ad0.png

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

b8c4a0f8-e7a9-11ec-ba43-dac502259ad0.png

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

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

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

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

原文标题:FPGA实现CAN通信

文章出处:【微信公众号:FPGA设计论坛】欢迎添加关注!文章转载请注明出处。

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FPGA
    +关注

    关注

    1598

    文章

    21261

    浏览量

    592731
  • CAN
    CAN
    +关注

    关注

    56

    文章

    2450

    浏览量

    458954
  • 物理层
    +关注

    关注

    1

    文章

    142

    浏览量

    34204

原文标题:FPGA实现CAN通信

文章出处:【微信号:gh_9d70b445f494,微信公众号:FPGA设计论坛】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    【社招】DSP软件工程师(物理层算法实现方向)

    TI的C64系列或CEVA等处理器一年以上工作经验者优先; 4、有无线通信物理层软件工作经验者,如开发过GSM, CDMA, Wimax或LTE等无线通信系统的物理层软件者优先。 联系人:tina邮箱:tina.liu@yong
    发表于 09-12 18:23

    Labview用在通信物理层可以做那些事?

    本人小菜鸟,刚开始接触Labview,想知道Labview用在通信物理层可以做那些事,可以实现那些功能,还望各位大神不吝赐教!
    发表于 10-31 15:35

    CAN总线不同的物理层

    CAN总线使用不归零(NRZ)的位填充。有两种不同的信令状态:显性(逻辑0)和隐性(逻辑1)。这些信令状态对应于所在物理层(存在几种不同的物理层)的某种电平。模块以线与逻辑连接到总线:哪怕只有一个节点发送逻辑0使得总线处于显性状
    发表于 05-23 07:35

    什么是以太网物理层?Ethernet物理层有哪些功能?

    什么是以太网物理层Ethernet物理层有哪些功能基于MDI,为您的系统选择合适的以太网物理层TI以太网物理层选择流程图
    发表于 03-18 08:07

    如何对CAN物理层进行调试?

    本文为您介绍一种对CAN物理层进行调试的较好工程方法。我们将介绍基础调试步骤,并说明一个CAN物理层应有的性能,以及找出问题的一些小技巧。
    发表于 04-19 08:02

    IIC物理层的特点是什么

    IIC简介  I2CI^2CI2C通讯协议是由Phiilps公司开发的,由于它引脚少,硬件实现简单,可扩展性强没不需要USART、CAN等通讯协议的外部收法设备,现在被广泛地使用在系统
    发表于 08-23 06:55

    浅析I2C物理层和协议

    I2C物理层的特点有哪些?I2C通讯设备之间的常用连接方式是什么?I2C协议的基本读写过程是怎样的?
    发表于 09-29 06:24

    浅析串口通讯协议物理层和协议

    什么是串口通讯?串口通讯协议物理层的结构是由哪些部分组成的?串口通讯协议协议的主要标准是什么?
    发表于 10-22 09:30

    STM32F407芯片的物理层协议分别有哪些呢

    STM32F407芯片的物理层协议分别有哪些呢?
    发表于 12-06 06:42

    串口通讯协议物理层和协议看完你就懂了

    串口通讯协议物理层和协议看完你就懂了
    发表于 12-10 06:00

    IIC物理层是由哪些部分组成的

    物理层和协议物理层规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输。协议
    发表于 12-13 08:09

    物理层和协议两方面来了解I2C总线

    和时钟线SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,高速I2C总线一般可达400kbps以上。下面从物理层和协议两方面来了解I2CI2C物理层
    发表于 12-13 07:37

    STM32的IIC协议简介

    文章目录(一)IIC协议简介(二)物理层和协议简介(三)IIC
    发表于 01-05 06:13

    串口通信物理层协议的相关资料推荐

    一.串口通信物理层协议物理层规定了通讯系统的机械、电子特性(相当于规定了用嘴巴还是肢体交流)协议
    发表于 02-17 07:07

    串口通讯协议物理层和协议是什么样的?

    串口通讯协议物理层和协议是什么样的?
    发表于 02-18 07:30