700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 【正点原子FPGA连载】第四十三章MT9V034摄像头RGB-LCD显示实验 -摘自【正点原子】新

【正点原子FPGA连载】第四十三章MT9V034摄像头RGB-LCD显示实验 -摘自【正点原子】新

时间:2023-08-20 15:58:37

相关推荐

【正点原子FPGA连载】第四十三章MT9V034摄像头RGB-LCD显示实验 -摘自【正点原子】新

1)实验平台:正点原子新起点V2开发板

2)平台购买地址:/item.htm?id=609758951113

2)全套实验源码+手册+视频下载地址:/thread-300792-1-1.html

3)对正点原子FPGA感兴趣的同学可以加群讨论:994244016

4)关注正点原子公众号,获取最新资料更新

第四十三章MT9V034摄像头RGB-LCD显示实验

MT9V034是ON Semiconductor(安森美半导体)公司生产的一颗CMOS图像传感器,该传感器功耗低、可靠性高以及采集速率快,主要应用机器视觉,双目视觉,宽温度工业场合等领域。本章我们将使用FPGA开发板实现对MT9V034的数字图像采集并通过LCD实时显示。

本章包括以下几个部分:

4242.1简介

42.2实验任务

42.3硬件设计

42.4程序设计

42.5下载验证

43.1简介

MT9V034是一款1/3英寸单芯片图像传感器,其感光阵列最大可达到752*480,能实现最快60fps VGA分辨率的图像采集,具有全局曝光和高动态范围(HDR)操作。这款CMOS图像传感器具有安森美半导体的突破性功能,实现了CCD图像质量的低噪声和CMOS成像技术(基于信噪比和低光灵敏度),同时保持固有尺寸、成本和CMOS的集成优势。下表为几个摄像头的功能对比。

通过上述的对比可以看出,相对于其他2款摄像头MT9V034的优势在于HDR模式和全局曝光。HDR模式的原理是根据不同的曝光时间的LDR(Low-Dynamic Range)图像,利用每个曝光时间相对应最佳细节的LDR图像来合成最终HDR图像,与普通的图像相比,可以提供更多的动态范围和图像细节。下图是线性模式和HDR模式的对比图。

图 43.1.1 HDR模式

图 43.1.2 线性模式

通过对比可以发现,开启了 HDR,会使拍到的图像亮度比较高的地方变暗,亮度比较低的地方变亮,总的来说就是使图像显示的更均衡。

卷帘曝光的原理是通过Sensor逐行曝光的方式实现的。在曝光开始的时候,Sensor逐行扫描逐行进行曝光,直至所有像素点都被曝光。与卷帘曝光不同 ,全局曝光整幅场景在同一时间曝光实现的,Sensor所有像素点同时收集光线,同时曝光。下面为两种模式的对比图。

图 43.1.3 全局曝光拍摄图

图 43.1.4 卷帘曝光拍摄图

当拍摄快速移动的物体时,全局曝光拍摄到的物体比较清晰,不会发生形变,而卷帘曝光拍摄的图片会出现部分曝光(partial exposure)、斜坡图形(skew)、晃动(wobble) 等现象,也就是传说中的果冻了。这是全局曝光其优势的一面,相对于卷帘曝光也有其劣势的一面。当全局曝光的曝光时间长(如大于500μs)时,其噪点情况严重,而运用卷帘曝光后,图片会有更低的噪声和更快的帧速。

下图为MT9V034的功能框图:

图 43.1.5 MT9V034功能框图

由上图可知,除了传统的并行逻辑输出,MT9V034还具有串行低压差分信号(LVDS)输出。该传感器可以在立体声相机中操作,并且该传感器被指定为立体声主机时,可以合并来自本身的立体声,还可以将从属传感器的数据合并为一个串行LVDS流。本次实验只采用传统的并行逻辑输出。

MT9V034通过两线串行接口将寄存器写入MT9V034和从中读取,以此来配置窗口大小和行场分辨率等寄存器。MT9V034是具有四个可能ID(0x90、0x98、0xB0和0xB8)由S_CTRL_ADR0和S_CTRL_ADR1输入引脚确定,本次实验所用的MT9V034的写器件ID是0x90。下图是器件ID的相关内容。

图 43.1.6 器件地址

MT9V034使用的是两线式接口总线,该接口总线包括SCLK串行时钟输入线和SDATA串行双向数据线,分别相当于IIC协议的SCL信号线和SDA信号线。本次实验所用的两线式接口总线兼容IIC协议,是所以不对相关协议详细介绍,有关IIC协议的详细介绍请大家参考“EEPROM读写实验”章节。

两线式接口总线的写传输协议如下图所示:

图 43.1.7写传输协议

上图中的ADDR是由7位器件地址和1位读写控制位构成(0:写 1:读),MT9V034的器件地址为7’h5c,所以在写传输协议中,ID Address(W) = 8’hb8(器件地址左移1位,低位补0);R0x09为8位寄存器地址,在MT9V034的数据手册中有些寄存器是可改写的,有些是只读的,只有可改写的寄存器才能正确写入;Write Data为16位写数据,每一个寄存器地址对应16位的配置数据。上图中的第9位ACK表示从机应答,该位是由从机(此处指MT9V034)发出应答信号来响应主机表示当前器件地址、寄存器地址和写数据是否传输完成,但是从机有可能不发出应答信号,因此主机(此处指FPGA)在此必须判断此处是否有应答,有应答即说明当前传输完成,无应答表示传输未完成。

我们可以发现,MT9V034的两线式接口总线和IIC写传输协议是极为相似的,只是在两线式接口总线写传输协议中,一个寄存器地址写入16位数据,而IIC写传输协议一个地址只写入8位数据。两线式接口总线的读传输协议和IIC有些差异,在IIC读传输协议中,一个寄存器地址只读出8位数据;而两线式接口总线传输协议中一个寄存器地址只读出16位数据,下图为两线式接口总线的读传输协议。

图 43.1.8 SCCB读传输协议

由上图可知,两线式接口总线读传输协议分为两个部分。第一部分是写器件地址和寄存器地址,即先进行一次虚写操作,通过这种虚写操作使地址指针指向虚写操作中寄存器地址的位置,当然虚写操作也可以通过前面介绍的写传输协议来完成。第二部分是读器件地址和读数据,此时读取到的数据才是寄存器地址对应的数据,注意ID Address(R) = 8’hB9(器件地址左移1位,低位补1)。上图中的NACK位由主机(这里指FPGA)产生,由于两线式接口总线不支持连续读写,因此NACK位必须为高电平。

MT9V034在上电后是存在默认寄存器的,即上电后就可以输出752x480分辨率的图像,如果大家需要其他的分辨率或模式就必须先对传感器进行初始化,可通过配置寄存器使其工作在预期的工作模式,以得到较好画质的图像。因为两线式接口总线的写传输协议和IIC几乎相同,因此我们可以直接使用IIC的驱动程序来配置摄像头。当然这么多寄存器也并非都需要配置,很多寄存器可以采用默认的值。ON Semiconductor公司提供了MT9V034的软件使用手册(MT9V034,位于开发板所随附的资料“7_硬件资料/7_MT9V034资料/MT9V034.pdf”),如果某些寄存器不知道如何配置可以参考此手册,下表是本程序用到的关键寄存器的配置说明。

0Xd5 Fine Shutter Width Total 0x0000 细调曝光时间的宽度

MT9V034的寄存器较多,对于其它寄存器的描述可以参MT9V034的数据手册。

下图为MT9V034的一些特性。

图 43.1.9 MT9V034的特性

从上图可以看出,MT9V034的输入时钟频率的范围是13Mhz~27Mhz;本次实验摄像头的输入时钟为24Mhz,是由外部晶振提供的;两线式接口总线的SCLK的时钟频率最大为400KHz。

图 43.1.10 PIXCLK和SYSCLK的关系

结合图 43.1.10和图 43.1.9可知,PIXCLK和SYSCLK是同频不同相的2个时钟,本次实验摄像头的输入时钟为24Mhz,所以摄像头的输出时钟也为24Mhz。

MT9V034在并行逻辑输出的模式下仅支持10bit的YUV(亮度参量和色度参量分开表示的像素格式),不支持其他格式。由于摄像头采集的图像最终要在LCD上显示,且新起点开发板上的数据接口为RGB888格式(详情请参考“LCD彩条显示实验”章节),因此必须将MT9V034摄像头输出的YUV格式的图像像素数据转换为RGB888格式。下图为摄像头输出的行时序图。

图 43.1.11 行时序图

LINE_VALID:数据有效使能。当其为高时,输出的数据为有效数据。

PIXCLK:像素时钟。由MT9V034产生的对外输出的时钟信号。

DOUT:有效数据。摄像头采集得到的像素数据,本次实验取其高8位。

图 43.1.12 场时序图

图 43.1.13 信号含义

LINE_VALID:数据有效使能。当其为高时,输出的数据为有效数据。

FRAME_VALID:帧(场) 同步信号。当此信号有效的时候就表示开始显示新的一帧数据。

43.2实验任务

本节实验任务是使用新起点开发板及MT9V034摄像头实现图像采集,并通过TFT-LCD接口驱动RGB LCD液晶屏(支持目前正点原子推出的所有RGB LCD屏),并实时显示出图像。

43.3硬件设计

新起点FPGA开发板上有一个摄像头扩展接口,该接口可以用来连接MT9V034/OV5640等摄像头模块,摄像头扩展接口原理图如图 43.3.1所示:

图 43.3.1 摄像头扩展接口原理图

图 43.3.2 摄像头接口

ATK-MT9V034是正点原子推出的一款高性能36W像素高清摄像头模块。该模块通过2*9排针(2.54mm间距)同外部连接,我们将摄像头的排针直接插在开发板上的摄像头接口即可,如下图所示:

图 43.3.3 MT9V034摄像头连接开发板图

前面说过,MT9V034在YUV模式中有效数据是D[9:0],而我们的摄像头排针上数据引脚的个数是8位,而摄像头排针上的8位数据连接的就是MT9V034传感器的D[9:2],所以我们直接使用摄像头排针上的8位数据引脚即可。

需要注意的是,由图 43.3.1可知,摄像头扩展口的第18个引脚定义为CMOS_PWDN,而我们的MT9V034摄像头模块的STB(CMOS_PWDN)引脚固定为低电平,也就是一直处于正常工作模式。MT9V034摄像头接口的第18个引脚定义为EXP,这个引脚是摄像头外部触发脉冲的引脚,只在快照模式下启用它。MT9V034摄像头模块内部自带24M晶振的,所以不需要FPGA输出时钟给摄像头。

由于LCD接口和SDRAM引脚数目较多且在前面相应的章节中已经给出它们的管脚列表,这里只列出摄像头相关管脚分配,如下表所示:

表 43.3.1 MT9V034摄像头管脚分配

摄像头TCL约束文件如下:

set_location_assignment PIN_T14 -to cmos_data[7]

set_location_assignment PIN_R14 -to cmos_data[6]

set_location_assignment PIN_N6 -to cmos_data[5]

set_location_assignment PIN_P6 -to cmos_data[4]

set_location_assignment PIN_M8 -to cmos_data[3]

set_location_assignment PIN_N8 -to cmos_data[2]

set_location_assignment PIN_P8 -to cmos_data[1]

set_location_assignment PIN_K9 -to cmos_data[0]

set_location_assignment PIN_M9 -to cmos_href

set_location_assignment PIN_R13 -to cmos_pclk

set_location_assignment PIN_R12 -to cmos_pwdn

set_location_assignment PIN_L9 -to cmos_reset

set_location_assignment PIN_N9 -to cmos_scl

set_location_assignment PIN_L10 -to cmos_sda

set_location_assignment PIN_P9 -to cmos_vsync

43.4程序设计

图 43.4.1是根据本章实验任务画出的系统框图。

图 43.4.1 顶层系统框图

由上图可知,时钟模块(pll_clk)为LCD顶层模块、SDRAM控制模块以及I2C驱动模块提供驱动时钟。I2C配置模块和I2C驱动模块控制着传感器初始化的开始与结束,传感器初始化完成后图像采集模块将采集到的数据写入SDRAM控制模块,LCD顶层模块从SDRAM控制模块中读出数据,完成了数据的采集、缓存与显示。需要注意的是图像数据采集模块是在SDRAM和传感器都初始化完成之后才开始输出数据的,避免了在SDRAM初始化过程中向里面写入数据。

MT9V034虽然在上电后不配置寄存器也能正常工作,但是此时输出的分辨率可能不是实验需要的分辨率,所以必须通过配置寄存器的值来达到实验所需要的分辨率。配置寄存器的协议和I2C协议在写操作时几乎一样,所以需要一个I2C驱动模块。为了使MT9V034在期望的模式下运行并且提高图像显示效果,需要配置较多的寄存器,这么多寄存器的地址与参数需要单独放在一个模块,因此还需要一个寄存配置信息的I2C配置模块。在摄像头配置完成后,开始输出图像数据,因此需要一个摄像头图像采集模块来采集图像;采集到的图像先进入SDRAM存储器进行缓存,最后LCD顶层模块读取SDRAM缓存的数据以达到最终实时显示的效果。

对比“OV7725摄像头TFT-LCD显示实验”的系统框图可以发现,本次实验只是把外设OV7725模块替换成了MT9V034模块,替换了图像采集模块和IIC配置模块,修改了IIC驱动模块和SDRAM读写模块,其余模块基本相同。替换图像采集模块和修改图像采集顶层模块的原因在于OV7725摄像头输出的是RGB565格式的16bit数据,而MT9V034输出的是YUV格式的8bit数据;替换IIC配置模块和修改IIC驱动模块的原因在于OV7725的一个寄存器地址只写8bit数据,而MT9V034的一个寄存器地址可以写16bit数据;本次实验所采用的摄像头 MT9V034的帧率为62帧,而LCD的屏幕的帧率有的比摄像头低,有的比摄线头高,而之前所采用的摄像头OV5640和OV7725的帧率都比LCD屏的帧率低,因此两帧的乒乓操作足以满足之前的设计需求,但相对于本次实验来说两帧的乒乓操作已经不能满足设计需求,所以需要修改SDRAM读写模块。SDRAM读写模块修改后的帧切换原理如下所示:

图 43.4.2 写比读快的示意图

图 43.4.3 读比写快的示意图

在图 43.4.2中的图2是写操作完成了一帧的存储,换了一个存储空间继续写,而读操作还没有读完一帧数据,继续读原来的存储空间;因为读帧率和写帧率的帧率比没有2倍,所以写操作端的第二帧还没有写完,读操作端已经读完了第一帧,换了一个存储空间继续读,如图3所示; 图4中写操作端完成了第二帧的存储,而读操作还没有读完第二帧数据;同理图5中写操作端完成了第三帧的存储,开始第四帧的写入,而读操作将要读完第二帧数据;图6表示读操作读完第二帧数据,而写操作端没有完成第四帧的存储,这里将存储空间跳转了2个,就是为了防止写操作追上读操作,出现画面撕裂的现象。

在图 43.4.3中图2是读操作读出了一帧的数据,而本次实验是根据写存储空间来判断读存储空间的,所以继续读之前的存储空间;图3是写操作端完成了第一帧的存储,而读操作还没有读完第二帧数据;图4是读操作端读完第二帧数据,而写操作还没有完成了第二帧的存储。

顶层模块的原理图如下图所示:

图 43.4.4 顶层模块原理图

FPGA顶层模块(mt9v034_rgb565_lcd)例化了以下六个模块:时钟模块(pll_clk)、I2C驱动模块(i2c_dri)、I2C配置模块(i2c_cfg)、图像采集顶层模块(cmos_data_top)、SDRAM控制模块(sdram_top)和LCD顶层模块(lcd_rgb_top)。

时钟模块(pll_clk):时钟模块通过调用PLL IP核实现,共输出3个时钟,频率分别为100Mhz(SDRAM参考时钟)、100Mhz偏移负75度时钟(用于SDRAM输出采样的偏移时钟)和50Mhz时钟,50Mhz时钟作为I2C驱动模块和LCD顶层模块的驱动时钟。

I2C驱动模块(i2c_dri):I2C驱动模块负责驱动MT9V034的两线式接口总线,用户可根据该模块提供的用户接口可以很方便的对MT9V034的寄存器进行配置,该模块和“EEPROM读写实验”章节中用到的I2C驱动模块为同一个模块,有关该模块的详细介绍请大家参考“EEPROM读写实验”章节。

I2C配置模块(i2c_cfg):I2C配置模块的驱动时钟是由I2C驱动模块输出的时钟提供的,这样方便了I2C驱动模块和I2C配置模块之间的数据交互。该模块寄存需要配置的寄存器地址和数据,同时该模块输出MT9V034的寄存器地址和数据以及控制I2C驱动模块开始执行的控制信号,直接连接到I2C驱动模块的用户接口,从而完成对MT9V034传感器的配置。

图像采集顶层模块(top_cmos_data):摄像头采集模块在像素时钟的驱动下将传感器输出的场同步信号、行同步信号以及8位数据转换成SDRAM控制模块的写使能信号和16位写数据信号,完成对MT9V034传感器图像的采集。如果LCD屏的分辨率小于MT9V034的分辨率,还要对MT9V034采集的数据进行裁剪,以匹配LCD屏的分辨率。

SDRAM控制模块(sdram_top):SDRAM读写控制器模块负责驱动SDRAM片外存储器,缓存图像传感器输出的图像数据。该模块将复杂的读写操作封装成类似FIFO的用户接口,非常方便用户的使用。有关SDRAM控制模块的详细介绍请大家参考“SDRAM读写实验”章节。

LCD顶层模块(lcd_rgb_top):LCD顶层模块负责驱动LCD屏的驱动信号的输出,同时为其他模块提供屏体参数、场同步信号和数据请求信号。有关LCD驱动模块的详细介绍请大家参考“OV7725摄像头RGB-LCD显示实验”章节。

顶层模块大部分的代码在介绍“OV7725摄像头RGB-LCD显示实验”章节时已经介绍过了,这里不再详述,但还有部分代码做了改动,改动的代码如下:

41 //parameter define 42 parameter SLAVE_ADD = 7'b1001_000; //slave address 43 parameter BIT_CTRL = 1'b0 ; //MT9V034的字节地址为8位 0:8位 1:16位44 parameter DATA_CTRL = 1'b1 ; //MT9V034的数据为16位 0:8位 1:16位45 parameter CLK_FREQ = 26'd50_000_000 ; //i2c_dri模块的驱动时钟频率 50.0MHz46 parameter I2C_FREQ = 18'd250_000 ; //I2C的SCL时钟频率,不超过400KHz

程序的第42行,修改了器件的地址。

程序的第44行,添加了参数DATA_CTRL,用以区分一个寄存器地址是写16位数据还是8位数据。

135 //图像采集顶层模块136 cmos_data_top u_cmos_data_top(137.rst_n (sys_init_done), //系统初始化完成之后再开始采集数据 138.clk_cmos (clk_24m),//24MHz CMOS Driver clock input 139.cam_pclk (cmos_pclk), 140.cmos_xclk (cmos_xclk), //24MHz drive clock 141.cam_vsync (cmos_vsync),142.cam_href (cmos_href),143.cam_data (cmos_data),144.lcd_id(lcd_id), 145.h_disp(h_disp),146.v_disp(v_disp), 147.h_pixel(h_pixel),148.v_pixel(v_pixel),149.sdram_addr_max (sdram_addr_max), 150.cmos_frame_vsync(cmos_frame_vsync),151.cmos_frame_href (cmos_frame_href),152.cmos_frame_valid(cmos_frame_valid),//数据有效使能信号153.cmos_frame_data (wr_data)//有效数据 154);

在程序的第139行,信号cmos_pclk是摄像头产生的输入时钟,即摄像头数据的采样时钟。

在程序的第140行,信号cmos_xclk是摄像头的输入时钟,但本次实验的摄像头采用的是外接24M时钟的晶振,所以不需要cmos_xclk,但这里还是将这个信号保留下来。

I2C配置模块寄存需要配置的寄存器地址、数据以及控制初始化的开始与结束,代码如下所示:

1 module i2c_cfg(2 inputclk ,3 inputrst_n ,4 inputi2c_done ,5 output regi2c_exec ,6 output reg [7:0] i2c_addr ,7 output reg [15:0] i2c_wr_data , 8 output regcfg_done //配置寄存器结束9 );10 //parameter define 11 parameter DELAY_MAX = 8'hff ;12 parameter ROW_NUM = 16'd480; //行数13 parameter COL_NUM = 16'd640; //列数14 15 //reg define 16 reg [7:0] delay_cnt ;17 regdelay_done ;18 reg [3:0] cfg_cnt;19 20 //*****************************************************21 //**main code22 //***************************************************** 23 24 always @(posedge clk or negedge rst_n)25 begin26if(rst_n==1'b0) begin27delay_cnt <= 1'b0 ;28delay_done <= 1'b0 ;29end30else begin31delay_done <= 1'b0 ;32if(i2c_done) begin33 delay_cnt <= 1'b0 ;34end 35else if(delay_cnt<DELAY_MAX) begin36 delay_cnt <= delay_cnt +1'b1 ;37 if(delay_cnt==DELAY_MAX-1'b1) begin38 delay_done <= 1'b1 ;39 end 40end41end42 end 43 44 always @(posedge clk or negedge rst_n)45 begin46if(rst_n==1'b0) begin47i2c_exec <= 1'b0;48i2c_addr <= 1'b0;49i2c_wr_data <= 1'b0;50cfg_cnt <= 1'b0;51end52else begin53i2c_exec <= 1'b0;54if(cfg_done==1'b0) begin55 if(delay_done) begin56 cfg_cnt <= cfg_cnt + 1'b1;57 case(cfg_cnt)58 4'd0 : begin59 i2c_exec <= 1'b1;60 i2c_addr <= 8'h03;//0361 i2c_wr_data <= ROW_NUM;62 end63 4'd1 : begin64 i2c_exec <= 1'b1;65 i2c_addr <= 8'h04;66 i2c_wr_data <= COL_NUM;67 end 68 default : ;69 endcase70 end 71end 72end73 end74 75 always @(posedge clk or negedge rst_n)begin76if(rst_n==1'b0) 77cfg_done <= 1'b0 ;78else if(cfg_cnt=='d2 && i2c_done)79cfg_done <= 1'b1 ;80else81cfg_done <= cfg_done ;82 end83 84 endmodule

在程序的第11行,定义了一个DELAY_MAX参数,用以保证在上电的时候不会立即配置寄存器,而是等待一段时间后再配置。

在程序的第12和13行定义了2个参数,即本次实验所需要的摄像头的分辨率。

在程序的第24至第42行,是用来对延时的计数器进行计数和清零。

在程序的第44行和73行,是对摄像头的行场分辨率进行配置。在第58行是设置需要配置寄存器的个数。

在程序的第75行和82行,是产生寄存器配置结束信号。

I2C驱动模块做了以下修改:

116 st_addr8: begin//8位字地址117 if(st_done) begin118 if(wr_flag==1'b0)//读写判断119 if(!data_ctrl)120next_state = st_data_wr_8;121 else 122next_state = st_data_wr_16;123 else124 next_state = st_addr_rd;125 end126 else begin127 next_state = st_addr8;128 end129 end130 st_data_wr_16: begin //写数据(8 bit)131 if(st_done)132 next_state = st_data_wr_8;133 else134 next_state = st_data_wr_16;135 end136 st_data_wr_8: begin //写数据(8 bit)137 if(st_done)138 next_state = st_stop; 139 else140 next_state = st_data_wr_8;141 end

代码116行至129行表示当状态发生跳转并且data_ctrl为1的时候,将状态跳转到st_data_wr_16,进行16位数据的读写,否则跳转到其他状态。

代码130行至135行表示当st_data_wr_16状态完成时,将状态跳转到st_data_wr_8,因为我们的16位数据读写其实是分两部分完成的st_data_wr_16只进行了高8位的读写,剩下低8位的数据还是在st_data_wr_8状态完成的。

337 st_data_wr_16: begin //写高数据(8 bit)338 case(cnt) 339 7'd0: begin 340sda_out <= data_wr_t[15];//I2C写高8位数据341sda_dir <= 1'b1; 342 end343 7'd1 : scl <= 1'b1; 344 7'd3 : scl <= 1'b0; 345 7'd4 : sda_out <= data_wr_t[14]; 346 7'd5 : scl <= 1'b1; 347 7'd7 : scl <= 1'b0; 348 7'd8 : sda_out <= data_wr_t[13]; 349 7'd9 : scl <= 1'b1; 350 7'd11: scl <= 1'b0; 351 7'd12: sda_out <= data_wr_t[12]; 352 7'd13: scl <= 1'b1; 353 7'd15: scl <= 1'b0; 354 7'd16: sda_out <= data_wr_t[11]; 355 7'd17: scl <= 1'b1; 356 7'd19: scl <= 1'b0; 357 7'd20: sda_out <= data_wr_t[10]; 358 7'd21: scl <= 1'b1; 359 7'd23: scl <= 1'b0; 360 7'd24: sda_out <= data_wr_t[9]; 361 7'd25: scl <= 1'b1; 362 7'd27: scl <= 1'b0; 363 7'd28: sda_out <= data_wr_t[8]; 364 7'd29: scl <= 1'b1; 365 7'd31: scl <= 1'b0; 366 7'd32: begin 367sda_dir <= 1'b0; 368sda_out <= 1'b1;369 end370 7'd33: scl <= 1'b1; 371 7'd34: begin //从机应答372st_done <= 1'b1;373if(sda_in == 1'b1) //高电平表示未应答374 i2c_ack <= 1'b1; //拉高应答标志位 375 end376 7'd35: begin 377scl <= 1'b0;378cnt <= 1'b0;379 end380 default : ;381 endcase382 end383 st_data_wr_8: begin //写数据(8 bit)384 case(cnt) 385 7'd0: begin 386sda_out <= data_wr_t[7];//I2C写低8位数据387sda_dir <= 1'b1; 388 end389 7'd1 : scl <= 1'b1; 390 7'd3 : scl <= 1'b0; 391 7'd4 : sda_out <= data_wr_t[6]; 392 7'd5 : scl <= 1'b1; 393 7'd7 : scl <= 1'b0; 394 7'd8 : sda_out <= data_wr_t[5]; 395 7'd9 : scl <= 1'b1; 396 7'd11: scl <= 1'b0; 397 7'd12: sda_out <= data_wr_t[4]; 398 7'd13: scl <= 1'b1; 399 7'd15: scl <= 1'b0; 400 7'd16: sda_out <= data_wr_t[3]; 401 7'd17: scl <= 1'b1; 402 7'd19: scl <= 1'b0; 403 7'd20: sda_out <= data_wr_t[2]; 404 7'd21: scl <= 1'b1; 405 7'd23: scl <= 1'b0; 406 7'd24: sda_out <= data_wr_t[1]; 407 7'd25: scl <= 1'b1; 408 7'd27: scl <= 1'b0; 409 7'd28: sda_out <= data_wr_t[0]; 410 7'd29: scl <= 1'b1; 411 7'd31: scl <= 1'b0; 412 7'd32: begin 413sda_dir <= 1'b0; 414sda_out <= 1'b1;415 end416 7'd33: scl <= 1'b1; 417 7'd34: begin //从机应答418st_done <= 1'b1;419if(sda_in == 1'b1) //高电平表示未应答420 i2c_ack <= 1'b1; //拉高应答标志位 421 end422 7'd35: begin 423scl <= 1'b0;424cnt <= 1'b0;425 end426 default : ;427 endcase428 end

代码337行至382行,表示写入数据的高8位。

代码383行至428行,表示写入数据的低8位。

图像采集顶层模块的原理图如下图所示:

图 43.4.5 图像采集顶层模块原理图(局部)

图像采集顶层模块(top_cmos_data)例化了以下二个模块:图像采集模块(cmos_capture_raw_gray)、图像裁剪模块(cmos_tailor)。有关图像采集顶层模块的详细介绍请大家参考“OV7725摄像头TFT-LCD显示实验”章节。

由上图可知,图像采集模块(cmos_capture_raw_gray)为其他模块提供摄像头8bit输入数据和数据使能以及摄像头稳定后的行场信号。图像裁剪模块(cmos_tailor)只有在LCD的器件ID为16’h4342时起作用,即摄像头的分辨率大于LCD屏的分辨率,起到裁剪图像数据,使图像的有效数据达到匹配LCD屏的尺寸。有关图像裁剪模块的详细介绍请大家参考“OV7725摄像头TFT-LCD显示实验”章节。

图像采集模块的代码如下:

1 module cmos_capture_raw_gray2 #(3 parameter CMOS_FRAME_WAITCNT = 4'd10 //等待数据稳定所需要的帧数4 5 )6 (7 //global clock8 input clk_cmos, //锁相环分频时钟9 input rst_n,//复位信号,低有效10 11//CMOS Sensor Interface12input cmos_pclk, //摄像头输入时钟13outputcmos_xclk, //摄像头驱动时钟14input cmos_vsync,//摄像头场信号15input cmos_href, //摄像头行信号16input [7:0] cmos_data, //摄像头数据1718//CMOS SYNC Data output19outputcmos_frame_vsync, //摄像头场有效信号20outputcmos_frame_href,//摄像头行有效信号21output[15:0]wr_data, //摄像头有效数据 22outputcmos_frame_clken, //摄像头数据有效使能2324//user interface25output[7:0] cmos_fps_rate //摄像头帧率26 );27 28 //parameter define 29 localparam DELAY_TOP = 2 * 24_000000; //2s delay30 31 //reg define32 reg [27:0] delay_cnt;33 reg frame_sync_flag;34 reg [3:0] cmos_fps_cnt;35 reg [1:0] cmos_vsync_r, cmos_href_r;36 reg [7:0] cmos_data_r0, cmos_data_r1;37 reg [8:0] cmos_fps_cnt2;38 reg [7:0] cmos_fps_rate;39 40 //wire define41 wire cmos_vsync_end;42 wire delay_2s;43 wire [7:0] cmos_frame_data;44 45 //*****************************************************46 //**main code47 //***************************************************** 48 49 assign cmos_vsync_end= (cmos_vsync_r[1] & ~cmos_vsync_r[0]) ? 1'b1 : 1'b0; 50 assign cmos_xclk = clk_cmos; //24MHz CMOS XCLK output51 assign cmos_frame_clken = frame_sync_flag ? cmos_href_r[1] : 1'b0;52 assign cmos_frame_vsync = frame_sync_flag ? cmos_vsync_r[1] : 1'b0;//DFF 2 clocks53 assign cmos_frame_href = frame_sync_flag ? cmos_href_r[1] : 1'b0; //DFF 2 clocks54 assign cmos_frame_data = frame_sync_flag ? cmos_data_r1 : 8'd0; //DFF 2 clocks55 assign delay_2s = (delay_cnt == DELAY_TOP - 1'b1) ? 1'b1 : 1'b0;56 assign wr_data = {cmos_frame_data[7:3],cmos_frame_data[7:2],cmos_frame_data[7:3]};57 58 always@(posedge cmos_pclk or negedge rst_n)begin59if(!rst_n)60begin61cmos_vsync_r <= 0;62cmos_href_r <= 0;63{cmos_data_r1, cmos_data_r0} <= 0;64end65else66begin67cmos_vsync_r <= {cmos_vsync_r[0], cmos_vsync};68cmos_href_r <= {cmos_href_r[0], cmos_href};69{cmos_data_r1, cmos_data_r0} <= {cmos_data_r0, cmos_data};70end71 end72 73 //Wait for Sensor output Data valid 10 Frame of OmniVision74 always@(posedge cmos_pclk or negedge rst_n)begin75if(!rst_n)76cmos_fps_cnt <= 0;77else //Wait until cmos init complete78begin79if(cmos_fps_cnt < CMOS_FRAME_WAITCNT) 80 cmos_fps_cnt <= cmos_vsync_end ? cmos_fps_cnt + 1'b1 : cmos_fps_cnt;81else82 cmos_fps_cnt <= CMOS_FRAME_WAITCNT;83end84 end85 86 //Come ture frame synchronization to ignore error frame or has not capture when vsync begin87 always@(posedge cmos_pclk or negedge rst_n)begin88if(!rst_n)89frame_sync_flag <= 0;90else if(cmos_fps_cnt == CMOS_FRAME_WAITCNT && cmos_vsync_end == 1)91frame_sync_flag <= 1;92else93frame_sync_flag <= frame_sync_flag;94 end95 96 //Delay 2s for cmos fps counter97 always@(posedge cmos_pclk or negedge rst_n)begin98if(!rst_n)99delay_cnt <= 0;100else if(delay_cnt < DELAY_TOP - 1'b1)101 delay_cnt <= delay_cnt + 1'b1;102else103 delay_cnt <= 0;104 end105 106 //cmos image output rate counter107 always@(posedge cmos_pclk or negedge rst_n)begin108if(!rst_n)109 begin110 cmos_fps_cnt2 <= 0;111 cmos_fps_rate <= 0;112 end113else if(delay_2s == 1'b0) //time is not reached114 begin115 cmos_fps_cnt2 <= cmos_vsync_end ? cmos_fps_cnt2 + 1'b1 : cmos_fps_cnt2;116 cmos_fps_rate <= cmos_fps_rate;117 end118else //time up119 begin120 cmos_fps_cnt2 <= 0;121 cmos_fps_rate <= cmos_fps_cnt2[8:1]; //divide by 2122 end123 end124 125 endmodule

CMOS图像采集模块第3行定义了参数CMOS_FRAME_WAITCNT(寄存器数据稳定等待的帧个数),这里设置等待帧是为了保证摄像头输出稳定后才采样数据。这里采集场同步信号的上升沿来统计帧数,计数器计数超过10次之后产生数据有效的标志(frame_sync_flag),开始采集图像。

在程序的第56行实现了8位数据转16位数据的功能,这里将8位的灰度数据按照高位赋值的方式将数据按照RGB565的格式分别赋给各个颜色分量。需要注意的是摄像头的图像数据是在像素时钟(cam_pclk)下输出的,因此摄像头的图像数据必须使用像素钟来采集,否则会造成数据采集错误。

在程序的第97行至104行,是对2s的时间进行计数。

在程序的第107行至123行,是对摄像头的帧率进行计数。在113行至117行,当2s的时间未到且当场信号的下降沿到来时,对帧率计数器进行累加,当2s的时间到来时,对帧率计数器进行清零,把帧率计数器除以2的值赋给帧率寄存器。

SDRAM读写模块做了以下修改:

135 //sdram写地址产生模块136 always @(posedge clk_ref or negedge rst_n) begin137if (!rst_n) begin138 sdram_wr_addr <= 24'd0;139 sw_bank_en <= 1'b0;140 rw_bank_flag <= 1'b0;141end142else if(wr_load_flag) begin //检测到写端口复位信号时,写地址复位143 sdram_wr_addr <= wr_min_addr; 144 sw_bank_en <= 1'b0;145 rw_bank_flag <= 1'b0;146end147else if(write_done_flag) begin //若突发写SDRAM结束,更改写地址148//若未到达写SDRAM的结束地址,则写地址累加149 if(sdram_pingpang_en) begin//SDRAM 读写乒乓使能150 if(sdram_wr_addr[21:0] < wr_max_addr - wr_length)151 sdram_wr_addr <= sdram_wr_addr + wr_length;152 else begin //切换BANK153 rw_bank_flag <= rw_bank_flag+1; 154 sw_bank_en <= 1'b1;//拉高切换BANK使能信号155 end 156 end 157//若突发写SDRAM结束,更改写地址158 else if(sdram_wr_addr < wr_max_addr - wr_length)159 sdram_wr_addr <= sdram_wr_addr + wr_length;160 else //到达写SDRAM的结束地址,回到写起始地址161 sdram_wr_addr <= wr_min_addr;162end163else if(sw_bank_en) begin//到达写SDRAM的结束地址,回到写起始地址164 sw_bank_en <= 1'b0;165 sdram_wr_addr <= {rw_bank_flag,wr_min_addr[21:0]};166end167 end168 169 //sdram读地址产生模块170 always @(posedge clk_ref or negedge rst_n) begin171if(!rst_n) begin172 sdram_rd_addr <= 24'd0;173end 174else if(rd_load_flag)//检测到读端口复位信号时,读地址复位175 sdram_rd_addr <= rd_min_addr;176else if(read_done_flag) begin //突发读SDRAM结束,更改读地址177//若未到达读SDRAM的结束地址,则读地址累加 178 if(sdram_pingpang_en) begin//SDRAM 读写乒乓使能 179 if(sdram_rd_addr[21:0] < rd_max_addr - rd_length)180 sdram_rd_addr <= sdram_rd_addr + rd_length;181 else begin //到达读SDRAM的结束地址,回到读起始地址 182 sdram_rd_addr <= {rw_bank_flag+2,rd_min_addr[21:0]}; 183 end 184 end185//若突发写SDRAM结束,更改写地址186 else if(sdram_rd_addr < rd_max_addr - rd_length) 187 sdram_rd_addr <= sdram_rd_addr + rd_length;188 else //到达写SDRAM的结束地址,回到写起始地址189 sdram_rd_addr <= rd_min_addr;190end191 end

SDRAM控制模块的改动是非常小的,主要就是读写地址乒乓操作稍微改了一下,在上文我们已经讲解过,因为摄像头输入速率和LCD显示屏不匹配(有的输入大于输出有的输入小于输出)原本的的双BANK乒乓操作已经不再适用,会有画面撕裂的现象。因此本节实验使用四个BANK去进行读写切换,确保读写不会发生冲突。在代码的第153行定义了写地址BANK的累加,每当完成一帧图片的写入,BANK地址就会加一,之后在代码第165行更新写地址。而代码182行则是读地址的切换操作,每当读完一帧图片信息后,读地址的BANK会根据当前写地址BANK进行切换,始终保持和写BANK地址相差2,这样写就不会追上读了,确保图片不会出现撕裂现象。

到这里本节实验的程序设计部分就全部讲解完了,如果有不清楚的可以去翻看前面的例程。

43.5下载验证

首先将FPC排线一端与RGB LCD模块上的J1接口连接,另一端与新起点开发板上的RGB TFTLCD接口连接。连接时,先掀开FPC连接器上的黑色翻盖,将FPC排线蓝色面朝上插入连接器,最后将黑色翻盖压下以固定FPC排线,如图 43.5.1和图 43.5.2所示。

图 43.5.1 正点原子RGBLCD模块FPC连接器

图 43.5.2 A7开发板连接RGB LCD液晶屏

接下来分别连接JTAG接口和电源线,并打开电源开关。

最后将下载器一端连电脑,另一端与开发板上的JTAG端口连接,连接电源线并打开电源开关。

接下来我们下载程序,验证MT9V034 TFT-LCD实时显示功能。下载完成后观察RGB LCD模块显示的图案如下图所示,说明MT9V034 TFT-LCD实时显示程序下载验证成功。

图 43.5.3 RGB TFT-LCD实时显示图像

【正点原子FPGA连载】第四十三章MT9V034摄像头RGB-LCD显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。