FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
登录
首页-技术文章/快讯-技术分享-正文

2026年5月:毕业设计选题——基于FPGA的实时图像边缘检测系统

二牛学FPGA二牛学FPGA
技术分享
1天前
0
0
9

Quick Start:最短路径跑通边缘检测

  • 准备硬件与工具:确认你有一块 FPGA 开发板(如 Xilinx Artix-7 / Zynq-7000 系列)、一根 Micro-USB 下载线、一台安装好 Vivado 2023.2 或更高版本的 PC。
  • 获取基础工程:从课程资源或 GitHub 下载“Sobel_Edge_Detection_FPGA”模板工程,包含 RTL 源码、约束文件(.xdc)和仿真测试平台。
  • 打开工程并综合:在 Vivado 中打开 .xpr 项目文件,点击“Run Synthesis”;等待约 3–5 分钟,观察综合报告无严重警告或错误。
  • 运行实现(Implementation):综合通过后,点击“Run Implementation”;实现完成后检查时序报告(Setup/Hold 无违例)。
  • 生成比特流并下载:点击“Generate Bitstream”,完成后连接开发板并下载 .bit 文件。
  • 观察输出:将摄像头(如 OV5640)或 HDMI 输入源接入板卡,通过 VGA/HDMI 显示器观察实时边缘检测效果。预期看到原始图像的 Sobel 边缘轮廓,延迟 < 1 帧(约 16.7ms @60fps)。

验收点:显示器上出现清晰的二值化边缘图像,无画面撕裂或明显闪烁。若失败,先检查摄像头时钟与复位、HDMI 驱动芯片初始化是否完成。

前置条件与环境

项目推荐值说明替代方案
FPGA 器件Xilinx Artix-7 XC7A35T逻辑单元 ~33k,片上 BRAM 约 1800 Kb,满足 640×480 实时处理Zynq-7010 / Cyclone IV E
EDA 工具Vivado 2023.2支持 Artix-7 全流程,含 IP 核与调试核ISE 14.7(仅限老器件)
仿真器Vivado Simulator 或 ModelSim SE-64 2020.4用于 RTL 与后仿QuestaSim / VCS
时钟源50 MHz 板载晶振经 MMCM/PLL 生成 25 MHz 像素时钟(640×480 @60fps)外部有源晶振
复位低电平有效,全局异步复位确保所有寄存器初始状态可控同步复位(增加面积)
接口依赖OV5640 摄像头模块 + HDMI 输出输入 8-bit 灰度数据,输出 8-bit 边缘二值图VGA 输出(需 DAC 芯片)
约束文件时序约束(create_clock)、I/O 约束(引脚分配)必须包含输入延迟与输出延迟约束自动推导(不推荐)

目标与验收标准

  • 功能点:实时采集摄像头图像 → 灰度转换 → 3×3 Sobel 边缘检测 → 二值化 → HDMI 输出显示。
  • 性能指标:处理延迟 ≤ 1 帧(16.7ms @60fps),像素时钟 25 MHz,吞吐率 ≥ 640×480×60 像素/秒。
  • 资源与 Fmax:LUT 占用 ≤ 1500(典型 Artix-7 约 1200 LUT + 4 BRAM),Fmax ≥ 100 MHz(像素时钟域)。
  • 验收方式

    实施步骤

    阶段一:工程结构与顶层模块

    创建工程目录结构:src/(RTL)、sim/(测试平台)、constr/(约束)、ip/(IP 核)。顶层模块 edge_detection_top 实例化摄像头驱动、灰度转换、Sobel 核、二值化、帧缓冲(BRAM)与 HDMI 输出。

    // edge_detection_top.v
    module edge_detection_top (
        input  wire        clk_50m,        // 板载 50 MHz 时钟
        input  wire        rst_n,          // 低电平复位
        // 摄像头接口
        input  wire        cam_pclk,       // 像素时钟
        input  wire        cam_vsync,      // 帧同步
        input  wire        cam_href,       // 行有效
        input  wire [7:0]  cam_data,       // 8-bit 灰度数据
        // HDMI 输出
        output wire        hdmi_clk,
        output wire        hdmi_vsync,
        output wire        hdmi_hsync,
        output wire [7:0]  hdmi_data
    );
    
        // 内部信号
        wire [7:0] gray_data;
        wire [7:0] edge_data;
        wire [7:0] frame_buf_data;
        wire       wr_en;
        wire       rd_en;
    
        // 实例化 Sobel 模块
        sobel_core u_sobel (
            .clk        (cam_pclk),
            .rst_n      (rst_n),
            .pixel_in   (cam_data),
            .pixel_out  (edge_data)
        );
    
        // 帧缓冲(双端口 BRAM)
        frame_buffer u_fb (
            .clk_a      (cam_pclk),
            .we_a       (wr_en),
            .addr_a     (wr_addr),
            .din_a      (edge_data),
            .clk_b      (hdmi_clk),
            .re_b       (rd_en),
            .addr_b     (rd_addr),
            .dout_b     (frame_buf_data)
        );
    
        // HDMI 输出驱动
        hdmi_driver u_hdmi (
            .clk        (hdmi_clk),
            .rst_n      (rst_n),
            .pixel_in   (frame_buf_data),
            .vsync      (hdmi_vsync),
            .hsync      (hdmi_hsync),
            .data       (hdmi_data)
        );
    
    endmodule

    逐行说明

    • 第 1–2 行:模块声明与输入输出端口。clk_50m 是板级主时钟,rst_n 为低电平有效异步复位。
    • 第 3–7 行:摄像头接口信号。cam_pclk 是像素时钟(通常 25 MHz),cam_vsync 高电平表示新帧开始,cam_href 高电平表示行数据有效,cam_data 是 8-bit 灰度值。
    • 第 8–12 行:HDMI 输出信号。hdmi_clk 是 HDMI 像素时钟(与输入像素时钟同频但可能不同相),hdmi_vsync/hsync 为同步信号,hdmi_data 是 8-bit 灰度边缘图。
    • 第 14–18 行:内部连线声明。gray_data 暂未使用(本例直接使用 cam_data 作为灰度输入),edge_data 是 Sobel 输出,frame_buf_data 是帧缓冲读出数据。
    • 第 20–25 行:实例化 sobel_core 模块,输入像素时钟域(cam_pclk),输出边缘检测结果。
    • 第 27–35 行:实例化双端口 BRAM 帧缓冲,写端口在 cam_pclk 域,读端口在 hdmi_clk 域,实现跨时钟域数据传递(注意:此处未显式处理 CDC,实际需添加异步 FIFO 或握手逻辑)。
    • 第 37–44 行:实例化 HDMI 驱动模块,将帧缓冲数据转换为标准 HDMI 时序输出。

    常见坑与排查

    • 坑 1:跨时钟域(cam_pclk → hdmi_clk)未做同步,导致显示花屏。解决:在帧缓冲前插入异步 FIFO(使用 Xilinx FIFO Generator IP)。
    • 坑 2:复位信号未同步到每个时钟域,导致模块初始化失败。解决:每个时钟域使用独立的同步复位链。

    阶段二:关键模块——Sobel 核

    Sobel 核采用流水线架构:3×3 窗口生成 → 梯度计算 → 二值化。以下为 RTL 实现。

    // sobel_core.v
    module sobel_core (
        input  wire        clk,
        input  wire        rst_n,
        input  wire [7:0]  pixel_in,
        output reg  [7:0]  pixel_out
    );
    
        // 行缓冲(Line Buffer):3 行,每行 640 像素
        reg [7:0] line_buf [0:2][0:639];
        reg [7:0] window [0:2][0:2];
    
        // 窗口移位逻辑
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                for (int i = 0; i &lt; 3; i++) begin
                    for (int j = 0; j &lt; 640; j++) begin
                        line_buf[i][j] &lt;= 8'd0;
                    end
                end
            end else begin
                // 将新像素写入第 0 行,并逐行下移
                line_buf[0][0] &lt;= pixel_in;
                for (int j = 1; j &lt; 640; j++) begin
                    line_buf[0][j] &lt;= line_buf[0][j-1];
                end
                line_buf[1] &lt;= line_buf[0];
                line_buf[2] &lt;= line_buf[1];
            end
        end
    
        // 从行缓冲提取 3×3 窗口
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                for (int i = 0; i &lt; 3; i++)
                    for (int j = 0; j &lt; 3; j++)
                        window[i][j] &lt;= 8'd0;
            end else begin
                window[0][0] &lt;= line_buf[0][0];
                window[0][1] &lt;= line_buf[0][1];
                window[0][2] &lt;= line_buf[0][2];
                window[1][0] &lt;= line_buf[1][0];
                window[1][1] &lt;= line_buf[1][1];
                window[1][2] &lt;= line_buf[1][2];
                window[2][0] &lt;= line_buf[2][0];
                window[2][1] &lt;= line_buf[2][1];
                window[2][2] &lt;= line_buf[2][2];
            end
        end
    
        // 计算 Gx 和 Gy
        wire signed [10:0] Gx = (window[0][2] + 2*window[1][2] + window[2][2])
                              - (window[0][0] + 2*window[1][0] + window[2][0]);
        wire signed [10:0] Gy = (window[0][0] + 2*window[0][1] + window[0][2])
                              - (window[2][0] + 2*window[2][1] + window[2][2]);
    
        // 计算梯度幅值(近似:|Gx| + |Gy|)
        wire [10:0] magnitude = (Gx &gt;= 0 ? Gx : -Gx) + (Gy &gt;= 0 ? Gy : -Gy);
    
        // 二值化(阈值 128)
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n)
                pixel_out &lt;= 8'd0;
            else if (magnitude &gt; 10'd128)
                pixel_out &lt;= 8'hFF;
            else
                pixel_out &lt;= 8'h00;
        end
    
    endmodule

    逐行说明

      第 1–6 行:模块声明。输入像素 pixel_in(8-bit 灰度),输出 pixel_out(8-bit 二值边缘)。第 8–9 行:定义行缓冲 line_buf[0:2][0:639] 存储 3 行图像数据,window[0:2][0:2] 为 3×3 滑动窗口。第 11–25 行:行缓冲更新逻辑。每个时钟周期将新像素写入 line_buf[0][0],并右移;同时 line_buf[1] 复制 line_buf[0],line_buf[2] 复制 line_buf[1],实现行延迟。注意:此处使用了阻塞赋值(=)会导致组合反馈,实际应使用非阻塞赋值(<=)并调整移位顺序,或采用双端口 BRAM 实现行缓冲以节省 LUT。第 27–40 行:从行缓冲提取 3×3 窗口。每个时钟周期将 line_buf 的 9 个像素赋值给 window 寄存器。第 42–44 行:计算水平梯度 Gx 和垂直梯度 Gy,使用 signed 运算防止溢出。公式:Gx = (P02+2P12+P22) - (P00+2P10+P20),Gy = (P00+2P01+P02) - (P20+2P21+P22)。第 46 行:近似梯度幅值 magnitude = |Gx| + |Gy|,避免开平方运算。第 48–55 行:二值化输出。阈值设为 128(可参数化),大于阈值输出 0xFF(白色),否则输出 0x00(黑色)。

    常见坑与排查

      坑 1:行缓冲实现错误导致边缘偏移。检查:仿真中对比输入图像与输出延迟,正确延迟应为 640×2 + 2 个像素时钟(2 行 + 窗口中心)。坑 2:梯度计算溢出。确保 Gx/Gy 位宽为 11-bit signed(最大 ±1020),magnitude 位宽 11-bit。

    阶段三:时序与约束

    在 .xdc 文件中添加以下约束:

    # 主时钟约束
    create_clock -period 20.000 [get_ports clk_50m]  # 50 MHz
    
    # 像素时钟(来自摄像头)
    create_clock -period 40.000 [get_ports cam_pclk]  # 25 MHz
    
    # HDMI 时钟(与像素时钟同频但需单独约束)
    create_clock -period 40.000 [get_ports hdmi_clk]  # 25 MHz
    
    # 输入延迟约束(摄像头数据相对 cam_pclk)
    set_input_delay -clock [get_clocks cam_pclk] -max 5.0 [get_ports cam_data*]
    set_input_delay -clock [get_clocks cam_pclk] -min 2.0 [get_ports cam_data*]
    
    # 输出延迟约束(HDMI 数据相对 hdmi_clk)
    set_output_delay -clock [get_clocks hdmi_clk] -max 4.0 [get_ports hdmi_data*]
    set_output_delay -clock [get_clocks hdmi_clk] -min 1.0 [get_ports hdmi_data*]

    逐行说明

      第 1–2 行:定义 50 MHz 主时钟,周期 20 ns。第 4–5 行:定义 25 MHz 像素时钟(cam_pclk),周期 40 ns。第 7–8 行:定义 HDMI 输出时钟,频率与像素时钟相同但需独立约束,便于时序分析。第 10–11 行:设置输入延迟,max=5ns 表示数据在时钟沿后 5ns 到达,min=2ns 表示数据保持时间。这些值需根据摄像头数据手册调整。第 13–14 行:设置输出延迟,max=4ns 表示 FPGA 输出数据到 HDMI 接收端需在时钟沿前 4ns 稳定,min=1ns 表示保持时间。

    常见坑与排查

      坑 1:未定义 cam_pclk 和 hdmi_clk 导致时序分析忽略这些路径,上板可能随机失败。解决:务必为所有时钟域创建时钟约束。坑 2:输入/输出延迟值过于乐观(如设为 0),导致时序报告显示违例但实际工作。解决:参考数据手册并留 20% 余量。

    阶段四:验证与仿真

    编写测试平台,输入 640×480 测试图像(如 Lena.bmp 转为 .coe 文件),通过 $readmemh 加载到仿真 ROM。运行 500,000 个时钟周期(约 20ms 仿真时间),检查输出图像与 MATLAB 参考结果。

    // tb_sobel.v 片段
    initial begin
        // 加载测试图像到行缓冲模拟器
        $readmemh("lena_gray.coe", img_mem);
        // 复位
        rst_n = 0;
        #100 rst_n = 1;
        // 等待 2 行 + 2 像素后开始检查
        #(640*2*40 + 2*40);  // 单位 ns
        // 检查第一个有效输出
        if (pixel_out == expected[0])
            $display("PASS: pixel 0");
        else
            $display("FAIL: pixel 0, got %d, expected %d", pixel_out, expected[0]);
    end

    逐行说明

      第 1–2 行:使用 $readmemh 将 .coe 文件加载到仿真内存 img_mem 中,模拟摄像头逐像素输出。第 3–5 行:复位逻辑,低电平保持 100 ns 后释放。第 6–7 行:等待 2 行 + 2 像素时间(640×2×40 ns + 2×40 ns = 51280 ns),确保 Sobel 核输出第一个有效像素。第 8–12 行:比较输出像素与期望值(由 MATLAB 预计算),打印 PASS/FAIL。

    常见坑与排查

      坑 1:仿真时间不足,未等到第一个有效输出。解决:计算延迟并设置足够长的仿真时间。坑 2:期望值计算错误(MATLAB 与 RTL 算法不一致)。解决:确保 MATLAB 使用相同 Sobel 核和阈值。

    原理与设计说明

    为什么选择 Sobel 算子:Sobel 是经典一阶微分算子,对噪声有一定抑制(通过加权平均),硬件实现只需加法和移位,无需乘法器。相比 Canny 算子(需要高斯滤波、非极大值抑制、双阈值),Sobel 资源开销低 5–10 倍,适合入门级 FPGA 实时系统。

    关键 Trade-off

    吞吐 vs 延迟:全流水线架构每个时钟输出一个像素,延迟固定为 2 行 + 2
      资源 vs Fmax:行缓冲使用 BRAM(每个 640×8-bit 约 5 Kb)比 LUT 移位寄存器节省 90% 逻辑资源,但 BRAM 读取延迟 1 时钟周期,需调整流水线深度。吞吐 vs 延迟:全流水线架构每个时钟输出一个像素,延迟固定为 2 行 + 2
    标签:
    本文原创,作者:二牛学FPGA,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
    如需转载,请注明出处:https://z.shaonianxue.cn/42183.html
    二牛学FPGA

    二牛学FPGA

    初级工程师
    这家伙真懒,几个字都不愿写!
    1.06K20.53W4.04W3.67W
    分享:
    成电国芯FPGA赛事课即将上线
    FPGA入门指南:从Verilog到时序收敛的完整学习路线(2026年Q2版)
    FPGA入门指南:从Verilog到时序收敛的完整学习路线(2026年Q2版)上一篇
    2026年Q2 FPGA就业市场对UVM验证与AI部署复合技能的需求分析指南下一篇
    2026年Q2 FPGA就业市场对UVM验证与AI部署复合技能的需求分析指南
    相关文章
    总数:1.10K
    大模型边缘部署:FPGA上实现轻量化Transformer推理框架

    大模型边缘部署:FPGA上实现轻量化Transformer推理框架

    QuickStart步骤一:准备环境。安装Vivado2023.2(…
    技术分享
    6天前
    0
    0
    12
    0
    基于国产FPGA的实时语音降噪系统设计与实现——毕设实施指南

    基于国产FPGA的实时语音降噪系统设计与实现——毕设实施指南

    QuickStart本指南帮助你在国产FPGA平台上快速搭建一个实时语…
    技术分享
    15小时前
    0
    0
    13
    0
    Verilog有限状态机三段式写法与资源优化

    Verilog有限状态机三段式写法与资源优化

    QuickStart步骤一:新建一个Vivado工程,器件选择X…
    技术分享
    15天前
    0
    0
    27
    0
    评论表单游客 您好,欢迎参与讨论。
    加载中…
    评论列表
    总数:0
    FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
    没有相关内容