第五十章OV5640摄像头LCD灰度显示实验 前面的实验介绍了OV5640摄像头的HDMI灰度显示实验,在本次实验中,将摄像头采集的RGB565格式数据到转换为YUV格式的数据,转换后的灰度数据送到LCD显示。 本章包括以下几个部分: 4949.1简介 49.2实验任务 49.3硬件设计 49.4程序设计 49.5下载验证
50.1简介 我们在“OV5640摄像头HDMI灰度显示实验”中对YUV灰度显示作了详细的介绍,如果大家对这部分内容不是很熟悉的话,请参考“OV5640摄像头HDMI灰度显示实验”中的简介部分。 50.2实验任务 本节实验任务是使用新起点开发板达到OV5640摄像头采集RGB565格式的数据,RGB565格式的数据通过算法转换,将数据格式转换为YCbCr格式,然后通过LCD屏实时显示灰度图的目的。 50.3硬件设计 本章节中硬件设计与OV5640的LCD显示实验完全相同,此处不再赘述。 50.4程序设计 根据实验任务,首先设计如图 50.4.1所示的系统框图,本章实验的系统框架延续了“OV5640摄像头HDMI灰度显示实验”的整体架构。本次实验包括以下模块:时钟模块、SDRAM控制器模块、IIC驱动模块、IIC配置模块、图像尺寸配置模块、摄像头采集模块、图像处理模块和LCD顶层模块。其中时钟模块、SDRAM控制器模块、IIC驱动模块、IIC配置模块、摄像头采集模块和图像处理模块本次实验没有做任何修改,这些模块在“OV5640摄像头HDMI灰度显示实验”中已经说明过,这里不再详述,只是将HDMI顶层模块替换成了LCD顶层模块,并添加了图像尺寸配置模块。 OV5640摄像头LCD灰度显示系统框图如下图所示:
图 50.4.1 顶层系统框图 由上图可知,时钟模块(pll)为LCD顶层模块、SDRAM控制模块以及IIC驱动模块提供驱动时钟。IIC驱动模块和IIC配置模块控制着传感器初始化的开始与结束,传感器初始化完成后将采集到的数据写入摄像头采集模块。数据在摄像头采集模块处理完成后写入图像处理模块,图像处理模块将摄像头数据进行处理后存入SDRAM控制模块。顶层模块从SDRAM控制模块中读出数据并驱动LCD屏显示,这时整个系统才完成了数据的采集、缓存与显示。需要注意的是图像数据采集模块是在SDRAM和传感器都初始化完成之后才开始输出数据的,避免了在SDRAM初始化过程中向里面写入数据。 顶层模块代码如下所示: 1 module ov5640_lcd_yuv( 2 input sys_clk , //系统时钟 3 input sys_rst_n , //系统复位,低电平有效 4 //摄像头 5 input cam_pclk , //cmos 数据像素时钟 6 input cam_vsync , //cmos 场同步信号 7 input cam_href , //cmos 行同步信号 8 input [7:0] cam_data , //cmos 数据 9 output cam_rst_n , //cmos 复位信号,低电平有效 10 output cam_pwdn , //cmos 电源休眠模式选择信号 11 output cam_scl , //cmos SCCB_SCL线 12 inout cam_sda , //cmos SCCB_SDA线 13 //SDRAM 14 output sdram_clk , //SDRAM 时钟 15 output sdram_cke , //SDRAM 时钟有效 16 output sdram_cs_n , //SDRAM 片选 17 output sdram_ras_n, //SDRAM 行有效 18 output sdram_cas_n, //SDRAM 列有效 19 output sdram_we_n , //SDRAM 写有效 20 output [1:0] sdram_ba , //SDRAM Bank地址 21 output [1:0] sdram_dqm , //SDRAM 数据掩码 22 output [12:0] sdram_addr , //SDRAM 地址 23 inout [15:0] sdram_data , //SDRAM 数据 24 //LCD 25 output lcd_hs , //LCD 行同步信号 26 output lcd_vs , //LCD 场同步信号 27 output lcd_de , //LCD 数据输入使能 28 inout [15:0] lcd_rgb , //LCD RGB565颜色数据 29 output lcd_bl , //LCD 背光控制信号 30 output lcd_rst , //LCD 复位信号 31 output lcd_pclk //LCD 采样时钟 32 ); 33 34 //parameter define 35 parameter SLAVE_ADDR = 7’h3c ; //OV5640的器件地址7’h3c 36 parameter BIT_CTRL = 1’b1 ; //OV5640的字节地址为16位 0:8位 1:16位 37 parameter CLK_FREQ = 27’d50_000_000 ; //i2c_dri模块的驱动时钟频率 38 parameter I2C_FREQ = 18’d250_000 ; //I2C的SCL时钟频率,不超过400KHz 39 40 //wire define 41 wire clk_100m ; //100mhz时钟,SDRAM操作时钟 42 wire clk_100m_shift ; //100mhz时钟,SDRAM相位偏移时钟 43 wire clk_50m_lcd ; //100mhz时钟,LCD顶层模块时钟 44 wire clk_lcd ; 45 wire locked ; 46 wire rst_n ; 47 wire sys_init_done ; //系统初始化完成(sdram初始化+摄像头初始化) 48 49 wire i2c_exec ; //I2C触发执行信号 50 wire [23:0] i2c_data ; //I2C要配置的地址与数据(高8位地址,低8位数据) 51 wire i2c_done ; //I2C寄存器配置完成信号 52 wire i2c_dri_clk ; //I2C操作时钟 53 wire [ 7:0] i2c_data_r ; //I2C读出的数据 54 wire i2c_rh_wl ; //I2C读写控制信号 55 wire cam_init_done ; //摄像头初始化完成 56 57 wire wr_en ; //sdram_ctrl模块写使能 58 wire [15:0] wr_data ; //sdram_ctrl模块写数据 59 wire rd_en ; //sdram_ctrl模块读使能 60 wire [15:0] rd_data ; //sdram_ctrl模块读数据 61 wire sdram_init_done ; //SDRAM初始化完成 62 63 wire [15:0] lcd_id ; //LCD的ID 64 wire [12:0] cmos_h_pixel ; //CMOS水平方向像素个数 65 wire [12:0] cmos_v_pixel ; //CMOS垂直方向像素个数 66 wire [12:0] total_h_pixel ; //水平总像素大小 67 wire [12:0] total_v_pixel ; //垂直总像素大小 68 wire [23:0] sdram_max_addr ; //sdram读写的最大地址 69 wire post_frame_vsync ; //处理后的场信号 70 wire post_frame_hsync ; //处理后的行信号 71 wire post_frame_de ; //处理后的数据使能 72 wire [15:0] post_rgb ; //处理后的数据 73 74 //***************************************************** 75 //** main code 76 //***************************************************** 77 78 assign rst_n = sys_rst_n & locked; 79 //系统初始化完成:SDRAM和摄像头都初始化完成 80 //避免了在SDRAM初始化过程中向里面写入数据 81 assign sys_init_done = sdram_init_done & cam_init_done; 82 //电源休眠模式选择 0:正常模式 1:电源休眠模式 83 assign cam_pwdn = 1’b0; 84 assign cam_rst_n = 1’b1; 85 86 //锁相环 87 pll u_pll( 88 .areset (~sys_rst_n ), 89 .inclk0 (sys_clk ), 90 91 .c0 (clk_100m ), 92 .c1 (clk_100m_shift), 93 .c2 (clk_50m_lcd ), 94 .locked (locked ) 95 ); 96 97 //摄像头图像分辨率设置模块 98 picture_size u_picture_size ( 99 .clk (clk_50m_lcd ), 100 .rst_n (rst_n ), 101 .id_lcd (lcd_id ), //LCD的ID,用于配置摄像头的图像大小 102 .cmos_h_pixel (cmos_h_pixel ), //摄像头水平方向分辨率 103 .cmos_v_pixel (cmos_v_pixel ), //摄像头垂直方向分辨率 104 .total_h_pixel (total_h_pixel ), //用于配置HTS寄存器 105 .total_v_pixel (total_v_pixel ), //用于配置VTS寄存器 106 .sdram_max_addr (sdram_max_addr) //sdram读写的最大地址 107 ); 108 109 //I2C配置模块 110 i2c_ov5640_rgb565_cfg u_i2c_cfg( 111 .clk (i2c_dri_clk ), 112 .rst_n (rst_n ), 113 114 .i2c_exec (i2c_exec ), 115 .i2c_data (i2c_data ), 116 .i2c_rh_wl (i2c_rh_wl ), //I2C读写控制信号 117 .i2c_done (i2c_done ), 118 .i2c_data_r (i2c_data_r ), 119 120 .cmos_h_pixel (cmos_h_pixel ), //CMOS水平方向像素个数 121 .cmos_v_pixel (cmos_v_pixel ), //CMOS垂直方向像素个数 122 .total_h_pixel (total_h_pixel ), //水平总像素大小 123 .total_v_pixel (total_v_pixel ), //垂直总像素大小 124 125 .init_done (cam_init_done ) 126 ); 127 128 //I2C驱动模块 129 i2c_dri #( 130 .SLAVE_ADDR (SLAVE_ADDR ), //参数传递 131 .CLK_FREQ (CLK_FREQ ), 132 .I2C_FREQ (I2C_FREQ ) 133 ) 134 u_i2c_dr( 135 .clk (clk_50m_lcd ), 136 .rst_n (rst_n ), 137 138 .i2c_exec (i2c_exec ), 139 .bit_ctrl (BIT_CTRL ), 140 .i2c_rh_wl (i2c_rh_wl ), //固定为0,只用到了IIC驱动的写操作 141 .i2c_addr (i2c_data[23:8]), 142 .i2c_data_w (i2c_data[7:0] ), 143 .i2c_data_r (i2c_data_r ), 144 .i2c_done (i2c_done ), 145 146 .scl (cam_scl ), 147 .sda (cam_sda ), 148 149 .dri_clk (i2c_dri_clk ) //I2C操作时钟 150 ); 151 152 //CMOS图像数据采集模块 153 cmos_capture_data u_cmos_capture_data( //系统初始化完成之后再开始采集数据 154 .rst_n (rst_n & sys_init_done), 155 156 .cam_pclk (cam_pclk ), 157 .cam_vsync (cam_vsync), 158 .cam_href (cam_href ), 159 .cam_data (cam_data ), 160 161 .cmos_frame_vsync (cmos_frame_vsync), 162 .cmos_frame_href (cmos_frame_href), 163 .cmos_frame_valid (wr_en ), //数据有效使能信号 164 .cmos_frame_data (wr_data ) //有效数据 165 ); 166 167 //图像处理模块 168 vip u_vip( 169 //module clock 170 .clk (cam_pclk), // 时钟信号 171 .rst_n (rst_n ), // 复位信号(低有效) 172 //图像处理前的数据接口 173 .pre_frame_vsync (cmos_frame_vsync ), 174 .pre_frame_hsync (cmos_frame_href ), 175 .pre_frame_de (wr_en ), 176 .pre_rgb (wr_data), 177 .xpos (0 ), 178 .ypos (0 ), 179 //图像处理后的数据接口 180 .post_frame_vsync (post_frame_vsync ), // 场同步信号 181 .post_frame_hsync ( ), // 行同步信号 182 .post_frame_de (post_frame_de ), // 数据输入使能 183 .post_rgb (post_rgb) // RGB565颜色数据 184 185 ); 186 187 //SDRAM 控制器顶层模块,封装成FIFO接口 188 //SDRAM 控制器地址组成: {bank_addr[1:0],row_addr[12:0],col_addr[8:0]} 189 sdram_top u_sdram_top( 190 .ref_clk (clk_100m), //sdram 控制器参考时钟 191 .out_clk (clk_100m_shift), //用于输出的相位偏移时钟 192 .rst_n (rst_n), //系统复位 193 194 //用户写端口 195 .wr_clk (cam_pclk), //写端口FIFO: 写时钟 196 .wr_en (post_frame_de), //写端口FIFO: 写使能 197 .wr_data (post_rgb), //写端口FIFO: 写数据 198 .wr_min_addr (24’d0), //写SDRAM的起始地址 199 .wr_max_addr (sdram_max_addr), //写SDRAM的结束地址 200 .wr_len (10’d512), //写SDRAM时的数据突发长度 201 .wr_load (~rst_n), //写端口复位: 复位写地址,清空写FIFO 202 203 //用户读端口 204 .rd_clk (lcd_clk), //读端口FIFO: 读时钟 205 .rd_en (rd_en), //读端口FIFO: 读使能 206 .rd_data (rd_data), //读端口FIFO: 读数据 207 .rd_min_addr (24’d0), //读SDRAM的起始地址 208 .rd_max_addr (sdram_max_addr), //读SDRAM的结束地址 209 .rd_len (10’d512), //从SDRAM中读数据时的突发长度 210 .rd_load (~rst_n), //读端口复位: 复位读地址,清空读FIFO 211 212 //用户控制端口 213 .sdram_read_valid (1’b1), //SDRAM 读使能 214 .sdram_pingpang_en (1’b1), //SDRAM 乒乓操作使能 215 .sdram_init_done (sdram_init_done), //SDRAM 初始化完成标志 216 217 //SDRAM 芯片接口 218 .sdram_clk (sdram_clk), //SDRAM 芯片时钟 219 .sdram_cke (sdram_cke), //SDRAM 时钟有效 220 .sdram_cs_n (sdram_cs_n), //SDRAM 片选 221 .sdram_ras_n (sdram_ras_n), //SDRAM 行有效 222 .sdram_cas_n (sdram_cas_n), //SDRAM 列有效 223 .sdram_we_n (sdram_we_n), //SDRAM 写有效 224 .sdram_ba (sdram_ba), //SDRAM Bank地址 225 .sdram_addr (sdram_addr), //SDRAM 行/列地址 226 .sdram_data (sdram_data), //SDRAM 数据 227 .sdram_dqm (sdram_dqm) //SDRAM 数据掩码 228 ); 229 230 //LCD顶层模块 231 lcd_rgb_top u_lcd_rgb_top( 232 .sys_clk (clk_50m_lcd ), 233 .sys_rst_n (rst_n ), 234 .sys_init_done (sys_init_done), 235 236 //lcd接口 237 .lcd_id (lcd_id), //LCD屏的ID号 238 .lcd_hs (lcd_hs), //LCD 行同步信号 239 .lcd_vs (lcd_vs), //LCD 场同步信号 240 .lcd_de (lcd_de), //LCD 数据输入使能 241 .lcd_rgb (lcd_rgb), //LCD 颜色数据 242 .lcd_bl (lcd_bl), //LCD 背光控制信号 243 .lcd_rst (lcd_rst), //LCD 复位信号 244 .lcd_pclk (lcd_pclk), //LCD 采样时钟 245 .lcd_clk (lcd_clk), //LCD 驱动时钟 246 //用户接口 247 .out_vsync (rd_vsync), //lcd场信号 248 .h_disp (), //行分辨率 249 .v_disp (), //场分辨率 250 .pixel_xpos (), 251 .pixel_ypos (), 252 .data_in (rd_data), //rfifo输出数据 253 .data_req (rd_en) //请求数据输入 254 ); 255 256 endmodule FPGA顶层模块(ov5640_lcd_yuv)例化了以下八个模块:时钟模块(pll)、I2C驱动模块(i2c_dri)、I2C配置模块(i2c_ov5640_rgb565_cfg)、图像尺寸配置模块(picture_size)、图像采集模块(cmos_capture_data)、图像处理模块(vip)、SDRAM控制模块(sdram_top)和LCD顶层模块(lcd_rgb_top)。 时钟模块:时钟模块通过调用PLL IP核实现,共输出3个时钟,频率分别为100M时钟、100M偏移-75度时钟和50M时钟。100Mhz时钟作为SDRAM控制模块的驱动时钟,100M偏移-75度时钟用来输出给外部SDRAM芯片使用,50Mhz时钟作为I2C驱动模块和LCD顶层模块的驱动时钟。 I2C驱动模块(i2c_dri):I2C驱动模块负责驱动OV5640 SCCB接口总线,用户可根据该模块提供的用户接口可以很方便的对OV5640的寄存器进行配置,该模块和“EEPROM读写实验”章节中用到的I2C驱动模块为同一个模块,有关该模块的详细介绍请大家参考“EEPROM读写实验”章节。 I2C配置模块(i2c_ov5640_rgb565_cfg):I2C配置模块的驱动时钟是由I2C驱动模块输出的时钟提供的,这样方便了I2C驱动模块和I2C配置模块之间的数据交互。该模块寄存需要配置的寄存器地址、数据以及控制初始化的开始与结束,同时该模块输出OV5640的寄存器地址和数据以及控制I2C驱动模块开始执行的控制信号,直接连接到I2C驱动模块的用户接口,从而完成对OV5640传感器的初始化。 图像采集模块(cmos_capture_data):摄像头采集模块在像素时钟的驱动下将传感器输出的场同步信号、行同步信号以及8位数据转换成SDRAM读写控制模块的写使能信号和16位写数据信号,完成对OV5640传感器图像的采集。OV5640和OV7725图像输出时序非常相似,有关该模块的详细介绍请大家参考“OV7725摄像头LCD显示实验”章节。 图像处理模块(vip):对采集后的图像数据进行处理,并将处理后的数据存入SDRAM控制模块。有关该模块的详细介绍请大家参考“OV5640摄像头HDMI灰度显示实验”章节。 SDRAM控制模块(sdram_top):SDRAM读写控制器模块负责驱动SDRAM片外存储器,缓存图像传感器输出的图像数据。有关该模块的详细介绍请大家参考“SDRAM读写测试实验”章节。 LCD顶层模块(lcd_rgb_top):LCD顶层模块负责驱动LCD屏的驱动信号的输出,同时为其他模块提供屏体参数、场同步信号和数据请求信号。 50.5下载验证 首先将FPC排线一端与RGB-LCD模块上的J1接口连接,另一端与新起点开发板上的RGB-LCD接口连接。连接时,先掀开FPC连接器上的黑色翻盖,将FPC排线蓝色面朝上插入连接器,最后将黑色翻盖压下以固定FPC排线,如图 50.5.1和图 50.5.2所示。
图 50.5.1 正点原子RGBLCD模块FPC连接器
图 50.5.2 新起点开发板连接RGB-LCD液晶屏 接下来分别连接JTAG接口和电源线,并打开电源开关。 最后将下载器一端连电脑,另一端与开发板上的JTAG端口连接,连接电源线并打开电源开关。 接下来我们下载程序,验证OV5640 RGB-LCD灰度显示功能。下载完成后观察RGB-LCD模块显示的图案如下图所示,说明OV5640 RGB-LCD灰度显示程序下载验证成功。
图 50.5.3 LCD实时显示灰度图像