当前位置: 首页 > article >正文

FPGA设计-基于SJA1000的can控制器设计

目录

简介

芯片方案

设计注意点

设计代码


简介

般来说, 每个CAN 模块能够被分成3 个不同的功能块,其结构如图1所示。CAN总线收发器提供CAN协议控制器与物理总线之间的接口, 控制从CAN 控制器到总线物理层或相反的逻辑电平信号。它的性能决定了总线接口、总线终端、总线长度和节点数, 是影响整个总线网络通信性能的关键因素之一。CAN 控制器执行在CAN 规范里规定的完整的CAN 协议, 它通常用于报文缓冲和验收滤波, 对外具有与主控制器和总线收发器的接口。主控制器负责执行应用的功能, 例如控制命令的发送、读传感器和处理人机接口等。它通过对CAN 控制器进行编程, 来控制CAN 总线的工作方式和工作状态, 以及进行数据的发送和接收。

芯片方案

SJA1000的AD0~ AD7地址数据复用端口、ALE地址锁存端口、RD、WR、片选CS端口均通过转换芯片与FPGA的I /O口相连

SJA1000 的中断输出信号INT连入FPGA, 使CAN通信可以采用中断或查询方式。RST 端口的电路实现SJA1000的上电自动复位功能。MODE 模式选择端接+ 5 V, 设置SJA1000控制器为Intel模式。SJA1000 的时钟晶振采用16MH z, 频率调整电容取15 pF. R16为终端电阻,设计中取120Ω。CAN 驱动器PCA82C250 的RS脚为工作模式选择位, 接地工作于高速模式, 接高工作于待机模式。系统通过电阻R14将芯片设定于斜率控制模式, 电阻值为47 kΩ , 这时CAN 总线应工作于低速模式, 可提高CAN 总线抵抗射频干扰的能力。在这种情况下, 可直接使用非屏蔽双绞线作为总线。

设计注意点

设计中有2点需要特别注意:第一点是FPGA 并没有与SJA1000直接相连。这是因为对于设计选取的FPGAXCV600, 其接口电平不支持5 V TTL的I/O 标准, 如果与5 VI/O标准的SJA1000直接相连, 将可能导致FPGA 管脚电流过大, 造成器件锁死或者烧毁。为此采用双向总线收发器74ALVC164245, 把SJA1000的5 V TTL电平信号AD0 ~ AD7、

设计代码

`timescale 1ns/1ps

//-----------------------------------------------------------------------------------------------------------------------------------------//
// Project    : sja1000驱动
// File       : 
// Version    : 1.0
// Description: sja1000物理接口。
// Test Note  :    Intel Mode
//                
//-----------------------------------------------------------------------------------------------------------------------------------------//

module    sja1000_phy_intf
(
                input                                sys_clk,                    //板载系统时钟50MHz                            
                //内部寄存器读写接口
                (* MARK_DEBUG="true" *)input                                wr_wvalid_i,
                (* MARK_DEBUG="true" *)output    reg                            wr_wready_o,
                (* MARK_DEBUG="true" *)input    [7:0]                        wr_addr_i,
                (* MARK_DEBUG="true" *)input    [7:0]                        wr_data_i,
                (* MARK_DEBUG="true" *)output    reg                            wr_done_o,
                (* MARK_DEBUG="true" *)input                                rd_rready_i,
                (* MARK_DEBUG="true" *)output    reg                            rd_ack_o,
                (* MARK_DEBUG="true" *)output    reg                            rd_rvalid_o,
                (* MARK_DEBUG="true" *)input    [7:0]                        rd_addr_i,
                (* MARK_DEBUG="true" *)output    reg                            rd_done_o,    
                (* MARK_DEBUG="true" *)output    wire    [7:0]                rd_data_o,
                
                //sja1000物理接口
                inout    [7:0]                        sja1000_ad,            
                (* MARK_DEBUG="true" *)output    reg                            sja1000_ale,
                (* MARK_DEBUG="true" *)output    reg                            sja1000_cs_n,
                (* MARK_DEBUG="true" *)output    reg                            sja1000_wr_n,
                (* MARK_DEBUG="true" *)output    reg                            sja1000_rd_n,
                //电平转换器件控制信号
                (* MARK_DEBUG="true" *)output    wire                        sja1000_dir
);

(* MARK_DEBUG="true" *)reg            sja_op_dir         = 1'b1;
(* MARK_DEBUG="true" *)reg [7:0]     wr_sja_adr_data    = 8'b0;

//assign    sja1000_dir        =    ~sja_op_dir;
assign    sja1000_dir        =    sja_op_dir;

generate
genvar i;
for (i = 0; i < 8; i = i + 1) begin : gen_data
      
IOBUF #
(    
      .DRIVE        (    12                    ),         // Specify the output drive strength
      .IBUF_LOW_PWR    (    "TRUE"                ),      // Low Power - "TRUE", High Performance = "FALSE" 
      .IOSTANDARD    (    "LVCMOS33"            ),         // Specify the I/O standard
      .SLEW            (    "SLOW"                )         // Specify the output slew rate
) IOBUF_inst         
(        
      .O            (    rd_data_o[i]        ),         // Buffer output
      .IO            (    sja1000_ad[i]        ),       // Buffer inout port (connect directly to top-level port)
      .I            (    wr_sja_adr_data[i]    ),         // Buffer input
      .T            (    ~sja_op_dir            )         // 3-state enable input, high=input, low=output
);      
            
end
endgenerate


reg     [3:0]        sja_op_cnt    =    4'd0;


localparam            SJA_IDLE        =        6'b00_0001,    //01
                    WR_SJA_ADR        =        6'b00_0010,    //02
                    WR_SJA_DATA        =        6'b00_0100,    //04
                    WR_SJA_DONE        =        6'b00_1000,    //08
                    RD_SJA_ADR        =        6'b01_0000,    //10
                    RD_SJA_DATA        =        6'b10_0000;    //20

reg [5:0]            sja_state    =    SJA_IDLE;

    
always@(posedge sys_clk)
begin
        case(sja_state)
            SJA_IDLE:                begin
                                            sja_op_dir            <=    1'b0;
                                            wr_sja_adr_data        <=    8'd0;
                                            
                                            wr_wready_o            <=    1'b1;
                                            wr_done_o            <=    1'b0;
                                                
                                            rd_rvalid_o            <=    1'b0;
                                            rd_done_o            <=    1'b0;
                                                
                                            sja1000_ale            <=    1'b0;
                                            sja1000_cs_n        <=    1'b1;
                                            sja1000_wr_n        <=    1'b1;
                                            sja1000_rd_n        <=    1'b1;
                                            
                                            sja_op_cnt            <=    4'd0;
                        
                                            if( wr_wvalid_i == 1'b1 )                    //写寄存器操作
                                                begin
                                                    sja_state            <=        WR_SJA_ADR;
                                                    wr_sja_adr_data        <=        wr_addr_i;    //先写地址
                                                end
                                            else if( rd_rready_i == 1'b1 )                //读寄存器操作
                                                begin
                                                    sja_state            <=        RD_SJA_ADR;
                                                    wr_sja_adr_data        <=        rd_addr_i;    //先写地址
                                                    rd_ack_o            <=        1'b1;
                                                end    
                                            else begin
                                                sja_state            <=        SJA_IDLE;
                                                wr_sja_adr_data        <=        8'b0;    
                                                rd_ack_o            <=        1'b0;    
                                            end        
                                    end    
                                        
            //写地址            
            WR_SJA_ADR:                begin
                                            sja_op_dir        <=        1'b1;                //写进程中......
                                            wr_wready_o        <=        1'b0;                                                                                                        
                                            wr_sja_adr_data    <=        wr_addr_i;
                                                                                        //写器件触发
                                            sja1000_ale        <=        ( sja_op_cnt >= 4'd0 && sja_op_cnt <= 4'd2 ) ? 1'b1 : 1'b0;                
                                            sja1000_wr_n    <=        1'b1;
                                            sja1000_rd_n    <=        1'b1;
                                            sja1000_cs_n    <=        1'b1;
                                            
                                            sja_op_cnt        <=        (  sja_op_cnt != 4'd4) ? ( sja_op_cnt + 1 ) : 4'd0;    
                                            sja_state        <=        (  sja_op_cnt == 4'd4) ? WR_SJA_DATA:WR_SJA_ADR;    
                                    end

            //写数据
            WR_SJA_DATA:            begin
                                            sja_op_cnt        <=        ( sja_op_cnt != 4'd4 ) ? ( sja_op_cnt + 1 ) : 4'd0;
                                            
                                            wr_sja_adr_data    <=        wr_data_i;            //再写数据
                                            
                                            sja1000_ale        <=        1'b0;                //写器件触发
                                            sja1000_wr_n    <=        ( sja_op_cnt >= 4'd0 && sja_op_cnt <= 4'd3 ) ? 1'b0 : 1'b1;    
                                            sja1000_rd_n    <=        1'b1;
                                            sja1000_cs_n    <=        ( sja_op_cnt >= 4'd0 && sja_op_cnt <= 4'd3 ) ? 1'b0 : 1'b1;                                    
                                            
                                            sja_state        <=        ( sja_op_cnt == 4'd4 ) ? WR_SJA_DONE : WR_SJA_DATA;        
                                    end
                                    
            //写操作完成                        
            WR_SJA_DONE:            begin    //延迟2个时钟后再允许操作
                                            
                                            //sja_op_cnt        <=        ( sja_op_cnt != 4'd1 ) ? ( sja_op_cnt + 1 ) : 4'd0;
                                            
                                            sja_op_dir        <=        1'b1;
                                            wr_wready_o        <=        1'b1;    
                                            
                                            //wr_done_o        <=        ( sja_op_cnt == 4'd1 ) ? 1'b1 : 1'b0;
                                            
                                            wr_done_o        <=        1'b1;
                                            
                                            sja1000_ale        <=        1'b0;
                                            sja1000_cs_n    <=        1'b1;
                                            sja1000_wr_n    <=        1'b1;
                                            sja1000_rd_n    <=        1'b1;    

                                            //sja_state        <=        ( sja_op_cnt == 4'd1 ) ? SJA_IDLE : WR_SJA_DONE;    
                                            
                                            sja_state        <=        SJA_IDLE;    
                                    end
                                                                
            
            //读地址信息
            RD_SJA_ADR:                    begin
                                            rd_ack_o        <=        1'b0;                //读应答
                                            sja_op_dir        <=        1'b1;                //写进程中......
                                            wr_sja_adr_data    <=        rd_addr_i;            //先传递地址信息
                                                                                        //写器件触发
                                            sja1000_ale        <=        ( sja_op_cnt >= 4'd0 && sja_op_cnt <= 4'd2 ) ? 1'b1 : 1'b0;                
                                            sja1000_wr_n    <=        1'b1;
                                            sja1000_rd_n    <=        1'b1;
                                            sja1000_cs_n    <=        1'b1;
                                            
                                            sja_op_cnt        <=        (  sja_op_cnt != 4'd4) ? ( sja_op_cnt + 1 ) : 4'd0;    
                                            sja_state        <=        (  sja_op_cnt == 4'd4) ? RD_SJA_DATA:RD_SJA_ADR;    
                                    end
            
            //读数据
            RD_SJA_DATA:            begin
                                            sja_op_cnt        <=        ( sja_op_cnt != 4'd5 ) ? ( sja_op_cnt + 1 ) : 4'd0;
                                            
                                            sja_op_dir        <=        1'b0;
                                            rd_rvalid_o        <=        1'b1;
                                            
                                            sja1000_ale        <=        1'b0;
                                            sja1000_wr_n    <=        1'b1;
                                            sja1000_rd_n    <=        ( sja_op_cnt >= 4'd1 && sja_op_cnt <= 4'd3 ) ? 1'b0 : 1'b1;
                                            sja1000_cs_n    <=        ( sja_op_cnt >= 4'd0 && sja_op_cnt <= 4'd4 ) ? 1'b0 : 1'b1;
                                            
                                            rd_done_o        <=        ( sja_op_cnt == 4'd4 ) ? 1'b1 : 1'b0;
                                                                                    
                                            sja_state        <=        ( sja_op_cnt == 4'd5 ) ? SJA_IDLE : RD_SJA_DATA;
                                    end
            
            default:                begin
                                            sja_state            <=        SJA_IDLE;
                                            sja_op_dir            <=        1'b0;
                                            wr_sja_adr_data        <=        8'd0;
                                                
                                            wr_wready_o            <=        1'b0;
                                            wr_done_o            <=        1'b0;
                                                    
                                            rd_rvalid_o            <=        1'b0;
                                            rd_ack_o            <=        1'b0;
                                            rd_done_o            <=        1'b0;
                                                
                                            sja_op_cnt            <=        4'd0;
                                            
                                            sja1000_ale            <=        1'b0;                //写器件触发
                                            sja1000_wr_n        <=        1'b1;
                                            sja1000_rd_n        <=        1'b1;
                                            sja1000_cs_n        <=        1'b1;                                            
                                    end
        endcase
end


endmodule


http://www.kler.cn/a/420932.html

相关文章:

  • 为什么编程语言会设计不可变的对象?字符串不可变?NSString *s = @“hello“变量s是不可变的吗?Rust内部可变性的意义?
  • html button 按钮单选且 高亮
  • 基于Java Springboot校园导航微信小程序
  • elementUI el-image的使用
  • 分布式集群下如何做到唯一序列号
  • Linux 进程管理详解
  • Mybatis 学习 之 XML 手册
  • debian ubuntu armbian部署asp.net core 项目 开机自启动
  • 贴片式内存卡 ​SD NAND​
  • 【数据结构】队列实现剖析:掌握队列的底层实现
  • 零基础快速掌握——【c语言基础】数组的相关概念及操作
  • 电子应用设计方案-37:智能鼠标系统方案设计
  • re正则通配表达式的详尽/简洁,从来不是一对悖论
  • 二叉树的概念及其在Java中的实现
  • 【第 1 章 初识 C 语言】1.6 C 语言标准:C89/90、C99、C11、C17、C23
  • Java中如何停止一个正在运行的线程
  • Vue 90 ,Element 13 ,Vue + Element UI 中 el-switch 使用小细节解析,避免入坑(获取后端的数据类型自动转变)
  • Python+Requests接口自动化测试框架:多线程-异步执行
  • Python 爬虫实战基于 Class 的天气查询与反爬虫练习
  • ArcGIS求取多个点距离线要素的最近距离以及距离倒数
  • 数据结构基础之《(10)—快速排序》
  • RoBERTa- 稳健优化的 BERT 预训练模型详解
  • AI - 谈谈RAG中的查询分析(2)
  • 《封装、继承与多态》问题一:封装只有类能做吗?结构体如何封装?名空间、文件能实现封装吗?还有没有其他方式?
  • Vue.js 中集成 Socket.IO 实现实时聊天功能
  • Microi 吾码:后端开发的创新引擎与代码艺术