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

时间:2025-06-16  作者: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总线通信控制器提供了一个可以应用的案例。

编辑:黄飞

猜您喜欢

现代电子设备中,封装技术的进步直接影响到芯片的性能和散热效率。WSON8_3X4MM_EP(Wafer-LevelChipScalePackage,8引脚,3x...
2025-02-24 14:26:20

汽车保险丝起着非常重要的作用。作为安全保护元件,保险丝能够有效防止电路过载和短路,保障汽车电子设备的正常运行。市场上汽车保险丝品牌众多,其中CJH汽车保险丝优良...
2021-09-26 11:15:30


陶瓷电阻是常见的电子元件,应用于各种电子设备中。其主要功能是限制电流的流动,以保护电路的安全和稳定。在陶瓷电阻的构造中,金属材料的选择对其性能和耐用性有着非常重...
2025-04-15 08:30:03


手电筒和应急灯在日常生活中是重要配件,主要优势不容忽视。手电筒体积小、便于携带,适合户外活动、夜间散步或突发停电时使用。应急灯通常具备强大的照明功能,能够提供持...
2010-07-21 00:00:00

热敏电阻作为温度检测和控制的重要元件,在各类电子设备中应用。奇力新(CHILISIN)作为一家知名电子元件制造商,其NTC热敏电阻产品因高品质性能和可靠品质受到...
2019-06-29 21:36:30

黑色贴片电阻在电子电路中十分常见,但对于初学者来说,如何快速识别其阻值可能是个难题。其实,通过观察电阻表面的色环或数字编码,就能轻松判断黑色贴片电阻的阻值。大多...
2024-11-29 10:25:52


保护电路安全的元件变得尤为重要。自恢复保险丝作为智能保护装置,因其能在故障解除后自动恢复而被采用。本文将重点介绍Dioal(典奥)品牌自恢复保险丝的分类及其特点...
2022-03-31 14:21:30