基于多帧累加平均的图像传感器暗电流抑制方法与 FPGA 实现

引言

CMOS 图像传感器存在暗电流和固定模式噪声(FPN),传统模拟域 CDS 电路难以完全抑制,数字域单次暗帧减易受随机噪声干扰。本文利用传感器读出链路前端的暗区参考行,提出一种多帧累加平均 + 乒乓 RAM 双缓冲 的实时暗电流抑制架构。核心思路是:在暗区对每个像素位置独立累加 16 帧暗像素,取平均后生成高精度暗基准;通过前台/后台双 RAM 乒乓切换,使暗基准的后台刷新与有效区读取并行工作,无帧间停顿;最终在有效像素区执行饱和减法输出。以下按照原始 Verilog 代码从上到下的书写顺序,逐步说明各模块的实现逻辑。

设计推导

为什么代码会写成下面这个顺序?每一段逻辑都是在前一段的基础上解决一个新遇到的问题。下面按照从现象到方案、从方案到问题、从问题到再方案的思路,推导出完整的实现步骤。

推导 1:先确定暗行在哪里 → Step 1

RAW 图像传感器输出的数据,前几行是暗像素(暗区参考行),后面才是有效图像。要消除 FPN,第一步必须区分出当前到来的行是暗行还是有效行 ,否则不知道哪些像素该用来生成暗基准、哪些该用来输出。这就是 Step 1 的 dark_phase 状态机。

推导 2:暗行要按像素位置存 RAM → Step 2

确定了"这是暗行"之后,还要把暗像素存进 RAM 里留着用。存 RAM 需要知道地址------存到第几个位置?传感器一行有 204 个像素,需要一个列地址计数器逐个索引过去,这就是 Step 2 的 col_addr。

推导 3:单帧暗行不够干净,要多帧累加 → Step 3 + Step 4

用一帧暗行做 FPN 消除为什么不行?因为单帧里的随机噪声还没被平均掉,暗基准本身就带噪声,消完反而会引入新噪声。解决办法是多帧累加取平均 。那就要知道"现在是第几帧",所以需要一个帧计数器 frame_cnt,以及累加 RAM acc_g1/b/r/g2。累加 16 帧后右移 4 位就是平均值。

推导 4:多帧累加时 RAM 读写会冲突 → Step 5 + Step 6

这是整个设计最关键的问题。数据是源源不断来的:新的一帧暗行正在往累加 RAM 里写,而上一轮累加完的结果又需要读出来取平均、写成新的暗基准。这两个动作同时发生在一个时钟域里,如果用同一组 RAM,地址会打架、数据会冲突。

解决办法是乒乓双缓冲:把 RAM 分成前台和后台两组。当前台在被"读暗基准供有效像素减法"时,后台同时在"写新累加完的暗基准"。16 帧累加完成时,翻转一次开关,前后台角色立刻交换,无缝衔接------没有任何停顿周期。

这就是 Step 5 的 buf_swap_pending 切换标志和 Step 6 的乒乓双 RAM 写入逻辑。

推导 5:有效像素到来时怎么减暗 → Step 7 + Step 8 + Step 9

暗基准存好了,接下来处理有效像素:从 RAM 里读暗基准 → 与原始像素对齐 → 相减输出。

读 RAM 有一个时钟周期的延迟,所以原始像素数据 pre_data 也要打一拍才能和读出来的暗基准 dark_val_r 对齐,这就是 Step 7(读暗基准)+ Step 8(数据打拍)。最后对齐后做饱和减法输出,这就是 Step 9。

饱和减法的额外考量:暗基准是 16 帧平均值,个别像素的瞬时值可能因噪声波动略低于基准值,此时如果直接截断为 0 会有偏差,强制归零更准确。

推导 6:步骤顺序为什么就是代码顺序

把上面的推导串起来,就是一条问题驱动的单向链:

复制代码
要消 FPN → 先找暗行在哪(Step 1)
→ 暗行存 RAM 要地址(Step 2)
→ 单帧不够干净要多帧累加(Step 3 + Step 4)
→ 多帧累加遇到 RAM 读写冲突 → 乒乓(Step 5 + Step 6)
→ 有效像素来的时候读基准对齐相减输出(Step 7 + Step 8 + Step 9)

原始 Verilog 代码恰好就是按这个推导顺序从上到下写的------先写 dark_phase,再写 col_addr,再写 frame_cnt,再写累加 RAM,再写乒乓切换,最后写输出。不存在任何一步顺序颠倒,因为逻辑上就不允许颠倒:没有 dark_phase 就不知道该不该累加,没有累加结果就写不出暗基准 RAM,没有暗基准 RAM 就没法减法输出。

下面的 Step 1 ~ Step 9 每一步即原始代码中对应那一段原样照搬。

实现步骤

Step 1:判定当前处于暗区还是有效区

利用传感器前端的暗区参考行(前 FPN_REDU=4 行)作为暗像素提取源。复位后默认为暗区;扫描完第 3 行(最后一行暗行)时切换为有效区;扫描到帧末行时回到暗区。每帧循环。

复制代码
reg dark_phase;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        dark_phase <= 1'b1;
    else if((norow == NOR_MAX-1 && unpack_cksum))
        dark_phase <= 1'b1;
    else if(norow == FPN_REDU-1 && unpack_cksum)
        dark_phase <= 1'b0;
end

dark_phase = 1 时当前行是暗像素,仅用于累加,不作为最终输出;

dark_phase = 0 时是有效像素区,需要输出减暗后的图像。

Step 2:生成像素列地址

复制代码
reg [DATA_WIDTH-1:0] col_addr;
always @(posedge clk) begin
    if(!rst_n)
        col_addr <= 10'b0;
    else if(pre_de)
        col_addr <= (col_addr == WR_IW-1'b1) ? 0 : col_addr + 1'b1;
    else
        col_addr <= 10'b0;
end

col_addr 在 pre_de 有效时从 0 递增到 203(WR_IW-1),pre_de 无效时清零。它作为像素水平索引,驱动后续所有 RAM 的地址端口,实现对每个像素位置的独立处理。

Step 3:帧计数器 + 首帧标志

复制代码
reg [3:0] frame_cnt;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        frame_cnt <= 4'd0;
    else if ((norow == NOR_MAX-1) && unpack_cksum)
        frame_cnt <= (frame_cnt == AVG_FRAMES-1) ? 4'd0 : frame_cnt + 1'b1;
end

wire first_frame = (frame_cnt == 4'd0);

每完成一帧(norow == NOR_MAX-1 && unpack_cksum)计数器加 1,范围 0~15,形成 16 帧累加窗口。first_frame 为新一轮累加的首帧标志,用于初始化累加器,避免沿用旧值。

Step 4:四通道独立累加

每个 Bayer 通道各配一组 13 位分布式 RAM,按 norow[1:0] 低两位做通道分离,仅在暗区累加 16 帧数据。这是整个模块的核心。

复制代码
(* ram_style = "distributed" *) reg [12:0] acc_g1 [WR_IW-1:0];
(* ram_style = "distributed" *) reg [12:0] acc_b  [WR_IW-1:0];
(* ram_style = "distributed" *) reg [12:0] acc_r  [WR_IW-1:0];
(* ram_style = "distributed" *) reg [12:0] acc_g2 [WR_IW-1:0];

always @(posedge clk) begin
    if (dark_phase && pre_de) begin
        case (norow[1:0])
            2'b00: acc_g1[col_addr] <= first_frame ? {3'd0, pre_data} : acc_g1[col_addr] + pre_data;
            2'b01: acc_b [col_addr] <= first_frame ? {3'd0, pre_data} : acc_b [col_addr] + pre_data;
            2'b10: acc_r [col_addr] <= first_frame ? {3'd0, pre_data} : acc_r [col_addr] + pre_data;
            2'b11: acc_g2[col_addr] <= first_frame ? {3'd0, pre_data} : acc_g2[col_addr] + pre_data;
        endcase
    end
end
  • 通道分离 :norow[1:0]=00→G1、01→B、10→R、11→G2,四个通道的暗电流特性独立,分开累加避免跨通道误差。

  • 首帧处理 :first_frame 时直接赋值 {3'd0, pre_data} 初始化累加器;后续帧执行 acc_x + pre_data 累加。

  • 13 位宽度:10 位原始数据最大值 1023,16 帧累加最大值 1023×16 =163681023×16=16368,需 14 位;这里取 13 位近似,FPGA 资源可省一个 LUT。

Step 5:乒乓缓冲切换标志

16 帧累加完成时,需要将新基准从后台 RAM 切换到前台,这由 buf_swap_pending 的翻转来协调。紧随累加逻辑之后,为下一步的乒乓写入做准备。

复制代码
reg buf_swap_pending;
wire frame_end = (frame_cnt == AVG_FRAMES-1)
               && (norow == NOR_MAX-1) && (unpack_cksum);

always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        buf_swap_pending <= 1'b0;
    else if (frame_end && buf_swap_pending == 0)
        buf_swap_pending <= 1'b1;
    else if (frame_end && buf_swap_pending)
        buf_swap_pending <= 1'b0;
end
  • frame_end:16 帧末脉冲,在 frame_cnt=15 且当前帧末时产生。

  • buf_swap_pending:每次 frame_end 翻转一次,控制前台/后台 RAM 的读写归属

Step 6:平均值计算与后台 RAM 写入

累加窗口完成后,在第 16 帧的有效区将累加值右移 4 位取平均,写入后台 RAM。这一段包含了 RAM 声明、写入条件和实际写入 always 三块内容。

写入触发信号 buf_sel 与地址遍历计数器 avg_cnt:

复制代码
wire buf_sel = (frame_cnt == AVG_FRAMES-1) && !dark_phase;

reg [7:0] avg_cnt;
always @(posedge clk) begin
    if(!rst_n)
        avg_cnt <= 'b0;
    else if(first_frame)
        avg_cnt <= 'b0;
    else if(buf_sel)
        avg_cnt <= (avg_cnt == WR_IW) ? avg_cnt : avg_cnt + 1'b1;
    else
        avg_cnt <= 'b0;
end

乒乓双 RAM 声明(每个通道两组,共 8 组 9 位分布式 RAM):

复制代码
(* ram_style = "distributed" *) reg [9:0] dark_ram_g1  [WR_IW:0];
(* ram_style = "distributed" *) reg [9:0] dark_ram_b   [WR_IW:0];
(* ram_style = "distributed" *) reg [9:0] dark_ram_r   [WR_IW:0];
(* ram_style = "distributed" *) reg [9:0] dark_ram_g2  [WR_IW:0];

(* ram_style = "distributed" *) reg [9:0] dark_ram1_g1 [WR_IW:0];
(* ram_style = "distributed" *) reg [9:0] dark_ram1_b  [WR_IW:0];
(* ram_style = "distributed" *) reg [9:0] dark_ram1_r  [WR_IW:0];
(* ram_style = "distributed" *) reg [9:0] dark_ram1_g2 [WR_IW:0];

后台写入 always(右移 4 位取平均):

复制代码
always @(posedge clk) begin
    if (buf_sel && avg_cnt < WR_IW) begin
        if (buf_swap_pending == 1'b0) begin
            dark_ram1_g1[avg_cnt] <= acc_g1[avg_cnt][AVG_SHIFT+8:AVG_SHIFT];
            dark_ram1_b [avg_cnt] <= acc_b [avg_cnt][AVG_SHIFT+8:AVG_SHIFT];
            dark_ram1_r [avg_cnt] <= acc_r [avg_cnt][AVG_SHIFT+8:AVG_SHIFT];
            dark_ram1_g2[avg_cnt] <= acc_g2[avg_cnt][AVG_SHIFT+8:AVG_SHIFT];
        end
        else begin
            dark_ram_g1 [avg_cnt] <= acc_g1[avg_cnt][AVG_SHIFT+8:AVG_SHIFT];
            dark_ram_b  [avg_cnt] <= acc_b [avg_cnt][AVG_SHIFT+8:AVG_SHIFT];
            dark_ram_r  [avg_cnt] <= acc_r [avg_cnt][AVG_SHIFT+8:AVG_SHIFT];
            dark_ram_g2 [avg_cnt] <= acc_g2[avg_cnt][AVG_SHIFT+8:AVG_SHIFT];
        end
    end
end

平均值计算:取累加值的 [AVG_SHIFT+8 : AVG_SHIFT] 位,等价于右移 4 位除以 16。9 位存储宽度可覆盖 1023/16×16 = 1023。

乒乓机制:

  • buf_swap_pending=0 → 前台读 dark_ram_*,后台写 dark_ram1_*

  • buf_swap_pending=1 → 前台读 dark_ram1_*,后台写 dark_ram_*

Step 7:有效区读取暗基准值

在有效区按 Bayer 通道类型,从前台 RAM 中读取对应像素位置的暗基准值,供后续减法使用。

复制代码
reg [DATA_WIDTH-1:0] dark_val_r;
always @(posedge clk) begin
    if (pre_de && !dark_phase) begin
        case (norow[1:0])
            2'b00: dark_val_r <= buf_swap_pending ? dark_ram1_g1[col_addr] : dark_ram_g1[col_addr];
            2'b01: dark_val_r <= buf_swap_pending ? dark_ram1_b [col_addr] : dark_ram_b [col_addr];
            2'b10: dark_val_r <= buf_swap_pending ? dark_ram1_r [col_addr] : dark_ram_r [col_addr];
            2'b11: dark_val_r <= buf_swap_pending ? dark_ram1_g2[col_addr] : dark_ram_g2[col_addr];
        endcase
    end
end

buf_swap_pending 决定读哪组 RAM,实现前后台切换。

Step 8:原始数据打拍对齐

暗基准 RAM 的读取有 1 拍延迟,需要对原始像素数据 pre_data 和有效信号 pre_de 各打一拍,使其与暗基准读取时序对齐后才能相减。

复制代码
reg [DATA_WIDTH-1:0] pre_data_r;
reg                  pre_de_r1;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        pre_data_r <= 'd0;
        pre_de_r1  <= 1'b0;
    end
    else begin
        pre_data_r <= pre_data;
        pre_de_r1  <= pre_de;
    end
end

Step 9:饱和减法输出

暗区直接透传,有效区执行饱和减法 post_data = max(pre_data_r - dark_val_r, 0)。这是整个流水线的最终输出段。

复制代码
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        post_data <= 'd0;
        post_de   <= 1'b0;
    end
    else if (dark_phase) begin
        post_data <= pre_data_r;
        post_de   <= pre_de_r1;
    end
    else if (pre_de_r1) begin
        post_data <= (pre_data_r >= dark_val_r) ? (pre_data_r - dark_val_r) : 'd0;
        post_de   <= 1'b1;
    end
    else begin
        post_data <= 'd0;
        post_de   <= 1'b0;
    end
end
  • 饱和减法:暗基准是 16 帧平均值,个别像素瞬时值可能略低于基准(噪声波动),强制归零避免截断误差。

  • 核心路径延迟 :暗基准读取 1 拍 + 输出打拍 1 拍 = 2 个时钟周期。

    ┌──────────── 每帧前 4 行(暗区) ────────────┐
    │ norow[1:0] → Bayer通道 → acc_x[col_addr] │
    │ first_frame首帧初始化,后续15帧累加 │
    └──────────────────────────────────────────────┘
    │
    ▼
    ┌──────────── 每帧后 812 行(有效区) ────────┐
    │ 路径A: 读前台dark_ram → dark_val_r │
    │ 路径B: pre_data打一拍 → pre_data_r │
    │ A/B对齐 → 饱和减法 → post_data │
    └──────────────────────────────────────────────┘
    │
    ▼ 16帧累计完成
    ┌──────────── 第16帧有效区后台刷新 ──────────┐
    │ acc_x[col] 右移4位 → 写入后台 dark_ram1_* │
    │ frame_end 触发 buf_swap_pending 翻转 │
    │ 前后台角色交换,下一帧立即使用新基准 │
    └──────────────────────────────────────────────┘

完整代码

复制代码
`timescale 1ns / 1ps
module Image_Sensor_r#(
    parameter WR_IW                 = 204  ,
    parameter NOR_MAX               = 816  ,
    parameter FPN_REDU              = 4    ,
    parameter AVG_FRAMES            = 16   ,
    parameter AVG_SHIFT             = 4    ,
	parameter DATA_WIDTH            = 'd10	
)(
    input                               clk                        ,
    input                               gclk_0                     ,
    input                               rst_n                      ,
    input              [DATA_WIDTH-1:0] norow                      ,
    input                               fifo_rst                   ,
    input                               unpack_cksum               ,
    input              [DATA_WIDTH-1:0] pre_data                   ,
    input                               pre_de                     ,
    output reg         [DATA_WIDTH-1:0] post_data                  ,
    output reg                          post_de                     
);

reg dark_phase;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        dark_phase <= 1'b1;
    else if((norow == NOR_MAX-1 && unpack_cksum))
        dark_phase <= 1'b1;    
    else if(norow == FPN_REDU-1 && unpack_cksum)
        dark_phase <= 1'b0;    
end

    reg      [DATA_WIDTH-1:0] col_addr                    ;
    always @(posedge clk ) begin
        if(!rst_n)col_addr    <=10'b0;
        else if(pre_de) col_addr  <=(col_addr ==WR_IW-1'b1)?0:col_addr +1'b1;
        else  col_addr    <=10'b0;
    end


    // ============================================================
    // 3. 帧结束脉冲 + 帧计数器
    // ============================================================


    reg [3:0] frame_cnt;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            frame_cnt <= 4'd0;
        else if ((norow == NOR_MAX-1) && unpack_cksum)
            frame_cnt <= (frame_cnt == AVG_FRAMES-1) ? 4'd0 : frame_cnt + 1'b1;
    end

     wire first_frame = (frame_cnt == 4'd0);   

    // ============================================================
    // 5. 多帧累加(13 位)
    // ============================================================
    (* ram_style = "distributed" *) reg [12:0] acc_g1 [WR_IW-1:0];
    (* ram_style = "distributed" *) reg [12:0] acc_b  [WR_IW-1:0];
    (* ram_style = "distributed" *) reg [12:0] acc_r  [WR_IW-1:0];
    (* ram_style = "distributed" *) reg [12:0] acc_g2 [WR_IW-1:0];

    always @(posedge clk) begin
        if (dark_phase && pre_de) begin
            case (norow[1:0])
                2'b00: acc_g1[col_addr] <= first_frame ? {3'd0, pre_data} : acc_g1[col_addr] + pre_data;
                2'b01: acc_b [col_addr] <= first_frame ? {3'd0, pre_data} : acc_b [col_addr] + pre_data;
                2'b10: acc_r [col_addr] <= first_frame ? {3'd0, pre_data} : acc_r [col_addr] + pre_data;
                2'b11: acc_g2[col_addr] <= first_frame ? {3'd0, pre_data} : acc_g2[col_addr] + pre_data;
            endcase
        end
    end

    // ============================================================
    // 5. 帧切换标志
    // ============================================================
    reg buf_swap_pending    ;
    wire frame_end   = (frame_cnt == AVG_FRAMES-1)&&(norow == NOR_MAX-1) && (unpack_cksum);
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            buf_swap_pending <= 1'b0;
        else if (frame_end&&buf_swap_pending==0)
            buf_swap_pending <= 1'b1;
        else if (frame_end && buf_swap_pending)
            buf_swap_pending <= 1'b0;
    end  
          
    // ============================================================
    // 6. 乒乓暗基准 RAM(9 位存,够用)
    // ============================================================
    (* ram_style = "distributed" *) reg [9:0] dark_ram_g1  [WR_IW:0];
    (* ram_style = "distributed" *) reg [9:0] dark_ram_b   [WR_IW:0];
    (* ram_style = "distributed" *) reg [9:0] dark_ram_r   [WR_IW:0];
    (* ram_style = "distributed" *) reg [9:0] dark_ram_g2  [WR_IW:0];
    (* ram_style = "distributed" *) reg [9:0] dark_ram1_g1 [WR_IW:0];
    (* ram_style = "distributed" *) reg [9:0] dark_ram1_b  [WR_IW:0];
    (* ram_style = "distributed" *) reg [9:0] dark_ram1_r  [WR_IW:0];
    (* ram_style = "distributed" *) reg [9:0] dark_ram1_g2 [WR_IW:0];
    // 写入后台 RAM:取累加值高 9 位
    wire  buf_sel =(frame_cnt == AVG_FRAMES-1)&& !dark_phase   ;

    reg [7:0]   avg_cnt ;
    always @(posedge clk ) begin
        if(!rst_n)avg_cnt<='b0;
        else if(first_frame)avg_cnt<='b0;
        else if(buf_sel)avg_cnt<=avg_cnt==WR_IW?avg_cnt:avg_cnt+1'b1;
        else avg_cnt<='b0;
    end

    always @(posedge clk) begin
        if (buf_sel&&avg_cnt<WR_IW) begin
            if (buf_swap_pending == 1'b0) begin
                dark_ram1_g1[avg_cnt] <= acc_g1[avg_cnt][AVG_SHIFT+8:AVG_SHIFT];
                dark_ram1_b [avg_cnt] <= acc_b [avg_cnt][AVG_SHIFT+8:AVG_SHIFT];
                dark_ram1_r [avg_cnt] <= acc_r [avg_cnt][AVG_SHIFT+8:AVG_SHIFT];
                dark_ram1_g2[avg_cnt] <= acc_g2[avg_cnt][AVG_SHIFT+8:AVG_SHIFT];
            end
            else begin
                dark_ram_g1 [avg_cnt] <= acc_g1[avg_cnt][AVG_SHIFT+8:AVG_SHIFT];
                dark_ram_b  [avg_cnt] <= acc_b [avg_cnt][AVG_SHIFT+8:AVG_SHIFT];
                dark_ram_r  [avg_cnt] <= acc_r [avg_cnt][AVG_SHIFT+8:AVG_SHIFT];
                dark_ram_g2 [avg_cnt] <= acc_g2[avg_cnt][AVG_SHIFT+8:AVG_SHIFT];
            end
        end
    end    


    // ============================================================
    // 9. 读前台暗基准
    // ============================================================
    reg [DATA_WIDTH-1:0] dark_val_r;
    always @(posedge clk) begin
        if (pre_de && !dark_phase) begin
            case (norow[1:0])
                2'b00: dark_val_r <= buf_swap_pending ? dark_ram1_g1[col_addr] : dark_ram_g1[col_addr];
                2'b01: dark_val_r <= buf_swap_pending ? dark_ram1_b [col_addr] : dark_ram_b [col_addr];
                2'b10: dark_val_r <= buf_swap_pending ? dark_ram1_r [col_addr] : dark_ram_r [col_addr];
                2'b11: dark_val_r <= buf_swap_pending ? dark_ram1_g2[col_addr] : dark_ram_g2[col_addr];
            endcase
        end
    end

    // ============================================================
    // 10. 打拍 + 饱和减法输出
    // ============================================================
    reg     [DATA_WIDTH-1:0] pre_data_r       ;
    reg                      pre_de_r1        ;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            pre_data_r <= 'd0;
            pre_de_r1  <= 1'b0;
        end
        else begin
            pre_data_r <= pre_data;
            pre_de_r1  <= pre_de;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            post_data <= 'd0;
            post_de   <= 1'b0;
        end
        else if (dark_phase) begin
            post_data <= pre_data_r;
            post_de   <= pre_de_r1;
        end
        else if (pre_de_r1) begin
            post_data <= (pre_data_r >= dark_val_r) ? (pre_data_r - dark_val_r): 'd0;
            post_de   <= 1'b1;
        end
        else begin
            post_data <= 'd0;
            post_de   <= 1'b0;
        end
    end


endmodule
相关推荐
幻智星科技2 小时前
FPGA纯硬件UDP桥接:微秒级响应新范式
c++·fpga开发·硬件架构
小蘇的FPGA2 小时前
基于FPGA实现PCIe H2C过程中TAG的一种管理方法
fpga开发
威嵌神州2 小时前
一台加固平板,能替掉几台现场设备?
嵌入式硬件·fpga开发·电脑·平板
幻智星科技15 小时前
树形架构破解FPGA扇出瓶颈
fpga开发·硬件架构
FPGA小迷弟18 小时前
FPGA开发Vivado Block Design/Vitis/SDK/xlinx/开发教程,保姆级别,一看就会!
网络协议·tcp/ip·fpga开发·verilog·fpga
FPGA小迷弟19 小时前
基于FPGA实现智能网卡的概念,什么是智能网卡
网络协议·tcp/ip·fpga开发·verilog·fpga
好焊孙辉21 小时前
螺柱焊机哪家质量好
python·fpga开发
XMAIPC_Robot1 天前
基于 i.MX95 平台在新一代工业控制器中的应用 — 工业车载平板、半导体设备主控方案解析
arm开发·人工智能·机器学习·fpga开发·rk3588+fpga·i.mx95
szxinmai主板定制专家1 天前
基于 AM62x + Artix-7 的低功耗 ARM+FPGA 平台在轨道交通车门控制中的应用
arm开发·人工智能·fpga开发·zynq·am62x