基于FPGA的ov7670摄像头视频VGA显示(含SDRAM)

框架简介:从摄像头采集得到的数据,先进入一个写FIFO缓存一下,然后写入SDRAM,将SDRAM的数据读取出来先放入一个读FIFO缓存一下,最后送给VGA驱动屏幕显示。

模块组成:
在这里插入图片描述

这里本来用到的设备只有OV7670的摄像头模组,还有一块型号为EP4CE10F17C8的板子,但是这个板子的VGA显示被烧坏了,最后加了一块5CEBA4F23C7的板子,把这两块板子用杜邦线集连在一起完成整个视频系统的功能,第二快板子仅仅是作为VGA显示的作用,而SDRAM、读写控制模块、以及FIFO数据缓存等等都是在第一个板子上实现的。
硬件设备:两块开发板 一个摄像头模组

在这里插入图片描述

下面来分别介绍一下上述框图结构中各模块所实现的功能

在说数据采集模块之前这里实际上还有一个部分省略了,那就是摄像头的配置,一个摄像头要想正常工作,或者改变它的工作方式,就需要对其进行配置,改变其模式寄存器数值就可以让他处于不同的工作模式下。配置摄像头模块有两种方法,常见的可以通过IIC总线来配置,或者利用软核nios来配置,前一种更为常见和实用,关于IIC配置的时序这里鉴于篇幅就不展开了(代码后面会一并奉上)。这个配置过程可以简单理解为,IIC有一根双向数据总线sda,sda先向摄像头sensor的模式寄存器写入一些数据,告诉这个摄像头该怎么工作,这样摄像头的配置工作就结束了,它开始向FPGA传输数据了。
然后下面就到了我们的数据采集模块,该模块的主要功能是把从摄像头 sensor进来的8bit数据流转换成16bit的数据流,这里VGA驱动采用的是RGB565的数据格式,所以要将sensor出来的数据转换成16bit的数据流。
下面就是我们的写控制模块,这个模块怎么写要取决于你SDRAM控制器是怎么写的,这里我的SDRAM控制器是这样要求的,每次想要向SDRAM里面写数据,要首先产生一个清零信号,这个信号有两个作用,第一是向SDRAM里面写数据之前清空写FIFO,避免FIFO里面有残留数据对接下来的数据产生影响,第二是把写SDRAM的初始地址给到SDRAM控制器,这个可以简单理解为你要向SDRAM里面写数据,你肯定要告诉它这个数据写在SDRAM的哪个地址上吧,最后还有一个就是最大地址信号,这个信号决定了你SDRAM最大能写多少数据,超过这个数值的时候,后面进到SDRAM的数据就会重新从你给的起始地址开始写,覆盖之前的数据。了解了这些之后,我们的写控制模块也就出来了,除了上面的清零信号、起始地址、最大地址、还有就是我们从数据采集模块接收到的16bit数据流,以及跟数据同步的行同步信hs_sync。

再然后我们的16bit数据流就流到了写FIFO的写端口,这里有一点值得注意,这里最好用异步FIFO,因为FIFO前端是写控制模块,后端是SDRAM控制器模块,这两个模块很可能是不同时钟域的,用异步FIFO可以起到很好的缓存作用,同时读时钟最好比写时钟大,这样读地快写地慢可能避免FIFO被写满。
接下来就到了SDRAM控制器模块了,本来SDRAM时序原理就是一个很长的话题,这里我就挑一些重点来说一下,写这个控制器第一步就是SDRAM的初始化,SDRAM存储数据的原理是电容的充放电,而电容从上电到它稳定工作这中间是有一段时间间隔,所以SDRAM都有一个上电初始化的过程,所有的读写控制命令、数据的写入或读出都只能再初始化完成后进行。初始化的过程要完成这样一些事,打开所有 bank地址所有的行地址进行预充电,对模式寄存器进行配置,一般低三位就是设置突发长度的,可以选择单字节突发读写,回着突发长度为2、4、8,还可以选择页读写(页读写就是行读写),就是一次读写一行数据。这里简单说一下SDRAM的存储模式,可以把SDRAM简单理解成几页纸,每一页纸就是一个bank , 一般的SDARM都是有4个bank,那么自然我们所说的行地址列地址就好比我们的一页纸上的第几行第几列。说完SDRAM的存储模式之后再简单说一下SDRAM比较重要的几条线,有数据总线、地址总线、bank总线(一般两位,因为只有4个bank)、片选信号(cs_n 低信号有效,说明这个芯片被选中),cke(时钟使能信号 可以简单处理成 随着复位信号后面一直拉高就行),ras_n(行地址选通,不管读还是写 我肯定要知道它在SDRAM的具体位置),cas_n(列地址选通,一样的理解),最后还有一个dqm,一个两位的数据掩码,用来屏蔽一些我们不关注的数据,这里我直接用的是2’b00。还有一个常称为命令总线的信号,其实就是把我们上面提到的{cs_n , ras_n ,cas_n ,we_n} 片选、行选通、列选通、写使能这四个信号组合在一起,发出不同的命令,常用的命令有空操作(NOP)、激活 (就是选通bank地址和行地址)、读操作、写操作。在了解这些之后,再来看SDRAM的读写时序就较为简单了,首先我们要向SDRAM里面写数据,那我们肯定要先通过命令总线告诉SDRAM,实际情况一般是这样的,外部发出写SDRAM请求,然后SDRAM激活,就是选中bank 和行地址(这两个实际上是一起选通的),然后再选通列地址,再发出写命令,然后我们就可以在数据总线上看到以突发长度为基本单位的数据了。同理读SDRAM的原理也是这样的,这里要注意由于器件的电器特性,发出命令到命令被执行这之间是有一个潜伏期的。说完这些基本的概念和读写操作时序之后,在顶层用一个大的状态机来控制SDRAM读、写、刷新这三种状态的调度。这里补一下刷新的概念,前面提到了SDRAM是用电容来存储数据的,我们知道电容是会不断掉电了,所以为了维持SDRAM里面的数据不变,我们就需要定时向SDRAM进行充电,以维持数据不变,具体多长时间刷新一次各个器件的手册都有说明。
SDRAM控制的知识很多,上述只是按照我的个人理解做了一个简单的说明,肯定还有很多遗漏的地方, 要把一个SDRAM调好调稳定不是一个简单的事情。
回到我们的框架图,在这个工程中,我们SDRAM的写请求是写FIFO的可读数据大于8(这个工程突发长度我设的8),也就是说只要写FIFO里面可以读的数大于8,它就会不断地往SDRAM里面写数据,再加上这个FIFO地读时钟我设的就比写时钟快,所以这个FIFO是肯定不会有写满的风险的。读FIFO那边同理,那边是可写数据不满128,我就不断通过SDRAM往读FIFO里面写数据。
接下来就是我们这个工程的最后一个模块了,VGA模块。这个模块很简单,要驱动VGA我们一共只需要四个信号 hs_sync (帧同步信号) ,
vs_sync (场同步信号) , 数据data( RGB565),以及驱动时钟,这里由于我的这块板子比较特别( 它可能内部集成了DAC,直接自动把数字信号转换成模拟信号送给VGA显示了),不需要驱动时钟也能驱动VGA,所以代码最后一部分是没有VGA的驱动clk的。我们只需要把 hs_sync信号作为读 FIFO的读使能去得到数据送给VGA就行( 我这里做的 hs_sync和数据是严格对其的,所以可以直接作为读使能,要是你的hs_sync含有消隐的部分,那可以直接再产生一个de数据有效信号,作为读使能)

视频显示
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/jiyishizhe/article/details/102985551
今日推荐