跳到主要内容
FPGA 实现 CAN 总线:原理、Verilog 代码与硬件验证 | 极客日志
编程语言
FPGA 实现 CAN 总线:原理、Verilog 代码与硬件验证 综述由AI生成 基于 FPGA 硬件逻辑实现 CAN 总线通信,涵盖物理层特性、协议帧结构、位时序设计及 Verilog 代码实现。文章详解了 50MHz 时钟下 500kHz 波特率的参数配置,提供了位时序、CRC 计算、发送接收及顶层模块的完整代码,并给出硬件连接与回环验证步骤。重点解决了位填充、仲裁机制及错误处理等关键技术点,适用于对实时性和可靠性要求较高的嵌入式系统设计。
PgDevote 发布于 2026/4/8 更新于 2026/5/23 13 浏览CAN(Controller Area Network,控制器局域网)是一种差分信号、多主从、异步串行通信总线,核心特点是高可靠性、实时性、抗干扰能力强,广泛应用于汽车电子、工业控制等领域。在 FPGA 中实现 CAN 接口,核心是通过硬件逻辑模拟 CAN 协议的位时序、帧结构、仲裁机制及错误处理,再配合 CAN 收发器(如 TJA1050)完成物理层连接。
一、CAN 总线核心原理
1. 物理层特性
总线电平 :采用差分信号传输。
显性电平 (Dominant):CAN_H 比 CAN_L 高约 2V(逻辑 0,优先级高);
隐性电平 (Recessive):CAN_H 与 CAN_L 电压差为 0V(逻辑 1,优先级低);
总线拓扑 :多节点挂在 CAN_H 和 CAN_L 总线上,两端需接 120Ω 终端电阻;
收发器 :FPGA 通过 CAN 收发器连接总线,将逻辑电平转换为差分信号。
2. 协议层核心概念
(1)位时序
CAN 的位时间由多个时间量子(TQ)组成。以 500kHz 波特率为例(适配 50MHz 系统时钟):
同步段(SYNC_SEG) :1TQ,用于检测总线电平跳变;
传播段(PROP_SEG) :1~8TQ,补偿传输延迟;
相位缓冲段 1/2(PHASE_SEG1/2) :各 1~8TQ,用于相位调整;
重同步跳转宽度(SJW) :1~4TQ,允许同步时调整相位范围;
总位时间 = SYNC_SEG + PROP_SEG + PHASE_SEG1 + PHASE_SEG2。
(2)帧结构(标准数据帧)
字段 长度(位) 描述 帧起始(SOF) 1 显性电平(0),标志帧开始 仲裁场 11+1 11 位标准 ID(越小优先级越高)+ RTR 位 控制场 6 IDE 位 + R0 位 + 4 位 DLC(数据长度 0~8) 数据场 0~64 数据长度由 DLC 决定 CRC 场 15+1 15 位 CRC 校验码 + 1 位界定符 ACK 场 2 1 位应答位 + 1 位界定符 帧结束(EOF) 7 7 位隐性电平
(3)关键机制
仲裁机制 :ID 竞争总线,显性电平优先;
位填充 :连续 5 个相同电平后插入 1 个相反电平;
错误处理 :检测到错误时发送错误帧;
应答机制 :接收方正确接收后拉低 ACK 位。
二、FPGA 实现 CAN 的核心模块
FPGA 实现需拆解为以下模块并通过顶层整合:
物理层接口 :连接 CAN 收发器,处理 TX/RX 信号;
位时序模块 :生成 TQ 时钟,实现位同步和相位调整;
发送模块 :构建帧,实现位填充、CRC 计算;
接收模块 :解析帧,解除位填充,校验 CRC;
错误处理模块 :检测总线错误,管理错误状态;
控制模块 :协调各模块,提供配置接口。
三、Verilog 代码实现(以 50MHz 时钟、500kHz 波特率为例)
1. 全局参数定义 // CAN 参数配置
parameter CLK_FREQ = 50_000_000; // 系统时钟频率(50MHz)
parameter CAN_BAUD = 500_000; // CAN 目标波特率(500kHz)
parameter TQ_COUNT = 10; // 每位包含的 TQ 数量(10TQ/位,2μ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)
// 帧字段参数
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) module can_bit_timing(
input logic clk, // 系统时钟(50MHz)
input logic rst_n, // 复位信号(低电平有效)
input logic can_rx, // CAN 接收信号
output logic tq_clk, // TQ 时钟(5MHz)
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 // 位结束标志
);
parameter DIVISOR = CLK_FREQ / (CAN_BAUD * TQ_COUNT); // 分频系数=10
logic [3:0] tq_cnt = 4'd0; // TQ 计数器
logic [3:0] bit_cnt = 4'd0; // 位计数器
logic can_rx_prev = 1'b1; // 上一周期 RX 值
// 生成 TQ 时钟
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)
tq_clk <= ~tq_clk;
if (tq_cnt == DIVISOR - 1)
tq_cnt <= 4'd0;
else
tq_cnt <= tq_cnt + 1'b1;
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;
end else begin
// 同步检测
if (can_rx != can_rx_prev)
bit_cnt <= 4'd0;
// 位计数器递增
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;
end
end
always_ff @(posedge tq_clk or negedge rst_n) begin
if (!rst_n) can_rx_prev <= 1'b1;
else can_rx_prev <= can_rx;
end
endmodule
3. CRC 计算模块(CAN CRC Generator) 功能:计算 CAN 帧(从 SOF 到数据场)的 15 位 CRC 校验码。
module can_crc(
input logic clk,
input logic rst_n,
input logic crc_en,
input logic data_bit,
output logic [CRC_LEN-1:0] crc_out,
output logic crc_done
);
logic [CRC_LEN:0] crc_reg = {CRC_LEN+1{1'b1}}; // 初始值全 1
logic [3:0] bit_cnt = 4'd0;
logic total_bits; // 需计算 CRC 的总位数
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
// CAN CRC-15 多项式逻辑
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
always_comb begin
total_bits = 1 + STD_ID_LEN + 1 + 1 + 1 + DLC_LEN + (8 * dlc);
end
input logic [DLC_LEN-1:0] dlc;
assign crc_out = crc_reg[CRC_LEN-1:0];
endmodule
4. 发送模块(CAN Transmitter) 功能:构建标准数据帧,实现位填充、CRC 计算、仲裁逻辑。
module can_transmitter(
input logic clk,
input logic rst_n,
input logic tq_clk,
input logic sync_seg,
input logic phase_seg1,
input logic phase_seg2,
input logic bit_end,
input logic can_rx,
input logic tx_en,
input logic [STD_ID_LEN-1:0] std_id,
input logic rtr,
input logic [DLC_LEN-1:0] dlc,
input logic [DATA_MAX_LEN*8-1:0] data,
output logic can_tx,
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;
logic crc_en, crc_done;
logic [5:0] stuff_cnt = 6'd0;
logic stuff_bit = 1'b0;
logic stuff_bit_prev = 1'b1;
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; 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;
can_tx <= 1'b0; 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;
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
can_tx <= ~can_tx; stuff_cnt <= 6'd0; stuff_bit <= 1'b1; crc_en <= 1'b0;
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
always_ff @(posedge tq_clk or negedge rst_n) begin
if (!rst_n) stuff_bit_prev <= 1'b1;
else stuff_bit_prev <= can_tx;
end
logic data_bit;
always_comb begin
case (state)
SYNC: data_bit <= 1'b0; // SOF
ARBITRATION: data_bit <= std_id[STD_ID_LEN-1 - bit_cnt];
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;
DATA: data_bit <= data_byte[7 - (bit_cnt % 8)];
default: data_bit <= 1'b0;
endcase
end
endmodule
5. 接收模块(CAN Receiver) 功能:解析 CAN 帧,实现位同步、位填充解除、CRC 校验、应答检测。
module can_receiver(
input logic clk,
input logic rst_n,
input logic tq_clk,
input logic sync_seg,
input logic phase_seg1,
input logic phase_seg2,
input logic bit_end,
input logic can_rx,
output logic rx_done,
output logic rx_error,
output logic [STD_ID_LEN-1:0] rx_std_id,
output logic rx_rtr,
output logic [DLC_LEN-1:0] rx_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, calc_crc;
logic crc_en, crc_done;
logic [5:0] stuff_cnt = 6'd0;
logic stuff_bit = 1'b0;
logic stuff_bit_prev = 1'b1;
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;
if (can_rx == 1'b0 && sync_seg) begin
state <= SYNC; bit_cnt <= 4'd0; crc_en <= 1'b1;
end
end
SYNC: begin
if (bit_end) begin
state <= ARBITRATION; bit_cnt <= 4'd0;
end
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
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_en <= !stuff_bit;
end
// 后续状态(CONTROL、DATA、CRC、ACK、EOF)类似,按帧结构接收对应字段,位填充解除,CRC 校验
EOF: begin
if (bit_end) begin
state <= IDLE; rx_done <= 1'b1;
if (calc_crc != crc_val) rx_error <= 1'b1;
end
end
default: state <= IDLE;
endcase
end
end
always_ff @(posedge tq_clk or negedge rst_n) begin
if (!rst_n) stuff_bit_prev <= 1'b1;
else stuff_bit_prev <= can_rx;
end
endmodule
6. 顶层模块(CAN Top Module) 功能:整合位时序、发送、接收模块,提供与 CAN 收发器的物理接口。
module can_top(
input logic clk,
input logic rst_n,
input logic tx_en,
input logic [STD_ID_LEN-1:0] std_id,
input logic rtr,
input logic [DLC_LEN-1:0] dlc,
input logic [DATA_MAX_LEN*8-1:0] data,
output logic can_tx,
input logic can_rx,
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,
output logic rx_rtr,
output logic [DLC_LEN-1:0] rx_dlc,
output logic [DATA_MAX_LEN*8-1:0] rx_data
);
logic tq_clk, sync_seg, prop_seg, phase_seg1, phase_seg2, bit_start, 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) :
can_tx → TXD;can_rx → RXD;
VCC(3.3V)→ VCC;GND → GND;
CAN 收发器 ↔ CAN 总线 :
CAN_H → 总线 CAN_H;CAN_L → 总线 CAN_L;
总线两端接 120Ω 终端电阻;
其他 :FPGA 需提供 50MHz 时钟和复位信号。
2. 验证步骤
参数配置 :设置发送参数(ID=0x001,DLC=2,数据=0x1234,波特率=500kHz);
发送数据 :触发 tx_en 信号,FPGA 生成 CAN 标准数据帧;
接收验证 :
若有其他节点,验证 ID、数据、DLC 是否正确;
若单板测试,将 can_tx 直连 can_rx(回环),确认 rx_done 和 rx_data;
错误测试 :修改发送数据,验证 rx_error 是否检测到 CRC 错误。
五、关键注意事项与扩展功能
1. 注意事项
时序精确性 :CAN 位时序对可靠性至关重要,需避免非整数分频导致的误差;
位填充与解除 :发送需插入填充位,接收需准确解除,否则解析错误;
仲裁机制 :多节点通信时需确保仲裁逻辑正确;
收发器匹配 :电平需与 FPGA IO 匹配(通常 3.3V);
终端电阻 :必须接终端电阻,防止信号反射。
2. 扩展功能
扩展帧支持 :添加 29 位扩展 ID;
多帧发送/接收 :添加 FIFO 缓冲区;
ID 过滤 :只接收特定 ID 的帧;
错误管理 :完善错误计数器和总线关闭恢复;
波特率自适应 :自动识别总线波特率。
六、总结 FPGA 实现 CAN 接口的核心是严格遵循 CAN 协议的时序和帧结构,通过硬件逻辑实现位时序生成、帧构建与解析、仲裁、错误处理等功能。相比单片机 CAN 控制器,FPGA 具有实时性强、可定制性高、并行处理能力强的优势,适用于对通信速率和可靠性要求较高的场景。实际应用中可根据需求扩展扩展帧、ID 过滤、多帧收发等功能,适配复杂的 CAN 总线系统。
相关免费在线工具 Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
JSON美化和格式化 将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online