如何用FPGA实现CAN总线通信控制器

时间:2025-04-28  作者:Diven  阅读:0

CAN 总线(Controller Area Network)是控制器局域网的简称,是 20 世纪 80 年代初德国 BOSCH 公司为解决现代汽车中众多的控制与测试仪器之间的数据交换而开发的串行数据通信协议。目前,CAN 总线已经被列入 ISO 国际标准,称为 ISO11898。CAN 总线已经成为工业数据通信的主流技术。

如何用FPGA实现CAN总线通信控制器

CAN 总线作为数字式串行通信技术,与其同类技术相比,在可靠性、实时性和灵活性方面具有独特的技术优势,主要特点如下:

CAN 总线是多主总线,总线上任意节点可在任意时刻主动地向网络上其节点发送信息而不分主次,因此可在各节点之间实现自由通信。

CAN 总线采用非破坏性总线仲裁技术。但多个节点同时向总线发送信息时,优先级低的节点会主动退出发送,而最高优先级的节点可以不受影响地继续传输数据,从而大大节省总线冲突的仲裁时间。即使在网络负载很重的情况下也不会发生网络瘫痪情况。

CAN 总线的通信介质可以是双绞线、同轴电缆或光导纤维,选择灵活。

CAN 总线的通信速率可达 1Mbit/s(此时通信距离最长为 40 米),通信距离最远可达 10km(速率在 5kbit/s 以下)。

CAN 总线上的节点信息分成不同的优先级,可以满足不同级别的实时要求,高优先级的数据可以在 134μs 内得到传输。

CAN 总线通过报文滤波即可实现点对点、一点对多点及全局广播等几种方式传送数据,无需专门的调度。

CAN 总线的数据采用短帧结构,传输时间短,受干扰概率低,具有极好的检错效果。

CAN 总线采用 CRC 检验并可提供相应的错误处理功能,保证了数据通信的可靠性。

CAN 总线上的器件可被置于无任何内部活动的睡眠方式,相当于未连接到总线上,可以有效降低系统功耗。

CAN 总线上的节点在错误严重的情况下具有自动关闭输出的功能,以使总线上其节点的操作不受影响。CAN 总线很好的特性、极高的可靠性和独特的设计,特别适合工业过程中监控设备的互连,越来越受到工业界的重视,并被公认为是最有前途的现场总线。另外,CAN 总线协议已被国际标准化组织认可,技术比较成熟,控制的芯片已经商品化,性价比高,特别适用于分布式测控系统之间的数通讯。

CAN 总线插卡可以任意插在 PC AT XT 兼容机上,方便地构成分布式监控系统。用 FPGA 实现 CAN 总线通信控制器具有非常重要的应用价值。本篇将通过一个实例讲解利用 FPGA 实现 CAN 总线通信控制器的实现方法。

第三篇内容摘要:本篇会介绍程序的仿真与测试以及等相关内容。

四、程序的仿真与测试

CAN 总线通信控制器的仿真程序,需要模拟数据的发送和接收。

下面是测试程序的部分代码:

//连接 can_top 模块

can_top i_can_top(

.cs_can_i(cs_can),

.clk_i(clk),

.rx_i(rx_and_tx),

.tx_o(tx),

.irq_on(irq),

.clkout_o(clkout)

);

//产生 24 MHz 时钟

initial

begin

clk=0;

forever #21 clk = ~clk;

end

//初始化

initial

begin

start_tb = 0;

cs_can = 0;

rx = 1;

extended_mode = 0;

tx_bypassed = 0;

rst_i = 1‘b0;

ale_i = 1’b0;

rd_i = 1‘b0;

wr_i = 1’b0;

port_0_o = 8‘h0;

port_0_en = 0;

port_free = 1;

rst_i = 1;

#200 rst_i = 0;

#200 start_tb = 1;

end

//产生延迟的 tx 信号(CAN 发送器延迟)

always

begin

wait (tx);

repeat (4*BRP) @ (posedge clk); // 4 time quants delay

#1 delayed_tx = tx;

wait (~tx);

repeat (4*BRP) @ (posedge clk); // 4 time quants delay

#1 delayed_tx = tx;

end

assign rx_and_tx = rx & (delayed_tx | tx_bypassed); // When this signal is on, tx is not

looped back to the rx.

//主程序

initial

begin

wait(start_tb);

//设置总线时序寄存器

write_register(8’d6, {`CAN_TIMING0_SJW, `CAN_TIMING0_BRP});

write_register(8‘d7, {`CAN_TIMING1_SAM, `CAN_TIMING1_TSEG2, `CAN_TIMING1_TSEG1});

// 设置时钟分频寄存器

extended_mode = 1’b0;

write_register(8‘d31, {extended_mode, 3’h0, 1‘b0, 3’h0}); // Setting the normal mode (not

extended)

//设置接收代码和接收寄存器

write_register(8‘d16, 8’ha6); // acceptance code 0

write_register(8‘d17, 8’hb0); // acceptance code 1

write_register(8‘d18, 8’h12); // acceptance code 2

write_register(8‘d19, 8’h30); // acceptance code 3

write_register(8‘d20, 8’h0); // acceptance mask 0

write_register(8‘d21, 8’h0); // acceptance mask 1

write_register(8‘d22, 8’h00); // acceptance mask 2

write_register(8‘d23, 8’h00); // acceptance mask 3

write_register(8‘d4, 8’he8); // acceptance code

write_register(8‘d5, 8’h0f); // acceptance mask

#10;

repeat (1000) @ (posedge clk);

//开关复位模式

write_register(8‘d0, {7’h0, ~(`CAN_MODE_RESET)});

repeat (BRP) @ (posedge clk);

// 在复位后设置总线空闲

repeat (11) send_bit(1);

test_full_fifo; // test currently switched on

send_frame; // test currently switched off

bus_off_test; // test currently switched off

forced_bus_off; // test currently switched off

send_frame_basIC; // test currently switched off

send_frame_extended; // test currently switched off

self_reception_request; // test currently switched off

manual_frame_basIC; // test currently switched off

manual_frame_ext; // test currently switched off

$display(“CAN Testbench finished !”);

$stop;

end

在测试过程中通过多个任务来分别验证程序的各个功能模块。下面的程序用于验证强制关闭总线任务:

//强制关闭总线任务

task forced_bus_off; // Forcing bus-off by writinf to tx_err_cnt register

begin

//切换到复位模式

write_register(8‘d0, {7’h0, `CAN_MODE_RESET});

// 设置时钟分频寄存器

write_register(8‘d31, {1’b1, 7‘h0}); // Setting the extended mode (not normal)

// 写数据到寄存器中

write_register(8’d15, 255);

// 切换复位模式

write_register(8‘d0, {7’h0, ~(`CAN_MODE_RESET)});

#2500000;

// 切换复位模式

write_register(8‘d0, {7’h0, `CAN_MODE_RESET});

// 写数据到寄存器中

write_register(8‘d15, 245);

//关闭复位模式

write_register(8’d0, {7‘h0, ~(`CAN_MODE_RESET)});

#1000000;

end

endtask // forced_bus_off

下面的程序验证如何发送一个基本格式的帧数据:

//发送一个基本格式的帧

task manual_frame_basic;

begin

// 切换到复位模式

write_register(8’d0, {7‘h0, (`CAN_MODE_RESET)});

//设置寄存器

write_register(8’d4, 8‘h28); // acceptance code

write_register(8’d5, 8‘hff); // acceptance mask

repeat (100) @ (posedge clk);

// 切换复位模式

write_register(8’d0, {7‘h0, ~(`CAN_MODE_RESET)});

// 模块复位后设置总线空闲

repeat (11) send_bit(1);

write_register(8’d10, 8‘h55); // Writing ID[10:3] = 0x55

write_register(8’d11, 8‘h57); // Writing ID[2:0] = 0x2, rtr = 1, length = 7

write_register(8’d12, 8‘h00); // data byte 1

write_register(8’d13, 8‘h00); // data byte 2

write_register(8’d14, 8‘h00); // data byte 3

write_register(8’d15, 8‘h00); // data byte 4

write_register(8’d16, 8‘h00); // data byte 5

write_register(8’d17, 8‘h00); // data byte 6

write_register(8’d18, 8‘h00); // data byte 7

write_register(8’d19, 8‘h00); // data byte 8

tx_bypassed = 1; // When this signal is on, tx is not looped back to the rx.

fork

begin

self_reception_request_command;

end

begin

#2200;

repeat (1)

//开始发送数据

begin

send_bit(0); // 帧起始

send_bit(0); // ID

send_bit(1); // ID

send_bit(0); // ID

send_bit(1); // ID

send_bit(0); // ID

send_bit(1); // ID

send_bit(0); // ID

send_bit(1); // ID

send_bit(0); // ID

send_bit(1); // ID

send_bit(0); // ID

send_bit(1); // RTR

send_bit(0); // IDE

send_bit(0); // r0

send_bit(0); // DLC

send_bit(1); // DLC

send_bit(1); // DLC

send_bit(1); // DLC

send_bit(1); // CRC

send_bit(1); // CRC

send_bit(0); // CRC stuff

send_bit(0); // CRC 6

send_bit(0); // CRC

send_bit(0); // CRC

send_bit(0); // CRC

send_bit(1); // CRC stuff

send_bit(0); // CRC 0

send_bit(0); // CRC

send_bit(1); // CRC

send_bit(0); // CRC

send_bit(1); // CRC 5

send_bit(1); // CRC

send_bit(0); // CRC

send_bit(1); // CRC

send_bit(1); // CRC b

send_bit(1); // CRC DELIM

send_bit(0); // ACK

send_bit(1); // ACK DELIM

send_bit(1); // EOF

send_bit(1); // EOF

send_bit(1); // EOF

send_bit(1); // EOF

send_bit(1); // EOF

send_bit(1); // EOF

send_bit(1); // EOF

send_bit(1); // INTER

send_bit(1); // INTER

send_bit(1); // INTER

end // repeat

end

join

//从接收缓冲中读取数据

read_receive_buffer;

release_rx_buffer_command;

read_receive_buffer;

release_rx_buffer_command;

read_receive_buffer;

#4000000;

end

endtask // manual_frame_basic

五、

本篇通过一个实例讲解如何用 FPGA 实现 CAN 总线通信控制器。首先讲解了 CAN 总线协议的有关内容,然后介绍了常用的 CAN 通信控制器 SJA1000 的主要特点。接下来讲解程序的主要框架和具体代码。最后通过一个测试程序验证了程序。这个实例为读者实现自己的 CAN总线通信控制器提供了一个可以应用的案例。

编辑:黄飞

猜您喜欢

电子电路设计中,钳位电路是常见的保护电路,用于限制信号电压的范围,防止过高或过低的电压对电路造成损坏。二极管作为钳位电路中的关键元件,其特性使得钳位电路在各种应...
2025-04-10 09:31:42

贴片电阻,顾名思义,就是贴在电路板表面的一种小型电子元件。它在电子设备中扮演着至关重要的角色,就像电路中的小小守护者,默默地维持着电路的正常工作。它的主要功能是...
2024-11-26 11:29:53

LED灯具以其节能环保、寿命长等优势,已经逐渐走进了千家万户。然而很多人不知道的是,选择合适的LED驱动器,对于LED灯具的性能和寿命同样很重要。面对市场上琳琅...
2024-07-28 00:00:00

应急照明集中电源控制箱是现代建筑中很重要的安全设备,其设计旨在确保在紧急情况下提供可靠的照明。这款产品支持多种照明设备的集中控制,具备出色的稳定性和便捷性。该控...
2024-10-13 00:00:00

现代电子设备和通信系统中,连接器的作用非常重要。CONN_12.95X5MM_TM是一款专为满足高性能需求而设计的连接器,广泛应用于各种领域,如消费电子、工业设...
2025-03-04 04:45:42

贴片电阻上的「250」并不是直接代表250欧姆。它实际上是一种数字编码,表示电阻的阻值。对于三位数字的编码,例如「250」,前两位数字「25」代表有效数字,最后...
2024-11-26 11:29:33

随着计算机技术的飞速发展,对性能和稳定性的追求一直是硬件市场的重要驱动力。DDR5内存应运而生,以其先进的技术和卓越的性能迅速占据了市场一席之地。在众多性能提升...
2024-11-02 00:00:00

力传感器与测压元件在现代工业和科研领域中是重要配件。主要优势体现在以下几个方面。力传感器具有高精度和高灵敏度,能够实时监测微小的力变化,确保数据的准确性。这对于...
2009-06-14 00:00:00

TERMINAL_20.55X12MM_TM是专为电子设备设计的连接终端,应用于各种行业,如汽车、电子产品和工业设备等。其独特的尺寸和结构使其在连接性能和稳定性...
2025-04-21 10:01:41