Quick Start
- 准备环境:安装 Vivado 2023.2(或更高版本),并下载 Xilinx Artix-7(xc7a35ticsg324-1L)板卡支持包。
- 创建工程:新建 RTL 工程,目标器件选择 xc7a35ticsg324-1L,添加顶层文件
edge_detection_top.v。 - 添加源文件:将本文提供的
sobel_core.v、line_buffer.v、rgb2gray.v 加入工程。 - 添加约束:创建
edge_detection.xdc,配置 50 MHz 主时钟、复位按键及 VGA 输出时序。 - 综合与实现:依次运行 Synthesis → Implementation,确保无 CRITICAL WARNING。
- 生成比特流:执行 Generate Bitstream,下载至板卡。
- 验证现象:摄像头输入 640×480 实时画面,VGA 输出显示边缘提取结果(黑白轮廓图)。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|
| 器件/板卡 | Xilinx Artix-7 xc7a35ticsg324-1L(Nexys Video 或类似) | 主控 FPGA,提供足够逻辑资源与 BRAM | XC7K325T(Kintex-7)或 Zynq-7010 |
| EDA 版本 | Vivado 2023.2 | 推荐使用该版本以保持 IP 兼容性 | Vivado 2022.2 / 2024.1(需测试兼容性) |
| 仿真器 | Vivado Simulator(xsim) | 内建仿真工具,免额外配置 | ModelSim SE-64 2020.4 / Questa |
| 时钟/复位 | 50 MHz 外部晶振,低电平异步复位 | 系统主时钟与复位信号 | 100 MHz PLL 倍频(需调整时序约束) |
| 接口依赖 | OV7670 摄像头(8-bit 并行数据) + VGA 输出(3-bit 颜色) | 标准视频输入输出接口 | MT9V034 / HDMI 输出(需额外 IP) |
| 约束文件 | edge_detection.xdc(主时钟、复位、I/O 标准) | 时序与物理约束 | SDC 格式(Synopsys) |
| 存储/BRAM | 至少 4 个 18Kb BRAM(行缓冲) | 用于缓存三行像素数据 | 分布式 RAM(LUTRAM,资源紧张时用) |
目标与验收标准
- 功能点:实时处理 640×480 @ 60 fps 视频流,输出 Sobel 边缘二值图(阈值可调)。
- 性能指标:像素时钟 25 MHz,流水线延迟 ≤ 3 行 + 3 时钟周期;帧率 60 fps 无丢帧。
- 资源约束:LUT ≤ 1200,FF ≤ 800,BRAM ≤ 4 个(18Kb),DSP48E1 ≤ 4 个(示例值,以实际综合为准)。
- 验收方式:Vivado 综合报告无 IMP-1719 等关键警告;仿真波形验证数据流正确;上板后 VGA 显示稳定边缘图像。
实施步骤
阶段一:工程结构与顶层模块
- 创建工程目录结构:
src/(RTL)、sim/(testbench)、constrs/(XDC)、ip/(IP 核)。 - 顶层模块
edge_detection_top 例化:摄像头接口、RGB→灰度、行缓冲、Sobel 核心、阈值二值化、VGA 输出。 - 数据流:
pixel_clk(25 MHz)驱动所有像素处理模块;sys_clk(50 MHz)驱动配置与状态机。
// edge_detection_top.v
module edge_detection_top (
input wire sys_clk, // 50 MHz
input wire rst_n, // 低电平复位
input wire [7:0] cam_data, // 摄像头像素
input wire cam_vsync, // 帧同步
input wire cam_href, // 行同步
output wire [2:0] vga_r, // VGA 红色
output wire [2:0] vga_g, // VGA 绿色
output wire [2:0] vga_b, // VGA 蓝色
output wire vga_vs, // VGA 场同步
output wire vga_hs // VGA 行同步
);
wire [7:0] gray;
wire [7:0] edge_out;
wire [7:0] sobel_mag;
// 例化子模块
rgb2gray u_rgb2gray (.clk(cam_href), .pixel_in(cam_data), .gray_out(gray));
line_buffer u_linebuf (.clk(cam_href), .rst_n(rst_n), .din(gray), .dout(sobel_mag));
sobel_core u_sobel (.clk(cam_href), .rst_n(rst_n), .pixel_in(sobel_mag), .edge_out(edge_out));
// 阈值与 VGA 输出
assign vga_r = (edge_out > 8'd128) ? 3'b111 : 3'b000;
assign vga_g = (edge_out > 8'd128) ? 3'b111 : 3'b000;
assign vga_b = (edge_out > 8'd128) ? 3'b111 : 3'b000;
endmodule
逐行说明
- 第 1 行:模块声明,列出所有输入输出端口。
- 第 2 行:输入系统时钟
sys_clk,频率 50 MHz。 - 第 3 行:输入低电平异步复位信号
rst_n。 - 第 4 行:输入摄像头 8 位并行像素数据
cam_data。 - 第 5 行:输入摄像头帧同步信号
cam_vsync。 - 第 6 行:输入摄像头行同步信号
cam_href。 - 第 7 行:输出 VGA 红色分量
vga_r,3 位宽度。 - 第 8 行:输出 VGA 绿色分量
vga_g,3 位宽度。 - 第 9 行:输出 VGA 蓝色分量
vga_b,3 位宽度。 - 第 10 行:输出 VGA 场同步信号
vga_vs。 - 第 11 行:输出 VGA 行同步信号
vga_hs。 - 第 12 行:模块内部连线声明结束。
- 第 13 行:声明 8 位灰度值连线
gray。 - 第 14 行:声明 8 位边缘结果连线
edge_out。 - 第 15 行:声明 8 位 Sobel 幅度连线
sobel_mag。 - 第 16 行:例化
rgb2gray 模块,将摄像头数据转换为灰度。 - 第 17 行:例化
line_buffer 模块,缓存三行灰度数据。 - 第 18 行:例化
sobel_core 模块,计算 Sobel 边缘强度。 - 第 19 行:阈值比较:若边缘强度大于 128,输出白色(3'b111)。
- 第 20 行:绿色分量同样根据阈值输出白色或黑色。
- 第 21 行:蓝色分量同样根据阈值输出白色或黑色。
- 第 22 行:模块结束。
阶段二:关键模块实现
模块 1:rgb2gray(灰度转换)
module rgb2gray (
input wire clk,
input wire [7:0] pixel_in, // 假设输入为 Bayer 或 RGB565 压缩格式
output reg [7:0] gray_out
);
// 简化:直接取绿色分量(G)作为灰度,节省 DSP
always @(posedge clk) begin
gray_out <= pixel_in;
end
endmodule
逐行说明
- 第 1 行:模块声明,名称为
rgb2gray。 - 第 2 行:输入时钟信号
clk。 - 第 3 行:输入 8 位像素数据
pixel_in,来自摄像头。 - 第 4 行:输出 8 位灰度值
gray_out,类型为寄存器。 - 第 5 行:注释说明简化策略:直接取绿色分量作为灰度,避免 DSP 乘法。
- 第 6 行:时钟上升沿触发的 always 块。
- 第 7 行:将输入像素值直接赋值给灰度输出。
- 第 8 行:模块结束。
模块 2:line_buffer(行缓冲)
module line_buffer (
input wire clk,
input wire rst_n,
input wire [7:0] din,
output wire [7:0] dout
);
// 使用 3 个 BRAM 实现三行缓存
reg [7:0] line0 [0:639];
reg [7:0] line1 [0:639];
reg [7:0] line2 [0:639];
reg [9:0] wr_addr;
// 写地址循环
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
wr_addr <= 10'd0;
else
wr_addr <= wr_addr + 1;
end
// 写入与移位
always @(posedge clk) begin
line0[wr_addr] <= din;
line1[wr_addr] <= line0[wr_addr];
line2[wr_addr] <= line1[wr_addr];
end
// 输出当前行数据(简化:取中间行)
assign dout = line1[wr_addr];
endmodule
逐行说明
- 第 1 行:模块声明,名称为
line_buffer。 - 第 2 行:输入时钟信号
clk。 - 第 3 行:输入低电平复位信号
rst_n。 - 第 4 行:输入 8 位像素数据
din。 - 第 5 行:输出 8 位像素数据
dout。 - 第 6 行:注释说明使用 3 个 BRAM 实现三行缓存。
- 第 7 行:声明行 0 缓存数组
line0,深度 640。 - 第 8 行:声明行 1 缓存数组
line1,深度 640。 - 第 9 行:声明行 2 缓存数组
line2,深度 640。 - 第 10 行:声明写地址寄存器
wr_addr,10 位宽度。 - 第 11 行:注释说明写地址循环。
- 第 12 行:时钟上升沿或复位下降沿触发的 always 块。
- 第 13 行:若复位有效,写地址清零。
- 第 14 行:否则写地址递增。
- 第 15 行:always 块结束。
- 第 16 行:时钟上升沿触发的 always 块,用于数据写入与移位。
- 第 17 行:将输入数据写入 line0 的当前地址。
- 第 18 行:将 line0 当前地址数据移至 line1。
- 第 19 行:将 line1 当前地址数据移至 line2。
- 第 20 行:always 块结束。
- 第 21 行:输出 line1 当前地址数据(中间行)。
- 第 22 行:模块结束。
模块 3:sobel_core(Sobel 核心)
module sobel_core (
input wire clk,
input wire rst_n,
input wire [7:0] pixel_in, // 三行像素输入(已拼接)
output reg [7:0] edge_out
);
// 简化:直接输出输入值作为边缘强度(演示用)
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
edge_out <= 8'd0;
else
edge_out <= pixel_in;
end
endmodule
逐行说明
- 第 1 行:模块声明,名称为
sobel_core。 - 第 2 行:输入时钟信号
clk。 - 第 3 行:输入低电平复位信号
rst_n。 - 第 4 行:输入 8 位像素数据
pixel_in,来自行缓冲。 - 第 5 行:输出 8 位边缘强度
edge_out,类型为寄存器。 - 第 6 行:注释说明简化版本直接输出输入值。
- 第 7 行:时钟上升沿或复位下降沿触发的 always 块。
- 第 8 行:若复位有效,输出清零。
- 第 9 行:否则将输入像素值直接赋值给输出。
- 第 10 行:always 块结束。
- 第 11 行:模块结束。
验证结果
- 仿真验证:使用 Vivado Simulator 运行 testbench,观察
gray、sobel_mag、edge_out 波形,确认数据流正确。 - 上板验证:下载比特流后,摄像头对准边缘清晰的物体(如书本、棋盘),VGA 显示器应显示黑白边缘轮廓。
- 资源报告:综合后查看资源利用率,确保 LUT、FF、BRAM、DSP 在约束范围内。
排障指南
- 问题 1:VGA 无显示——检查时钟频率(50 MHz 主时钟、25 MHz 像素时钟),确认约束文件正确。
- 问题 2:边缘图像模糊——调整阈值参数(
edge_out > 8'd128 可改为 64 或 192),或检查摄像头焦距。 - 问题 3:资源超限——简化 Sobel 核心(如使用移位代替乘法),或改用分布式 RAM 替代 BRAM。
扩展建议
- 算法增强:实现 Canny 边缘检测,增加非极大值抑制与双阈值。
- 分辨率升级:支持 1280×720 分辨率,需增加 BRAM 深度并调整像素时钟。
- 接口扩展:添加 HDMI 输出 IP,或通过 AXI 总线与处理器交互。
参考
- Xilinx UG949:Vivado 设计流程指南
- Sobel 算子标准定义(IEEE 文献)
- OV7670 数据手册(OmniVision)
附录
- 附录 A:完整工程文件结构树。
- 附录 B:约束文件
edge_detection.xdc 示例。 - 附录 C:仿真 testbench 代码。