FPGA入门:CAN总线原理与Verilog代码详解

FPGA入门:CAN总线原理与Verilog代码详解

目录

一、CAN 总线核心原理

1. 物理层特性

2. 协议层核心概念

(1)位时序

(2)帧结构(标准数据帧)

(3)关键机制

二、FPGA 实现 CAN 的核心模块

三、Verilog 代码实现(以 50MHz 时钟、1Mbps 波特率为例)

1. 全局参数定义

2. 位时序模块(CAN Bit Timing Generator)

3. CRC 计算模块(CAN CRC Generator)

4. 发送模块(CAN Transmitter)

5. 接收模块(CAN Receiver)

6. 顶层模块(CAN Top Module)

四、硬件验证

1. 硬件连接

2. 验证步骤

五、关键注意事项与扩展功能

1. 注意事项

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+111 位标准 ID(优先级:ID 越小优先级越高)+ RTR 位(0 = 数据帧,1 = 远程帧);
控制场6IDE 位(0 = 标准帧,1 = 扩展帧)+ R0 位(保留)+ 4 位 DLC(数据长度 0~8);
数据场0~64数据长度由 DLC 决定(1 字节 = 8 位);
CRC 场15+115 位 CRC 校验码 + 1 位 CRC 界定符(隐性电平);
ACK 场21 位 ACK 位(接收方拉低为应答)+ 1 位 ACK 界定符(隐性电平);
帧结束(EOF)77 位隐性电平,标志帧结束。
(3)关键机制
  • 仲裁机制:多节点同时发送时,通过 ID 竞争总线,ID 越小优先级越高,发送显性电平的节点获胜,其他节点转为接收;
  • 位填充:为保证同步,连续 5 个相同电平后插入 1 个相反电平(接收时需解除填充);
  • 错误处理:节点检测到错误(位错误、填充错误、CRC 错误等)时,发送错误帧(6 个显性电平),通知其他节点;
  • 应答机制:接收方正确接收数据后,在 ACK 位拉低总线(显性)应答。

二、FPGA 实现 CAN 的核心模块

FPGA 实现 CAN 接口需拆解为物理层接口、位时序模块、发送模块、接收模块、错误处理模块、控制模块,再通过顶层模块整合:

  1. 物理层接口:连接 CAN 收发器(TJA1050),处理 TX/RX 信号;
  2. 位时序模块:生成 CAN 位时间的 TQ 时钟,实现位同步和相位调整;
  3. 发送模块:构建 CAN 帧(标准数据帧),实现位填充、CRC 计算、仲裁逻辑;
  4. 接收模块:解析 CAN 帧,实现位同步、位填充解除、CRC 校验、应答检测;
  5. 错误处理模块:检测总线错误,发送错误帧,管理节点错误状态;
  6. 控制模块:协调各模块工作,提供外部配置接口(如波特率、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
  • CAN 收发器 ↔ CAN 总线
    • TJA1050 的CAN_H → CAN 总线CAN_H
    • TJA1050 的CAN_L → CAN 总线CAN_L
    • CAN 总线两端接 120Ω 终端电阻;
  • 其他:FPGA 需提供 50MHz 时钟和复位信号(可通过按键或外部电路实现)。

2. 验证步骤

  1. 参数配置:通过 FPGA 的配置接口(如 JTAG)设置发送参数(ID=0x001,DLC=2,数据 = 0x1234,波特率 = 500kHz);
  2. 发送数据:触发tx_en信号,FPGA 生成 CAN 标准数据帧并发送到总线;
  3. 接收验证
    • 若总线上有其他 CAN 节点(如 CANoe 仿真节点、另一块 FPGA 开发板),可接收并解析该帧,验证 ID、数据、DLC 是否正确;
    • 若只有一块 FPGA,可将can_tx直接连接到can_rx(回环测试),FPGA 接收自己发送的数据,通过rx_donerx_data确认接收正确;
  4. 错误测试:故意修改发送数据,验证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 总线系统。

Read more

AI绘画创业第一步:Stable Diffusion 3.5低成本验证方案

AI绘画创业第一步:Stable Diffusion 3.5低成本验证方案 你是不是也经常刷到别人用AI画出精美插画、定制头像、甚至接单赚钱?看着心动,但又怕买设备、学软件、投钱打水漂?别担心,作为一个从零开始摸索过来的自由职业者,我完全理解你的顾虑。 今天我要分享的,是一套专为自由职业者设计的AI绘画副业启动方案——利用 Stable Diffusion 3.5(SD 3.5) 和云端GPU资源,实现“零硬件投入、低时间成本、快速出图变现”的可行性验证路径。整个过程不需要你懂编程,也不用买显卡,只要会打字、会上网,就能在几个小时内跑通全流程。 为什么选 SD 3.5?因为它不仅是目前开源图像生成模型中的“顶流”,还特别适合商业应用:支持更高分辨率、细节更精细、文字渲染能力更强,而且对提示词(prompt)的理解更加自然。更重要的是,

By Ne0inhk

让大模型变小:Llama Factory量化压缩一站式方案

让大模型变小:Llama Factory量化压缩一站式方案 作为一名移动端开发者,你是否遇到过这样的困境:想把强大的大语言模型(如LLaMA、ChatGLM等)部署到手机应用中,却发现原版模型体积庞大、资源占用高,直接加载会导致APP崩溃?今天我要分享的Llama Factory量化压缩一站式方案,正是解决这个痛点的利器。这个开源框架能帮你将大模型"瘦身"到移动端可承受的范围,同时保持不错的推理性能。这类任务通常需要GPU环境,目前ZEEKLOG算力平台提供了包含该镜像的预置环境,可快速部署验证。 为什么需要量化压缩? 在移动端部署大模型时,我们面临几个核心挑战: * 模型体积过大:原版7B参数的模型通常需要20GB以上存储空间,远超手机应用合理范围。 * 内存占用过高:推理时显存需求可能超过4GB,导致低端设备崩溃。 * 计算资源不足:手机CPU/GPU难以承受浮点矩阵的密集运算。 Llama Factory提供的量化方案能有效解决这些问题: * 通过4-bit/8-bit量化将模型体积压缩至原大小的1/4~1/2 * 显著降低推理时的内存占用 * 保持原始

By Ne0inhk
夸克网盘免费资源电子书籍安卓软件经典游戏音乐歌曲精品教程AI绘画学习资料合集

夸克网盘免费资源电子书籍安卓软件经典游戏音乐歌曲精品教程AI绘画学习资料合集

一、夸克网盘免费资源说明 夸克网盘免费资源,来自全网整理二次精选,涵盖了几乎所有资源类型,网盘资源目录的分享链接,仅限一级目录和二级目录,一级目录是网盘资源的根目录,包括电子书籍、软件资源、游戏资源、视频资源、音乐音频、美食技术和学习资料等,二级目录是一级目录的子目录,均为资源专题形式,比如,Kindle原版书籍合集、U盘车载音乐歌曲、DeepSeek全套资源、全网专业摄影书籍、TikTok全球解锁版本、IOS巨魔专用资源、TED演讲视频合集、剪映教学全套资源、全网热门漫画精选,等等,相信其中会有你所需要的。 特别说明: 1、夸克网盘与百度网盘不同,不仅支持查看分享链接的资源大小,而且支持在分享链接页面里搜索资源,可以查询其中是否有你所需要的。 2、夸克官方一直都有福利活动,新用户可以免费领取1TB空间,具体操作方法请查看文本文件(在分享链接里)。 3、一级目录《全网精选2000T优质资料》,提供了很有价值的海量夸克资源,分享链接存放在电子表格里,整个目录大小只有9.7M,建议转存收藏。 二、夸克网盘一级目录资源 电子书籍+

By Ne0inhk
【GitHub开源AI精选】WhisperLiveKi:开源实时语音识别利器,实时转写+说话人识别+完全本地部署,一键搞定会议纪要

【GitHub开源AI精选】WhisperLiveKi:开源实时语音识别利器,实时转写+说话人识别+完全本地部署,一键搞定会议纪要

系列篇章💥 No.文章1【GitHub开源AI精选】LLM 驱动的影视解说工具:Narrato AI 一站式高效创作实践2【GitHub开源AI精选】德国比勒费尔德大学TryOffDiff——高保真服装重建的虚拟试穿技术新突破3【GitHub开源AI精选】哈工大(深圳)& 清华力作 FilmAgent:剧本自动生成 + 镜头智能规划,开启 AI 电影制作新时代4【GitHub开源AI精选】Lumina - Image 2.0 文生图模型,以小参数量实现高分辨率多图生成新突破5【GitHub开源AI精选】探索 Mobile-Agent:X-PLUG 推出的创新型移动智能操作代理6【GitHub开源AI精选】吴恩达团队开源VisionAgent:用自然语言开启计算机视觉新时代7【GitHub开源AI精选】Oumi:一站式AI开发平台,涵盖训练、评估与部署全流程8【GitHub开源AI精选】深入剖析RealtimeSTT:开源实时语音转文本库的强大功能与应用9【GitHub开源AI精选】PodAgent:多智能体协作播客生成框架,

By Ne0inhk