fpga摄像头模块_【正点原子FPGA连载】第三十八章 LCD显示实验-摘自【正点原子】开拓者 FPGA 开发指南...

1)实验平台:正点原子开拓者FPGA 开发板

2)摘自《开拓者FPGA开发指南》关注官方微信号公众号,获取更多资料:正点原子

3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-13912-1-1.html

ee200ae72217450e3a5564c2432e485d.png

第三十八章 OV5640摄像头RGB TFT-LCD显示实验

在OV5640摄像头VGA显示实验中,我们成功地在VGA显示器上实时显示出了摄像头采集的图

像。本章我们将使用FPGA开发板实现对OV5640的数字图像采集并通过RGB LCD液晶屏实时显示。

本章包括以下几个部分:

38.1 OV5640简介

38.2 实验任务

38.3 硬件设计

38.4 程序设计

38.5 下载验证

OV5640简介

我们在“OV5640摄像头VGA显示实验”中对OV5640的视频传输时序、SCCB协议以及寄存器

的配置信息等内容作了详细的介绍,如果大家对这部分内容不是很熟悉的话,请参考“OV5640

摄像头VGA显示实验”中的OV5640简介部分。

实验任务

本节实验任务是使用开拓者开发板及OV5640摄像头实现图像采集,并通过正点原子推出的

RGBLCD液晶屏模块实时显示,支持4.3寸480*272、7寸800*480、7寸1024*600、10.1寸1280*800

这些尺寸/分辨率的屏幕。

硬件设计

摄像头扩展接口原理图及OV5640模块说明与“OV5640摄像头VGA显示实验”完全相同,请

参考“OV5640摄像头VGA显示实验”硬件设计部分。RGB TFT-LCD接口部分的硬件设计请参考

“RGB TFT-LCD彩条显示实验”中的硬件设计部分。

由于OV5640、LCD接口和SDRAM引脚数目较多且在前面相应的章节中已经给出它们的管脚列

表,这里不再列出管脚分配。

程序设计

图 38.4.1是根据本章实验任务画出的系统框图。PLL时钟模块为I2C驱动模块、LCD顶层模

块以及SDRAM控制器模块提供驱动时钟;I2C驱动模块和I2C配置模块、图像尺寸配置模块用于

初始化OV5640图像传感器;摄像头采集模块负责采集摄像头图像数据,并且把图像数据写入

SDRAM读写控制模块;SDRAM读写控制模块负责将用户数据写入和读出片外SDRAM存储器中;LCD

顶层模块负责驱动RGB TFT-LCD显示屏。

OV5640摄像头RGB TFT-LCD显示系统框图如下图所示:

cd3d38a8d895e704b379e62ca47b717c.png

图 38.4.1 OV5640摄像头RGB TFT-LCD显示系统框图

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

24ce5ce81c3bd8bcb2bcee09902b104a.png

图 38.4.2 顶层模块原理图

由上图可知,I2C配置模块和I2C驱动模块控制着传感器初始化的开始与结束,传感器初始

化完成后图像采集模块将采集到的数据写入SDRAM读写控制模块,LCD模块从SDRAM控制模块中

读出数据,完成了数据的采集、缓存与显示。其中图像数据采集模块是在SDRAM和传感器都初

始化完成之后才开始输出数据的,避免了在SDRAM初始化过程中向里面写入数据。

FPGA顶层模块(ov5640_rgb565_lcd)例化了以下六个模块:PLL时钟模块(pll_clk)、

I2C驱动模块(i2c_dri)、I2C配置模块(i2c_ov7725_rgb565_cfg)、摄像头图像采集模块

(cmos_capture_data)、图像尺寸配置模块(picture_size)、SDRAM读写控制模块(sdram_top)

和LCD顶层模块(lcd)。

PLL时钟模块(pll_clk):PLL时钟模块通过调用锁相环(PLL)IP核实现,总共输出3个

时钟,频率分别为100Mhz、100Mhz(SDRAM相位偏移时钟)和100Mhz(LCD分频用)时钟。100Mhz

时钟和100Mhz相位偏移时钟作为SDRAM读写控制模块的驱动时钟,最后一个100Mhz时钟作为LCD

顶层模块的驱动时钟。

I2C驱动模块(i2c_dri):I2C驱动模块负责驱动OV5640 SCCB接口总线,用户可根据该模

块提供的用户接口对OV5640的寄存器进行配置,该模块和“EEPROM读写实验”章节中用到的

I2C驱动模块为同一个模块,有关该模块的详细介绍请大家参考“EEPROM读写实验”章节。

I2C配置模块(i2c_ov5640_rgb565_cfg):I2C配置模块的驱动时钟是由I2C驱动模块输出

的时钟提供的,这样方便了I2C驱动模块和I2C配置模块之间的数据交互。该模块寄存需要配置

的寄存器地址、数据以及控制初始化的开始与结束,同时该模块输出OV5640的寄存器地址和数

据以及控制I2C驱动模块开始执行的控制信号,直接连接到I2C驱动模块的用户接口,从而完成

对OV5640传感器的初始化。有关该模块的详细介绍请大家参考“OV5640摄像头VGA显示实验”

章节。在本章节中对该模块稍加了一些修改,我们会在后面进行讲解。

摄像头图像采集模块(cmos_capture_data):摄像头采集模块在像素时钟的驱动下将传

感器输出的场同步信号、行同步信号以及8位数据转换成SDRAM读写控制模块的写使能信号和16

位写数据信号,完成对OV5640传感器图像的采集。OV7725和OV5640图像输出时序非常相似,有

关该模块的详细介绍请大家参考“OV7725摄像头VGA显示实验”章节。

图像尺寸配置模块(picture_size):图像尺寸配置模块用于配置摄像头输出图像尺寸的

大小,以及设置摄像头输出图像的帧率(每秒输出图像的帧数),此外还完成了SDRAM的读写

结束地址设置。

SDRAM读写控制模块(sdram_top):SDRAM读写控制器模块负责驱动SDRAM片外存储器,缓

存图像传感器输出的图像数据。该模块将SDRAM复杂的读写操作封装成类似FIFO的用户接口,

非常方便用户的使用。在“SDRAM读写测试实验”的程序中,读写操作地址都是SDRAM的同一存储空间,如果只使用一个存储空间缓存图像数据,那么同一存储空间中会出现两帧图像叠加的

情况,为了避免这一情况,我们在SDRAM的其它BANK中开辟一个相同大小的存储空间,使用乒

乓操作的方式来写入和读取数据,所以本次实验在“SDRAM读写测试实验”的程序里做了一个

小小的改动,有关该模块的详细介绍请大家参考“SDRAM读写测试实验”章节,修改部分请大

家参考“OV7725摄像头VGA显示实验”章节。

LCD顶层模块(lcd):LCD顶层模块下例化了以下三个模块:读ID模块(rd_id)、分频模

块(clk_div)、LCD驱动模块(lcd_driver)。

读ID模块(rd_id):读ID模块用于读取连接到开发板上的LCD的ID。

分频模块(clk_div):分频模块用于输出驱动LCD所需要的时钟。由于不同尺寸/分辨率

RGB LCD的驱动时钟频率是不一样的,该模块实现不同LCD的驱动时钟适配。

LCD驱动模块(lcd_driver):LCD驱动模块负责驱动LCD液晶屏,该模块通过读取SDRAM读

写控制模块来输出像素数据,本次实验将LCD驱动模块的内部信号data_req(数据请求信号)

输出至端口,方便从SDRAM控制器中读取数据。有关LCD驱动模块的详细介绍请大家参考“RGB

TFT-LCD彩条显示实验”章节。

下面是顶层模块中参数设置需要注意的地方,顶层模块参数设置代码如下:

55 //parameter define

56 parameter SLAVE_ADDR = 7'h3c ; //OV5640的器件地址7'h3c

57 parameter BIT_CTRL = 1'b1 ; //OV5640的字节地址为16位 0:8位 1:16位

58 parameter CLK_FREQ = 27'd100_000_000; //i2c_dri模块的驱动时钟频率 100MHz

59 parameter I2C_FREQ = 18'd250_000 ; //I2C的SCL时钟频率,不超过400KHz

在代码的第56行定义了OV5640的器件地址,其器件地址为7’h3c;第58行定义了寄存器地

址的位宽,BIT_CTRL=0表示地址位宽为8位,BIT_CTRL=1表示地址位宽为16位。因为OV5640的

地址位宽为16位,所以BIT_CTRL设置为1。

下面是顶层模块里锁相环例化部分:

82 //锁相环

83 pll u_pll(

84 .areset (~sys_rst_n),

85 .inclk0 (sys_clk),

86 .c0 (clk_100m),

87 .c1 (clk_100m_shift),

88 .c2 (clk_100m_lcd),

89 .locked (locked)

90 );

在代码的第88行输出了一个100Mhz的时钟clk_100m_lcd,作为LCD顶层模块(lcd)和I2C

驱动模块(i2c_dri)的驱动时钟。

为了适配现有的RGB LCD屏幕,我们对I2C配置模块的端口部分进行了修改:

92 //I2C配置模块

93 i2c_ov5640_rgb565_cfg u_i2c_cfg(

94 .clk (i2c_dri_clk),

95 .rst_n (rst_n),

96 .i2c_done (i2c_done),

97 .i2c_exec (i2c_exec),

98 .i2c_data (i2c_data),

99 .i2c_rh_wl (i2c_rh_wl), //I2C读写控制信号

100 .i2c_data_r (i2c_data_r),

101 .init_done (cam_init_done),

102 .cmos_h_pixel (cmos_h_pixel), //CMOS水平方向像素个数

103 .cmos_v_pixel (cmos_v_pixel) , //CMOS垂直方向像素个数

104 .total_h_pixel (total_h_pixel), //水平总像素大小

105 .total_v_pixel (total_v_pixel) //垂直总像素大小

106 );

在“OV5640摄像头VGA显示实验”中,cmos_h_pixel、cmos_v_pixel是通过参数传递的方

式将数值传递给I2C配置模块。在本实验中,这两个配置需要依据LCD尺寸的大小而改变。此外,

total_h_pixel及total_v_pixel的参数也需要进行配置,它们会影响到输出图像的帧率。所以,

在代码的第102行至代码的105行,我们添加了4个端口,用于根据LCD的尺寸大小适配摄像头输

出图像的尺寸及帧率。

在顶层模块中例化了图像尺寸配置模块(picture_size),它输出了上一段提到的四个变

量的具体数值:

145 //摄像头图像分辨率设置模块

146 picture_size u_picture_size (

147 .rst_n (rst_n ),

148

149 .ID_lcd (ID_lcd ), //LCD的ID,用于配置摄像头的图像大小

150

151 .cmos_h_pixel (cmos_h_pixel ), //CMOS水平方向像素个数

152 .cmos_v_pixel (cmos_v_pixel ), //CMOS垂直方向像素个数

153 .total_h_pixel (total_h_pixel ), //用于配置HTS寄存器

154 .total_v_pixel (total_v_pixel ), //用于配置VTS寄存器

155 .sdram_max_addr (sdram_max_addr) //sdram读写的最大地址

156 );

模块内部的代码如下:

1 module picture_size (

2 input rst_n ,

3

4 input [15:0] ID_lcd ,

5

6 output reg [12:0] cmos_h_pixel,

7 output reg [12:0] cmos_v_pixel,

8 output reg [12:0] total_h_pixel,

9 output reg [12:0] total_v_pixel,

10 output reg [23:0] sdram_max_addr

11 );

12

13 //parameter define

14 parameter ID_4342 = 0;

15 parameter ID_7084 = 1;

16 parameter ID_7016 = 2;

17 parameter ID_1018 = 5;

18

19 //*****************************************************

20 //** main code

21 //*****************************************************

22

23 //配置摄像头输出图像的尺寸大小

24 always @(*) begin

25 case(ID_lcd )

26 ID_4342 : begin

27 cmos_h_pixel = 13'd480;

28 cmos_v_pixel = 13'd272;

29 sdram_max_addr =23'd130560;

30 end

31 ID_7084 : begin

32 cmos_h_pixel = 13'd800;

33 cmos_v_pixel = 13'd480;

34 sdram_max_addr =23'd384000;

35 end

36 ID_7016 : begin

37 cmos_h_pixel = 13'd1024;

38 cmos_v_pixel = 13'd600;

39 sdram_max_addr =23'd614400;

40 end

41 ID_1018 : begin

42 cmos_h_pixel = 13'd1280;

43 cmos_v_pixel = 13'd800;

44 sdram_max_addr =23'd1024000;

45 end

46 default : begin

47 cmos_h_pixel = 13'd480;

48 cmos_v_pixel = 13'd272;

49 sdram_max_addr =23'd130560;

50 end

51 endcase

52 end

53

54 //对HTS及VTS的配置会影响摄像头输出图像的帧率

55 always @(*) begin

56 case(ID_lcd)

57 ID_4342 : begin

58 total_h_pixel = 13'd1800;

59 total_v_pixel = 13'd1000;

60 end

61 ID_7084 : begin

62 total_h_pixel = 13'd1800;

63 total_v_pixel = 13'd1000;

64 end

65 ID_7016 : begin

66 total_h_pixel = 13'd2200;

67 total_v_pixel = 13'd1000;

68 end

69 ID_1018 : begin

70 total_h_pixel = 13'd2570;

71 total_v_pixel = 13'd980;

72 end

73 default : begin

74 total_h_pixel = 13'd1800;

75 total_v_pixel = 13'd1000;

76 end

77 endcase

78 end

79

80 endmodule

我们通过组合逻辑对变量进行赋值,在代码的第24至第59行,对摄像头输出图像尺寸相关

的参数(cmos_h_pixel、cmos_v_pixel)进行了配置。同时还配置了一帧图像在SDRAM里的结

束缓存地址sdram_max_addr,它是cmos_h_pixel、cmos_v_pixel的乘积。

在代码的61至93行,对影响帧率的参数total_h_pixel、total_v_pixel进行了配置。

在顶层代码例化SDRAM顶层模块的170和179行,我们进行了修改。如下所示,SDRAM顶层模

块的wr_max_addr与rd_max_addr端口和sdram_max_addr信号直连。

158 //SDRAM 控制器顶层模块,封装成FIFO接口

159 //SDRAM 控制器地址组成: {bank_addr[1:0],row_addr[12:0],col_addr[8:0]}

160 sdram_top u_sdram_top(

161 .ref_clk (clk_100m), //sdram 控制器参考时钟

162 .out_clk (clk_100m_shift), //用于输出的相位偏移时钟

163 .rst_n (rst_n), //系统复位

164

165 //用户写端口

166 .wr_clk (cam_pclk), //写端口FIFO: 写时钟

167 .wr_en (wr_en), //写端口FIFO: 写使能

168 .wr_data (wr_data), //写端口FIFO: 写数据

169 .wr_min_addr (24'd0), //写SDRAM的起始地址

170 .wr_max_addr (sdram_max_addr), //写SDRAM的结束地址

171 .wr_len (10'd512), //写SDRAM时的数据突发长度

172 .wr_load (~rst_n), //写端口复位: 复位写地址,清空写FIFO

173

174 //用户读端口

175 .rd_clk (clk_lcd), //读端口FIFO: 读时钟

176 .rd_en (rd_en), //读端口FIFO: 读使能

177 .rd_data (rd_data), //读端口FIFO: 读数据

178 .rd_min_addr (24'd0), //读SDRAM的起始地址

179 .rd_max_addr (sdram_max_addr), //读SDRAM的结束地址

180 .rd_len (10'd512), //从SDRAM中读数据时的突发长度

181 .rd_load (~rst_n), //读端口复位: 复位读地址,清空读FIFO

在顶层模块代码的最后,例化了LCD顶层模块(lcd),如下所示:

201 //例化LCD顶层模块

202 lcd u_lcd(

203 .clk (clk_100m_lcd),

204 .rst_n (rst_n),

205

206 .lcd_hs (lcd_hs),

207 .lcd_vs (lcd_vs),

208 .lcd_de (lcd_de),

209 .lcd_rgb (lcd_rgb),

210 .lcd_bl (lcd_bl),

211 .lcd_rst (lcd_rst),

212 .lcd_pclk (lcd_pclk),

213

214 .clk_lcd (clk_lcd),

215

216 .pixel_data (rd_data),

217 .rd_en (rd_en),

218

219 .ID_lcd (ID_lcd)

220 );

LCD顶层模块里例化了读ID模块(rd_id)、分频模块(clk_div)、LCD驱动模块(lcd_driver),

如下所示:

1 module lcd(

2 input clk ,

3 input rst_n ,

4 //RGB LCD接口

5 output lcd_hs , //LCD 行同步信号

6 output lcd_vs , //LCD 场同步信号

7 output lcd_de , //LCD 数据输入使能

8 inout [15:0] lcd_rgb , //LCD RGB565颜色数据

9 output lcd_bl , //LCD 背光控制信号

10 output lcd_rst , //LCD 复位信号

11 output lcd_pclk, //LCD 采样时钟

12

13 output clk_lcd,

14 input [15:0] pixel_data, //像素点数据

15 output rd_en , //请求像素点颜色数据输入

16

17 output [15:0] ID_lcd //LCD的ID

18 );

19

20 //*****************************************************

21 //** main code

22 //*****************************************************

23

24 //RGB565数据输出

25 assign lcd_rgb = lcd_de ? pixel_data : 16'dz;

26

27 //读rgb lcd ID 模块

28 rd_id u_rd_id(

29 .clk (clk),

30 .rst_n (rst_n),

31 .lcd_rgb (lcd_rgb),

32 .ID_lcd (ID_lcd)

33

34 );

35

36 //分频模块,根据不同的LCD ID输出相应的频率的驱动时钟

37 clk_div u_clk_div(

38 .clk (clk),

39 .rst_n (rst_n),

40 .ID_lcd (ID_lcd),

41 .clk_lcd (clk_lcd)

42 );

43

44 //lcd驱动模块

45 lcd_driver u_lcd_driver(

46 .lcd_clk (clk_lcd),

47 .sys_rst_n (rst_n),

48

49 .lcd_hs (lcd_hs),

50 .lcd_vs (lcd_vs),

51 .lcd_de (lcd_de),

52 .lcd_bl (lcd_bl),

53 .lcd_rst (lcd_rst),

54 .lcd_pclk (lcd_pclk),

55

56 .data_req (rd_en), //请求像素点颜色数据输入

57 .pixel_xpos (),

58 .pixel_ypos (),

59 .ID_lcd (ID_lcd)

60 );

61

62 endmodule

在代码的第25行,当lcd_de信号为高电平时,即像素数据有效的时候,输出pixel_data信

号给lcd_rgb。当lcd_de信号为低电平时,给lcd_rgb信号赋值为高阻态。我们就是在当lcd_rgb

信号高阻态的时候,读取LCD的ID。读ID模块的代码如下:

1 module rd_id(

2 input clk ,

3 input rst_n ,

4 input [15:0] lcd_rgb, //像素点数据

5

6 output reg [15:0] ID_lcd

7 );

8

9 //reg define

10 reg ID_rd_en;

11

12 //*****************************************************

13 //** main code

14 //*****************************************************

15

16 //通过读r、g、b信号的最高位来获取LCD的ID

17 always @(posedge clk or negedge rst_n) begin

18 if (!rst_n) begin

19 ID_lcd <= 16'd5;

20 ID_rd_en <= 1'b0;

21 end

22 else if(!ID_rd_en) begin

23 ID_lcd <= {13'b0,lcd_rgb[4],lcd_rgb[10],lcd_rgb[15]};

24 ID_rd_en <= 1'b1;

25 end

26 else

27 ID_lcd <= ID_lcd;

28 end

29

30 endmodule

代码的第17行至28行实现了读取LCD ID的操作,在复位信号拉高后读取一次ID。

在前面,我们提到过不同尺寸的LCD所需的驱动时钟频率是不一样的。比如4.3寸LCD需要

9MHz的时钟,

7寸800*480分辨率的LCD需要33.3MHz的时钟,7寸1024*600分辨率的LCD需要50MHz

的时钟,10.1寸LCD需要70MHz的时钟。为了适配不同尺寸/分辨率的LCD,就需要输出相应的驱

动时钟。分频模块的代码如下:

1 module clk_div(

2 input clk ,

3 input rst_n ,

4 input [15:0] ID_lcd , //LCD的ID

5 output reg clk_lcd //驱动LCD的时钟

6

7 );

8

9 //parameter define

10 parameter ID_4342 = 0;

11 parameter ID_7084 = 1;

12 parameter ID_7016 = 2;

13 parameter ID_1018 = 5;

14

15 //wire define

16 wire clk_33m;

17

18 //reg define

19 reg [1:0] state_33m_0;

20 reg [1:0] state_33m_1;

21 reg [2:0] cnt_10m;

22 reg clk_50m;

23 reg clk_10m;

24

25 //*****************************************************

26 //** main code

27 //*****************************************************

28

29 //50m hz时钟分频

30 always @ (posedge clk or negedge rst_n) begin

31 if(!rst_n)

32 clk_50m <= 1'b0;

33 else

34 clk_50m <= ~ clk_50m ;

35 end

36

37 //33.3m hz时钟分频

38 always @ (posedge clk ) begin

39 case (state_33m_0)

40 2'b00 : state_33m_0 <= 2'b1;

41 2'b01 : state_33m_0 <= 2'b10;

42 2'b10 : state_33m_0 <= 2'b0;

43 default : state_33m_0 <= 2'b0;

44 endcase

45 end

46

47 always @ (negedge clk ) begin

48 case (state_33m_1)

49 2'b00 : state_33m_1 <= 2'b01;

50 2'b01 : state_33m_1 <= 2'b10;

51 2'b10 : state_33m_1 <= 2'b0;

52 default : state_33m_1 <= 2'b0;

53 endcase

54 end

55

56 assign clk_33m = state_33m_0[1] | state_33m_1[1];

57

58 //10m hz时钟分频

59 always @ (posedge clk or negedge rst_n) begin

60 if(!rst_n)

61 cnt_10m <= 3'd0;

62 else if(cnt_10m == 3'd4)

63 cnt_10m <= 3'd0;

64 else

65 cnt_10m <= cnt_10m + 1'b1;

66 end

67

68 always @ (posedge clk or negedge rst_n) begin

69 if(!rst_n)

70 clk_10m <= 1'b0;

71 else if(cnt_10m == 3'd4)

72 clk_10m <= ~ clk_10m;

73 else

74 clk_10m <= clk_10m;

75 end

76

77 //选择输出的时钟

78 always @ ( * ) begin

79 case(ID_lcd)

80 ID_4342 : clk_lcd = clk_10m;

81 ID_7084 : clk_lcd = clk_33m;

82 ID_7016 : clk_lcd = clk_50m;

83 ID_1018 : clk_lcd = clk_50m;

84 default : clk_lcd = clk_10m;

85 endcase

86 end

87

88 endmodule

我们决定把100MHz的输入时钟分频为10MHz、33.3MHz、50Mhz这三个频率的时钟。7寸1024*600分辨率的LCD和10.1寸的LCD共用50MHz的时钟。在代码的第78至90行,根据LCD的ID选

取合适频率的时钟输出。

仿真的结果如下所示:

a314273dad0791133b81d9228217184d.png

图 38.4.3 分频模块仿真结果图

为了能够驱动不同的LCD,LCD驱动模块也需要进行适当的修改:

1 module lcd_driver(

2 input lcd_clk, //lcd模块驱动时钟

3 input sys_rst_n, //复位信号

4 //RGB LCD接口

5 output lcd_hs, //LCD 行同步信号

6 output lcd_vs, //LCD 场同步信号

7 output lcd_de, //LCD 数据输入使能

8 output lcd_bl, //LCD 背光控制信号

9 output lcd_rst, //LCD 复位信号

10 output lcd_pclk, //LCD 采样时钟

11

12 output data_req , //请求像素点颜色数据输入

13 output [10:0] pixel_xpos, //像素点横坐标

14 output [10:0] pixel_ypos, //像素点纵坐标

15 input [15:0] ID_lcd //LCD的ID

16 );

17

18 //parameter define

19 // 4.3' 480*272

20 parameter H_SYNC_4342 = 11'd41; //行同步

21 parameter H_BACK_4342 = 11'd2; //行显示后沿

22 parameter H_DISP_4342 = 11'd480; //行有效数据

23 parameter H_FRONT_4342 = 11'd2; //行显示前沿

24 parameter H_TOTAL_4342 = 11'd525; //行扫描周期

25

26 parameter V_SYNC_4342 = 11'd10; //场同步

27 parameter V_BACK_4342 = 11'd2; //场显示后沿

28 parameter V_DISP_4342 = 11'd272; //场有效数据

29 parameter V_FRONT_4342 = 11'd2; //场显示前沿

30 parameter V_TOTAL_4342 = 11'd286; //场扫描周期

31

32 // 7' 800*480

33 parameter H_SYNC_7084 = 11'd128; //行同步

34 parameter H_BACK_7084 = 11'd88; //行显示后沿

35 parameter H_DISP_7084 = 11'd800; //行有效数据

36 parameter H_FRONT_7084 = 11'd40; //行显示前沿

37 parameter H_TOTAL_7084 = 11'd1056; //行扫描周期

38

39 parameter V_SYNC_7084 = 11'd2; //场同步

40 parameter V_BACK_7084 = 11'd33; //场显示后沿

41 parameter V_DISP_7084 = 11'd480; //场有效数据

42 parameter V_FRONT_7084 = 11'd10; //场显示前沿

43 parameter V_TOTAL_7084 = 11'd525; //场扫描周期

44

45 // 7' 1024*600

46 parameter H_SYNC_7016 = 11'd20; //行同步

47 parameter H_BACK_7016 = 11'd140; //行显示后沿

48 parameter H_DISP_7016 = 11'd1024; //行有效数据

49 parameter H_FRONT_7016 = 11'd160; //行显示前沿

50 parameter H_TOTAL_7016 = 11'd1344; //行扫描周期

51

52 parameter V_SYNC_7016 = 11'd3; //场同步

53 parameter V_BACK_7016 = 11'd20; //场显示后沿

54 parameter V_DISP_7016 = 11'd600; //场有效数据

55 parameter V_FRONT_7016 = 11'd12; //场显示前沿

56 parameter V_TOTAL_7016 = 11'd635; //场扫描周期

57

58 // 10.1' 1280*800

59 parameter H_SYNC_1018 = 11'd10; //行同步

60 parameter H_BACK_1018 = 11'd80; //行显示后沿

61 parameter H_DISP_1018 = 11'd1280; //行有效数据

62 parameter H_FRONT_1018 = 11'd70; //行显示前沿

63 parameter H_TOTAL_1018 = 11'd1440; //行扫描周期

64

65 parameter V_SYNC_1018 = 11'd3; //场同步

66 parameter V_BACK_1018 = 11'd10; //场显示后沿

67 parameter V_DISP_1018 = 11'd800; //场有效数据

68 parameter V_FRONT_1018 = 11'd10; //场显示前沿

69 parameter V_TOTAL_1018 = 11'd823; //场扫描周期

70

71 //LCD的ID

72 parameter ID_4342 = 0;

73 parameter ID_7084 = 1;

74 parameter ID_7016 = 2;

75 parameter ID_1018 = 5;

76

77 //reg define

78 reg [10:0] cnt_h;

79 reg [10:0] cnt_v;

80 reg [10:0] h_sync ;

81 reg [10:0] h_back ;

82 reg [10:0] h_disp ;

83 reg [10:0] h_total;

84 reg [10:0] v_sync ;

85 reg [10:0] v_back ;

86 reg [10:0] v_disp ;

87 reg [10:0] v_total;

88

89 //wire define

90 wire lcd_en;

91

92 //*****************************************************

93 //** main code

94 //*****************************************************

95 assign lcd_bl = 1'b1; //RGB LCD显示模块背光控制信号

96 assign lcd_rst = 1'b1; //RGB LCD显示模块系统复位信号

97 assign lcd_pclk = lcd_clk; //RGB LCD显示模块采样时钟

98

99 //RGB LCD 采用数据输入使能信号同步时,行场同步信号需要拉高

100 assign lcd_de = lcd_en; //LCD输入的颜色数据采用数据输入使能信号同步

101 assign lcd_hs = 1'b1;

102 assign lcd_vs = 1'b1;

103

104 //使能RGB565数据输出

105 assign lcd_en = (((cnt_h >= h_sync+h_back) && (cnt_h < h_sync+h_back+h_disp))

106 &&((cnt_v >= v_sync+v_back) && (cnt_v < v_sync+v_back+v_disp)))

107 ? 1'b1 : 1'b0;

108

109 //请求像素点颜色数据输入

110 assign data_req = (((cnt_h >= h_sync+h_back-1'b1) && (cnt_h < h_sync+h_back+h_disp-1'b1))

111 && ((cnt_v >= v_sync+v_back) && (cnt_v < v_sync+v_back+v_disp)))

112 ? 1'b1 : 1'b0;

113

114 //像素点坐标

115 assign pixel_xpos = data_req ? (cnt_h - (h_sync + h_back - 1'b1)) : 11'd0;

116 assign pixel_ypos = data_req ? (cnt_v - (v_sync + v_back - 1'b1)) : 11'd0;

117

118 //行场时序参数

119 always @(*) begin

120 case(ID_lcd)

121 ID_4342 : begin

122 h_sync = H_SYNC_4342;

123 h_back = H_BACK_4342;

124 h_disp = H_DISP_4342;

125 h_total = H_TOTAL_4342;

126 v_sync = V_SYNC_4342;

127 v_back = V_BACK_4342;

128 v_disp = V_DISP_4342;

129 v_total = V_TOTAL_4342;

130 end

131 ID_7084 : begin

132 h_sync = H_SYNC_7084;

133 h_back = H_BACK_7084;

134 h_disp = H_DISP_7084;

135 h_total = H_TOTAL_7084;

136 v_sync = V_SYNC_7084;

137 v_back = V_BACK_7084;

138 v_disp = V_DISP_7084;

139 v_total = V_TOTAL_7084;

140 end

141 ID_7016 : begin

142 h_sync = H_SYNC_7016;

143 h_back = H_BACK_7016;

144 h_disp = H_DISP_7016;

145 h_total = H_TOTAL_7016;

146 v_sync = V_SYNC_7016;

147 v_back = V_BACK_7016;

148 v_disp = V_DISP_7016;

149 v_total = V_TOTAL_7016;

150 end

151 ID_1018 : begin

152 h_sync = H_SYNC_1018;

153 h_back = H_BACK_1018;

154 h_disp = H_DISP_1018;

155 h_total = H_TOTAL_1018;

156 v_sync = V_SYNC_1018;

157 v_back = V_BACK_1018;

158 v_disp = V_DISP_1018;

159 v_total = V_TOTAL_1018;

160 end

161 default : begin

162 h_sync = H_SYNC_4342;

163 h_back = H_BACK_4342;

164 h_disp = H_DISP_4342;

165 h_total = H_TOTAL_4342;

166 v_sync = V_SYNC_4342;

167 v_back = V_BACK_4342;

168 v_disp = V_DISP_4342;

169 v_total = V_TOTAL_4342;

170 end

171 endcase

172 end

173

174 //行计数器对像素时钟计数

175 always @(posedge lcd_clk or negedge sys_rst_n) begin

176 if (!sys_rst_n)

177 cnt_h <= 11'd0;

178 else begin

179 if(cnt_h < h_total - 1'b1)

180 cnt_h <= cnt_h + 1'b1;

181 else

182 cnt_h <= 11'd0;

183 end

184 end

185

186 //场计数器对行计数

187 always @(posedge lcd_clk or negedge sys_rst_n) begin

188 if (!sys_rst_n)

189 cnt_v <= 11'd0;

190 else if(cnt_h == h_total - 1'b1) begin

191 if(cnt_v < v_total - 1'b1)

192 cnt_v <= cnt_v + 1'b1;

193 else

194 cnt_v <= 11'd0;

195 end

196 end

197

198 endmodule

在代码的第20至70行,定义了四种RGB LCD屏的时序参数。在代码的第120行至第185行,

根据LCD的ID对参数变量进行赋值。

本章实验程序设计与“OV5640摄像头VGA显示实验”比较近似,除了例举出来的部分,其

余模块(除PLL时钟模块外)完全相同。有关本章实验的详细设计详情请大家参考“OV5640摄

像头VGA显示实验”章节。

下载验证

首先我们打开OV5640摄像头RGB TFT-LCD显示实验工程,在工程所在的路径下打开

ov5640_rgb565_lcd/par文件夹,在里面找到“ov5640_rgb565_lcd.qpf”并双击打开。注意工

程所在的路径名只能由字母、数字以及下划线组成,不能出现中文、空格以及特殊字符等。工

程打开后如图 38.5.1所示:

6e39ccdd35983aa29394c3cf209f90c6.png

图 38.5.1 OV5640摄像头RGB TFT-LCD显示实验工程

然后将OV5640摄像头插入开发板上的摄像头扩展接口(注意摄像头镜头朝外);将FPC 排

线一端与ALIENTEK的RGB接口模块上的J1接口连接,另一端与开拓者开发板上的J1接口连接;

连接时,先掀开FPC连接器上的黑色翻盖,将FPC排线蓝色面朝上插入连接器,最后将黑色翻盖

压下以固定FPC排线。

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

关。

接下来我们下载程序,验证OV5640摄像头RGB TFT-LCD实时显示功能。

工程打开后通过点击工具栏中的“Programmer”图标打开下载界面,通过“Add File”按

钮选择ov5640_rgb565_lcd/par/output_files目录下的“ov5640_rgb565_lcd.sof”文件。开

发板电源打开后,在程序下载界面点击“Hardware Setup”,在弹出的对话框中选择当前的硬

件连接为“USB-Blaster[USB-0]”。然后点击“Start”将工程编译完成后得到的sof文件下载

到开发板中,如图 38.5.所示:

554bf5fa9b882046f8a66dc3b3e3fd81.png

图 38.5.2 程序下载完成界面

下载完成后观察显示器的显示图像如图 38.5.所示,说明OV5640摄像头LCD显示程序下载

验证成功。

905b7fa2e5b8c6a315aa90faf5f41ea6.png

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值