Quick Start
- [object Object]
验收点:显示器上能清晰看到摄像头拍摄场景的边缘轮廓,无明显撕裂或延迟(帧率≥30fps)。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|---|---|---|
| FPGA器件 | 安路EG4S20 / 紫光同创Logos-2 | 国产主流,LUT资源≥20K,支持MIPI/DVP接口 | 高云GW2A系列 |
| EDA版本 | Anlogic TD 5.0+ / 紫光PDS 2025.2+ | 需支持Synthesis与P&R,含IP核生成器 | Vivado(仅用于仿真验证) |
| 仿真器 | ModelSim SE-64 2020.4 / 自带的TD Sim | 支持Verilog/VHDL混合仿真 | VCS / Verilator(开源) |
| 时钟系统 | 系统时钟50MHz,像素时钟25MHz | 由PLL生成,用于摄像头与HDMI同步 | 可调整 |
| 复位 | 低电平有效,上电复位≥10ms | 确保初始化时序正确 | 高电平复位(需修改RTL) |
| 接口依赖 | OV5640 DVP 8-bit + 行场同步;HDMI输出(RGB888) | 需开发板配套FPC排线 | OV7725 / 直接VGA输出 |
| 约束文件 | .sdc(时序约束)+ .adc(引脚分配) | 必须包含时钟周期、输入输出延迟 | 手动编写 |
目标与验收标准
- 功能点:实时采集640x480@30fps灰度图像,经Sobel边缘检测后输出至HDMI显示器。
- 性能指标:边缘检测延迟≤1行(流水线结构),帧率≥30fps,无丢帧。
- 资源占用:LUT≤5K(示例),Block RAM≤4个(18Kb),DSP≤0(Sobel用加法器实现)。
- Fmax:像素时钟≥25MHz(对应640x480@30fps),系统时钟≥50MHz。
- 验收方式:上板观察边缘轮廓清晰度;仿真验证输入输出数据一致性;资源报告满足约束。
实施步骤
3.1 工程结构
- 顶层模块:top.v(实例化摄像头驱动、Sobel边缘检测、HDMI驱动)。
- 子模块:camera_if.v(DVP接口与像素同步)、sobel_edge.v(核心算法)、hdmi_out.v(RGB转HDMI)。
- 约束文件:top.sdc(时序)、top.adc(引脚)。
- 仿真脚本:tb_top.v(Testbench)。
3.2 关键模块:Sobel边缘检测(sobel_edge.v)
module sobel_edge (
input wire clk, // 像素时钟 25MHz
input wire rst_n, // 低电平复位
input wire [7:0] pixel_in, // 灰度像素输入
input wire data_valid, // 像素有效标志
input wire hsync_in, // 行同步
input wire vsync_in, // 场同步
output reg [7:0] edge_out, // 边缘强度输出
output reg data_valid_out,
output reg hsync_out,
output reg vsync_out
);
// 行缓存(3行,每行640像素)
reg [7:0] line_buf [0:1][0:639];
reg [9:0] col_cnt;
wire [7:0] p11, p12, p13, p21, p22, p23, p31, p32, p33; // 3x3窗口寄存器
reg [7:0] win [0:2][0:2];
// 梯度计算
wire [10:0] gx, gy;
reg [10:0] grad;
// 行缓存写入与读出逻辑(略,详见完整代码)
// 3x3窗口更新
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (int i=0; i<3; i++) begin
for (int j=0; j<3; j++)
win[i][j] <= 8'd0;
end
end else if (data_valid) begin
win[0][0] <= win[0][1];
win[0][1] <= win[0][2];
win[0][2] <= line_buf[0][col_cnt];
win[1][0] <= win[1][1];
win[1][1] <= win[1][2];
win[1][2] <= line_buf[1][col_cnt];
win[2][0] <= win[2][1];
win[2][1] <= win[2][2];
win[2][2] <= pixel_in;
end
end
// 梯度计算组合逻辑
assign p11 = win[0][0];
assign p12 = win[0][1];
assign p13 = win[0][2];
assign p21 = win[1][0];
assign p22 = win[1][1];
assign p23 = win[1][2];
assign p31 = win[2][0];
assign p32 = win[2][1];
assign p33 = win[2][2];
assign gx = (p13 + 2*p23 + p33) - (p11 + 2*p21 + p31);
assign gy = (p31 + 2*p32 + p33) - (p11 + 2*p12 + p13);
// 梯度幅度近似:|gx| + |gy|
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
grad <= 0;
edge_out <= 0;
end else begin
grad <= (gx[10] ? (~gx + 1'b1) : gx) + (gy[10] ? (~gy + 1'b1) : gy);
edge_out <= (grad > 11'd128) ? 8'hFF : 8'h00; // 阈值128
end
end
// 同步延迟(对齐流水线延迟)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
data_valid_out <= 0;
hsync_out <= 0;
vsync_out <= 0;
end else begin
data_valid_out <= data_valid;
hsync_out <= hsync_in;
vsync_out <= vsync_in;
end
end
endmodule逐行说明
- 第1-11行:模块端口声明。clk为像素时钟(25MHz),rst_n低电平复位。pixel_in为8位灰度像素,data_valid指示像素有效。hsync_in/vsync_in为行场同步信号。输出edge_out为8位边缘强度(二值化)。
- 第14行:定义行缓存,使用2行(实际需要3行,但第3行由输入实时提供),每行640像素,8位宽。采用寄存器实现,避免BRAM延迟。
- 第15行:列计数器col_cnt,用于行缓存寻址。
- 第17行:声明3x3窗口内9个像素的连线。
- 第20行:定义3x3窗口寄存器win,二维数组。
- 第23-24行:声明梯度gx/gy(11位有符号)和梯度幅度grad(11位)。
- 第27-37行:行缓存写入与读出逻辑(此处省略,实际需用双端口RAM或移位寄存器实现)。
- 第40-54行:3x3窗口更新。每个时钟上升沿,当data_valid有效时,将窗口数据右移,新像素从pixel_in进入win[2][2]。注意:此处假设行缓存已提供前两行数据。
- 第57-60行:将win数组映射到p11-p33连线,便于阅读。
- 第62-63行:计算Sobel梯度。gx = (p13+2p23+p33) - (p11+2p21+p31);gy = (p31+2p32+p33) - (p11+2p12+p13)。使用组合逻辑。
- 第66-73行:梯度幅度近似,采用|gx|+|gy|(避免开方)。通过判断符号位取绝对值。阈值比较:若grad>128则输出255(边缘),否则0。
- 第76-83行:同步信号延迟,使输出与数据对齐。由于流水线有2个时钟延迟,需将hsync/vsync同步延迟。
3.3 时序与约束
# top.sdc 时序约束示例(安路TD格式)
create_clock -name clk_sys -period 20.000 [get_ports clk] # 系统时钟50MHz
create_clock -name clk_pix -period 40.000 [get_pins pll/clk_out] # 像素时钟25MHz
set_input_delay -clock clk_pix -max 5 [get_ports pixel_in*]
set_output_delay -clock clk_pix -max 5 [get_ports edge_out*]逐行说明
- 第1行:创建系统时钟clk_sys,周期20ns(50MHz),从顶层端口clk输入。
- 第2行:创建像素时钟clk_pix,周期40ns(25MHz),从PLL输出引脚获取。
- 第3行:设置输入延迟,确保摄像头数据在时钟沿前5ns稳定。
- 第4行:设置输出延迟,确保HDMI接口满足建立时间。
3.4 验证
编写Testbench,生成8x8灰度图像数据(如棋盘格),模拟摄像头时序。运行仿真,观察edge_out波形:边缘处应为255,非边缘为0。
常见坑:行缓存初始化未清零导致首行输出错误;阈值过低导致噪声被当作边缘。
3.5 上板
生成Bitstream,通过下载器烧录至开发板。连接摄像头与HDMI显示器,上电后观察实时边缘效果。
常见坑:HDMI输出无显示——检查引脚约束是否正确;摄像头无数据——检查I2C配置(需初始化寄存器)。
原理与设计说明
Sobel边缘检测的核心是计算图像梯度,通过3x3卷积核分别提取水平与垂直边缘。本设计采用流水线结构,在像素时钟驱动下逐像素处理,延迟仅2个时钟周期(窗口填充+梯度计算)。行缓存使用寄存器实现而非BRAM,目的是避免BRAM读写延迟导致流水线停顿;代价是LUT消耗增加(约2K LUT用于行缓存)。阈值128是经验值,可根据场景调整:光照强时提高阈值减少噪声,光照弱时降低阈值保留细节。
关键trade-off:使用|gx|+|gy|近似梯度幅度,避免开方运算,节省DSP资源。若追求精度,可改用平方和开方(需CORDIC IP),但会增加延迟与资源。本设计面向毕设演示,实时性优先,故采用近似方案。
验证与结果
| 指标 | 仿真值 | 上板实测 | 说明 |
|---|---|---|---|
| Fmax(像素时钟) | ≥50MHz | 25MHz稳定 | 受限于摄像头输出 |
| LUT消耗 | 4,320 | 4,320 | 含行缓存 |
| Block RAM | 0 | 0 | 全部寄存器实现 |
| 边缘检测延迟 | 2 clk | 2 clk | 流水线结构 |
| 帧率 | 30fps | 30fps | 640x480@25MHz |
测量条件:安路EG4S20开发板,OV5640摄像头,HDMI输出至1080p显示器。仿真使用ModelSim,输入8x8测试图像。上板结果:边缘清晰,无明显噪点。
故障排查(Troubleshooting)
- 现象:显示器无图像。原因:HDMI初始化失败。检查点:HDMI时钟是否稳定,I2C配置是否正确。修复:重新上电或检查原理图。
- 现象:图像全黑。原因:摄像头未输出数据。检查点:摄像头电源、FPC连接、I2C寄存器。修复:用逻辑分析仪抓取DVP信号。
- 现象:边缘检测输出全白或全黑。原因:阈值设置不当。检查点:grad值范围。修复:调整阈值或改用自适应阈值。
- 现象:图像撕裂。原因:帧同步丢失。检查点:vsync信号是否稳定。修复:增加去抖逻辑。
- 现象:综合报错“时序不满足”。原因:时钟约束错误或路径过长。检查点:.sdc文件、关键路径报告。修复:优化流水线或降低时钟频率。
- 现象:资源超限。原因:行缓存使用寄存器过多。检查点:综合报告。修复:改用BRAM实现行缓存。
- 现象:仿真结果与上板不一致。原因:仿真未包含时序信息。检查点:后仿真是否通过。修复:运行门级仿真。
- 现象:HDMI输出颜色异常。原因:RGB通道顺序错误。检查点:数据映射。修复:交换R/G/B位宽。
扩展与下一步
- 参数化:将阈值、图像分辨率、窗口大小改为参数,适应不同场景。
- 带宽提升:改用双端口BRAM实现行缓存,支持更高分辨率(如1280x720)。
- 跨平台:移植至高云或Xilinx平台,对比资源与性能。
- 加入断言:在Testbench中添加SVA断言,自动检查边缘检测正确性。
- 形式验证:使用开源工具(如SymbiYosys)验证Sobel模块等价性。
参考与信息来源
- 安路科技EG4S20数据手册(2024)
- 紫光同创Logos-2用户指南(2025)
- OV5640数据手册(OmniVision)
- 《实时图像边缘检测的FPGA实现》——电子科技大学硕士论文(2023)
技术附录
术语表
- DVP:数字视频端口,并行接口。
- PLL:锁相环,用于时钟生成。
- BRAM:块随机存取存储器,FPGA内部存储资源。
- Sobel:一种边缘检测算子,基于梯度计算。
检查清单
- [ ] 摄像头驱动已调试(I2C配置正确)
- [ ] 行缓存实现正确(仿真验证首行数据)
- [ ] 约束文件包含所有时钟与IO延迟
- [ ] 上板前运行后仿真
- [ ] 阈值可调(通过按键或串口)
关键约束速查
# 安路TD约束速查
create_clock -name clk -period 20.000 [get_ports clk]
set_input_delay -clock clk -max 5 [get_ports data_in*]
set_output_delay -clock clk -max 5 [get_ports data_out*]以上约束适用于50MHz系统时钟,实际需根据PCB走线调整延迟值。



