目录
[一、CAN 总线核心原理](#一、CAN 总线核心原理)
[1. 物理层特性](#1. 物理层特性)
[2. 协议层核心概念](#2. 协议层核心概念)
[二、FPGA 实现 CAN 的核心模块](#二、FPGA 实现 CAN 的核心模块)
[三、Verilog 代码实现(以 50MHz 时钟、1Mbps 波特率为例)](#三、Verilog 代码实现(以 50MHz 时钟、1Mbps 波特率为例))
[1. 全局参数定义](#1. 全局参数定义)
[2. 位时序模块(CAN Bit Timing Generator)](#2. 位时序模块(CAN Bit Timing Generator))
[3. CRC 计算模块(CAN CRC Generator)](#3. CRC 计算模块(CAN CRC Generator))
[4. 发送模块(CAN Transmitter)](#4. 发送模块(CAN Transmitter))
[5. 接收模块(CAN Receiver)](#5. 接收模块(CAN Receiver))
[6. 顶层模块(CAN Top Module)](#6. 顶层模块(CAN Top Module))
[1. 硬件连接](#1. 硬件连接)
[2. 验证步骤](#2. 验证步骤)
[1. 注意事项](#1. 注意事项)
[2. 扩展功能](#2. 扩展功能)
CAN(Controller Area Network,控制器局域网)是一种差分信号、多主从、异步串行通信总线 ,核心特点是高可靠性、实时性、抗干扰能力强 ,广泛应用于汽车电子、工业控制、智能家居等领域。FPGA 实现 CAN 接口的核心是通过硬件逻辑 模拟 CAN 协议的位时序、帧结构、仲裁机制、错误处理 ,再配合 CAN 收发器(如 TJA1050)实现物理层连接。以下从原理、核心模块设计、Verilog 代码实现、硬件验证四方面详细说明:
一、CAN 总线核心原理
1. 物理层特性
- 总线电平 :采用差分信号传输,分为两种状态:
- 显性电平(Dominant):CAN_H 比 CAN_L 高约 2V(总线电平为 0,优先级高);
- 隐性电平(Recessive):CAN_H 与 CAN_L 电压差为 0V(总线电平为 1,优先级低);
- 总线拓扑:多节点挂在 CAN_H 和 CAN_L 总线上,总线两端需接 120Ω 终端电阻;
- 收发器:FPGA 需通过 CAN 收发器(如 TJA1050)连接总线,FPGA 输出的逻辑电平(TX)转换为差分信号,总线差分信号转换为逻辑电平(RX)给 FPGA。
2. 协议层核心概念
(1)位时序
CAN 的位时间由多个时间量子(TQ) 组成,标准位时序划分(以 1Mbps 波特率为例):
- 同步段(SYNC_SEG):1TQ,用于节点同步,检测总线电平跳变;
- 传播段(PROP_SEG):1~8TQ,补偿总线传输延迟;
- 相位缓冲段 1(PHASE_SEG1):1~8TQ,用于相位调整;
- 相位缓冲段 2(PHASE_SEG2):1~8TQ,用于相位调整;
- 重同步跳转宽度(SJW):1~4TQ,允许节点在同步时调整相位的最大范围;
- 总位时间 = SYNC_SEG + PROP_SEG + PHASE_SEG1 + PHASE_SEG2(通常为 8~25TQ)。
(2)帧结构(标准数据帧)
CAN 帧分为数据帧、远程帧、错误帧、过载帧,最常用的是标准数据帧,结构如下:
| 字段 | 长度(位) | 描述 |
|---|---|---|
| 帧起始(SOF) | 1 | 显性电平(0),标志帧开始; |
| 仲裁场 | 11+1 | 11 位标准 ID(优先级:ID 越小优先级越高)+ RTR 位(0 = 数据帧,1 = 远程帧); |
| 控制场 | 6 | IDE 位(0 = 标准帧,1 = 扩展帧)+ R0 位(保留)+ 4 位 DLC(数据长度 0~8); |
| 数据场 | 0~64 | 数据长度由 DLC 决定(1 字节 = 8 位); |
| CRC 场 | 15+1 | 15 位 CRC 校验码 + 1 位 CRC 界定符(隐性电平); |
| ACK 场 | 2 | 1 位 ACK 位(接收方拉低为应答)+ 1 位 ACK 界定符(隐性电平); |
| 帧结束(EOF) | 7 | 7 位隐性电平,标志帧结束。 |
(3)关键机制
- 仲裁机制:多节点同时发送时,通过 ID 竞争总线,ID 越小优先级越高,发送显性电平的节点获胜,其他节点转为接收;
- 位填充:为保证同步,连续 5 个相同电平后插入 1 个相反电平(接收时需解除填充);
- 错误处理:节点检测到错误(位错误、填充错误、CRC 错误等)时,发送错误帧(6 个显性电平),通知其他节点;
- 应答机制:接收方正确接收数据后,在 ACK 位拉低总线(显性)应答。
二、FPGA 实现 CAN 的核心模块
FPGA 实现 CAN 接口需拆解为物理层接口、位时序模块、发送模块、接收模块、错误处理模块、控制模块,再通过顶层模块整合:
- 物理层接口:连接 CAN 收发器(TJA1050),处理 TX/RX 信号;
- 位时序模块:生成 CAN 位时间的 TQ 时钟,实现位同步和相位调整;
- 发送模块:构建 CAN 帧(标准数据帧),实现位填充、CRC 计算、仲裁逻辑;
- 接收模块:解析 CAN 帧,实现位同步、位填充解除、CRC 校验、应答检测;
- 错误处理模块:检测总线错误,发送错误帧,管理节点错误状态;
- 控制模块:协调各模块工作,提供外部配置接口(如波特率、ID、数据长度等)。
三、Verilog 代码实现(以 50MHz 时钟、1Mbps 波特率为例)
1. 全局参数定义
verilog
// CAN参数配置
parameter CLK_FREQ = 50_000_000; // 系统时钟频率(50MHz)
parameter CAN_BAUD = 1_000_000; // CAN目标波特率(1Mbps)
parameter TQ_COUNT = 10; // 每位包含的TQ数量(10TQ/位,1Mbps对应1μs/位)
parameter SYNC_SEG = 1; // 同步段(1TQ)
parameter PROP_SEG = 3; // 传播段(3TQ)
parameter PHASE_SEG1 = 3; // 相位缓冲段1(3TQ)
parameter PHASE_SEG2 = 3; // 相位缓冲段2(3TQ)
parameter SJW = 2; // 重同步跳转宽度(2TQ)
// CAN帧字段参数
parameter STD_ID_LEN = 11; // 标准ID长度(11位)
parameter DLC_LEN = 4; // DLC长度(4位)
parameter DATA_MAX_LEN= 8; // 最大数据长度(8字节)
parameter CRC_LEN = 15; // CRC长度(15位)
// 状态定义
typedef enum {IDLE, SYNC, ARBITRATION, CONTROL, DATA, CRC, ACK, EOF} can_state_t;
2. 位时序模块(CAN Bit Timing Generator)
功能:生成 TQ 时钟(位时间的最小单位),实现位同步和相位调整。
verilog
module can_bit_timing(
input logic clk, // 系统时钟(50MHz)
input logic rst_n, // 复位信号(低电平有效)
input logic can_rx, // CAN接收信号(来自收发器)
output logic tq_clk, // TQ时钟(1TQ=100ns,50MHz/500=100kHz?不对,重新计算:50MHz/(TQ_COUNT*CAN_BAUD) = 50e6/(10*1e6)=5MHz,TQ时钟5MHz,1TQ=200ns,10TQ=2μs?哦,之前参数错了,1Mbps位时间1μs,所以TQ_COUNT=10的话,TQ时钟=50e6/(10*1e6)=5MHz,1TQ=200ns,10TQ=2μs,不对,应该TQ_COUNT=5,50e6/(5*1e6)=10MHz,1TQ=100ns,5TQ=500ns,还是不对,正确计算:TQ时钟频率 = CLK_FREQ / (位时间的TQ数),位时间=1/CAN_BAUD=1μs,所以TQ时钟频率= TQ_COUNT / 位时间 = TQ_COUNT * CAN_BAUD,比如TQ_COUNT=8,TQ时钟=8*1e6=8MHz,50MHz/8MHz=6.25,不是整数,所以选TQ_COUNT=10,CAN_BAUD=500kHz,这样TQ时钟=10*500kHz=5MHz,50MHz/5MHz=10,整数分频,之前波特率调整为500kHz更合理,避免非整数分频误差,这里修正参数:
// 修正参数(50MHz时钟,500kHz波特率,10TQ/位,位时间2μs)
parameter CAN_BAUD = 500_000; // CAN目标波特率(500kHz)
parameter TQ_COUNT = 10; // 每位包含的TQ数量(10TQ/位,2μs/位)
parameter TQ_CLK_FREQ = CAN_BAUD * TQ_COUNT; // TQ时钟频率=5MHz
parameter DIVISOR = CLK_FREQ / TQ_CLK_FREQ; // 分频系数=50e6/5e6=10
output logic sync_seg, // 同步段标志
output logic prop_seg, // 传播段标志
output logic phase_seg1, // 相位缓冲段1标志
output logic phase_seg2, // 相位缓冲段2标志
output logic bit_start, // 位开始标志
output logic bit_end // 位结束标志
);
logic [3:0] tq_cnt = 4'd0; // TQ计数器(0~TQ_COUNT-1)
logic [3:0] bit_cnt = 4'd0; // 位计数器(0~TQ_COUNT-1)
logic sync_detected = 1'b0; // 同步检测标志
// 生成TQ时钟(5MHz)
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
tq_cnt <= 4'd0;
tq_clk <= 1'b0;
end else begin
if (tq_cnt == DIVISOR/2 - 1) begin // 50%占空比
tq_clk <= ~tq_clk;
tq_cnt <= tq_cnt + 1'b1;
end else if (tq_cnt == DIVISOR - 1) begin
tq_clk <= ~tq_clk;
tq_cnt <= 4'd0;
end else begin
tq_cnt <= tq_cnt + 1'b1;
end
end
end
// 位时序分段
always_ff @(posedge tq_clk or negedge rst_n) begin
if (!rst_n) begin
bit_cnt <= 4'd0;
sync_seg <= 1'b0;
prop_seg <= 1'b0;
phase_seg1 <= 1'b0;
phase_seg2 <= 1'b0;
bit_start <= 1'b0;
bit_end <= 1'b0;
sync_detected <= 1'b0;
end else begin
// 同步检测(检测CAN_RX电平跳变)
if (can_rx != can_rx_prev) begin
sync_detected <= 1'b1;
bit_cnt <= 4'd0; // 同步时重置位计数器
end
// 位计数器递增
if (bit_cnt == TQ_COUNT - 1) begin
bit_cnt <= 4'd0;
bit_end <= 1'b1;
end else begin
bit_cnt <= bit_cnt + 1'b1;
bit_end <= 1'b0;
end
// 时序分段标志
sync_seg <= (bit_cnt < SYNC_SEG) ? 1'b1 : 1'b0;
prop_seg <= (bit_cnt >= SYNC_SEG && bit_cnt < SYNC_SEG + PROP_SEG) ? 1'b1 : 1'b0;
phase_seg1 <= (bit_cnt >= SYNC_SEG + PROP_SEG && bit_cnt < SYNC_SEG + PROP_SEG + PHASE_SEG1) ? 1'b1 : 1'b0;
phase_seg2 <= (bit_cnt >= SYNC_SEG + PROP_SEG + PHASE_SEG1 && bit_cnt < TQ_COUNT) ? 1'b1 : 1'b0;
// 位开始标志(同步段开始时)
bit_start <= (bit_cnt == 4'd0) ? 1'b1 : 1'b0;
// 同步检测标志清零
if (sync_seg) begin
sync_detected <= 1'b0;
end
end
end
logic can_rx_prev;
always_ff @(posedge tq_clk or negedge rst_n) begin
if (!rst_n) begin
can_rx_prev <= 1'b1; // CAN空闲时为隐性电平(1)
end else begin
can_rx_prev <= can_rx;
end
end
endmodule
3. CRC 计算模块(CAN CRC Generator)
功能:计算 CAN 帧(从 SOF 到数据场)的 15 位 CRC 校验码。
verilog
module can_crc(
input logic clk, // 系统时钟(50MHz)
input logic rst_n, // 复位信号(低电平有效)
input logic crc_en, // CRC计算使能
input logic data_bit, // 输入数据位
output logic [CRC_LEN-1:0] crc_out, // CRC输出
output logic crc_done // CRC计算完成
);
logic [CRC_LEN:0] crc_reg = {CRC_LEN+1{1'b1}}; // CRC初始值为全1
logic [3:0] bit_cnt = 4'd0;
logic total_bits; // 需计算CRC的总位数(SOF(1) + ID(11) + RTR(1) + IDE(1) + R0(1) + DLC(4) + 数据位(8*DLC))
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
crc_reg <= {CRC_LEN+1{1'b1}};
bit_cnt <= 4'd0;
crc_done <= 1'b0;
end else if (crc_en) begin
// CRC计算逻辑(CAN CRC-15多项式:x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1)
crc_reg[0] <= data_bit ^ crc_reg[CRC_LEN];
crc_reg[1] <= crc_reg[0];
crc_reg[2] <= crc_reg[1];
crc_reg[3] <= crc_reg[2] ^ data_bit ^ crc_reg[CRC_LEN];
crc_reg[4] <= crc_reg[3] ^ data_bit ^ crc_reg[CRC_LEN];
crc_reg[5] <= crc_reg[4];
crc_reg[6] <= crc_reg[5];
crc_reg[7] <= crc_reg[6] ^ data_bit ^ crc_reg[CRC_LEN];
crc_reg[8] <= crc_reg[7] ^ data_bit ^ crc_reg[CRC_LEN];
crc_reg[9] <= crc_reg[8];
crc_reg[10] <= crc_reg[9] ^ data_bit ^ crc_reg[CRC_LEN];
crc_reg[11] <= crc_reg[10];
crc_reg[12] <= crc_reg[11];
crc_reg[13] <= crc_reg[12];
crc_reg[14] <= crc_reg[13] ^ data_bit ^ crc_reg[CRC_LEN];
crc_reg[15] <= crc_reg[14] ^ data_bit ^ crc_reg[CRC_LEN];
// 计数到总位数后完成
if (bit_cnt == total_bits - 1) begin
crc_done <= 1'b1;
bit_cnt <= 4'd0;
end else begin
bit_cnt <= bit_cnt + 1'b1;
crc_done <= 1'b0;
end
end else begin
crc_done <= 1'b0;
end
end
// 计算需CRC的总位数(标准数据帧)
always_comb begin
total_bits = 1 + STD_ID_LEN + 1 + 1 + 1 + DLC_LEN + (8 * dlc); // SOF + ID + RTR + IDE + R0 + DLC + 数据
end
input logic [DLC_LEN-1:0] dlc; // 数据长度(来自控制模块)
endmodule
4. 发送模块(CAN Transmitter)
功能:构建标准数据帧,实现位填充、CRC 计算、仲裁逻辑。
verilog
module can_transmitter(
input logic clk, // 系统时钟(50MHz)
input logic rst_n, // 复位信号(低电平有效)
input logic tq_clk, // TQ时钟
input logic sync_seg, // 同步段标志
input logic phase_seg1, // 相位缓冲段1标志
input logic phase_seg2, // 相位缓冲段2标志
input logic bit_end, // 位结束标志
input logic can_rx, // CAN接收信号(用于仲裁)
input logic tx_en, // 发送使能
input logic [STD_ID_LEN-1:0] std_id, // 标准ID
input logic rtr, // 远程帧请求(0=数据帧,1=远程帧)
input logic [DLC_LEN-1:0] dlc, // 数据长度
input logic [DATA_MAX_LEN*8-1:0] data,// 发送数据
output logic can_tx, // CAN发送信号(到收发器)
output logic tx_done, // 发送完成标志
output logic tx_error // 发送错误标志
);
can_state_t state = IDLE;
logic [3:0] bit_cnt = 4'd0; // 字段内位计数器
logic [7:0] data_byte = 8'd0; // 当前发送的数据字节
logic [CRC_LEN-1:0] crc_val; // CRC值
logic crc_en; // CRC使能
logic crc_done; // CRC完成
logic [5:0] stuff_cnt = 6'd0; // 位填充计数器
logic stuff_bit = 1'b0; // 填充位
// 实例化CRC模块
can_crc u_crc(
.clk(clk),
.rst_n(rst_n),
.crc_en(crc_en),
.data_bit(data_bit),
.crc_out(crc_val),
.crc_done(crc_done),
.dlc(dlc)
);
// 状态机控制
always_ff @(posedge tq_clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
bit_cnt <= 4'd0;
data_byte <= 8'd0;
can_tx <= 1'b1; // 空闲时为隐性电平(1)
tx_done <= 1'b0;
tx_error <= 1'b0;
stuff_cnt <= 6'd0;
stuff_bit <= 1'b0;
crc_en <= 1'b0;
end else begin
case (state)
IDLE: begin
tx_done <= 1'b0;
tx_error <= 1'b0;
stuff_cnt <= 6'd0;
if (tx_en) begin
state <= SYNC;
bit_cnt <= 4'd0;
// 准备发送SOF(显性电平0)
can_tx <= 1'b0;
// 启动CRC计算(SOF为第一个数据位)
crc_en <= 1'b1;
data_bit <= 1'b0;
end else begin
can_tx <= 1'b1;
end
end
SYNC: begin
if (bit_end) begin
state <= ARBITRATION;
bit_cnt <= 4'd0;
// 发送ID的最高位
can_tx <= std_id[STD_ID_LEN-1 - bit_cnt];
data_bit <= std_id[STD_ID_LEN-1 - bit_cnt];
// 仲裁:检测总线电平是否与发送电平一致
if (can_rx != can_tx) begin
// 仲裁失败,转为接收
state <= IDLE;
tx_error <= 1'b1;
end
end else begin
// 位填充检测
if (can_tx == stuff_bit_prev) begin
stuff_cnt <= stuff_cnt + 1'b1;
if (stuff_cnt == 5'd5) begin
// 连续5个相同电平,插入填充位(相反电平)
can_tx <= ~can_tx;
stuff_cnt <= 6'd0;
stuff_bit <= 1'b1;
crc_en <= 1'b0; // 填充位不参与CRC
end
end else begin
stuff_cnt <= 6'd0;
stuff_bit <= 1'b0;
crc_en <= 1'b1;
end
end
end
// 后续状态(ARBITRATION、CONTROL、DATA、CRC、ACK、EOF)类似,按帧结构发送对应字段
// 此处省略详细状态跳转,核心逻辑:按帧字段顺序发送,位填充,仲裁检测,CRC计算
// ...
EOF: begin
if (bit_end) begin
state <= IDLE;
tx_done <= 1'b1;
end
end
default: state <= IDLE;
endcase
end
end
logic stuff_bit_prev;
always_ff @(posedge tq_clk or negedge rst_n) begin
if (!rst_n) begin
stuff_bit_prev <= 1'b1;
end else begin
stuff_bit_prev <= can_tx;
end
end
logic data_bit; // 输入到CRC的数据位
always_comb begin
case (state)
SYNC: data_bit <= 1'b0; // SOF
ARBITRATION: data_bit <= std_id[STD_ID_LEN-1 - bit_cnt]; // ID位
CONTROL: data_bit <= (bit_cnt == 0) ? 1'b0 : (bit_cnt == 1) ? 1'b0 : (bit_cnt >= 2 && bit_cnt < 2+DLC_LEN) ? dlc[DLC_LEN-1 - (bit_cnt-2)] : 1'b0; // IDE=0,R0=0,DLC位
DATA: data_bit <= data_byte[7 - (bit_cnt % 8)]; // 数据位
default: data_bit <= 1'b0;
endcase
end
endmodule
5. 接收模块(CAN Receiver)
功能:解析 CAN 帧,实现位同步、位填充解除、CRC 校验、应答检测。
verilog
module can_receiver(
input logic clk, // 系统时钟(50MHz)
input logic rst_n, // 复位信号(低电平有效)
input logic tq_clk, // TQ时钟
input logic sync_seg, // 同步段标志
input logic phase_seg1, // 相位缓冲段1标志
input logic phase_seg2, // 相位缓冲段2标志
input logic bit_end, // 位结束标志
input logic can_rx, // CAN接收信号(来自收发器)
output logic rx_done, // 接收完成标志
output logic rx_error, // 接收错误标志
output logic [STD_ID_LEN-1:0] rx_std_id, // 接收标准ID
output logic rx_rtr, // 接收RTR位
output logic [DLC_LEN-1:0] rx_dlc, // 接收DLC
output logic [DATA_MAX_LEN*8-1:0] rx_data// 接收数据
);
can_state_t state = IDLE;
logic [3:0] bit_cnt = 4'd0; // 字段内位计数器
logic [7:0] data_byte = 8'd0; // 当前接收的数据字节
logic [CRC_LEN-1:0] crc_val; // 接收的CRC值
logic [CRC_LEN-1:0] calc_crc; // 计算的CRC值
logic crc_en; // CRC计算使能
logic crc_done; // CRC完成
logic [5:0] stuff_cnt = 6'd0; // 位填充计数器
logic stuff_bit = 1'b0; // 填充位标志
// 实例化CRC模块(用于校验)
can_crc u_crc(
.clk(clk),
.rst_n(rst_n),
.crc_en(crc_en),
.data_bit(can_rx),
.crc_out(calc_crc),
.crc_done(crc_done),
.dlc(rx_dlc)
);
// 状态机控制
always_ff @(posedge tq_clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
bit_cnt <= 4'd0;
data_byte <= 8'd0;
rx_done <= 1'b0;
rx_error <= 1'b0;
rx_std_id <= {STD_ID_LEN{1'b0}};
rx_rtr <= 1'b0;
rx_dlc <= {DLC_LEN{1'b0}};
rx_data <= {DATA_MAX_LEN*8{1'b0}};
stuff_cnt <= 6'd0;
stuff_bit <= 1'b0;
crc_en <= 1'b0;
end else begin
case (state)
IDLE: begin
rx_done <= 1'b0;
rx_error <= 1'b0;
stuff_cnt <= 6'd0;
// 检测SOF(显性电平0)
if (can_rx == 1'b0 && sync_seg) begin
state <= SYNC;
bit_cnt <= 4'd0;
// 启动CRC计算(SOF为第一个数据位)
crc_en <= 1'b1;
end
end
SYNC: begin
if (bit_end) begin
state <= ARBITRATION;
bit_cnt <= 4'd0;
end
// 位填充解除:检测连续5个相同电平,跳过第6个填充位
if (can_rx == stuff_bit_prev) begin
stuff_cnt <= stuff_cnt + 1'b1;
if (stuff_cnt == 5'd5) begin
stuff_bit <= 1'b1; // 标记填充位,下一位跳过
stuff_cnt <= 6'd0;
end
end else begin
stuff_cnt <= 6'd0;
stuff_bit <= 1'b0;
end
end
ARBITRATION: begin
if (bit_end && !stuff_bit) begin
if (bit_cnt == STD_ID_LEN - 1) begin
// 接收完ID,下一位是RTR
rx_std_id[STD_ID_LEN-1 - bit_cnt] <= can_rx;
state <= CONTROL;
bit_cnt <= 4'd0;
end else begin
rx_std_id[STD_ID_LEN-1 - bit_cnt] <= can_rx;
bit_cnt <= bit_cnt + 1'b1;
end
end else if (stuff_bit) begin
stuff_bit <= 1'b0; // 跳过填充位
end
// CRC继续计算(ID位)
crc_en <= !stuff_bit;
end
// 后续状态(CONTROL、DATA、CRC、ACK、EOF)类似,按帧结构接收对应字段,位填充解除,CRC校验
// ...
EOF: begin
if (bit_end) begin
state <= IDLE;
rx_done <= 1'b1;
// CRC校验
if (calc_crc != crc_val) begin
rx_error <= 1'b1;
end
end
end
default: state <= IDLE;
endcase
end
end
logic stuff_bit_prev;
always_ff @(posedge tq_clk or negedge rst_n) begin
if (!rst_n) begin
stuff_bit_prev <= 1'b1;
end else begin
stuff_bit_prev <= can_rx;
end
end
endmodule
6. 顶层模块(CAN Top Module)
功能:整合位时序、发送、接收模块,提供与 CAN 收发器的物理接口和外部配置接口。
verilog
module can_top(
input logic clk, // 系统时钟(50MHz)
input logic rst_n, // 复位信号(低电平有效)
input logic tx_en, // 发送使能
input logic [STD_ID_LEN-1:0] std_id, // 发送标准ID
input logic rtr, // 发送RTR位
input logic [DLC_LEN-1:0] dlc, // 发送DLC
input logic [DATA_MAX_LEN*8-1:0] data,// 发送数据
output logic can_tx, // CAN发送信号(到TJA1050 TXD)
input logic can_rx, // CAN接收信号(来自TJA1050 RXD)
output logic tx_done, // 发送完成标志
output logic tx_error, // 发送错误标志
output logic rx_done, // 接收完成标志
output logic rx_error, // 接收错误标志
output logic [STD_ID_LEN-1:0] rx_std_id, // 接收标准ID
output logic rx_rtr, // 接收RTR位
output logic [DLC_LEN-1:0] rx_dlc, // 接收DLC
output logic [DATA_MAX_LEN*8-1:0] rx_data// 接收数据
);
// 中间信号
logic tq_clk;
logic sync_seg;
logic prop_seg;
logic phase_seg1;
logic phase_seg2;
logic bit_start;
logic bit_end;
// 实例化位时序模块
can_bit_timing u_bit_timing(
.clk(clk),
.rst_n(rst_n),
.can_rx(can_rx),
.tq_clk(tq_clk),
.sync_seg(sync_seg),
.prop_seg(prop_seg),
.phase_seg1(phase_seg1),
.phase_seg2(phase_seg2),
.bit_start(bit_start),
.bit_end(bit_end)
);
// 实例化发送模块
can_transmitter u_transmitter(
.clk(clk),
.rst_n(rst_n),
.tq_clk(tq_clk),
.sync_seg(sync_seg),
.phase_seg1(phase_seg1),
.phase_seg2(phase_seg2),
.bit_end(bit_end),
.can_rx(can_rx),
.tx_en(tx_en),
.std_id(std_id),
.rtr(rtr),
.dlc(dlc),
.data(data),
.can_tx(can_tx),
.tx_done(tx_done),
.tx_error(tx_error)
);
// 实例化接收模块
can_receiver u_receiver(
.clk(clk),
.rst_n(rst_n),
.tq_clk(tq_clk),
.sync_seg(sync_seg),
.phase_seg1(phase_seg1),
.phase_seg2(phase_seg2),
.bit_end(bit_end),
.can_rx(can_rx),
.rx_done(rx_done),
.rx_error(rx_error),
.rx_std_id(rx_std_id),
.rx_rtr(rx_rtr),
.rx_dlc(rx_dlc),
.rx_data(rx_data)
);
endmodule
四、硬件验证
1. 硬件连接
- FPGA ↔ CAN 收发器(TJA1050) :
- FPGA 的
can_tx→ TJA1050 的TXD; - FPGA 的
can_rx→ TJA1050 的RXD; - FPGA 的
VCC(3.3V)→ TJA1050 的VCC; - FPGA 的
GND→ TJA1050 的GND;
- FPGA 的
- CAN 收发器 ↔ CAN 总线 :
- TJA1050 的
CAN_H→ CAN 总线CAN_H; - TJA1050 的
CAN_L→ CAN 总线CAN_L; - CAN 总线两端接 120Ω 终端电阻;
- TJA1050 的
- 其他:FPGA 需提供 50MHz 时钟和复位信号(可通过按键或外部电路实现)。
2. 验证步骤
- 参数配置:通过 FPGA 的配置接口(如 JTAG)设置发送参数(ID=0x001,DLC=2,数据 = 0x1234,波特率 = 500kHz);
- 发送数据 :触发
tx_en信号,FPGA 生成 CAN 标准数据帧并发送到总线; - 接收验证 :
- 若总线上有其他 CAN 节点(如 CANoe 仿真节点、另一块 FPGA 开发板),可接收并解析该帧,验证 ID、数据、DLC 是否正确;
- 若只有一块 FPGA,可将
can_tx直接连接到can_rx(回环测试),FPGA 接收自己发送的数据,通过rx_done和rx_data确认接收正确;
- 错误测试 :故意修改发送数据,验证
rx_error是否能检测到 CRC 错误。
五、关键注意事项与扩展功能
1. 注意事项
- 时序精确性:CAN 位时序对传输可靠性至关重要,需严格按照协议规范设计 TQ 时钟和位段划分,避免非整数分频导致的时序误差;
- 位填充与解除:发送时需正确插入填充位,接收时需准确解除填充,否则会导致帧解析错误;
- 仲裁机制:多节点通信时,需确保仲裁逻辑正确,避免总线冲突;
- 收发器匹配:CAN 收发器的电平需与 FPGA 的 IO 电平匹配(通常为 3.3V),否则需添加电平转换电路;
- 终端电阻:CAN 总线必须接终端电阻,否则信号反射会导致通信失败。
2. 扩展功能
- 扩展帧支持:在帧结构中添加 29 位扩展 ID,修改仲裁场和控制场的处理逻辑;
- 多帧发送 / 接收:添加 FIFO 缓冲区,支持连续多帧数据的收发,避免数据丢失;
- ID 过滤:接收模块添加 ID 过滤逻辑,只接收特定 ID 的帧,减少无效数据处理;
- 错误管理:完善错误处理模块,支持错误计数器管理和总线关闭恢复;
- 波特率自适应:设计波特率检测逻辑,支持自动识别总线波特率。
六、总结
FPGA 实现 CAN 接口的核心是严格遵循 CAN 协议的时序和帧结构 ,通过硬件逻辑实现位时序生成、帧构建与解析、仲裁、错误处理等功能。相比单片机的 CAN 控制器,FPGA 的硬件实现具有实时性强、可定制性高、并行处理能力强的优势,适用于对通信速率和可靠性要求较高的场景。实际应用中,可根据需求扩展扩展帧、ID 过滤、多帧收发等功能,适配复杂的 CAN 总线系统。