1. 项目概述:基于Xilinx FPGA的CAN总线通信实现
在工业控制和汽车电子领域,CAN总线因其高可靠性和实时性成为首选通信协议。最近我在开发一个车载控制系统时,需要实现FPGA与多个ECU节点的CAN通信。虽然Xilinx官方提供了完善的文档,但实际搭建可用的通信模块时,还是遇到了不少"坑"。本文将分享基于Xilinx 7系列FPGA的CAN通信实现方案,包含完整的Verilog源码和实战经验。
这个方案采用Xilinx AXI4-Lite接口的CAN IP核,兼容Kintex-7和Artix-7系列FPGA。代码经过实际项目验证,关键位置都有详细注释,可直接集成到现有工程中。特别适合需要快速实现CAN通信的开发者,或者作为学习FPGA与CAN总线交互的参考案例。
2. 硬件架构与IP核配置
2.1 系统整体架构设计
我们的CAN通信模块采用典型的FPGA+PHY架构:
- FPGA侧:Xilinx 7系列芯片(如XC7K325T或XC7A100T)
- CAN PHY:TI SN65HVD23x系列收发器
- 时钟系统:主时钟100MHz,CAN时钟20MHz独立晶振
注意:虽然Xilinx IP核支持内部时钟分频,但建议使用外部晶振提供CAN时钟,可避免因PLL抖动导致的时序问题。
2.2 CAN IP核关键配置
在Vivado中配置CAN IP核时,需要特别注意以下参数:
- 选择AXI4-Lite接口版本(比传统PLBv46接口更高效)
- 时钟域配置为"Independent CAN clock"
- 波特率设置为1Mbps(需与总线其他节点一致)
- 接收FIFO深度建议设为32(平衡资源占用与抗突发能力)
verilog复制// IP核寄存器映射示例(基地址0x43C00000)
#define CAN_CTRL_REG (*((volatile uint32_t *)0x43C00000))
#define CAN_STATUS_REG (*((volatile uint32_t *)0x43C00004))
#define CAN_TX_DATA_REG (*((volatile uint32_t *)0x43C00008))
#define CAN_RX_DATA_REG (*((volatile uint32_t *)0x43C0000C))
3. Verilog核心代码实现
3.1 寄存器配置模块
verilog复制// CAN控制器寄存器配置
reg [31:0] can_ctrl_regs [0:15];
always @(posedge clk) begin
if(axi_awvalid && (axi_awaddr[15:0] >= 16'h0000)) begin
can_ctrl_regs[axi_awaddr[7:2]] <= axi_wdata;
end
end
这段代码实现了AXI4-Lite总线的寄存器写入逻辑。注意:
- 地址对齐采用7:2位索引(32位寄存器按字对齐)
- 寄存器组深度16对应IP核的64字节地址空间
- 实际项目中建议添加写保护逻辑,防止关键寄存器被意外修改
3.2 数据发送模块
verilog复制// 发送数据打包
wire [63:0] tx_packet = {
8'h02, // 数据长度(DLC)
24'h0123, // 标准帧ID(11位补零到24位)
8'hAA,8'hBB, // 数据字节0-1
8'hCC,8'hDD, // 数据字节2-3
8'hEE,8'hFF, // 数据字节4-5
8'h00,8'h55 // 数据字节6-7
};
// 报文发送状态机
reg tx_busy = 1'b0;
always @(posedge can_clk) begin
if(can_ctrl_regs[3][0] && !tx_busy) begin // 检查发送使能位
can_tx_data <= tx_packet;
tx_busy <= 1'b1;
end
if(tx_done) tx_busy <= 1'b0;
end
数据打包技巧:
- 采用位拼接而非结构体,提高综合后时序性能
- 标准帧ID左对齐,低位补零(扩展帧需特殊处理)
- 实际项目中建议添加CRC校验字段
4. 接收处理与硬件过滤
4.1 接收FIFO管理
Xilinx CAN IP核的接收FIFO默认采用"覆盖模式"(新报文覆盖旧报文),这在高速通信场景下可能导致数据丢失。建议:
- 在IP配置中启用"报文丢失中断"
- 添加软件层面的FIFO监控逻辑
- 对于关键数据,实现双缓冲机制
4.2 硬件过滤器配置
verilog复制// 硬件过滤器实例化
can_filter_config #(
.FILTER_MODE(2'b01), // 标识符掩码模式
.FILTER_ID(28'h1234567), // 基准标识符
.FILTER_MASK(28'h1FFFFFFF) // 掩码设置
) filter_inst (
.can_clk(can_clk),
.filter_en(1'b1)
);
过滤器配置要点:
- 掩码模式中,'1'表示必须匹配,'0'表示不关心
- 标准帧ID实际只有11位,高位补零到28位
- 多个过滤器可并联实现复杂过滤策略
实测案例:设置FILTER_ID=0x123,FILTER_MASK=0x7FF时,只接收ID=0x123的标准帧
5. 时序约束与调试技巧
5.1 关键时序约束
tcl复制# 时钟定义
create_clock -period 10.000 [get_ports clk] # 100MHz系统时钟
create_clock -period 20.000 [get_ports can_clk] # 50MHz CAN时钟
# 跨时钟域约束
set_clock_groups -asynchronous \
-group [get_clocks -include_generated_clocks clk] \
-group [get_clocks -include_generated_clocks can_clk]
常见时序问题排查:
- CRC错误:检查CAN时钟精度(要求±0.5%以内)
- 发送失败:确认总线终端电阻(120Ω)已连接
- 接收异常:用示波器检查CANH/CANL差分信号质量
5.2 ILA调试技巧
-
触发设置:
- 发送触发:can_tx_en上升沿
- 接收触发:can_rx_valid高电平
-
关键信号监测:
- 发送状态:tx_busy, tx_done
- 错误标志:bus_error, stuff_error
- FIFO状态:rx_fifo_empty, rx_fifo_full
-
波形解读技巧:
- ACK槽位:发送端发隐性位,接收端应回显性位
- EOF字段:连续11个隐性位表示帧结束
6. 常见问题与解决方案
6.1 问题排查速查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 无法发送 | 总线未终端 | 检查120Ω终端电阻 |
| CRC错误 | 时钟不同步 | 检查CAN时钟精度 |
| FIFO溢出 | 处理不及时 | 优化接收中断服务程序 |
| 通信不稳定 | 信号反射 | 检查布线长度(<1m@1Mbps) |
6.2 实际项目经验
-
抗干扰设计:
- 在CANH/CANL线上串联共模扼流圈
- 添加TVS二极管防止浪涌
- PCB布线保持差分对等长(ΔL<5mm)
-
性能优化:
- 对于高频小报文,禁用自动重传
- 使用DMA传输减少CPU开销
- 关键报文采用高优先级ID
-
兼容性测试:
- 与不同厂商ECU互操作测试
- 极端温度环境测试(-40℃~85℃)
- EMC辐射与抗扰度测试
7. 工程部署与扩展应用
7.1 工程部署步骤
- 在Vivado中创建工程(建议2019.1及以上版本)
- 通过IP Integrator添加CAN控制器IP核
- 导入提供的Verilog源码
- 运行Implementation生成比特流
- 使用SDK或Vitis开发驱动软件
7.2 扩展应用:CAN Bootloader
基于此CAN通信模块,可进一步实现FPGA固件远程更新:
- 设计轻量级协议栈(帧类型:0x1-命令,0x2-数据)
- 实现Flash编程状态机
- 添加CRC32校验机制
- 开发上位机配置工具
实测在Artix-7上,传输速率可达800kbps,更新512kB镜像约需6秒。