拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

FPGA驱动OV5640图像采集:从SCCB配置到DVP显示完整实战

FPGA驱动OV5640图像采集:从SCCB配置到DVP显示完整实战

接手一个摄像头驱动项目,很多朋友第一时间会想到用ARM或者树莓派。但如果你做的是实时图像处理、高速采集,或者想彻底搞懂图像数据从传感器到显示器的完整链路,FPGA驱动OV5640几乎是绕不开的一课。这篇博文就围绕“FPGA + OV5640”这套黄金组合,从底层的SCCB配置协议,到DVP接口的图像数据采集,再到缓存与显示联动,把整个实操链路完整过一遍。内容会尽量贴近实际调试现场,把踩过的坑、验证过的参数、以及代码思路都摊开来讲,适合已经能跑通LED流水灯、想往图像方向进阶的FPGA学习者,也适合正在被摄像头时序折磨的工程师参考。

1. 整体方案与系统设计思路

1.1 为什么选OV5640,为什么用FPGA,而不是直接用ARM或树莓派

OV5640这颗传感器在图像处理领域的使用频率极高,它是一颗500万像素的CMOS图像传感器,支持DVP和MIPI两种输出接口。在FPGA开发板上,绝大多数都是通过DVP接口来接,因为它引脚数少、时序相对直观,非常适合用来学习图像采集的完整流程。

那么问题来了,既然树莓派或者STM32也都能驱动OV5640,为什么还要用FPGA来做?

从我个人的实践体会来看,有三个核心原因。

第一,FPGA是并行硬件架构,采集和处理的时钟节拍完全可控。OV5640输出的像素时钟PCLK、行同步HREF、场同步VSYNC这些信号,本质上都是硬件时序信号,FPGA用硬件逻辑去做同步、采样、缓存,天然就是匹配的。你用MCU靠中断去抓这些信号,很容易被系统调度打断,导致丢帧或者花屏。

第二,图像采集只是起点,后面往往还跟着灰度转换、二值化、边缘检测、帧差法这些算法。FPGA做这些操作是流水线式的,处理完一帧图像的时间基本是固定的,不像CPU那样受主频和操作系统调度的影响。做实时图像处理的项目,这个确定性太重要了。

第三,从学习的角度,FPGA驱动OV5640能让你把“传感器配置—数据链路—缓存—显示”这条完整链路都亲手搭一遍,尤其是SCCB寄存器配置和DVP时序对齐这两块,属于图像硬件开发的基操,搞懂之后再做MIPI、再做ISP,会有非常扎实的基础。

1.2 系统整体架构与模块划分

在写代码之前,我习惯先把整个系统的模块框图在纸上画出来。FPGA的开发一定要先定架构再动手写代码,直接上来就写模块,后面大概率会返工。

这套OV5640采集显示系统,整体可以划分为四个大模块。

  • SCCB配置模块(i2c_cfg):负责上电后对OV5640的寄存器进行初始化配置,比如输出分辨率、像素格式、时钟分频等。这个模块本质上是I2C主机控制器,只不过遵循的协议是SCCB。
  • 图像采集模块(cmos_capture):负责接收OV5640通过DVP接口输出的PCLK、VSYNC、HREF、DATA[15:0]信号,按照行场时序把有效像素数据解析出来。
  • 数据缓存模块(frame_buffer):因为采集端的写时钟是PCLK,显示端的读时钟是VGA或HDMI的像素时钟,两个时钟域不同,需要FIFO或者双口RAM做跨时钟域缓存。
  • 显示驱动模块(vga_driver / hdmi_driver):把缓存图像数据按照显示器的时序刷出去。

模块划分清楚之后,你需要明确一个关键问题:系统上电后,FPGA必须先通过SCCB把OV5640配置好,传感器才会开始输出图像数据。所以SCCB配置模块的优先级最高,而且配置完成后需要留出一段稳定时间,再让采集模块开始接收数据。

提示:很多新手会在上电后立刻开始采集,结果图像全黑或者全是噪声。原因就是OV5640还没完成初始化,传感器根本没开始输出有效数据。建议在配置模块完成后打一个标志信号,比如cfg_done,采集模块等这个信号拉高后再开始等待VSYNC。

2. SCCB协议与寄存器配置实战

2.1 SCCB协议本质:和I2C的相似与不同

SCCB全称是Serial Camera Control Bus,是OmniVision(豪威)定义的串行控制总线协议。它的时序和I2C非常像,但有几个细节差异,如果不注意,会出现一个很典型的故障:寄存器写不进去,读取出来的数据全是0xFF。

SCCB的时序核心组成如下。

  • 起始条件:SCCL为高时,SDA从高变低,表示传输开始。
  • 停止条件:SCCL为高时,SDA从低变高,表示传输结束。
  • 数据有效性:SDA必须在SCCL高电平期间保持稳定,SCCL低电平期间允许变化。这点和I2C完全一致。
  • 应答位:这是SCCB和I2C最大的区别。I2C在第9个时钟周期有ACK应答,而SCCB在写操作时,第9位是Don't Care位,主机不需要检测从机应答;在读操作时,有NAK机制。

换句话说,SCCB的从机地址字节只有7位有效,第8位固定为X(Don't Care),第9位也是Don't Care。所以OV5640的SCCB地址通常写作0x78(7位地址0x3C左移一位)或0x7A(7位地址0x3D左移一位),具体由OV5640的SCCB_ID引脚电平决定。

读操作也有一点特殊:SCCB读操作必须先发送写地址和寄存器地址,再重新发送读地址(在I2C里这叫重复起始条件)。我之前调试时,就是因为漏掉了这个先写后读的步骤,读寄存器一直返回0xFF,排查了很久。

2.2 寄存器配置核心项与计算过程

OV5640的寄存器数量非常多,密密麻麻几百个。做FPGA驱动时不需要全部配置,我们只需要把关键寄存器配好,让传感器输出我们想要的格式。

常用的配置项如下。

功能寄存器地址典型值说明
软件复位0x31030x03复位整个传感器
输出格式0x43000x02配置为RGB565格式,Y[9:2]输出
输出尺寸-高字节0x38080x05输出图像宽度高8位
输出尺寸-低字节0x38090x10输出图像宽度低8位,即0x510=1296
输出尺寸-高字节0x380A0x02输出图像高度高8位
输出尺寸-低字节0x380B0x58输出图像高度低8位,即0x258=600
时钟分频配置0x31080x01PLL相关配置
寄存器HTS0x380C0x0A行总长度高字节
寄存器HTS0x380D0x20行总长度低字节,0xA20=2592
寄存器VTS0x380E0x07帧总长度高字节
寄存器VTS0x380F0xD0帧总长度低字节,0x7D0=2000

这里拿时钟频率计算举个例子。OV5640的输入时钟XCLK通常给24MHz,通过PLL倍频后可以得到传感器内部的工作时钟。PLL的倍频分频配置主要体现在寄存器0x3034、0x3035、0x3036、0x3037中。

常用的一个组合是:0x3035 = 0x11(PLL倍频系数),0x3036 = 0xC2(PLL分频系数),0x3037 = 0x0E(PLL分频系数)。PLL输出频率的计算公式大致是:

F = XCLK × PLL倍频系数 / PLL分频系数

举个实例:XCLK=24MHz,倍频系数0x11对应17,分频系数0x0E对应14,那么F = 24 × 17 / 14 ≈ 29.14MHz,这个频率就是传感器内部的工作时钟,再经过分频后产生PCLK。

PCLK的具体值和HTS、VTS相关。PCLK = F × 分频系数,然后再根据HTS×VTS×帧率的关系计算最终的输出帧率。比如一帧总像素 = HTS × VTS = 2592 × 2000 = 5184000,如果PCLK约等于42MHz,那么帧率约等于42MHz / 5184000 ≈ 8fps,实际上OV5640的输出帧率就是在这些参数之间互相制约的。我做720P@30fps时,配置的是0x380C=0x0A20,0x380E=0x07D0,最终实测帧率约30fps。

注意:0x4300这个寄存器特别容易配错。配置成0x02是RGB565格式(输出Y[9:2]),配置成0x30是YUV422格式(输出UYVY)。如果显示出来颜色完全不对或者图像偏色怪异,第一个检查项就应该是0x4300的值。

2.3 SCCB读写时序的具体实现思路

SCCB的底层代码本质上是状态机,核心状态包括:IDLE、START、SEND_ADDR、SEND_REG_ADDR、SEND_DATA、STOP等。对于读操作,需要在SEND_REG_ADDR之后,插入一个读地址发送状态,实现“伪停止”或“重复起始”的效果。

SCCL的频率建议控制在100KHz~400KHz之间。我的做法是在FPGA内部做一个分频计数器,从系统时钟50MHz分频得到200KHz的SCCL。SDA引脚用三态门来控制,输出时置为高阻态,配合外部上拉电阻实现释放总线。

SCCB写入寄存器的基本流程如下。

  1. 发送起始条件。
  2. 发送从机地址0x78(写方向)。
  3. 发送寄存器地址高字节。
  4. 发送寄存器地址低字节。
  5. 发送要写入的数据。
  6. 发送停止条件。

SCCB读取寄存器的基本流程如下。

  1. 发送起始条件。
  2. 发送从机地址0x78(写方向)。
  3. 发送寄存器地址高字节。
  4. 发送寄存器地址低字节。
  5. 再次发送起始条件(或者伪停止)。
  6. 发送从机地址0x79(读方向)。
  7. 读取从机返回的数据。
  8. 主机发送NAK,然后发送停止条件。

在状态机里,每个bit的切换都要严格卡在SCCL的边沿上。我习惯的做法是:SCCL高电平期间,SDA数据必须保持稳定;SCCL低电平期间,SDA才允许切换。这个时序细节,建议用示波器量一下SCCL和SDA的波形来验证,特别是起始和停止条件的建立时间,一旦不满足,传感器会完全不理你。

3. DVP接口图像数据采集与信号解析

3.1 DVP接口信号定义与关键时序

OV5640通过DVP接口输出的信号主要有5组。

  • PCLK:像素时钟,每个上升沿对应一个像素数据。
  • VSYNC:场同步信号,一帧图像开始/结束的标志。
  • HREF:行同步信号,高电平期间输出一行有效像素。
  • DATA[15:0]:16位并行像素数据,在PCLK上升沿采样。
  • XCLK:外部输入时钟,FPGA提供给传感器的工作时钟。

这里面最容易让人迷糊的是RGB565格式下的数据排列。配置0x4300=0x02时,OV5640输出的是RGB565,但它是怎么排列到16位数据线上的呢?经过实际抓取波形验证,RGB565格式下,16位数据线上直接对应RGB565,但需要注意高低字节顺序可能是RGB(高位在前)或BGR(低位在前),这个由0x4300的其他位决定。

而如果你配置成YUV422格式,数据排列又不一样了。比如YUV422的UYVY格式,第一个像素输出U,第二个像素输出Y,第三个像素输出V,第四个像素输出Y,以此类推。这个格式和RGB565的字节序完全不同,调试时最容易头大。

拿RGB565来说,DVP接口输出的数据顺序是:

  • 第一个PCLK上升沿,DATA[15:0] = R[4:0] << 11 | G[5:0] << 5 | B[4:0],对应第一个像素。
  • 第二个PCLK上升沿,输出第二个像素。

HREF信号在有效像素输出期间保持高电平,行消隐期间为低电平。VSYNC信号标识一帧开始。这里要特别留意,OV5640的VSYNC是高有效还是低有效,可以通过寄存器0x3820的bit1、bit0来配置。复位默认值是低有效,很多人在VGA显示时发现图像上下翻转或者同步错乱,就是VSYNC极性没配对。

3.2 图像采集模块的状态机设计

采集模块的核心任务就是正确地抓取每一帧的每一行有效像素。我的设计思路是用一个状态机,加上行、场计数器。

状态机转移逻辑大致如下。

  1. IDLE状态:等待VSYNC有效。VSYNC来后(这里以低有效为例),进入VSYNC_LOW状态。
  2. VSYNC_LOW状态:等待VSYNC拉高,表示新的一帧将要开始。进入LINE_WAIT状态。
  3. LINE_WAIT状态:等待HREF拉高。HREF拉高后,进入DATA_SAMPLE状态,同时开始逐像素采样。
  4. DATA_SAMPLE状态:在HREF高电平期间,每个PCLK上升沿采样一次DATA[15:0],同时像素计数器累加。当HREF拉低,说明一行结束,行计数器加1,回到LINE_WAIT状态等待下一行。
  5. 当行计数器达到预设分辨率,比如480行采样完毕之后,回到IDLE状态,等待下一帧VSYNC。

在写这部分代码时,有一个关键技巧:PCLK和HREF、DATA在进入FPGA之后,必须先经过两级寄存器打拍同步,再用于状态机判断和采样。

这背后的原因是亚稳态问题。OV5640和FPGA是异步器件,PCLK虽然由传感器产生,但相对于FPGA内部时钟是异步的。如果你直接用FPGA的系统时钟去采样PCLK上的数据,或者把HREF直接用于状态机判断,一旦HREF在数据变化边沿附近发生电平翻转,就可能触发触发器亚稳态,导致整个采集逻辑错乱,图像出现随机花屏或者行错位。

正确的做法是:先用PCLK对HREF和DATA进行打拍采样,保证和PCLK同源;再用打拍后的信号做后续逻辑。或者反过来,用系统时钟同步PCLK的上升沿,确保在PCLK稳定后再采样数据。我在Xilinx的Artix-7和Altera的Cyclone IV上都试过,这个同步步骤绝对不能省。

3.3 像素数据的有效窗口与消隐处理

很多新手搞不清楚HREF和有效像素的对应关系,导致采集到的图像左边或者右边有一条黑边或者错位。这里说一个非常实用的经验。

OV5640在输出一帧图像时,并不是持续的像素流,而是按照“行有效 + 行消隐”的方式输出。在一行有效数据开始前,HREF有一个建立时间;在有效数据结束后,HREF拉低,进入行消隐。帧头也有帧消隐。

所以你在配置采集模块时,通常要用HREF作为数据有效标志,而不是简单地数PCLK脉冲。如果直接用PCLK总数除以图像的宽度来定位行,会因为消隐期的存在而完全对不上。

我曾经踩过一个很实在的坑:在某个工程里,我只用了VSYNC和PCLK去采集数据,没有用HREF做行门控,结果图像是一条一条错位的斜纹。后来加上HREF门控,严格按照HREF高电平时才采样,问题立刻消失。

VGA显示时还有一个同步问题。OV5640输出的RGB565图像,像素数据需要按照从左到右、从上到下的顺序写入显示缓存。而VGA输出也是同样的顺序。两者如果不同步,画面会出现撕裂或者偏移。解决办法是:采集端写入FIFO时,行号、列号都从0开始计数,和VGA显示端的行列计数保持一致。

4. 数据缓存与显示联动设计

4.1 FIFO还是双口RAM:图像缓存方案怎么选

采集端PCLK和显示端像素时钟不在同一个时钟域,必须做跨时钟域处理。常见的方案有两种:异步FIFO和双口RAM。

如果只是做实时的图像透传,也就是采一帧显示一帧,用异步FIFO就够了。优点是不需要管理复杂的读写地址,FIFO内部会处理读写指针。但FIFO的容量必须能覆盖一行有效像素的数据量,因为显示端的读取是连续的,如果FIFO深度不够,显示端会读到空数据,图像出现条纹。

如果要做帧缓存,也就是采集端在写一帧的同时,显示端在读上一帧,这需要至少两帧的存储空间,也就是乒乓操作。这时候双口RAM更合适。因为你可以把RAM分成两个bank,一个bank写新帧,一个bank读旧帧,每帧交替切换。当然,如果分辨率高、帧率大,存储资源消耗也很大。以720P的RGB565为例,一帧数据 = 1280 × 720 × 16bit ≈ 1.84MB,至少需要两帧就是3.68MB,这已经超出了很多中低端FPGA的片上存储容量。

所以实际工程中,我们通常不会做完整的一帧缓存,而是做行缓存。具体来说,在显示端以行为单位,设置一个足够容纳一行像素的FIFO。采集端持续往FIFO里写,显示端持续从FIFO里读。在行与行的切换间隙,利用消隐时间做同步。

这种方案对资源的要求低很多,但有个前提:采集端的PCLK频率和显示端的像素时钟频率不能差太多,否则FIFO会不断积累误差,最终上溢或者下溢。解决方法是让两端的有效行长度保持完全相同,并且在每一行开始前,对FIFO进行一次清空复位,保证写入和读取从同一个起点开始。

提示:如果你用的是Intel/Altera的FPGA,Quartus自带FIFO IP核,配置时务必选择“异步时钟”选项,读写时钟分别为PCLK和显示像素时钟。不要图省事选同步模式,那样根本解决不了跨时钟域问题。

4.2 从采集到VGA显示的完整数据通路

我用一个720P的RGB565项目来具体说明。

系统时钟50MHz,PCLK约为42MHz,VGA像素时钟需要74.25MHz。因为两个时钟不同,直接用一个FIFO做缓存。数据通路如下。

  1. OV5640通过DVP接口输出PCLK、HREF、DATA[15:0]。
  2. 采集模块在HREF高电平期间,每个PCLK上升沿把DATA[15:0]写入行FIFO。FIFO写时钟用PCLK。
  3. VGA驱动模块在显示有效区域,每个像素时钟到来时,从FIFO读出一个像素数据。FIFO读时钟用VGA像素时钟。
  4. VGA驱动模块把读出的RGB565数据拆分成R、G、B分量,分别送给VGA接口的DAC转换。

这里有一个很实在的问题:VGA显示时,如果显示器的扫描顺序和FIFO读出的顺序不一致,画面就会撕裂。所以需要在VGA驱动模块中,同样设置行计数和场计数,只有当行计数值和场计数值都在有效区域时,才从FIFO读取数据并输出到显示器。

VGA时序的关键参数,以常见的720P为例。

参数数值
行同步像素数16
行后肩像素数62
行有效像素数1280
行前肩像素数62
一行总像素数1420
场同步行数5
场后肩行数27
场有效行数720
场前肩行数28
一帧总行数780
像素时钟74.25MHz

当然,如果用HDMI则不需要DAC转换,直接在FPGA内部做TMDS编码即可,但要注意DDC通道和热插拔检测的处理。篇幅原因,这里主要讲VGA通路,HDMI编码的细节下次单独开一篇。

4.3 乒乓缓存:如何保证显示画面不撕裂

前面我提到,只要FIFO容量够大,实时透传也可以显示。但透传有个天然的问题:采集端和显示端如果没有做到严格的行同步对齐,画面会出现水平撕裂,也就是图像上半部分来自当前帧,下半部分来自上一帧或者下一帧。

要彻底消除撕裂,最稳妥的办法还是做帧级别的乒乓缓存。

乒乓操作的核心思想是:准备两块存储区域,标记为A和B。在帧N+1写入A区期间,显示端读取B区的帧N;在帧N+2写入B区期间,显示端读取A区的帧N+1。这样读写操作交替进行,显示端永远看到的都是完整的一帧。

实现乒乓的关键是一个帧切换标志。采集端在检测到新的一帧VSYNC到来时,把写地址切换到另一块区域;显示端在扫描到垂直消隐期间,把读地址也切换到另一块区域。

实际工程中需要注意:写地址的切换和读地址的切换,必须有足够的时序余量。最简单的实现方式是用一个1bit的ram_sel寄存器。采集端在VSYNC有效时翻转,显示端在场消隐时翻转。为了保证两边看到的ram_sel是同步的,建议在两端各自打两拍再做地址切换,避免读端正在读A区的时候,写端突然切换到了A区。

4.4 显示效果调试:颜色、位置、同步三个维度

图像能上屏之后,接下来的调试主要看三个维度:颜色对不对、位置偏不偏、画面稳不稳。

颜色不对,先查OV5640的0x4300寄存器是否配置为RGB565,再查DVP数据线的高低字节有没有接反。很多开发板的原理图上DATA端口顺序是反的,导致红色和蓝色互换,画面整体呈蓝紫色调。这种问题不用改代码,可以通过调换数据线位置解决。

位置偏移,通常是HREF的有效窗口和VGA的有效窗口没对齐。如果你发现整个画面往左偏移或者往上偏移,多半是采集模块少计了行消隐或者场消隐的周期。解决的办法是:在采集模块里,行数据从HREF拉高后的第一个PCLK开始采样,而显示端从VGA行同步之后,经过后肩、有效数据等标准时序再开始读FIFO,这样两边的时钟节拍就对上了。

画面不稳定,优先检查FIFO的读写指针是否平衡。如果采集端写入快于显示端读取,FIFO会溢出;反过来会下溢。我的经验是:在每帧开始或每行开始的时候,对FIFO的读指针做一次重置,把两端重新拉回同一个起点,可以大幅降低不同步的概率。

5. 常见问题与调试排查技巧实录

5.1 图像全黑,SCCB读寄存器全是0xFF

这是出现频率最高的问题。排查方法按以下顺序来。

第一步,用I2C调试工具或者逻辑分析仪,检查上电后SCCL和SDA线上有没有波形。如果完全没有波形,说明SCCB模块没有启动,检查FPGA代码里状态机的触发条件,比如cfg_start信号有没有拉高。

第二步,有波形但读回来全是0xFF,大概率是从机地址不对。OV5640的SCCB_ID引脚如果悬空或接高电平,设备地址是0x78;如果接地,地址是0x7A。先把原理图确认清楚,再看代码里的设备地址。

第三步,如果地址也对但通信不稳定,检查SCCL频率。有些OV5640对SCCL的上限频率有要求,超过400KHz会通信失败。建议把SCCL分频到200KHz左右,稳妥。

第四步,确认PWDN引脚没有被拉高。PWDN是掉电引脚,如果它被拉高了,OV5640直接进入掉电模式,SCCB肯定响应不了。

注意:OV5640的复位引脚,也就是RESETB,需要在上电后拉高。有些板子设计的是低电平复位,如果你一直复位,传感器也不会工作。上电时序里要先拉低复位,再拉高复位,同时保持PWDN低电平,再开始执行SCCB配置,这个顺序别搞反。

5.2 图像有斜条纹或者整条错位

之前说过,多半是没用HREF做行门控,或者HREF同步没做好。

排查思路是这样的:用逻辑分析仪抓HREF和PCLK,确认HREF的周期是否等于一行有效像素的时间。如果HREF高电平期间PCLK的脉冲数和预期分辨率不一致,说明OV5640输出配置有问题,或者PCLK分频配置不对。

另外,DVP接口的DATA线在PCLK跳变沿附近有毛刺,也可能导致采到错误数据。建议在PCB上给DVP数据线做等长处理,同时在FPGA内部对每个数据位都打两拍。我实测过,同样的代码,在优化了PCB走线后,花屏现象明显减少。

5.3 图像颜色偏蓝或偏红

颜色通道互换是最常见的原因。RGB565格式下,如果硬件上把R和B通道接反了,画面就会整体偏蓝。排查方法是:不修改代码,先通过抓取一张纯色画面的波形,确认DATA[15:11]对应的是不是标准RGB565的R通道。

如果确认接反了,有两个修复办法。第一,修改原理图,把数据线调换过来;第二,在FPGA内部做数据位重新映射。比如采集到的data[15:11]本来是R,但实际硬件接的是B,就在代码里用assign r = data[4:0]这种方式做重新映射。

5.4 FIFO溢出,画面出现横条纹滚动

这个现象典型的表现为:画面可以显示,但有一条或者多条横条纹在垂直方向不断滚动。

原因基本就是FIFO的读写速率不匹配。显示端读取速率大于采集端写入速率,FIFO读空,显示端输出了空数据;反之亦然。

解决思路有以下几种。

  • 调整VGA时序里的有效像素总数,让显示端的像素消耗速度和采集端的写入速度尽量接近。
  • 在每帧开始或者每行开始的时候,对FIFO做一次清空,重新建立同步。
  • 如果使用了帧缓存,可以锁存一帧完成后产生的write_done信号,在写端完成整帧切换后再允许读端读取。

5.5 图像分辨率错误,画面只显示一部分

如果画面只显示左上角一部分,后续区域是黑屏,大概率是采集模块里的行场计数器配置错了。比如你配置OV5640输出1280x720,但采集模块里把最大行数设置成了480,那么超过480行的数据,直接就被状态机忽略了,自然显示不出来。

还有一个容易忽略的点:OV5640在输出RGB565时,可能默认配置为“输出尺寸小于最大尺寸”的模式。比如0x3808和0x3809配置的是输出宽度,0x380C和0x380D配置的是行总长度HTS。如果输出尺寸和HTS不匹配,也会导致数据错乱。建议对照OV5640的官方配置表,确认在特定分辨率下,0x3808/0x380A/0x380C/0x380E这几组寄存器的值是否配套。

6. 扩展思考:从DVP到MIPI,以及ISP算法的切入点

如果你把这套DVP接口的采集流程跑通了,其实就已经具备了对图像数据做进一步处理的基础。从趋势来看,越来越多的传感器开始转向MIPI接口,OV5640本身也支持MIPI,用FPGA接收MIPI信号需要做差分信号处理、通道对齐、字节解包等,复杂度比DVP高不少,但核心思路依然是一样的:对齐同步信号,解析有效数据,缓存并显示。

另外,图像采集只是前菜,真正的重头戏是采集之后的ISP处理。比如坏点校正、黑电平校正、去马赛克、白平衡、伽马校正这些算法,在FPGA里基本都是逐像素的流水线处理。我之前在采集到RGB565之后,紧接着加了一个灰度转换模块和一个Sobel边缘检测模块,整条流水线的延迟只有几个像素时钟,实时性非常好,这就是CPU方案做不到的。

写在最后的调试心得

我最初调通这套OV5640系统,前后花了两三天时间,其中一半时间都耗在SCCB配置和DVP行场同步上。回头看,最核心的经验有三条:第一,时序问题一定要用逻辑分析仪或者示波器去看波形,靠猜是猜不出来的;第二,SCCB配置完成后必须留足够的时间让传感器稳定,采集模块不要抢跑;第三,FIFO的读写时钟域隔离和帧同步标志,宁可做得复杂一点,也不要为了省资源留下隐患。这套系统调通之后,后面再做MIPI接入、再做图像算法加速,都会顺畅很多。

返回列表