目录
三、Verilog 代码实现(以 50MHz 时钟、1Mbps 波特率为例)
2. 位时序模块(CAN Bit Timing Generator)
3. CRC 计算模块(CAN CRC Generator)
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*)MHz,TQns,TQns,还是不对,正确计算:TQ时钟频率 CLK_FREQ (位时间的TQ数),位时间CAN_BAUDμs,所以TQ时钟频率 TQ_COUNT 位时间 TQ_COUNT CAN_BAUD,比如TQ_COUNT,TQ时钟MHz,MHzMHz,不是整数,所以选TQ_COUNT,CAN_BAUDkHz,这样TQ时钟kHzMHz,MHzMHz,整数分频,之前波特率调整为kHz更合理,避免非整数分频误差,这里修正参数: 修正参数(MHz时钟,kHz波特率,TQ位,位时间μs) CAN_BAUD _000; CAN目标波特率(kHz) TQ_COUNT ; 每位包含的TQ数量(TQ位,μs位) TQ_CLK_FREQ CAN_BAUD TQ_COUNT; TQ时钟频率MHz DIVISOR CLK_FREQ TQ_CLK_FREQ; 分频系数 output logic sync_seg, 同步段标志 output logic prop_seg, 传播段标志 output logic phase_seg1, 相位缓冲段标志 output logic phase_seg2, 相位缓冲段标志 output logic bit_start, 位开始标志 output logic bit_end 位结束标志 ); logic [:] tq_cnt d0; 位计数器(TQ_COUNT) logic sync_detected d0; tq_clk b1; if (tq_cnt DIVISOR ) tq_clk tq_clk; tq_cnt b1; 位时序分段 always_ff @(posedge tq_clk negedge rst_n) if (rst_n) bit_cnt b0; prop_seg b0; phase_seg2 b0; bit_end b0; 同步检测(检测CAN_RX电平跳变) if (can_rx can_rx_prev) sync_detected d0; 同步时重置位计数器 位计数器递增 if (bit_cnt TQ_COUNT ) bit_cnt b1; bit_cnt bit_cnt b0; 时序分段标志 sync_seg (bit_cnt SYNC_SEG) ? b0; prop_seg (bit_cnt SYNC_SEG bit_cnt SYNC_SEG PROP_SEG) ? b0; phase_seg1 (bit_cnt SYNC_SEG PROP_SEG bit_cnt SYNC_SEG PROP_SEG PHASE_SEG1) ? b0; phase_seg2 (bit_cnt SYNC_SEG PROP_SEG PHASE_SEG1 bit_cnt TQ_COUNT) ? b0; 位开始标志(同步段开始时) bit_start (bit_cnt b1 : b0; logic can_rx_prev; always_ff @(posedge tq_clk negedge rst_n) if (rst_n) can_rx_prev
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_reg <= crc_reg ^ data_bit ^ crc_reg; crc_reg <= crc_reg; crc_reg <= crc_reg; crc_reg <= crc_reg ^ data_bit ^ crc_reg; crc_reg <= crc_reg ^ data_bit ^ crc_reg; crc_reg <= crc_reg; crc_reg <= crc_reg ^ data_bit ^ crc_reg; crc_reg <= crc_reg; crc_reg <= crc_reg; crc_reg <= crc_reg; crc_reg <= crc_reg ^ data_bit ^ crc_reg; crc_reg <= crc_reg ^ data_bit ^ crc_reg; // 计数到总位数后完成 if (bit_cnt == total_bits - ) begin crc_done <= 'b1; bit_cnt <= 'd0; end else begin bit_cnt <= bit_cnt + 'b1; crc_done <= 'b0; end end else begin crc_done <= 'b0; end end // 计算需CRC的总位数(标准数据帧) always_comb begin total_bits = + STD_ID_LEN + + + + DLC_LEN + ( * dlc); // SOF + ID + RTR + IDE + R0 + DLC + 数据 end logic 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 (!rst_n) begin <= IDLE; bit_cnt <= d0; can_tx <= b0; tx_error <= d0; stuff_bit <= b0; end begin case () IDLE: begin tx_done <= b0; stuff_cnt <= d0; 准备发送SOF(显性电平) can_tx <= b1; data_bit <= b1; end end SYNC: begin (bit_end) begin <= ARBITRATION; bit_cnt <= b1; end end begin // 位填充检测 (can_tx == stuff_bit_prev) begin stuff_cnt <= stuff_cnt + d5) begin // 连续个相同电平,插入填充位(相反电平) can_tx <= ~can_tx; stuff_cnt <= b1; crc_en <= d0; stuff_bit <= b1; end end end // 后续状态(ARBITRATION、CONTROL、DATA、CRC、ACK、EOF)类似,按帧结构发送对应字段 // 此处省略详细状态跳转,核心逻辑:按帧字段顺序发送,位填充,仲裁检测,CRC计算 // ... EOF: begin (bit_end) begin <= IDLE; tx_done <= b1; end begin stuff_bit_prev <= can_tx; end end logic data_bit; 输入到CRC的数据位 always_comb begin case () SYNC: data_bit <= b0 : (bit_cnt == ) ? b0; IDE=,R0=,DLC位 DATA: data_bit <= data_byte[ - (bit_cnt % )]; 数据位 default: data_bit <=
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 <= IDLE; bit_cnt <= d0; rx_done <= b0; rx_std_id <= {STD_ID_LEN{b0; rx_dlc <= {DLC_LEN{b0}}; stuff_cnt <= b0; crc_en <= b0; rx_error <= d0; 检测SOF(显性电平) (can_rx == d0; 启动CRC计算(SOF为第一个数据位) crc_en <= d0; end // 位填充解除:检测连续个相同电平,跳过第个填充位 (can_rx == stuff_bit_prev) begin stuff_cnt <= stuff_cnt + d5) begin stuff_bit <= d0; end end begin stuff_cnt <= b0; end end ARBITRATION: begin (bit_end && !stuff_bit) begin (bit_cnt == STD_ID_LEN - ) begin // 接收完ID,下一位是RTR rx_std_id[STD_ID_LEN- - bit_cnt] <= can_rx; <= CONTROL; bit_cnt <= b1; end end (stuff_bit) begin stuff_bit <= b1; CRC校验 (calc_crc != crc_val) begin rx_error <= b1; end 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:] rx_data/
四、硬件验证
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 总线系统。


