跳到主要内容FPGA 摄像头采集到 HDMI 显示完整链路设计 | 极客日志编程语言算法
FPGA 摄像头采集到 HDMI 显示完整链路设计
综述由AI生成基于 FPGA 的摄像头采集到 HDMI 显示的完整链路设计。内容涵盖系统架构、OV5640 摄像头驱动与 SCCB 配置、DVP 接口时序与数据采集、SDRAM 缓存管理与乒乓操作、以及 HDMI 显示输出与 TMDS 编码。文章提供了 Verilog 代码示例、工程架构设计、引脚与时序约束配置,并总结了常见问题调试技巧及性能优化建议,适用于嵌入式视频处理系统的开发参考。
不知所云31 浏览 FPGA 摄像头采集到 HDMI 显示完整链路设计
在视频监控、工业检测、医疗成像等领域,实时图像采集和显示已成为必不可少的功能。FPGA 因其高并行处理能力和低延迟特性,成为实现高性能视频处理系统的首选方案。
一、摄像头采集显示系统架构
1.1 系统整体框架
一个完整的 FPGA 摄像头采集处理显示系统由以下几个主要部分组成:
┌─────────────────────────────────────┐
│ FPGA 摄像头采集处理显示系统架构 │
├─────────────────────────────────────┤
│ ├─ 1️⃣ 摄像头采集模块 │
│ │ ├─ OV5640 摄像头驱动 │
│ │ ├─ SCCB 配置接口(I2C 兼容) │
│ │ └─ DVP 数据采集(PCLK/HREF/VSYNC)│
│ ├─ 2️⃣ 图像处理模块 │
│ │ ├─ 数据格式转换(RGB565/YUV422)│
│ │ ├─ 图像缩放/裁剪 │
│ │ └─ 色彩空间转换
️⃣ 存储缓存模块
存储
双端口 缓冲
乒乓帧缓冲管理
️⃣ 显示输出模块
时序生成
驱动控制
编码与差分输出
️⃣ 时钟管理模块
时钟生成
多时钟域同步
时序约束配置
│
│
├─
3
│
│
│
├─
SDRAM
/
DDR3
│
│
│
├─
RAM
│
│
│
└─
│
│
├─
4
│
│
│
├─
VGA
│
│
│
├─
HDMI
│
│
│
└─
TMDS
│
│
└─
5
│
│
├─
PLL
│
│
├─
│
│
└─
│
└─────────────────────────────────────┘
1.2 核心模块功能
| 模块名称 | 功能描述 | 关键参数 |
|---|
| OV5640 驱动 | 摄像头初始化、寄存器配置、数据采集 | 分辨率、帧率、数据格式 |
| SCCB 控制器 | I2C 兼容的摄像头配置接口 | 时钟频率、地址宽度 |
| DVP 采集 | 并行数据采集、时序同步 | 像素时钟、行列同步 |
| 图像缓存 | 帧数据存储、读写管理 | 缓存大小、带宽 |
| VGA 驱动 | 显示时序生成、数据输出 | 分辨率、刷新率 |
| HDMI 驱动 | HDMI 信号编码、差分输出 | 分辨率、色深 |
1.3 数据流向与时序
摄像头 (OV5640) ↓ [DVP 接口:PCLK, HREF, VSYNC, Y[7:0]]
FPGA 采集模块 ↓ [16 位 RGB565 或 YUV422]
图像处理模块 ↓ [处理后的图像数据]
SDRAM/DDR3 缓存 ↓ [读取请求]
VGA/HDMI 驱动 ↓ [HDMI 差分信号]
显示器
- 摄像头输出像素时钟 PCLK(通常 24-96MHz)
- 行同步信号 HREF(高电平表示有效数据)
- 帧同步信号 VSYNC(脉冲表示新帧开始)
- 每个 PCLK 周期输出 8 位数据,RGB565 需要 2 个周期
二、OV5640 摄像头基础
2.1 OV5640 摄像头简介
OV5640 是 OmniVision 公司设计的一款高性能 CMOS 图像传感器,广泛应用于监控、医疗、工业检测等领域。
- 像素规格:500 万像素(2592×1944 分辨率)
- 视频输出:支持 1080P、720P、VGA、QVGA 等多种分辨率
- 数据格式:RGB565/RGB555/RGB444、YUV422/420、YCbCr422、JPEG
- 帧率范围:15-60fps 可调(根据分辨率配置)
- 功能支持:自动对焦 (AF)、自动曝光 (AEC)、自动白平衡 (AWB)
- 功耗:150-200mW(工作功率)
- 工作温度:-30
70℃(稳定工作 050℃)
2.2 OV5640 引脚定义与功能
| 引脚名称 | 类型 | 功能描述 |
|---|
| XCLK | 输入 | 外部时钟输入(24-96MHz),驱动摄像头芯片 |
| PCLK | 输出 | 像素同步时钟,数据在其上升沿有效 |
| HREF | 输出 | 行同步信号(高电平表示有效数据) |
| VSYNC | 输出 | 帧同步信号(脉冲表示新帧开始) |
| Y[7:0] | 输出 | 8 位像素数据输出 |
| SIO_C | 输入 | SCCB 时钟线(类似 I2C 的 SCL) |
| SIO_D | I/O | SCCB 数据线(类似 I2C 的 SDA) |
| RESET | 输入 | 系统复位(低电平有效) |
| PWDN | 输入 | 掉电/省电模式(高电平有效) |
2.3 DVP 接口时序详解
OV5640 采用 DVP(Digital Video Parallel)接口输出图像数据。
- 数据在 PCLK 上升沿有效
- HREF 高电平表示该行有效数据
- VSYNC 脉冲表示帧开始
- RGB565 格式:每个像素需要 2 个 PCLK 周期(16 位数据分两次输出)
PCLK: ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘
HREF: ┌─────────────────────────┐ └─────────────────────────┘
DATA: [R4R0G5G3][G2G0B4B0][R4R0G5G3]...
↑第 1 字节 ↑第 2 字节 ↑第 3 字节
2.4 SCCB 配置协议
SCCB(Serial Camera Control Bus)是 OV 摄像头的配置接口,与 I2C 协议兼容。
- 发送 START 信号
- 发送摄像头地址(0x78,7 位地址)
- 发送寄存器地址(16 位)
- 发送寄存器数据(8 位)
- 发送 STOP 信号
- 发送 START 信号
- 发送摄像头地址 + 写位
- 发送寄存器地址(16 位)
- 发送 RESTART 信号
- 发送摄像头地址 + 读位
- 读取寄存器数据(8 位)
- 发送 STOP 信号
2.5 OV5640 初始化配置
摄像头初始化需要配置大量寄存器。以 VGA(640×480) 分辨率为例:
| 寄存器地址 | 功能描述 | 典型值 |
|---|
| 0x3008 | 系统复位与时钟控制 | 0x82 |
| 0x3103 | 时钟源选择 | 0x02 |
| 0x3017 | 时钟使能 | 0x00 |
| 0x3018 | 时钟使能 | 0x00 |
| 0x3034 | PLL 倍频系数 | 0x1A |
| 0x3035 | PLL 分频系数 | 0x21 |
| 0x3036 | PLL 系统分频 | 0x69 |
| 0x3037 | PLL 根分频 | 0x13 |
| 0x4300 | 输出格式选择 | 0x61(RGB565) |
| 0x5001 | ISP 控制 | 0x80 |
- 复位摄像头(RESET 拉低后拉高)
- 配置时钟系统(XCLK、PLL)
- 配置输出分辨率(ISP 输入/输出大小)
- 配置输出格式(RGB565/YUV422 等)
- 配置图像处理(白平衡、曝光、饱和度等)
- 启用输出(设置 0x3008 寄存器)
// SCCB 写操作
task write_sccb(input [15:0] addr, input [7:0] data);
begin
// 发送 START
sccb_start();
// 发送设备地址 (0x78)
sccb_write_byte(8'h78);
// 发送寄存器地址高字节
sccb_write_byte(addr[15:8]);
// 发送寄存器地址低字节
sccb_write_byte(addr[7:0]);
// 发送数据
sccb_write_byte(data);
// 发送 STOP
sccb_stop();
end
endtask
// 初始化序列
initial begin
write_sccb(16'h3008, 8'h82); // 系统复位
#100000; // 等待复位完成
write_sccb(16'h3103, 8'h02); // 时钟源选择
write_sccb(16'h3034, 8'h1A); // PLL 配置
// ... 更多寄存器配置
end
三、图像采集模块设计
3.1 DVP 采集模块架构
DVP 采集模块是连接摄像头和 FPGA 的关键桥梁,负责接收摄像头的并行数据流并进行时序同步。
- 行列计数与地址生成
- 数据缓冲与格式转换
- 时序同步与边沿检测
- 帧同步与数据有效性判断
3.2 行列计数器设计
- HREF 高电平时,行计数器递增
- HREF 下降沿时,行计数器清零,列计数器递增
- VSYNC 脉冲时,列计数器清零
module dvp_capture(
input clk,
input rst_n,
input vsync, // 帧同步信号
input href, // 行同步信号
input [7:0] data, // 8 位像素数据
output [15:0] pix_data, // 16 位 RGB565 像素
output [11:0] haddr, // 行地址
output [11:0] vaddr, // 列地址
output data_vld // 数据有效
);
reg [11:0] h_cnt, v_cnt;
reg [7:0] data_r1, data_r2;
reg href_r, vsync_r;
// 打一拍用于边沿检测
always @(posedge clk or negedge rst_n)
if(!rst_n) begin
href_r <= 0; vsync_r <= 0; data_r1 <= 0; data_r2 <= 0;
end else begin
href_r <= href; vsync_r <= vsync;
data_r1 <= data; data_r2 <= data_r1;
end
// 行计数器
always @(posedge clk or negedge rst_n)
if(!rst_n) h_cnt <= 0;
else if(href_r) h_cnt <= h_cnt + 1;
else h_cnt <= 0;
// 列计数器
always @(posedge clk or negedge rst_n)
if(!rst_n) v_cnt <= 0;
else if({vsync_r, vsync} == 2'b10) // VSYNC 下降沿
v_cnt <= 0;
else if({href_r, href} == 2'b10) // HREF 下降沿
v_cnt <= v_cnt + 1;
// RGB565 数据拼接(两个 8 位数据组成一个 16 位像素)
always @(posedge clk or negedge rst_n)
if(!rst_n) pix_data <= 0;
else pix_data <= {pix_data[7:0], data_r2};
// 数据有效信号(每两个时钟周期产生一个有效像素)
assign data_vld = href_r && h_cnt[0];
assign haddr = h_cnt[11:1];
assign vaddr = v_cnt;
endmodule
3.3 数据格式转换
OV5640 输出 8 位数据,需要转换为 16 位 RGB565 格式。
第 1 字节:[R4 R3 R2 R1 R0 G5 G4 G3]
第 2 字节:[G2 G1 G0 B4 B3 B2 B1 B0]
合并后:[R4 R3 R2 R1 R0 G5 G4 G3 G2 G1 G0 B4 B3 B2 B1 B0]
- 每个 PCLK 周期接收 8 位数据
- 两个周期后组成 16 位 RGB565 像素
- 使用移位寄存器实现数据拼接
3.4 时序同步与稳定性
- 打一拍处理:对所有输入信号进行打一拍,优化时序
- 边沿检测:使用
{pre_signal, signal} 检测上升/下降沿
- 初帧丢弃:系统启动后丢弃前 10 帧数据,确保图像稳定
- 跨时钟域:使用 FIFO 或同步器处理不同时钟域信号
// 丢弃初始帧
reg [3:0] frame_cnt;
reg output_enable;
always @(posedge clk or negedge rst_n)
if(!rst_n) begin
frame_cnt <= 0; output_enable <= 0;
end else if({vsync_r, vsync} == 2'b10) begin
if(frame_cnt < 10) frame_cnt <= frame_cnt + 1;
else output_enable <= 1;
end
// 最终数据有效信号
assign data_vld = href_r && h_cnt[0] && output_enable;
3.5 采集模块集成
完整的采集模块需要与 SDRAM 控制器集成,实现数据的写入。
- 输入:clk、rst_n、vsync、href、data[7:0]
- 输出:pix_data[15:0]、haddr[11:0]、vaddr[11:0]、data_vld
- 与 SDRAM 的连接:写地址、写数据、写使能
// 采集模块输出
wire [15:0] capture_data;
wire [11:0] capture_h, capture_v;
wire capture_vld;
dvp_capture u_capture(
.clk(pclk), .rst_n(rst_n), .vsync(vsync), .href(href), .data(dvp_data),
.pix_data(capture_data), .haddr(capture_h), .vaddr(capture_v), .data_vld(capture_vld)
);
// 计算 SDRAM 写地址
wire [31:0] write_addr = {capture_v, capture_h} << 1;
// 连接到 SDRAM 写端口
assign sdram_wr_data = capture_data;
assign sdram_wr_addr = write_addr;
assign sdram_wr_en = capture_vld;
四、图像处理与缓存管理
4.1 SDRAM/DDR3 缓存的必要性
摄像头采集的高速数据流无法直接驱动显示器,需要通过缓存实现采集与显示的解耦。
- 采集速率与显示速率不匹配
- 实现读写分离,避免数据冲突
- 存储完整帧数据,支持图像处理
- 缓冲高速数据流,提高系统稳定性
单帧容量 = 宽度 × 高度 × 字节数
VGA(640×480): 640 × 480 × 2 = 614.4KB
1080P(1920×1080): 1920 × 1080 × 2 = 4.15MB
4.2 乒乓操作原理
乒乓操作是 FPGA 视频处理中的经典设计模式,通过双缓冲实现无缝数据流处理。
时间轴:T0 T1 T2 T3 T4
├──────┼──────┼──────┼──────┤
写入:[缓冲 A 写入第 1 帧][缓冲 B 写入第 2 帧][缓冲 A 写入第 3 帧]
读取:[缓冲 A 读取第 1 帧][缓冲 B 读取第 2 帧]
优势:采集和显示同时进行,无停顿
- 采集与显示完全独立
- 避免读写冲突
- 无缝数据流处理
- 节省缓存空间(相比单缓冲)
4.3 帧缓冲管理
SDRAM 地址空间分配(以 4MB 为例)
┌─────────────────────────────┐
│ 缓冲 A (帧 0): 0x00000 - 0x9FFFF │ 640KB
├─────────────────────────────┤
│ 缓冲 B (帧 1): 0xA0000 - 0x13FFFF │ 640KB
├─────────────────────────────┤
│ 缓冲 C (帧 2): 0x140000 - 0x1DFFFF │ 640KB
├─────────────────────────────┤
│ 保留区域 │
└─────────────────────────────┘
module frame_buffer_ctrl(
input clk,
input rst_n,
input vsync, // 采集帧同步
input display_vsync, // 显示帧同步
output [31:0] write_base_addr, // 采集写地址
output [31:0] read_base_addr, // 显示读地址
output frame_switch_flag // 帧切换标志
);
reg [1:0] write_frame_idx; // 采集帧索引 (0-2)
reg [1:0] read_frame_idx; // 显示帧索引 (0-2)
reg vsync_r, display_vsync_r;
// 采集帧切换
always @(posedge clk or negedge rst_n)
if(!rst_n) write_frame_idx <= 0;
else if({vsync_r, vsync} == 2'b10) // VSYNC 下降沿
write_frame_idx <= (write_frame_idx + 1) % 3;
// 显示帧切换(延迟一帧)
always @(posedge clk or negedge rst_n)
if(!rst_n) read_frame_idx <= 0;
else if({display_vsync_r, display_vsync} == 2'b10)
read_frame_idx <= (read_frame_idx + 1) % 3;
// 地址计算
assign write_base_addr = write_frame_idx * 32'hA0000;
assign read_base_addr = read_frame_idx * 32'hA0000;
assign frame_switch_flag = (write_frame_idx != read_frame_idx);
endmodule
4.4 SDRAM 控制器集成
SDRAM 控制器通常由 FPGA 厂商提供的 IP 核生成。
- 用户侧:地址、数据、读写控制信号
- 芯片侧:行列地址、控制信号、数据总线
写操作:
clk: ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ └─┘ └─┘ └─┘ └─┘ └─┘
wr_en: ┌─────────┐
wr_addr: [地址 A][地址 B]
wr_data: [数据 1][数据 2]
↑写入延迟 (通常 2-3 个周期)
读操作:
rd_en: ┌─────────┐
rd_addr: [地址 A]
rd_data: [数据 1][数据 2]
↑读取延迟 (通常 3-4 个周期)
4.5 图像处理模块
- 色彩空间转换:RGB↔YUV
- 缩放/裁剪:改变分辨率
- 滤波:高斯、中值等
- 特征提取:边缘检测、角点检测
// RGB565 转灰度
wire [7:0] gray = (r[4:0] * 77 + g[5:0] * 150 + b[4:0] * 29) >> 8;
// 灰度转 RGB565
wire [15:0] gray_rgb565 = {gray[7:3], gray[7:2], gray[7:3]};
4.6 跨时钟域处理
- 异步 FIFO:自动处理时钟域转换
- 同步器:使用打拍链同步信号
- 握手协议:通过握手信号同步
// 采集时钟域写入,显示时钟域读出
async_fifo #( .WIDTH(16), .DEPTH(1024) ) u_fifo(
.wr_clk(pclk), .wr_en(capture_vld), .wr_data(capture_data),
.rd_clk(display_clk), .rd_en(display_rd_en), .rd_data(display_data),
.empty(fifo_empty), .full(fifo_full)
);
五、HDMI 显示输出
5.1 HDMI 接口基础
HDMI(High-Definition Multimedia Interface)是现代显示设备的标准接口。
| HDMI 版本 | 最高分辨率 | 带宽 | 发布年份 |
|---|
| HDMI 1.4 | 4K@30Hz | 10.2Gbps | 2008 |
| HDMI 2.0 | 4K@60Hz | 18Gbps | 2013 |
| HDMI 2.1 | 8K@60Hz | 48Gbps | 2017 |
HDMI 接口引脚分布
┌─────────────────────────────┐
│ 1 2 3 4 5 6 7 8 9 │ 第一排
│ 10 11 12 13 14 15 16 17 18 │ 第二排
│ 19 │ 第三排
└─────────────────────────────┘
关键引脚:
- 1-3: TMDS 数据通道 0(蓝色)
- 4-6: TMDS 数据通道 1(绿色)
- 7-9: TMDS 数据通道 2(红色)
- 10-12: TMDS 时钟通道
- 13: CEC(消费电子控制)
- 14: 接地
- 15-16: DDC(显示数据通道)
- 17-18: 接地
- 19: +5V 电源
5.2 VGA 时序生成
HDMI 显示基于 VGA 时序标准。VGA 时序定义了像素、行、帧的同步关系。
水平时序:
总像素数:2200
有效像素:1920
前廊 (Front Porch): 88
同步脉冲 (Sync): 44
后廊 (Back Porch): 148
垂直时序:
总行数:1125
有效行:1080
前廊:4
同步脉冲:5
后廊:36
module vga_timing(
input clk, // 像素时钟
input rst_n,
output hsync, // 行同步
output vsync, // 帧同步
output de, // 数据有效
output [11:0] haddr, // 水平地址
output [11:0] vaddr // 垂直地址
);
parameter H_TOTAL = 2200;
parameter H_ACTIVE = 1920;
parameter H_SYNC_START = 2008;
parameter H_SYNC_END = 2052;
parameter V_TOTAL = 1125;
parameter V_ACTIVE = 1080;
parameter V_SYNC_START = 1084;
parameter V_SYNC_END = 1089;
reg [11:0] h_cnt, v_cnt;
// 水平计数器
always @(posedge clk or negedge rst_n)
if(!rst_n) h_cnt <= 0;
else if(h_cnt == H_TOTAL - 1) h_cnt <= 0;
else h_cnt <= h_cnt + 1;
// 垂直计数器
always @(posedge clk or negedge rst_n)
if(!rst_n) v_cnt <= 0;
else if(h_cnt == H_TOTAL - 1) begin
if(v_cnt == V_TOTAL - 1) v_cnt <= 0;
else v_cnt <= v_cnt + 1;
end
// 同步信号生成
assign hsync = (h_cnt >= H_SYNC_START) && (h_cnt < H_SYNC_END);
assign vsync = (v_cnt >= V_SYNC_START) && (v_cnt < V_SYNC_END);
assign de = (h_cnt < H_ACTIVE) && (v_cnt < V_ACTIVE);
assign haddr = h_cnt;
assign vaddr = v_cnt;
endmodule
5.3 TMDS 编码与差分驱动
HDMI 使用 TMDS(Transition Minimized Differential Signaling)编码传输数据。
- 最小化信号转换,降低 EMI
- 8 位数据编码为 10 位
- 高速串行传输(165MHz-600MHz)
- 支持 HDCP 版权保护
8 位数据 → 8B/10B 编码 → 序列化 → 差分驱动 → HDMI 输出
- 使用 FPGA 内部的 SERDES(串行/并行转换器)
- 或使用外部 HDMI 驱动芯片(如 SiI9134)
- 配置 PLL 生成高速时钟
5.4 HDMI 驱动模块设计
完整的 HDMI 驱动模块集成 VGA 时序和数据输出。
module hdmi_driver(
input clk, // 像素时钟
input clk_5x, // 5 倍像素时钟(用于 TMDS)
input rst_n,
input [15:0] rgb_data, // RGB565 数据
output hdmi_clk_p, hdmi_clk_n, // TMDS 时钟
output [2:0] hdmi_d_p, // TMDS 数据正
output [2:0] hdmi_d_n // TMDS 数据负
);
wire hsync, vsync, de;
wire [11:0] haddr, vaddr;
// VGA 时序生成
vga_timing u_vga(
.clk(clk), .rst_n(rst_n), .hsync(hsync), .vsync(vsync),
.de(de), .haddr(haddr), .vaddr(vaddr)
);
// 数据读取与 TMDS 编码
// ... TMDS 编码逻辑 ...
// 差分驱动输出
// ... 差分驱动逻辑 ...
endmodule
5.5 常见 HDMI 驱动芯片
| 芯片型号 | 最高分辨率 | 接口 | 功耗 | 应用 |
|---|
| SiI9134 | 1080P@60Hz | LVDS | 低 | 工业应用 |
| ADV7511 | 4K@30Hz | I2C | 中 | 消费类 |
| TFP410 | 1080P@60Hz | DVI | 低 | 显示器 |
FPGA → 并行 RGB 数据 → SiI9134 → HDMI 输出
I2C 配置接口
5.6 HDMI 显示调试技巧
- 无信号输出
- 检查时钟是否正常
- 验证 VGA 时序参数
- 检查 HDMI 驱动芯片配置
- 显示异常(花屏、闪烁)
- 检查数据有效信号 (DE)
- 验证 RGB 数据格式
- 检查时序约束
- 分辨率不匹配
- 确认显示器支持该分辨率
- 检查 PLL 时钟配置
- 验证 VGA 时序参数
- 逻辑分析仪:观察 HDMI 信号
- 示波器:检查差分信号质量
- HDMI 测试仪:验证 EDID 和 HDCP
六、完整实战案例
6.1 工程架构设计
一个完整的 FPGA 摄像头采集显示工程通常包含以下文件结构:
project_root/
├── rtl/
│ ├── top.v
│ ├── dvp_capture.v
│ ├── frame_buffer_ctrl.v
│ ├── vga_timing.v
│ ├── hdmi_driver.v
│ ├── sccb_master.v
│ └── pll.v
├── sim/
│ ├── tb_dvp_capture.v
│ └── tb_hdmi_driver.v
├── constraints/
│ ├── pins.xdc
│ └── timing.xdc
├── ip/
│ ├── sdram_ctrl.xci
│ └── clk_pll.xci
└── doc/
└── design_spec.md
6.2 顶层模块集成
module top(
input clk_100m, // 100MHz 外部时钟
input rst_n, // 复位
// 摄像头接口
input [7:0] dvp_data,
input dvp_pclk,
input dvp_href,
input dvp_vsync,
output dvp_xclk,
output dvp_reset,
output dvp_pwdn,
// SCCB 接口
inout sccb_sda,
inout sccb_scl,
// HDMI 接口
output hdmi_clk_p, hdmi_clk_n,
output [2:0] hdmi_d_p, hdmi_d_n,
// 调试接口
output [7:0] led,
input [3:0] btn
);
// 时钟生成
wire pclk, clk_100m_pll, clk_5x;
clk_pll u_pll(
.clk_in(clk_100m),
.clk_out1(pclk), // 50MHz 像素时钟
.clk_out2(clk_100m_pll),
.clk_out3(clk_5x) // 250MHz TMDS 时钟
);
// DVP 采集
wire [15:0] capture_data;
wire [11:0] capture_h, capture_v;
wire capture_vld;
dvp_capture u_capture(
.clk(pclk), .rst_n(rst_n), .vsync(dvp_vsync), .href(dvp_href), .data(dvp_data),
.pix_data(capture_data), .haddr(capture_h), .vaddr(capture_v), .data_vld(capture_vld)
);
// 帧缓冲控制
wire [31:0] write_addr, read_addr;
frame_buffer_ctrl u_fbuf(
.clk(pclk), .rst_n(rst_n), .vsync(dvp_vsync), .display_vsync(display_vsync),
.write_base_addr(write_addr), .read_base_addr(read_addr)
);
// SDRAM 控制器
wire [31:0] sdram_wr_addr, sdram_rd_addr;
wire [15:0] sdram_wr_data, sdram_rd_data;
wire sdram_wr_en, sdram_rd_en;
sdram_ctrl u_sdram(
.clk(clk_100m_pll), .rst_n(rst_n),
.wr_addr(sdram_wr_addr), .wr_data(sdram_wr_data), .wr_en(sdram_wr_en),
.rd_addr(sdram_rd_addr), .rd_data(sdram_rd_data), .rd_en(sdram_rd_en),
// SDRAM 芯片接口
.sdram_clk(sdram_clk), .sdram_cke(sdram_cke), .sdram_cs_n(sdram_cs_n),
.sdram_ras_n(sdram_ras_n), .sdram_cas_n(sdram_cas_n), .sdram_we_n(sdram_we_n),
.sdram_ba(sdram_ba), .sdram_a(sdram_a), .sdram_dq(sdram_dq)
);
// HDMI 驱动
hdmi_driver u_hdmi(
.clk(pclk), .clk_5x(clk_5x), .rst_n(rst_n), .rgb_data(display_data),
.hdmi_clk_p(hdmi_clk_p), .hdmi_clk_n(hdmi_clk_n),
.hdmi_d_p(hdmi_d_p), .hdmi_d_n(hdmi_d_n)
);
// SCCB 控制器(摄像头配置)
sccb_master u_sccb(
.clk(clk_100m), .rst_n(rst_n), .sda(sccb_sda), .scl(sccb_scl)
);
endmodule
6.3 引脚约束配置
引脚约束文件定义 FPGA 引脚与外部接口的映射。
# 时钟
set_property PACKAGE_PIN E3 [get_ports clk_100m]
set_property IOSTANDARD LVCMOS33 [get_ports clk_100m]
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports clk_100m]
# 复位
set_property PACKAGE_PIN D9 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
# DVP 接口
set_property PACKAGE_PIN A13 [get_ports dvp_pclk]
set_property PACKAGE_PIN B13 [get_ports dvp_href]
set_property PACKAGE_PIN C13 [get_ports dvp_vsync]
set_property PACKAGE_PIN {A14 B14 C14 D14 E14 F14 G14 H14} [get_ports {dvp_data[7:0]}]
set_property IOSTANDARD LVCMOS33 [get_ports dvp_*]
# SCCB 接口
set_property PACKAGE_PIN L18 [get_ports sccb_sda]
set_property PACKAGE_PIN M18 [get_ports sccb_scl]
set_property IOSTANDARD LVCMOS33 [get_ports sccb_*]
set_property PULLUP TRUE [get_ports sccb_sda]
set_property PULLUP TRUE [get_ports sccb_scl]
# HDMI 接口
set_property PACKAGE_PIN D17 [get_ports hdmi_clk_p]
set_property PACKAGE_PIN D18 [get_ports hdmi_clk_n]
set_property PACKAGE_PIN E18 [get_ports {hdmi_d_p[0]}]
set_property PACKAGE_PIN E19 [get_ports {hdmi_d_n[0]}]
set_property IOSTANDARD TMDS_33 [get_ports hdmi_*]
6.4 时序约束配置
# 多时钟域约束
create_clock -add -name pclk -period 20.00 [get_pins u_pll/clk_out1]
create_clock -add -name clk_5x -period 4.00 [get_pins u_pll/clk_out3]
# 时钟域交叉约束
set_clock_groups -asynchronous -group {sys_clk_pin} -group {pclk}
# 输入延迟约束
set_input_delay -clock pclk -min 2 [get_ports dvp_data*]
set_input_delay -clock pclk -max 8 [get_ports dvp_data*]
# 输出延迟约束
set_output_delay -clock clk_5x -min 0 [get_ports hdmi_*]
set_output_delay -clock clk_5x -max 2 [get_ports hdmi_*]
# 虚拟时钟用于 I/O 约束
create_clock -name virtual_clk -period 20.00
set_input_delay -clock virtual_clk -min 2 [get_ports sccb_*]
set_input_delay -clock virtual_clk -max 8 [get_ports sccb_*]
6.5 上板验证与调试
- 编译综合,检查是否有错误
- 运行时序分析,确保满足约束
- 生成比特流文件
- 烧写 FPGA
- 观察 LED 指示灯
- 连接显示器,验证图像输出
- 使用逻辑分析仪调试信号
- 无图像输出:检查时钟、复位、SCCB 配置
- 图像异常:检查 DVP 时序、数据格式
- 显示器无信号:检查 HDMI 驱动、VGA 时序
6.6 性能指标与优化
- 分辨率:VGA(640×480)~1080P(1920×1080)
- 帧率:30fps~60fps
- 延迟:50~100ms(采集到显示)
- 资源占用:LUT 30%~50%,BRAM 40%~60%
- 时钟优化:使用 PLL 生成精确时钟
- 流水线设计:增加管道级数,提高吞吐量
- 缓存优化:使用 BRAM 而非 SDRAM 存储中间数据
- 并行处理:多路并行采集与处理
总结
本文详细讲解了 FPGA 摄像头采集到 HDMI 显示的完整链路:
核心知识点回顾
| 模块 | 关键技术 | 核心参数 |
|---|
| 摄像头采集 | DVP 接口、SCCB 配置 | PCLK、HREF、VSYNC |
| 图像处理 | RGB565 格式、行列计数 | 分辨率、帧率 |
| 缓存管理 | 乒乓操作、帧缓冲 | SDRAM 容量、带宽 |
| 显示输出 | VGA 时序、TMDS 编码 | 分辨率、刷新率 |
实战要点
- 系统设计:理解完整的数据流向与时序关系
- 模块设计:掌握各核心模块的设计方法
- 集成验证:学会顶层集成与约束配置
- 调试技巧:掌握常见问题的排查方法
进阶方向
- 图像处理:实现滤波、缩放、特征提取等算法
- 视频编码:集成 H.264/H.265 编码器
- 网络传输:通过以太网或 USB 传输视频流
- AI 加速:集成深度学习推理引擎
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online