简介:面向FPGA嵌入式与图像显示方向的开发者,这套资料完整演示了在黑金AX301平台上将OV7060 CMOS摄像头采集的图像实时显示到VGA显示器的实现方案。内容聚焦于OV7060的I2C寄存器配置、图像数据采集、SDRAM缓冲调度、YUV转RGB色彩空间转换,以及符合VGA标准的行场同步时序控制等核心环节,能帮助读者构建从摄像头到显示器的全链路驱动框架。压缩包共302个文件,大小仅8.18MB,以Verilog源码为主,同时包含Quartus工程文件、时序约束脚本、I2C配置Tcl脚本、各阶段备份文件及仿真/综合报告,并附有PDF/readme说明材料,目录结构便于对照查找。工程虽面向OV7060,其设计方法与代码结构也可迁移至OV7670等同类CMOS摄像头方案。已有698人学习下载,适合正在调试OV7670/OV7060摄像头驱动,或希望从零搭建VGA显示链路的嵌入式开发者参考。 把摄像头拍到的画面实时送到显示器上,是学习FPGA图像处理最入门也最有成就感的一步。我之前一直习惯了用串口或上位机回传图像,这套方案不是不行,但一旦想在画面基础上做边缘检测、目标追踪,回传链路就成了大瓶颈。这次我把目光放在手头这块黑金AX301开发板上,板载Cyclone IV EP4CE6,虽然只是个入门级FPGA,但搭配OV7060摄像头模块和板载VGA接口,完全能搭出一条“像素从Sensor产生,到RGB信号出现在显示器”的完整链路。把这条链路跑通后,后面接任何图像算法,都有一个能可视化验证的实时画面平台。这篇文章就记录我这次实现OV7060驱动并显示到VGA的全过程,包括选型逻辑、SCCB初始化寄存器、FPGA关键代码和调试中踩过的坑,给正在折腾同类板卡的朋友一个参考。
1. 为什么这个组合值得做:选型背后的逻辑
1.1 三个器件各自的角色
先说开发板。黑金AX301用的是Altera/Intel Cyclone IV系列的EP4CE6F17C8,逻辑单元大约6K,片内RAM有30个M9K块,合计约270Kb,板载50MHz有源晶振,板上直接带了VGA座、串口、LED、按键、SDRAM。这个资源量在FPGA里算入门,但与单片机相比,它的并行优势非常明显,而且PLL、LVCMOS IO这些外设足够支撑视频信号处理。
再说摄像头。OV7060是OmniVision的入门级CMOS传感器,最大分辨率640x480,也就是VGA分辨率,输出接口是DVP并行总线,8位数据线加PCLK、HREF、VSYNC,配置接口是SCCB。它最吸引人的地方是没有MIPI那种高速差分信号,也不需要USB协议栈,直接用FPGA普通IO就能对接,非常适合用来理解“传感器如何输出像素”。
最后是VGA。很多新手项目会用HDMI,但HDMI需要专门的编码器和高速差分引脚,入门FPGA上做起来麻烦。VGA本质是模拟RGB信号加行场同步,FPGA只要按时序输出数字颜色值和同步脉冲,外部经过简单的电阻网络就能转成显示器能识别的信号。调试时用示波器看同步波形也方便,是学习视频时序最好的载体。
1.2 帧缓存难题:内部RAM装不下,怎么办
遇到这种视频项目,第一反应是加一块帧缓存,把摄像头一帧数据存下来再刷到VGA上。但仔细算一下就会发现,640x480的RGB565图像,一帧需要640 × 480 × 2字节,约600KB。而EP4CE6内部RAM只有270Kb,就算全拿来存图像也塞不进一帧。板上有SDRAM,但直接上SDRAM控制器会让移植性变差,也会掩盖视频链路的本质问题。
所以我最终选择了行缓冲加流水线处理的方案。也就是不存整帧,只缓存当前正在扫描的一行或两行数据,摄像头像素边进来边往VGA送。这种做法对入门项目非常合适,资源占用小,结构清晰,虽然视频信号在帧边界上会有轻微撕裂或漂移,但作为学习和验证图像链路完全够用。等以后想认真做帧冻结、缩放等功能,再把SDRAM帧缓存模块加进去就行。
2. 硬件连接与引脚约束
2.1 摄像头接口怎么接
OV7060摄像头模块的接口并不复杂,核心信号有XCLK(主时钟输入)、PCLK(像素时钟输出)、VSYNC(帧同步)、HREF(行有效)、D7-D0(8位像素数据),以及SCCB的两根线SIOC和SIOD,还有复位和掉电引脚。AX301开发板上一般都有专门的2.54mm排针摄像头接口,直接把模块插上去即可。
实际连接时,需要特别注意信号电平匹配。OV7060的IO供电可以做到3.3V,与Cyclone IV的IO电平兼容,不需要额外电平转换。如果用的是5V供电的摄像头模块,那就要仔细查原理图,避免把FPGA引脚打坏。我手上这块模块是3.3V供电,所以接线非常简单。
引脚约束方面,我以自己板卡为例,摄像头模块通过这些名称连到FPGA:
| 信号 | 方向 | 说明 |
|---|---|---|
| CAM_XCLK | 输出 | 由FPGA提供,24MHz或25MHz |
| CAM_PCLK | 输入 | 传感器输出的像素时钟 |
| CAM_VSYNC | 输入 | 帧同步信号 |
| CAM_HREF | 输入 | 行有效信号,也有叫HSYNC的 |
| CAM_D[7:0] | 输入 | 8位像素数据 |
| CAM_SIOC | 双向 | SCCB时钟,可复用I2C |
| CAM_SIOD | 双向 | SCCB数据 |
| CAM_RESET | 输出 | 复位,低有效 |
| CAM_PWDN | 输出 | 掉电控制,高有效 |
上电前建议用万用表量一下模块电源引脚和FPGA电源引脚,确认都是3.3V再插。SIOD和SIOC通常是开漏结构,模块上一般已经带有上拉电阻,如果没有则需要自己在PCB上补齐。
2.2 VGA输出的电阻网络和“VGA优先级”误区
VGA接口直接连到FPGA引脚,颜色信号并不是真正的模拟电压,而是通过电阻网络做DAC转换。AX301板上的VGA部分一般用一组排阻把FPGA数字引脚合成模拟RGB,颜色位宽常见的是RGB各2位或各3位,总体能显示的色深有限,但学习用途完全够。
在搜索VGA相关资料时,很多人会看到一个关键词叫做“vga priority(onboard / offboard)”,这是PC主板上BIOS里选择板载显卡还是独立显卡优先输出的选项。FPGA开发板没有这个概念,因为VGA信号来自FPGA的IO引脚,不是来自任何GPU控制器。你不需要在显示器菜单里找什么显卡优先级,只需要保证FPGA逻辑的HS、VS时序正确,并且显示器输入源切换到“VGA/模拟”通道,画面就会正常出现。
VGA同步信号极性也是有讲究的。640x480@60Hz的标准时序中,行同步和场同步都是负极性,也就是空闲时为高电平,同步脉冲为低电平。有些项目直接把同步信号接反,显示器就可能报“不支持信号”或者黑屏。后续在逻辑设计里,要严格按照标准时序来生成这两个信号。
3. OV7060的SCCB初始化与寄存器配置
3.1 SCCB协议和摄像头配置思路
SCCB协议和I2C非常相似,核心是起始条件、停止条件、8位数据加ACK应答。OV7060的器件地址一般是0x42(8位写地址),FPGA每次写入时发送这个地址,接着发寄存器地址,再发寄存器值,最后产生停止条件。整个初始化过程就是往几十个寄存器里写入预设值,配置分辨率、输出格式、时钟分频、图像方向等参数。
这里有个容易踩的坑:SCCB的时序频率不能太高。摄像头内部寄存器写操作虽然不要求毫秒级响应,但SIOC时钟过高时,信号边沿受模块上拉电阻和寄生电容影响,容易出现误码。我实测把SCCB时钟设置在400KHz以内比较稳妥,建I2C控制器时最好能通过参数调节速率,方便调试时降速。
上电初始化顺序也很重要。建议先给摄像头复位引脚一个低脉冲,然后拉高PWDN引脚让模块正常上电工作,再开始SCCB配置流程。配置完成后不要立刻采集,最好等待几个帧周期让自动曝光和自动增益收敛,否则前几帧画面会忽明忽暗。
3.2 常用寄存器表与参数解析
不同的OV7060批次和数据手册在寄存器细节上会有差异,以下是我这次使用的参考参数,适用OV7670/OV7060这类常见DVP传感器,但拿到手一定要对照你自己的数据手册核准,再烧到板上实测:
| 寄存器地址 | 写入值 | 作用 |
|---|---|---|
| 0x12 | 0x80 | 先执行软件复位 |
| 0x12 | 0x04 | 选择RGB输出,VGA分辨率 |
| 0x40 | 0xC0 | 输出RGB565格式,高位在前 |
| 0x3A | 0x04 | 设置扫描顺序,避免图像上下颠倒 |
| 0x11 | 0x03 | 设置内部时钟分频,让PCLK匹配输出 |
| 0x3D | 0x00 | 关闭测试图形输出 |
| 0x3E | 0x00 | 关闭测试图形输出 |
我把寄存器列表放在FPGA内部ROM中,由SCCB控制器按地址递增逐条写入。写完后再读一次关键寄存器,比如0x12,检查写入返回值是否为0x04,用这种方式快速确认I2C链路是否正常。建议在工程里做一个初始化完成标志,点亮一个LED,方便调试时知道配置流程到底走完没有。
4. FPGA逻辑设计与实现要点
4.1 顶层框架与时钟树
整个工程顶层模块端口如下:
module top_ov7060_vga ( input CLOCK_50, // 摄像头 output CAM_XCLK, input CAM_PCLK, input CAM_VSYNC, input CAM_HREF, input [7:0] CAM_D, output CAM_RESET, output CAM_PWDN, inout CAM_SIOC, inout CAM_SIOD, // VGA output [2:0] VGA_R, output [2:0] VGA_G, output [2:0] VGA_B, output VGA_HS, output VGA_VS );时钟上,板载50MHz晶振进入PLL,产生两个主要时钟:一个XCLK给摄像头提供工作主时钟,另一个作为VGA像素时钟。我实测使用25MHz作为VGA像素时钟,同时给摄像头提供25MHz的XVCLK,这样摄像头输出的PCLK经过内部分频后也落在25MHz附近,流水线处理时跨时钟域压力小很多。如果板卡PLL输出资源够,也可以把XCLK单独设为24MHz,只要保证VGA像素时钟和PCLK大致同频即可。
PLL例化只需要在Quartus里调用ALTPLL IP核,注意给每个输出时钟取好名字,方便约束文件和顶层模块引用。
4.2 像素采集模块:8位数据拼成RGB565
OV7060输出RGB565时,每个像素由两个字节组成,高位字节在前还是低位字节在前可以通过寄存器配置。以高位在前为例,FPGA在HREF有效期间接收第一字节作为R[4:0]和高位G,第二字节作为低位G和B[4:0],拼成一个16位像素。核心代码如下:
reg [15:0] rgb_data; reg byte_low; reg pix_valid; always @(posedge CAM_PCLK or negedge rst_n) begin if (~rst_n) begin byte_low <= 1'b0; pix_valid <= 1'b0; end else if (~CAM_HREF) begin byte_low <= 1'b0; pix_valid <= 1'b0; end else begin if (~byte_low) begin rgb_data[15:8] <= CAM_D; byte_low <= 1'b1; end else begin rgb_data[7:0] <= CAM_D; pix_valid <= 1'b1; byte_low <= 1'b0; end end end这里有个细节,很多摄像头模块的PCLK默认在数据稳定后的上升沿有效,但也有些传感器可以通过寄存器翻转PCLK极性。如果采集出来画面颜色错乱或者有明显偏移,可以尝试用PCLK下降沿采样,或者在SCCB寄存器里把PCLK反向位改一下。这个问题在调试阶段特别容易让人抓狂,我一开始就是没注意极性,画面全是花屏。
4.3 VGA时序生成与行缓冲对齐
VGA 640x480@60Hz的时序参数是固定的,一行总共800个像素时钟,其中有效显示区640个,前沿16个,同步脉冲96个,后沿48个;一帧总共525行,有效显示480行,前沿10行,同步脉冲2行,后沿33行。实现方式就是两个计数器分别统计行内像素和当前行数,到了边界就复位。
行缓冲这块,我使用双时钟RAM:写入侧用摄像头PCLK,读取侧用VGA像素时钟,两端地址都由各自的HREF或有效显示信号控制。这样能避免PCLK和VGA像素时钟因相位不一致造成的错位,画面行与行之间会更稳定。如果摄像头PCLK和VGA像素时钟恰好完全同源,也可以直接旁路掉行缓冲,但作为工程实践,保留行缓冲更可靠。
VGA输出颜色信号时,需要根据当前显示有效区域来选通数据。刚开始玩的时候很容易忽略同步信号和有效数据之间的对齐关系,结果画面整体左移或者右移,调整VGA计数器里的前沿后沿参数通常能解决。调试时可以在有效显示区域内输出纯色,比如全红、全绿,用来确认时序起点是否正确。
5. 调试实录:那些踩过的坑
5.1 黑屏:先分清楚是配置还是时序的问题
VGA黑屏最常见原因不是图像数据没来,而是同步信号根本没产生。先拿示波器或者逻辑分析仪看VGA_HS和VGA_VS,如果VGA_HS频率在31.5KHz左右、VGA_VS在60Hz左右,说明VGA时序模块工作正常;如果完全没波形,反过来查PLL输出和计数器是否复位,重点确认PLL的locked信号有没有接到复位链路上。
再检查摄像头一侧。用示波器看CAM_PCLK有没有时钟输出,没有就说明传感器没工作,重点查PWDN和RESET引脚,以及SCCB初始化是否完成。我当时把摄像头的掉电引脚接反了,模块一直处于PWDN状态,PCLK完全没有输出,黑屏查了半天才发现。建议把这些控制引脚都固定在顶层模块里,做成常量,避免和约束文件不一致。
5.2 花屏错位:颜色错乱与上下颠倒排查
画面能出现但颜色完全不对,十有八九是高低字节顺序反了。RGB565高低字节对调后,原本的红色通道会跑到蓝色通道,画面整体偏紫或偏绿。解决方式很简单,交换拼接顺序,或者修改寄存器把输出改成低位在前。
图像上下颠倒,检查传感器扫描方向寄存器,也就是TSLB相关位。参考表里我把0x3A设置为0x04,就是为了避开倒像问题。如果改了寄存器还是倒像,可以试试在FPGA端通过调整行写入顺序来翻转,但这样需要额外的行缓存,不如直接从寄存器解决。
最让人头疼的是画面斜纹或者轻微错行。这个通常是行缓冲两端的读写指针不同步导致的,解决办法是在帧同步信号到来时同时复位写地址和读地址,让行缓冲从同一行起点开始工作。实测下来,一帧画面偶尔在顶部或底部出现一条错位线是比较常见的,不影响主体显示,如果要求严格,可以上完整帧缓存。
5.3 使用LED和逻辑分析仪快速定位
工程里一定要加几个状态指示LED。我的做法是:初始化完成点亮一个LED,检测到VSYNC上升沿翻转另一个LED,检测到有效像素数据时再置位一个标志。这样即使没有示波器,也能从LED状态大致判断流程卡在哪一步。比如第一个LED亮了但第二个不闪,说明摄像头侧没有任何帧信号进来,问题在传感器或初始化;两个LED都正常但VGA还是黑屏,问题就在VGA时序模块。
逻辑分析仪在调试SCCB时特别有用,抓取SIOC和SIOD两根线,看起始条件、器件地址、ACK位是否正常。如果发现数据线上只有写地址没有寄存器地址,说明状态机跳转有问题,重点检查位计数器的长度是否多了一位或少了一位。
6. 运行效果与后续扩展方向
6.1 当前方案的性能边界
最终跑通后,显示器上能实时看到OV7060采集的彩色画面,帧率取决于摄像头输出配置和VGA刷新率,大约在30到60帧之间。由于没有完整帧缓存,画面在帧边界上会有轻微撕裂,但在日常画面变化不剧烈时基本看不出来。颜色深度受VGA电阻网络限制,实际观感比专业显示器差一些,但作为FPGA图像处理学习平台已经足够。
这套方案最大的优点是复用性好。我把像素采集模块、VGA时序模块、SCCB控制器都做成了独立模块,后续接OV7670、OV7725或者OV5640时,只需要改寄存器表和少量引脚约束,主体逻辑基本不用动。
6.2 下一步可以加什么
有了实况视频流,后续就能做很多有意思的扩展。最基础的是在RGB到VGA之间插一个灰度转换模块,把RGB565转成YUV后提取Y分量,再输出成黑白画面。往上可以做二值化、边缘提取,甚至用三个乘法器做简单的Sobel算子,这些在EP4CE6上都能跑。
如果想做帧冻结或者画中画,就需要把SDRAM控制器加进来,把整帧数据存入外部SDRAM。AX301板载SDRAM能放下好几帧VGA图像,那时再做图像缩放、叠加、相机预览窗口,就有充分的缓存空间了。
最后再分享一个心得:调VGA这类视频项目,千万别怕加状态指示灯和抓波形,我当时为了少写一段代码,省掉了初始化完成标志,结果摄像头配置出错时只能靠猜,白白浪费了一个晚上。视频链路是一环扣一环的,把每个阶段的完成信号可视化,整个调试过程会轻松很多。希望你也能顺顺利利跑出第一帧实时画面。
本文还有配套的精品资源,点击获取