接口/总线/驱动
导读
CAN 总线(Controller Area Network)是控制器局域网的简称,是 20 世纪 80 年代初德国 BOSCH 公司为解决现代汽车中众多的控制与测试仪器之间的数据交换而开发的一种串行数据通信协议。目前,CAN 总线已经被列入 ISO 国际标准,称为 ISO11898。CAN 总线已经成为工业数据通信的主流技术之一。
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 时钟initialbeginclk=0;forever #21 clk = ~clk;end//初始化initialbeginstart_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 发送器延迟)alwaysbeginwait (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;endassign rx_and_tx = rx & (delayed_tx | tx_bypassed); // When this signal is on, tx is notlooped back to the rx.//主程序initialbeginwait(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 (notextended)//设置接收代码和接收寄存器write_register(8'd16, 8'ha6); // acceptance code 0write_register(8'd17, 8'hb0); // acceptance code 1write_register(8'd18, 8'h12); // acceptance code 2write_register(8'd19, 8'h30); // acceptance code 3write_register(8'd20, 8'h0); // acceptance mask 0write_register(8'd21, 8'h0); // acceptance mask 1write_register(8'd22, 8'h00); // acceptance mask 2write_register(8'd23, 8'h00); // acceptance mask 3write_register(8'd4, 8'he8); // acceptance codewrite_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 onsend_frame; // test currently switched offbus_off_test; // test currently switched offforced_bus_off; // test currently switched offsend_frame_basic; // test currently switched offsend_frame_extended; // test currently switched offself_reception_request; // test currently switched offmanual_frame_basic; // test currently switched offmanual_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 registerbegin//切换到复位模式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)});// 切换复位模式write_register(8'd0, {7'h0, `CAN_MODE_RESET});// 写数据到寄存器中write_register(8'd15, 245);//关闭复位模式write_register(8'd0, {7'h0, ~(`CAN_MODE_RESET)});endendtask // forced_bus_off
下面的程序验证如何发送一个基本格式的帧数据:
//发送一个基本格式的帧task manual_frame_basic;begin// 切换到复位模式write_register(8'd0, {7'h0, (`CAN_MODE_RESET)});//设置寄存器write_register(8'd4, 8'h28); // acceptance codewrite_register(8'd5, 8'hff); // acceptance maskrepeat (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] = 0x55write_register(8'd11, 8'h57); // Writing ID[2:0] = 0x2, rtr = 1, length = 7write_register(8'd12, 8'h00); // data byte 1write_register(8'd13, 8'h00); // data byte 2write_register(8'd14, 8'h00); // data byte 3write_register(8'd15, 8'h00); // data byte 4write_register(8'd16, 8'h00); // data byte 5write_register(8'd17, 8'h00); // data byte 6write_register(8'd18, 8'h00); // data byte 7write_register(8'd19, 8'h00); // data byte 8tx_bypassed = 1; // When this signal is on, tx is not looped back to the rx.forkbeginself_reception_request_command;endbegin#2200;repeat (1)//开始发送数据beginsend_bit(0); // 帧起始send_bit(0); // IDsend_bit(1); // IDsend_bit(0); // IDsend_bit(1); // IDsend_bit(0); // IDsend_bit(1); // IDsend_bit(0); // IDsend_bit(1); // IDsend_bit(0); // IDsend_bit(1); // IDsend_bit(0); // IDsend_bit(1); // RTRsend_bit(0); // IDEsend_bit(0); // r0send_bit(0); // DLCsend_bit(1); // DLCsend_bit(1); // DLCsend_bit(1); // DLCsend_bit(1); // CRCsend_bit(1); // CRCsend_bit(0); // CRC stuffsend_bit(0); // CRC 6send_bit(0); // CRCsend_bit(0); // CRCsend_bit(0); // CRCsend_bit(1); // CRC stuffsend_bit(0); // CRC 0send_bit(0); // CRCsend_bit(1); // CRCsend_bit(0); // CRCsend_bit(1); // CRC 5send_bit(1); // CRCsend_bit(0); // CRCsend_bit(1); // CRCsend_bit(1); // CRC bsend_bit(1); // CRC DELIMsend_bit(0); // ACKsend_bit(1); // ACK DELIMsend_bit(1); // EOFsend_bit(1); // EOFsend_bit(1); // EOFsend_bit(1); // EOFsend_bit(1); // EOFsend_bit(1); // EOFsend_bit(1); // EOFsend_bit(1); // INTERsend_bit(1); // INTERsend_bit(1); // INTERend // repeatendjoin//从接收缓冲中读取数据read_receive_buffer;release_rx_buffer_command;read_receive_buffer;release_rx_buffer_command;read_receive_buffer;#4000000;endendtask // manual_frame_basic
五、总结
本篇通过一个实例讲解如何用 FPGA 实现 CAN 总线通信控制器。首先讲解了 CAN 总线协议的有关内容,然后介绍了一种常用的 CAN 通信控制器 SJA1000 的主要特点。接下来讲解程序的主要框架和具体代码。最后通过一个测试程序验证了程序。这个实例为读者实现自己的 CAN总线通信控制器提供了一个可以应用的案例。
全部0条评论
快来发表一下你的评论吧 !