赞
踩
OV5640 是一款 1/4 英寸单芯片图像传感器,其感光阵列达到 25921944(即 500W 像素),能实现最快 15fps QSXVGA(25921944)或者 90fps VGA(640*480)分辨率的图像采集。传感器采用 OmniVision 推出的 OmniBSI(背面照度)技术,使传感器达到更高的性能,如高灵敏度、低串扰和低噪声。传感器内 部集成了图像处理的功能,包括自动曝光控制(AEC)、自动白平衡(AWB)等。同时该传感器支持 LED 补光、MIPI(移动产业处理器接口)输出接口和 DVP(数字视频并行)输出接口选择、ISP(图像信号处理) 以及 AFC(自动聚焦控制)等功能。输出最大为 500 万像素的图像 (2592x1944 分辨率),支持使用 VGA 时序输出图像数据,输出图像的数据格式支持 YUV(422/420)、YCbCr422、 RGB565 以及 JPEG 格式,若直接输出 JPEG 格式的图 像时可大大减少数据量,方便网络传输。
(1) 控制寄存器标号1处的是 OV5640 的控制寄存器,它根据这些寄存器配置的参数来运行,而 这些参数是由外部控制器通过 SIO_C 和 SIO_D 引脚写入的, SIO_C 与 SIO_D 使用的通讯协议跟 I2C 十分类似。
(2) 通信、控制信号及时钟标号2处包含了 OV5640 的通信、控制信号及外部时钟,其中 PCLK、 HREF 及 VSYNC 分别是像素同步时钟、行同步信号以及帧同步信号,这与液晶屏控制中的信号是很类似的。 RESETB 引脚为低电平时,用于复位整个传感器芯片, PWDN 用于控制芯片进入低功耗模式。注意最后的一个 XCLK 引脚,它跟 PCLK 是完全不同的, XCLK 是用于驱动整个传感器芯片的时钟信号,是外部输入到 OV5640 的信号;而 PCLK 是 OV5640 输出数据时的同步信号,它是由 OV5640 输 出的信号。 XCLK 可以外接晶振或由外部控制器提供。
(3) 感光矩阵标号3处的是感光矩阵,光信号在这里转化成电信号,经过各种处理,这些信号存储成由一个个像素点表示的数字图像。
(4) 数据输出信号标号4处包含了 DSP 处理单元,它会根据控制寄存器的配置做一些基本的图像处理运算。这部分还包含了图像格式转换单元及压缩单元,转换出的数据最终通过 Y0-Y9 引脚输出,OV5640 在 RGB565 模式下只有高 8 位数据是有效的即 D[9:2], 因此我们仅使用 Y2-Y9 引脚。
(5) 数据输出信号标号5处为 VCM 处理单元,他会通过图像分析来实现图像的自动对焦功能。要实现自动对焦还需要下载自动对焦固件到模组,后面摄像头实验详细介绍这个功能。
外部控制器对 OV5640 寄存器的配置参数是通过 SCCB 总线传输过去的,而 SCCB 总线跟 I2C 十分类似。
起始信号: 在 SCL(图中为 SIO_C) 为高电平时, SDA(图中为 SIO_D)出现一 个下降沿,则 SCCB 开始传输。
停止信号:在 SCL 为高电平时, SDA 出现一个上升沿,则 SCCB 停止传输。
数据有效性:除了开始和停止状态, 在数据传输过程中,当 SCL 为高电平时, 必须保证 SDA 上的数据稳定,也就是说, SDA 上的电平变换只能发生在 SCL 为 低电平的时候,SDA 的信号在 SCL 为高电平时被采集。
在 SCCB 协议中定义的读写操作与 I2C 也是一样的,只是换了一种说法。它定义了两种写操作,即三步写操作和两步写操作。三步写操作可向从设备的一个目的寄存器中写入 数据,见图 。在三步写操作中,第一阶段发送从设备的 ID 地址+W 标志(等于 I2C 的 设备地址: 7 位设备地址+读写方向标志),第二阶段发送从设备目标寄存器的 8 位地址, 第三阶段发送要写入寄存器的 8 位数据。图中的“X”数据位可写入 1 或 0,对通讯无影响。
而两步写操作没有第三阶段,即只向从器件传输了设备 ID+W 标志和目的寄存器的地址,见图 。两步写操作是用来配合后面的读寄存器数据操作的,它与读操作一起使 用,实现 I2C 的复合过程。
两步读操作,它用于读取从设备目的寄存器中的数据,见图 。在第一阶段中发送从设备的设备 ID+R 标志(设备地址+读方向标志)和自由位,在第二阶段中读取寄存器中的 8 位数据和写 NA 位(非应答信号)。 由于两步读操作没有确定目的寄存器的地址,所以在 读操作前,必需有一个两步写操作,以提供读操作中的寄存器地址。
控制 OV5640 涉及到它很多的寄存器,可直接查询《ov5640datasheet》了解,通过这 些寄存器的配置,可以控制它输出图像的分辨率大小、图像格式及图像方向等。要注意的是 OV5640 寄存器地址为 16 位。
对 OV5640 采用 SCCB 协议进行控制,而它输出图像时则使用 VGA 时序(还可用 SVGA、 UXGA,这些时序都差不多),这跟控制液晶屏输入图像时很类似。 OV5640 输出 图像时,一帧帧地输出,在帧内的数据一般从左到右,从上到下,一个像素一个像素地输 出(也可通过寄存器修改方向)。 若我们使用 Y2-Y9 数据线,图像格式设置为 RGB565,进行数据输出 时, Y2-Y9 数据线会在 1 个像素同步时钟 PCLK 的驱动下发送 1 字节的数据信号,所以 2 个 PCLK 时钟可发送 1 个 RGB565 格式的像素数据。像素数据依次传输,每传输完一行数据时,行同步信号 HREF 会输出一个电平跳变信号,每传输完一帧图像时, VSYNC 会输出一个电平跳变信号。
图像采集模块的主要功能是接收并拼接 OV5640 摄像头传入的图像数据
在实际的使用过程中,为了避免采集到摄像头刚初始化完成时的不稳定数据,一般舍弃前10帧图像后,再采集数据。因此通过 摄像头输入的帧同步信号来进行帧数的判断ov5640_vsync
。同时,因为我们仅仅使用摄像头的8跟数据线采集数据,因此两个时钟周期才可以传输一个完整RGB565像素数据,对此,我们需要将采集到的数据进行拼接。
`timescale 1ns / 1ns // // Company: // Engineer: // // Create Date: 2024/03/20 17:06:21 // Design Name: // Module Name: OV5640_data // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // // module OV5640_data#( parameter PIC_CNT_MAX = 4'd10 //舍弃前10帧不稳定图像数据 ) ( input wire sys_rst_n , //复位信号 // OV5640 input wire ov5640_pclk , //摄像头像素时钟 input wire ov5640_href , //摄像头行同步信号 input wire ov5640_vsync , //摄像头场同步信号 input wire [ 7:0] ov5640_data , //摄像头图像数据 // 写FIFO output wire ov5640_wr_en , //图像数据有效使能信号 output wire [15:0] ov5640_data_out //图像数据 ); //********************************************************************// //****************** Parameter and Internal Signal *******************// //********************************************************************// //wire define wire pic_flag ; //帧图像标志信号,每拉高一次,代表一帧完整图像 //reg define reg ov5640_vsync_dly ; //摄像头输入场同步信号打拍 reg [3:0] cnt_pic ; //图像帧计数器 reg pic_valid ; //帧有效标志信号 reg [7:0] pic_data_reg ; //输入8位图像数据缓存 reg [15:0] data_out_reg ; //输出16位图像数据缓存 reg data_flag ; //输入8位图像数据缓存 reg data_flag_dly1 ; //图像数据拼接标志信号打拍 //********************************************************************// //***************************** Main Code ****************************// //********************************************************************// //ov5640_vsync_dly:摄像头输入场同步信号打拍 always@(posedge ov5640_pclk or negedge sys_rst_n) if(sys_rst_n == 1'b0) ov5640_vsync_dly <= 1'b0; else ov5640_vsync_dly <= ov5640_vsync; //pic_flag:帧图像标志信号,每拉高一次,代表一帧完整图像 assign pic_flag = ((ov5640_vsync_dly == 1'b0) && (ov5640_vsync == 1'b1)) ? 1'b1 : 1'b0; //cnt_pic:图像帧计数器 always@(posedge ov5640_pclk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_pic <= 4'd0; else if(cnt_pic < PIC_CNT_MAX) cnt_pic <= cnt_pic + 1'b1; else cnt_pic <= cnt_pic; //pic_valid:帧有效标志 always@(posedge ov5640_pclk or negedge sys_rst_n) if(sys_rst_n == 1'b0) pic_valid <= 1'b0; else if((cnt_pic == PIC_CNT_MAX) && (pic_flag == 1'b1)) pic_valid <= 1'b1; else pic_valid <= pic_valid; //data_out_reg,pic_data_reg,data_flag:输出16位图像数据缓冲 //输入8位图像数据缓存输入8位,图像数据缓存 always@(posedge ov5640_pclk or negedge sys_rst_n) if(sys_rst_n == 1'b0) begin data_out_reg <= 16'd0; pic_data_reg <= 8'd0; data_flag <= 1'b0; end else if(ov5640_href == 1'b1) begin data_flag <= ~data_flag; pic_data_reg <= ov5640_data; data_out_reg <= data_out_reg; if(data_flag == 1'b1) data_out_reg <= {pic_data_reg,ov5640_data}; else data_out_reg <= data_out_reg; end else begin data_flag <= 1'b0; pic_data_reg <= 8'd0; data_out_reg <= data_out_reg; end //data_flag_dly1:图像数据缓存打拍 always@(posedge ov5640_pclk or negedge sys_rst_n) if(sys_rst_n == 1'b0) data_flag_dly1 <= 1'b0; else data_flag_dly1 <= data_flag; //ov5640_data_out:输出16位图像数据 assign ov5640_data_out = (pic_valid == 1'b1) ? data_out_reg : 16'b0; //ov5640_wr_en:输出16位图像数据使能 assign ov5640_wr_en = (pic_valid == 1'b1) ? data_flag_dly1 : 1'b0; endmodule
要想 OV5640 摄像头正常工作,需要先对摄像头进行寄存器配置,即向摄像头寄存器 写入对应指令。
此处需要注意的是,摄像头上电之后,需要等待一段时间之后,才可以开始寄存器的配置。配置则是通过iic总线向寄存器发送命令。
`timescale 1ns / 1ns // // Company: // Engineer: // // Create Date: 2024/03/20 20:44:00 // Design Name: // Module Name: ov5640_cfg // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // // module ov5640_cfg#( parameter CFG_NUM = 9'd250, //需配置的寄存器个数 parameter CNT_WAIT_MAX = 15'd20000 //ov640配置寄存器等待时间 ) ( input wire sys_clk , //时钟,IIC时钟 input wire sys_rst_n , //复位 input wire cfg_one_end , //一个寄存器配置完成,IIC传来 output reg cfg_start , //单个寄存器配置信号 output reg [23:0] cfg_data , //寄存器地址+数据 output reg cfg_done //寄存器配置完成 ); //reg define reg [8:0] cfg_num ; //寄存器配置完成计数器 reg [14:0] wait_cnt; //等待计数 //main code //cfg_num已配置寄存器计数 always @(posedge sys_clk or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) cfg_num <= 1'b0; else if(cfg_one_end == 1'b1) cfg_num <= cfg_num + 1'b1; else cfg_num <= cfg_num; end //wait_cnt ov5640上电后需要等待一段时间,才可以开始配置寄存器 always @(posedge sys_clk or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) wait_cnt <= 1'b0; else if(wait_cnt < CNT_WAIT_MAX ) wait_cnt <= wait_cnt + 1'b1; else wait_cnt <= wait_cnt; end //cfg_start:单个寄存器配置触发信号 always @(posedge sys_clk or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) cfg_start <= 1'b0; else if((cfg_one_end == 1'b1) && (cfg_num < CFG_NUM)) cfg_start <= 1'b1; else if(wait_cnt == CNT_WAIT_MAX - 1'b1) cfg_start <= 1'b1; else cfg_start <= 1'b0; end //cfg_done always @(posedge sys_clk or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) cfg_done <= 1'b0; else if(cfg_num == CFG_NUM && cfg_one_end == 1'b1) cfg_done <= 1'b1; else cfg_done <= cfg_done; end //---------------------------------------------------- always @(posedge sys_clk or negedge sys_rst_n) begin if(!sys_rst_n) cfg_data <= 24'b0; else if(cfg_done == 1'b0)begin case(cfg_num) //先对寄存器进行软件复位,使寄存器恢复初始值 //寄存器软件复位后,需要延时1ms才能配置其它寄存器 8'd0 : cfg_data <= {16'h300a,8'h0}; // 8'd1 : cfg_data <= {16'h300b,8'h0}; // 8'd2 : cfg_data <= {16'h3008,8'h82}; //Bit[7]:复位 Bit[6]:电源休眠 8'd3 : cfg_data <= {16'h3008,8'h02}; //正常工作模式 8'd4 : cfg_data <= {16'h3103,8'h02}; //Bit[1]:1 PLL Clock //引脚输入/输出控制 FREX/VSYNC/HREF/PCLK/D[9:6] 8'd5 : cfg_data <= {8'h30,8'h17,8'hff}; //引脚输入/输出控制 D[5:0]/GPIO1/GPIO0 8'd6 : cfg_data <= {16'h3018,8'hff}; 8'd7 : cfg_data <= {16'h3037,8'h13}; //PLL分频控制 8'd8 : cfg_data <= {16'h3108,8'h01}; //系统根分频器 8'd9 : cfg_data <= {16'h3630,8'h36}; 8'd10 : cfg_data <= {16'h3631,8'h0e}; 8'd11 : cfg_data <= {16'h3632,8'he2}; 8'd12 : cfg_data <= {16'h3633,8'h12}; 8'd13 : cfg_data <= {16'h3621,8'he0}; 8'd14 : cfg_data <= {16'h3704,8'ha0}; 8'd15 : cfg_data <= {16'h3703,8'h5a}; 8'd16 : cfg_data <= {16'h3715,8'h78}; 8'd17 : cfg_data <= {16'h3717,8'h01}; 8'd18 : cfg_data <= {16'h370b,8'h60}; 8'd19 : cfg_data <= {16'h3705,8'h1a}; 8'd20 : cfg_data <= {16'h3905,8'h02}; 8'd21 : cfg_data <= {16'h3906,8'h10}; 8'd22 : cfg_data <= {16'h3901,8'h0a}; 8'd23 : cfg_data <= {16'h3731,8'h12}; 8'd24 : cfg_data <= {16'h3600,8'h08}; //VCM控制,用于自动聚焦 8'd25 : cfg_data <= {16'h3601,8'h33}; //VCM控制,用于自动聚焦 8'd26 : cfg_data <= {16'h302d,8'h60}; //系统控制 8'd27 : cfg_data <= {16'h3620,8'h52}; 8'd28 : cfg_data <= {16'h371b,8'h20}; 8'd29 : cfg_data <= {16'h471c,8'h50}; 8'd30 : cfg_data <= {16'h3a13,8'h43}; //AEC(自动曝光控制) 8'd31 : cfg_data <= {16'h3a18,8'h00}; //AEC 增益上限 8'd32 : cfg_data <= {16'h3a19,8'hf8}; //AEC 增益上限 8'd33 : cfg_data <= {16'h3635,8'h13}; 8'd34 : cfg_data <= {16'h3636,8'h03}; 8'd35 : cfg_data <= {16'h3634,8'h40}; 8'd36 : cfg_data <= {16'h3622,8'h01}; 8'd37 : cfg_data <= {16'h3c01,8'h34}; 8'd38 : cfg_data <= {16'h3c04,8'h28}; 8'd39 : cfg_data <= {16'h3c05,8'h98}; 8'd40 : cfg_data <= {16'h3c06,8'h00}; //light meter 1 阈值[15:8] 8'd41 : cfg_data <= {16'h3c07,8'h08}; //light meter 1 阈值[7:0] 8'd42 : cfg_data <= {16'h3c08,8'h00}; //light meter 2 阈值[15:8] 8'd43 : cfg_data <= {16'h3c09,8'h1c}; //light meter 2 阈值[7:0] 8'd44 : cfg_data <= {16'h3c0a,8'h9c}; //sample number[15:8] 8'd45 : cfg_data <= {16'h3c0b,8'h40}; //sample number[7:0] 8'd46 : cfg_data <= {16'h3810,8'h00}; //Timing Hoffset[11:8] 8'd47 : cfg_data <= {16'h3811,8'h10}; //Timing Hoffset[7:0] 8'd48 : cfg_data <= {16'h3812,8'h00}; //Timing Voffset[10:8] 8'd49 : cfg_data <= {16'h3708,8'h64}; 8'd50 : cfg_data <= {16'h4001,8'h02}; //BLC(黑电平校准)补偿起始行号 8'd51 : cfg_data <= {16'h4005,8'h1a}; //BLC(黑电平校准)补偿始终更新 8'd52 : cfg_data <= {16'h3000,8'h00}; //系统块复位控制 8'd53 : cfg_data <= {16'h3004,8'hff}; //时钟使能控制 8'd54 : cfg_data <= {16'h4300,8'h61}; //格式控制 RGB565 8'd55 : cfg_data <= {16'h501f,8'h01}; //ISP RGB 8'd56 : cfg_data <= {16'h440e,8'h00}; 8'd57 : cfg_data <= {16'h5000,8'ha7}; //ISP控制 8'd58 : cfg_data <= {16'h3a0f,8'h30}; //AEC控制;stable range in high 8'd59 : cfg_data <= {16'h3a10,8'h28}; //AEC控制;stable range in low 8'd60 : cfg_data <= {16'h3a1b,8'h30}; //AEC控制;stable range out high 8'd61 : cfg_data <= {16'h3a1e,8'h26}; //AEC控制;stable range out low 8'd62 : cfg_data <= {16'h3a11,8'h60}; //AEC控制; fast zone high 8'd63 : cfg_data <= {16'h3a1f,8'h14}; //AEC控制; fast zone low //LENC(镜头校正)控制 16'h5800~16'h583d 8'd64 : cfg_data <= {16'h5800,8'h23}; 8'd65 : cfg_data <= {16'h5801,8'h14}; 8'd66 : cfg_data <= {16'h5802,8'h0f}; 8'd67 : cfg_data <= {16'h5803,8'h0f}; 8'd68 : cfg_data <= {16'h5804,8'h12}; 8'd69 : cfg_data <= {16'h5805,8'h26}; 8'd70 : cfg_data <= {16'h5806,8'h0c}; 8'd71 : cfg_data <= {16'h5807,8'h08}; 8'd72 : cfg_data <= {16'h5808,8'h05}; 8'd73 : cfg_data <= {16'h5809,8'h05}; 8'd74 : cfg_data <= {16'h580a,8'h08}; 8'd75 : cfg_data <= {16'h580b,8'h0d}; 8'd76 : cfg_data <= {16'h580c,8'h08}; 8'd77 : cfg_data <= {16'h580d,8'h03}; 8'd78 : cfg_data <= {16'h580e,8'h00}; 8'd79 : cfg_data <= {16'h580f,8'h00}; 8'd80 : cfg_data <= {16'h5810,8'h03}; 8'd81 : cfg_data <= {16'h5811,8'h09}; 8'd82 : cfg_data <= {16'h5812,8'h07}; 8'd83 : cfg_data <= {16'h5813,8'h03}; 8'd84 : cfg_data <= {16'h5814,8'h00}; 8'd85 : cfg_data <= {16'h5815,8'h01}; 8'd86 : cfg_data <= {16'h5816,8'h03}; 8'd87 : cfg_data <= {16'h5817,8'h08}; 8'd88 : cfg_data <= {16'h5818,8'h0d}; 8'd89 : cfg_data <= {16'h5819,8'h08}; 8'd90 : cfg_data <= {16'h581a,8'h05}; 8'd91 : cfg_data <= {16'h581b,8'h06}; 8'd92 : cfg_data <= {16'h581c,8'h08}; 8'd93 : cfg_data <= {16'h581d,8'h0e}; 8'd94 : cfg_data <= {16'h581e,8'h29}; 8'd95 : cfg_data <= {16'h581f,8'h17}; 8'd96 : cfg_data <= {16'h5820,8'h11}; 8'd97 : cfg_data <= {16'h5821,8'h11}; 8'd98 : cfg_data <= {16'h5822,8'h15}; 8'd99 : cfg_data <= {16'h5823,8'h28}; 8'd100: cfg_data <= {16'h5824,8'h46}; 8'd101: cfg_data <= {16'h5825,8'h26}; 8'd102: cfg_data <= {16'h5826,8'h08}; 8'd103: cfg_data <= {16'h5827,8'h26}; 8'd104: cfg_data <= {16'h5828,8'h64}; 8'd105: cfg_data <= {16'h5829,8'h26}; 8'd106: cfg_data <= {16'h582a,8'h24}; 8'd107: cfg_data <= {16'h582b,8'h22}; 8'd108: cfg_data <= {16'h582c,8'h24}; 8'd109: cfg_data <= {16'h582d,8'h24}; 8'd110: cfg_data <= {16'h582e,8'h06}; 8'd111: cfg_data <= {16'h582f,8'h22}; 8'd112: cfg_data <= {16'h5830,8'h40}; 8'd113: cfg_data <= {16'h5831,8'h42}; 8'd114: cfg_data <= {16'h5832,8'h24}; 8'd115: cfg_data <= {16'h5833,8'h26}; 8'd116: cfg_data <= {16'h5834,8'h24}; 8'd117: cfg_data <= {16'h5835,8'h22}; 8'd118: cfg_data <= {16'h5836,8'h22}; 8'd119: cfg_data <= {16'h5837,8'h26}; 8'd120: cfg_data <= {16'h5838,8'h44}; 8'd121: cfg_data <= {16'h5839,8'h24}; 8'd122: cfg_data <= {16'h583a,8'h26}; 8'd123: cfg_data <= {16'h583b,8'h28}; 8'd124: cfg_data <= {16'h583c,8'h42}; 8'd125: cfg_data <= {16'h583d,8'hce}; //AWB(自动白平衡控制) 16'h5180~16'h519e 8'd126: cfg_data <= {16'h5180,8'hff}; 8'd127: cfg_data <= {16'h5181,8'hf2}; 8'd128: cfg_data <= {16'h5182,8'h00}; 8'd129: cfg_data <= {16'h5183,8'h14}; 8'd130: cfg_data <= {16'h5184,8'h25}; 8'd131: cfg_data <= {16'h5185,8'h24}; 8'd132: cfg_data <= {16'h5186,8'h09}; 8'd133: cfg_data <= {16'h5187,8'h09}; 8'd134: cfg_data <= {16'h5188,8'h09}; 8'd135: cfg_data <= {16'h5189,8'h75}; 8'd136: cfg_data <= {16'h518a,8'h54}; 8'd137: cfg_data <= {16'h518b,8'he0}; 8'd138: cfg_data <= {16'h518c,8'hb2}; 8'd139: cfg_data <= {16'h518d,8'h42}; 8'd140: cfg_data <= {16'h518e,8'h3d}; 8'd141: cfg_data <= {16'h518f,8'h56}; 8'd142: cfg_data <= {16'h5190,8'h46}; 8'd143: cfg_data <= {16'h5191,8'hf8}; 8'd144: cfg_data <= {16'h5192,8'h04}; 8'd145: cfg_data <= {16'h5193,8'h70}; 8'd146: cfg_data <= {16'h5194,8'hf0}; 8'd147: cfg_data <= {16'h5195,8'hf0}; 8'd148: cfg_data <= {16'h5196,8'h03}; 8'd149: cfg_data <= {16'h5197,8'h01}; 8'd150: cfg_data <= {16'h5198,8'h04}; 8'd151: cfg_data <= {16'h5199,8'h12}; 8'd152: cfg_data <= {16'h519a,8'h04}; 8'd153: cfg_data <= {16'h519b,8'h00}; 8'd154: cfg_data <= {16'h519c,8'h06}; 8'd155: cfg_data <= {16'h519d,8'h82}; 8'd156: cfg_data <= {16'h519e,8'h38}; //Gamma(伽马)控制 16'h5480~16'h5490 8'd157: cfg_data <= {16'h5480,8'h01}; 8'd158: cfg_data <= {16'h5481,8'h08}; 8'd159: cfg_data <= {16'h5482,8'h14}; 8'd160: cfg_data <= {16'h5483,8'h28}; 8'd161: cfg_data <= {16'h5484,8'h51}; 8'd162: cfg_data <= {16'h5485,8'h65}; 8'd163: cfg_data <= {16'h5486,8'h71}; 8'd164: cfg_data <= {16'h5487,8'h7d}; 8'd165: cfg_data <= {16'h5488,8'h87}; 8'd166: cfg_data <= {16'h5489,8'h91}; 8'd167: cfg_data <= {16'h548a,8'h9a}; 8'd168: cfg_data <= {16'h548b,8'haa}; 8'd169: cfg_data <= {16'h548c,8'hb8}; 8'd170: cfg_data <= {16'h548d,8'hcd}; 8'd171: cfg_data <= {16'h548e,8'hdd}; 8'd172: cfg_data <= {16'h548f,8'hea}; 8'd173: cfg_data <= {16'h5490,8'h1d}; //CMX(彩色矩阵控制) 16'h5381~16'h538b 8'd174: cfg_data <= {16'h5381,8'h1e}; 8'd175: cfg_data <= {16'h5382,8'h5b}; 8'd176: cfg_data <= {16'h5383,8'h08}; 8'd177: cfg_data <= {16'h5384,8'h0a}; 8'd178: cfg_data <= {16'h5385,8'h7e}; 8'd179: cfg_data <= {16'h5386,8'h88}; 8'd180: cfg_data <= {16'h5387,8'h7c}; 8'd181: cfg_data <= {16'h5388,8'h6c}; 8'd182: cfg_data <= {16'h5389,8'h10}; 8'd183: cfg_data <= {16'h538a,8'h01}; 8'd184: cfg_data <= {16'h538b,8'h98}; //SDE(特殊数码效果)控制 16'h5580~16'h558b 8'd185: cfg_data <= {16'h5580,8'h06}; 8'd186: cfg_data <= {16'h5583,8'h40}; 8'd187: cfg_data <= {16'h5584,8'h10}; 8'd188: cfg_data <= {16'h5589,8'h10}; 8'd189: cfg_data <= {16'h558a,8'h00}; 8'd190: cfg_data <= {16'h558b,8'hf8}; 8'd191: cfg_data <= {16'h501d,8'h40}; //ISP MISC //CIP(颜色插值)控制 (16'h5300~16'h530c) 8'd192: cfg_data <= {16'h5300,8'h08}; 8'd193: cfg_data <= {16'h5301,8'h30}; 8'd194: cfg_data <= {16'h5302,8'h10}; 8'd195: cfg_data <= {16'h5303,8'h00}; 8'd196: cfg_data <= {16'h5304,8'h08}; 8'd197: cfg_data <= {16'h5305,8'h30}; 8'd198: cfg_data <= {16'h5306,8'h08}; 8'd199: cfg_data <= {16'h5307,8'h16}; 8'd200: cfg_data <= {16'h5309,8'h08}; 8'd201: cfg_data <= {16'h530a,8'h30}; 8'd202: cfg_data <= {16'h530b,8'h04}; 8'd203: cfg_data <= {16'h530c,8'h06}; 8'd204: cfg_data <= {16'h5025,8'h00}; //系统时钟分频 Bit[7:4]:系统时钟分频 input clock =24Mhz, PCLK = 48Mhz 8'd205: cfg_data <= {16'h3035,8'h11}; 8'd206: cfg_data <= {16'h3036,8'h3c}; //PLL倍频 8'd207: cfg_data <= {16'h3c07,8'h08}; //时序控制 16'h3800~16'h3821 8'd208: cfg_data <= {16'h3820,8'h46}; 8'd209: cfg_data <= {16'h3821,8'h01}; 8'd210: cfg_data <= {16'h3814,8'h31}; 8'd211: cfg_data <= {16'h3815,8'h31}; 8'd212: cfg_data <= {16'h3800,8'h00}; 8'd213: cfg_data <= {16'h3801,8'h00}; 8'd214: cfg_data <= {16'h3802,8'h00}; 8'd215: cfg_data <= {16'h3803,8'h04}; 8'd216: cfg_data <= {16'h3804,8'h0a}; 8'd217: cfg_data <= {16'h3805,8'h3f}; 8'd218: cfg_data <= {16'h3806,8'h07}; 8'd219: cfg_data <= {16'h3807,8'h9b}; //设置输出像素个数 //DVP 输出水平像素点数高4位 8'd220: cfg_data <= {16'h3808,8'h04}; //DVP 输出水平像素点数低8位 8'd221: cfg_data <= {16'h3809,8'h00}; //DVP 输出垂直像素点数高3位 8'd222: cfg_data <= {16'h380a,8'h02}; //DVP 输出垂直像素点数低8位 8'd223: cfg_data <= {16'h380b,8'h58}; //水平总像素大小高5位 8'd224: cfg_data <= {16'h380c,8'h08}; //水平总像素大小低8位 8'd225: cfg_data <= {16'h380d,8'h98}; //垂直总像素大小高5位 8'd226: cfg_data <= {16'h380e,8'h03}; //垂直总像素大小低8位 8'd227: cfg_data <= {16'h380f,8'hE8}; 8'd228: cfg_data <= {16'h3813,8'h06}; 8'd229: cfg_data <= {16'h3618,8'h00}; 8'd230: cfg_data <= {16'h3612,8'h29}; 8'd231: cfg_data <= {16'h3709,8'h52}; 8'd232: cfg_data <= {16'h370c,8'h03}; 8'd233: cfg_data <= {16'h3a02,8'h17}; //60Hz max exposure 8'd234: cfg_data <= {16'h3a03,8'h10}; //60Hz max exposure 8'd235: cfg_data <= {16'h3a14,8'h17}; //50Hz max exposure 8'd236: cfg_data <= {16'h3a15,8'h10}; //50Hz max exposure 8'd237: cfg_data <= {16'h4004,8'h02}; //BLC(背光) 2 lines 8'd238: cfg_data <= {16'h4713,8'h03}; //JPEG mode 3 8'd239: cfg_data <= {16'h4407,8'h04}; //量化标度 8'd240: cfg_data <= {16'h460c,8'h22}; 8'd241: cfg_data <= {16'h4837,8'h22}; //DVP CLK divider 8'd242: cfg_data <= {16'h3824,8'h02}; //DVP CLK divider 8'd243: cfg_data <= {16'h5001,8'ha3}; //ISP 控制 8'd244: cfg_data <= {16'h3b07,8'h0a}; //帧曝光模式 //彩条测试使能 8'd245: cfg_data <= {16'h503d,8'h00}; //8'h00:正常模式 8'h80:彩条显示 //测试闪光灯功能 8'd246: cfg_data <= {16'h3016,8'h02}; 8'd247: cfg_data <= {16'h301c,8'h02}; 8'd248: cfg_data <= {16'h3019,8'h02}; //打开闪光灯 8'd249: cfg_data <= {16'h3019,8'h00}; //关闭闪光灯 //只读存储器,防止在case中没有列举的情况,之前的寄存器被重复改写 default : cfg_data <= {16'h300a,8'h00}; //器件ID高8位 endcase end end endmodule
sccb时序与iic时序几乎一模一样,因此直接使用iic模块来进行摄像头寄存器的配置
`timescale 1ns / 1ns module i2c_ctrl #( parameter DEVICE_ADDR = 7'h3c , //i2c设备地址 parameter SYS_CLK_FREQ = 26'd50_000_000 , //输入系统时钟频率 parameter SCL_FREQ = 18'd250_000 //i2c设备scl时钟频率 ) ( input wire sys_clk , //输入系统时钟,25MHz input wire sys_rst_n , //输入复位信号,低电平有效 input wire wr_en , //输入写使能信号 input wire rd_en , //输入读使能信号 input wire i2c_start , //输入i2c触发信号 input wire addr_num , //输入i2c字节地址字节数 input wire [15:0] byte_addr , //输入i2c字节地址 input wire [7:0] wr_data , //输入i2c设备数据 output reg i2c_clk , //i2c驱动时钟 output reg i2c_end , //i2c一次读/写操作完成 output reg [7:0] rd_data , //输出i2c设备读取数据 output reg i2c_scl , //输出至i2c设备的串行时钟信号scl inout wire i2c_sda //输出至i2c设备的串行数据信号sda ); //************************************************************************// //******************** Parameter and Internal Signal *********************// //************************************************************************// // parameter define parameter CNT_CLK_MAX = (SYS_CLK_FREQ/SCL_FREQ) >> 2'd3 ; //cnt_clk计数器计数最大值 parameter CNT_START_MAX = 8'd100; //cnt_start计数器计数最大值 parameter IDLE = 4'd00, //初始状态 START_1 = 4'd01, //开始状态1 SEND_D_ADDR = 4'd02, //设备地址写入状态 + 控制写 ACK_1 = 4'd03, //应答状态1 SEND_B_ADDR_H = 4'd04, //字节地址高八位写入状态 ACK_2 = 4'd05, //应答状态2 SEND_B_ADDR_L = 4'd06, //字节地址低八位写入状态 ACK_3 = 4'd07, //应答状态3 WR_DATA = 4'd08, //写数据状态 ACK_4 = 4'd09, //应答状态4 START_2 = 4'd10, //开始状态2 SEND_RD_ADDR = 4'd11, //设备地址写入状态 + 控制读 ACK_5 = 4'd12, //应答状态5 RD_DATA = 4'd13, //读数据状态 N_ACK = 4'd14, //非应答状态 STOP = 4'd15; //结束状态 // wire define wire sda_in ; //sda输入数据寄存 wire sda_en ; //sda数据写入使能信号 // reg define reg [7:0] cnt_clk ; //系统时钟计数器,控制生成clk_i2c时钟信号 reg [3:0] state ; //状态机状态 reg cnt_i2c_clk_en ; //cnt_i2c_clk计数器使能信号 reg [1:0] cnt_i2c_clk ; //clk_i2c时钟计数器,控制生成cnt_bit信号 reg [2:0] cnt_bit ; //sda比特计数器 reg ack ; //应答信号 reg i2c_sda_reg ; //sda数据缓存 reg [7:0] rd_data_reg ; //自i2c设备读出数据 //************************************************************************// //******************************* Main Code ******************************// //************************************************************************// // cnt_clk:系统时钟计数器,控制生成clk_i2c时钟信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_clk <= 8'd0; else if(cnt_clk == CNT_CLK_MAX - 1'b1) cnt_clk <= 8'd0; else cnt_clk <= cnt_clk + 1'b1; // i2c_clk:i2c驱动时钟 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) i2c_clk <= 1'b1; else if(cnt_clk == CNT_CLK_MAX - 1'b1) i2c_clk <= ~i2c_clk; // cnt_i2c_clk_en:cnt_i2c_clk计数器使能信号 always@(posedge i2c_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_i2c_clk_en <= 1'b0; else if((state == STOP) && (cnt_bit == 3'd3) &&(cnt_i2c_clk == 3)) cnt_i2c_clk_en <= 1'b0; else if(i2c_start == 1'b1) cnt_i2c_clk_en <= 1'b1; // cnt_i2c_clk:i2c_clk时钟计数器,控制生成cnt_bit信号 always@(posedge i2c_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_i2c_clk <= 2'd0; else if(cnt_i2c_clk_en == 1'b1) cnt_i2c_clk <= cnt_i2c_clk + 1'b1; // cnt_bit:sda比特计数器 always@(posedge i2c_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_bit <= 3'd0; else if((state == IDLE) || (state == START_1) || (state == START_2) || (state == ACK_1) || (state == ACK_2) || (state == ACK_3) || (state == ACK_4) || (state == ACK_5) || (state == N_ACK)) cnt_bit <= 3'd0; else if((cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3)) cnt_bit <= 3'd0; else if((cnt_i2c_clk == 2'd3) && (state != IDLE)) cnt_bit <= cnt_bit + 1'b1; // state:状态机状态跳转 always@(posedge i2c_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) state <= IDLE; else case(state) IDLE: if(i2c_start == 1'b1) state <= START_1; else state <= state; START_1: if(cnt_i2c_clk == 3) state <= SEND_D_ADDR; else state <= state; SEND_D_ADDR: if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3)) state <= ACK_1; else state <= state; ACK_1: if((cnt_i2c_clk == 3) && (ack == 1'b0)) begin if(addr_num == 1'b1) state <= SEND_B_ADDR_H; else state <= SEND_B_ADDR_L; end else state <= state; SEND_B_ADDR_H: if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3)) state <= ACK_2; else state <= state; ACK_2: if((cnt_i2c_clk == 3) && (ack == 1'b0)) state <= SEND_B_ADDR_L; else state <= state; SEND_B_ADDR_L: if((cnt_bit == 3'd7) && (cnt_i2c_clk == 3)) state <= ACK_3; else state <= state; ACK_3: if((cnt_i2c_clk == 3) && (ack == 1'b0)) begin if(wr_en == 1'b1) state <= WR_DATA; else if(rd_en == 1'b1) state <= START_2; else state <= state; end else state <= state; WR_DATA: if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3)) state <= ACK_4; else state <= state; ACK_4: if((cnt_i2c_clk == 3) && (ack == 1'b0)) state <= STOP; else state <= state; START_2: if(cnt_i2c_clk == 3) state <= SEND_RD_ADDR; else state <= state; SEND_RD_ADDR: if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3)) state <= ACK_5; else state <= state; ACK_5: if((cnt_i2c_clk == 3) && (ack == 1'b0)) state <= RD_DATA; else state <= state; RD_DATA: if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3)) state <= N_ACK; else state <= state; N_ACK: if(cnt_i2c_clk == 3) state <= STOP; else state <= state; STOP: if((cnt_bit == 3'd3) &&(cnt_i2c_clk == 3)) state <= IDLE; else state <= state; default: state <= IDLE; endcase // ack:应答信号 always@(*) case (state) IDLE,START_1,SEND_D_ADDR,SEND_B_ADDR_H,SEND_B_ADDR_L, WR_DATA,START_2,SEND_RD_ADDR,RD_DATA,N_ACK: ack <= 1'b1; ACK_1,ACK_2,ACK_3,ACK_4,ACK_5: if(cnt_i2c_clk == 2'd0) ack <= sda_in ; else ack <= ack; default: ack <= 1'b1; endcase // i2c_scl:输出至i2c设备的串行时钟信号scl always@(*) case (state) IDLE: i2c_scl <= 1'b1; START_1: if(cnt_i2c_clk == 2'd3) i2c_scl <= 1'b0; else i2c_scl <= 1'b1; SEND_D_ADDR,ACK_1,SEND_B_ADDR_H,ACK_2,SEND_B_ADDR_L, ACK_3,WR_DATA,ACK_4,START_2,SEND_RD_ADDR,ACK_5,RD_DATA,N_ACK: if((cnt_i2c_clk == 2'd1) || (cnt_i2c_clk == 2'd2)) i2c_scl <= 1'b1; else i2c_scl <= 1'b0; STOP: if((cnt_bit == 3'd0) &&(cnt_i2c_clk == 2'd0)) i2c_scl <= 1'b0; else i2c_scl <= 1'b1; default: i2c_scl <= 1'b1; endcase // i2c_sda_reg:sda数据缓存 always@(*) case (state) IDLE: begin i2c_sda_reg <= 1'b1; rd_data_reg <= 8'd0; end START_1: if(cnt_i2c_clk <= 2'd0) i2c_sda_reg <= 1'b1; else i2c_sda_reg <= 1'b0; SEND_D_ADDR: if(cnt_bit <= 3'd6) i2c_sda_reg <= DEVICE_ADDR[6 - cnt_bit]; else i2c_sda_reg <= 1'b0; ACK_1: i2c_sda_reg <= 1'b1; SEND_B_ADDR_H: i2c_sda_reg <= byte_addr[15 - cnt_bit]; ACK_2: i2c_sda_reg <= 1'b1; SEND_B_ADDR_L: i2c_sda_reg <= byte_addr[7 - cnt_bit]; ACK_3: i2c_sda_reg <= 1'b1; WR_DATA: i2c_sda_reg <= wr_data[7 - cnt_bit]; ACK_4: i2c_sda_reg <= 1'b1; START_2: if(cnt_i2c_clk <= 2'd1) i2c_sda_reg <= 1'b1; else i2c_sda_reg <= 1'b0; SEND_RD_ADDR: if(cnt_bit <= 3'd6) i2c_sda_reg <= DEVICE_ADDR[6 - cnt_bit]; else i2c_sda_reg <= 1'b1; ACK_5: i2c_sda_reg <= 1'b1; RD_DATA: if(cnt_i2c_clk == 2'd2) rd_data_reg[7 - cnt_bit] <= sda_in; else rd_data_reg <= rd_data_reg; N_ACK: i2c_sda_reg <= 1'b1; STOP: if((cnt_bit == 3'd0) && (cnt_i2c_clk < 2'd3)) i2c_sda_reg <= 1'b0; else i2c_sda_reg <= 1'b1; default: begin i2c_sda_reg <= 1'b1; rd_data_reg <= rd_data_reg; end endcase // rd_data:自i2c设备读出数据 always@(posedge i2c_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) rd_data <= 8'd0; else if((state == RD_DATA) && (cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3)) rd_data <= rd_data_reg; // i2c_end:一次读/写结束信号 always@(posedge i2c_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) i2c_end <= 1'b0; else if((state == STOP) && (cnt_bit == 3'd3) &&(cnt_i2c_clk == 3)) i2c_end <= 1'b1; else i2c_end <= 1'b0; // sda_in:sda输入数据寄存 assign sda_in = i2c_sda; // sda_en:sda数据写入使能信号 assign sda_en = ((state == RD_DATA) || (state == ACK_1) || (state == ACK_2) || (state == ACK_3) || (state == ACK_4) || (state == ACK_5)) ? 1'b0 : 1'b1; // i2c_sda:输出至i2c设备的串行数据信号sda assign i2c_sda = (sda_en == 1'b1) ? i2c_sda_reg : 1'bz; endmodule
`timescale 1ns / 1ns // // Company: // Engineer: // // Create Date: 2024/03/21 17:51:47 // Design Name: // Module Name: ov5640_top // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // // module ov5640_top#( parameter PIC_CNT_MAX = 4'd10 , //舍弃前10帧不稳定图像数据 parameter SLAVE_ADDR = 7'h3c , // 器件地址(SLAVE_ADDR) parameter BIT_CTRL = 1'b1 , // 字地址位控制参数(16b/8b) parameter CLK_FREQ = 26'd25_000_000, // i2c_dri 模块的驱动时钟频率(CLK_FREQ) parameter I2C_FREQ = 18'd250_000 , // I2C 的 SCL 时钟频率 parameter CFG_NUM = 9'd256 , // 需配置的寄存器个数 parameter CNT_WAIT_MAX = 15'd20000 //配置寄存最大等待时间,超过跳过该寄存器配置 ) ( input wire sys_clk , //50Mhz input wire sys_rst_n , input wire ov5640_pclk , //摄像头像素时钟 input wire ov5640_vsync , //摄像头行同步信号 input wire ov5640_href , //摄像头场同步信号 input wire sys_init_done , //系统初始化完成(DDR3 SDRAM + 摄像头) input wire [ 7:0] ov5640_data , //摄像头图像数据 output wire ov5640_wr_en , //图像数据有效使能信号 output wire [15:0] ov5640_data_out , //图像数据 output wire cfg_done , output wire sccb_scl , output wire sccb_sda ); //wire define wire cfg_one_end ; wire cfg_start ; wire [23:0] cfg_data ; wire cfg_clk ; i2c_ctrl #( .DEVICE_ADDR (SLAVE_ADDR ), //i2c设备地址 .SYS_CLK_FREQ (CLK_FREQ ), //输入系统时钟频率 .SCL_FREQ (I2C_FREQ ) //i2c设备scl时钟频率 ) i2c_ctrl_inst ( .sys_clk (sys_clk ), //输入25MHz .sys_rst_n (sys_rst_n ), //输入复位信号,低电平有效 .wr_en (1'b1 ), //输入写使能信号 .rd_en (1'b0 ), //输入读使能信号 .i2c_start (cfg_start ), //输入i2c触发信号 .addr_num (BIT_CTRL ), //输入i2c字节地址字节数 .byte_addr (cfg_data[23:8]), //输入i2c字节地址 .wr_data (cfg_data[7:0] ), //输入i2c设备数据. .i2c_clk (cfg_clk ), //i2c驱动时钟 .i2c_end (cfg_one_end ), //i2c一次读/写操作完成 .rd_data ( ), //输出i2c设备读取数据 .i2c_scl (sccb_scl ), //输出至i2c设备的串行时钟信号scl .i2c_sda (sccb_sda ) //输出至i2c设备的串行数据信号sda ); OV5640_data #( .PIC_CNT_MAX (PIC_CNT_MAX) //舍弃前10帧不稳定图像数据 ) OV5640_data_inst ( .ov5640_pclk (ov5640_pclk ), //摄像头工作时钟 25MHz .sys_rst_n (sys_rst_n & sys_init_done), //复位信号 .ov5640_vsync (ov5640_vsync ), //场同步信号 .ov5640_href (ov5640_href ), //行同步信号 .ov5640_data (ov5640_data ), //图像信息 .ov5640_wr_en (ov5640_wr_en ), //像素数据写入DDR3使能 .ov5640_data_out (ov5640_data_out) //写入DDR3使能的像素数据 ); ov5640_cfg#( .CFG_NUM (CFG_NUM ), //需配置的寄存器个数 .CNT_WAIT_MAX (CNT_WAIT_MAX) //配置寄存最大等待时间,超过跳过该寄存器配置 ) ov5640_cfg_inst ( .sys_clk (cfg_clk ), //时钟,IIC时钟 .sys_rst_n (sys_rst_n ), //复位 .cfg_one_end (cfg_one_end), //一个寄存器配置完成,IIC传来 .cfg_start (cfg_start ), //单个寄存器配置信号 .cfg_data (cfg_data ), //寄存器地址+数据 .cfg_done (cfg_done ) //寄存器配置完成 ); endmodule
摄像头各个模块例化
最后为了实现摄像头在LCD屏幕上的显示,又增加了DDR3模块与LCD显示模块。将摄像头数据缓存至ddr3之中,然后显示屏在读取。
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2024/03/23 18:48:44 // Design Name: // Module Name: ov5640_ddr_lcd // Project Name: // Target Devices: // Tool Versions: // Description: // ov5640摄像头采集数据,存储到DDR3中(带乒乓操作),然后显示到LCD上 // Dependencies: // 2116815480@qq.com // https://www.cnblogs.com/fangrunze // Revision: // Revision 0.01 - File Created // Additional Comments: // // module ov5640_ddr_lcd#( parameter SLAVE_ADDR = 7'h3c , // ov5640器件地址(SLAVE_ADDR) parameter BIT_CTRL = 1'b1 , // 字地址位控制参数(16b/8b) parameter CLK_FREQ = 26'd50_000_000, // i2c_dri 模块的驱动时钟频率(CLK_FREQ) parameter I2C_FREQ = 18'd250_000 , // I2C 的 SCL 时钟频率 parameter CFG_NUM = 9'd250 , // 需配置的寄存器个数 parameter CNT_WAIT_MAX = 15'd20000 , //配置ov5640寄存器等待时间 parameter PIC_CNT_MAX = 4'd10 , //舍弃前10帧不稳定图像数据 parameter H_PIC = 11'd1024 , //图片长度 parameter W_PIC = 10'd600 , //图片宽度 parameter PIC_SIZE = 20'd614400 , //图片像素个数 parameter baby_blue = 24'hADD8E6 //空余位置图片颜色 浅蓝色 ) ( input wire sys_clk, input wire sys_rst_n, //OV5640 input wire ov5640_pclk , //摄像头像素时钟 input wire ov5640_vsync , //摄像头行同步信号 input wire ov5640_href , //摄像头场同步信号 input wire [ 7:0] ov5640_data , //摄像头图像数据 output wire ov5640_rst_n , //摄像头复位 output wire ov5640_pwdn , //摄像头电源控制 output wire sccb_scl , //sccb_scl output wire sccb_sda , //sccb_sda //RGB-LCD 接口 output wire lcd_de , //LCD 数据使能信号 output wire lcd_hs , //LCD 行同步信号 output wire lcd_vs , //LCD 场同步信号 output wire lcd_bl , //LCD 背光控制信号 output wire lcd_clk , //LCD 像素时钟 output wire lcd_rst , //LCD 复位 output wire [23:0] lcd_rgb , //LCD RGB数据 //DDR3接口 inout wire [15:0] ddr3_dq , //DDR3 数据 inout wire [1:0] ddr3_dqs_n , //DDR3 dqs负 inout wire [1:0] ddr3_dqs_p , //DDR3 dqs正 output wire [13:0] ddr3_addr , //DDR3 地址 output wire [2:0] ddr3_ba , //DDR3 banck 选择 output wire ddr3_ras_n , //DDR3 行选择 output wire ddr3_cas_n , //DDR3 列选择 output wire ddr3_we_n , //DDR3 读写选择 output wire ddr3_reset_n , //DDR3 复位 output wire [0:0] ddr3_ck_p , //DDR3 时钟正 output wire [0:0] ddr3_ck_n , //DDR3 时钟负 output wire [0:0] ddr3_cke , //DDR3 时钟使能 output wire [0:0] ddr3_cs_n , //DDR3 片选 output wire [1:0] ddr3_dm , //DDR3_dm output wire [0:0] ddr3_odt //DDR3_odt ); //wire define============================================================== wire locked ; //锁相环锁定 wire rst_n ; //系统复位 wire clk_50m ; //ov5640输入时钟 50Mhz wire sys_init_done ; //系统初始化完成(DDR3 SDRAM + 摄像头) wire ov5640_wr_en ; //图像数据有效使能信号 wire [15:0] ov5640_data_out ; //ov5640输出的图像数据 wire cfg_done ; //ov5640寄存器配置完成 wire ddr3_clk ; //DDR3时钟 wire data_req ; //ddr3读使能 wire [15:0] rd_data ; //FIFO读数据 wire calib_done ; //DDR3 控制器初始化完成标,高有效 assign sys_init_done = (calib_done)&(cfg_done); //系统初始化完成(DDR3 SDRAM + 摄像头) assign rst_n = sys_rst_n & calib_done & locked ; //系统复位 //ov5640_rst_n:摄像头复位,固定高电平 assign ov5640_rst_n = 1'b1; assign ov5640_pwdn = 1'b0; //========================================================================= //===时钟模块 //========================================================================= clk_gen clk_gen_inst ( .clk_in1 (sys_clk ) , .reset (~sys_rst_n ) , .clk_out1 (clk_50m ) , //50Mhz .clk_out2 (ddr3_clk ) , //200Mhz .locked (locked ) ); //========================================================================= //===ov5640模块 //========================================================================= ov5640_top#( .SLAVE_ADDR (SLAVE_ADDR ), // 器件地址 .BIT_CTRL (BIT_CTRL ), // 字地址位控制参数 .CLK_FREQ (CLK_FREQ ), // i2c_dri 模块的驱动时钟频率 .I2C_FREQ (I2C_FREQ ), // I2C 的 SCL 时钟频率 .CFG_NUM (CFG_NUM ), // 需配置的寄存器个数 .CNT_WAIT_MAX (CNT_WAIT_MAX ) //配置ov5640寄存器等待时间 ) ov5640_top_inst ( .sys_clk (clk_50m ) , //25Mhz .sys_rst_n (rst_n ) , .ov5640_pclk (ov5640_pclk ) , //摄像头像素时钟 .ov5640_vsync (ov5640_vsync ) , //摄像头行同步信号 .ov5640_href (ov5640_href ) , //摄像头场同步信号 .sys_init_done (sys_init_done ) , //系统初始化完成(DDR3 SDRAM + 摄像头) .ov5640_data (ov5640_data ) , //摄像头图像数据 .ov5640_wr_en (ov5640_wr_en ) , //图像数据有效使能信号 .ov5640_data_out (ov5640_data_out) , //图像数据 .cfg_done (cfg_done ) , //ov5640寄存器配置完成 .sccb_scl (sccb_scl ) , //sccb_scl .sccb_sda (sccb_sda ) //sccb_sda ); //========================================================================= //==DDR读写控制部分 //========================================================================= ddr3_ctrl_2port #( .FIFO_DW (16 ), //模块写 FIFO 的写数据和读 FIFO 的读数据位宽 .PINGPANG (1 ), //PingPang 模式选择,0:PingPang 模式,1:PingPang 模式 .WR_BYTE_ADDR_BEGIN (0 ), //写数据存储空间的起始地址, .WR_BYTE_ADDR_END ( PIC_SIZE*2 ), //写数据存储空间的终止地址,一个地址对应一个字节 .RD_BYTE_ADDR_BEGIN (0 ), //取数据存储空间的起始地址, .RD_BYTE_ADDR_END ( PIC_SIZE*2 ) //取数据存储空间的终止地址, ) inst_ddr3_ctrl_2port( .ddr3_clk200m (ddr3_clk ), //DDR3时钟 .ddr3_rst_n (sys_rst_n&locked ), //DDR3复位 .ddr3_init_done (calib_done ), //DDR3 控制器初始化完成标,高有效 .wrfifo_clr ( ~(sys_rst_n&locked) ), //写 FIFO 清空控制信号,给高电平 .wrfifo_clk (ov5640_pclk ), //FIFO写时钟 .wrfifo_wren (ov5640_wr_en ), //fifo写使能 .wrfifo_din (ov5640_data_out ), //fifo写数据 .wrfifo_full ( ), //写 FIFO 的写满标识信号 .wrfifo_wr_cnt ( ), //写 FIFO 的写数据计数 .rdfifo_clr ( ~(sys_rst_n&locked) ), //读 FIFO 清空控制信号,给高电平 .rdfifo_clk (lcd_clk ), //FIFO读时钟 .rdfifo_rden (data_req ), //FIFO读使能 .rdfifo_dout (rd_data ), //FIFO读数据 .rdfifo_empty ( ), //读FIFO空标志 .rdfifo_rd_cnt ( ), //读FIFO读数据计数 //ddr3接口 .ddr3_dq (ddr3_dq ), //DDR3 数据 .ddr3_dqs_n (ddr3_dqs_n ), //DDR3 dqs负 .ddr3_dqs_p (ddr3_dqs_p ), //DDR3 dqs正 .ddr3_addr (ddr3_addr ), //DDR3 地址 .ddr3_ba (ddr3_ba ), //DDR3 banck 选择 .ddr3_ras_n (ddr3_ras_n ), //DDR3 行选择 .ddr3_cas_n (ddr3_cas_n ), //DDR3 列选择 .ddr3_we_n (ddr3_we_n ), //DDR3 读写选择 .ddr3_reset_n (ddr3_reset_n ), //DDR3 复位 .ddr3_ck_p (ddr3_ck_p ), //DDR3 时钟正 .ddr3_ck_n (ddr3_ck_n ), //DDR3 时钟负 .ddr3_cke (ddr3_cke ), //DDR3 时钟使能 .ddr3_cs_n (ddr3_cs_n ), //DDR3 片选 .ddr3_dm (ddr3_dm ), //DDR3_dm .ddr3_odt (ddr3_odt ) //DDR3_odt ); //========================================================================= //===LCD控制部分 //========================================================================= top_rgb#( .H_PIC (H_PIC ), //图片长度 .W_PIC (W_PIC ), //图片宽度 .PIC_SIZE (PIC_SIZE ), //图片像素个数 .baby_blue (baby_blue) //空余位置图片颜色 浅蓝色 ) top_rgb_inst ( .sys_clk (clk_50m ), //系统时钟 .sys_rst_n (rst_n &sys_init_done ), //系统复位 .lcd_pclk (clk_50m ), //lcd 时钟 .pi_data (rd_data ), //FIFO读数据 .data_req (data_req ), //ddr3读使能 .lcd_de (lcd_de ), //LCD 数据使能信号 .lcd_hs (lcd_hs ), //LCD 行同步信号 .lcd_vs (lcd_vs ), //LCD 场同步信号 .lcd_bl (lcd_bl ), //LCD 背光控制信号 .lcd_clk (lcd_clk ), //LCD 像素时钟 .lcd_rst (lcd_rst ), //LCD 复位 .lcd_rgb (lcd_rgb ) //LCD RGB数据 ); endmodule
顶层模块例化了3个小模块,其中时钟模块产生的50MHz时钟,用于系统以及摄像头使用,200MHz则提供给DDR3使用。
注意:这里的ddr3使用的是小梅哥的程序,但是经过修改,支持乒乓操作,如果不使用乒乓操作,则会使得的显示的摄像头图像有明显的撕裂感!!!!!!!!!(具体修改见代码)
其他模块就不再介绍,代码,下边上传的工程代码之中,会有详细的代码注释。
使用屏幕:1024*600 屏幕
使用开发板:正点原子达芬奇FPGA开发板
链接: https://pan.baidu.com/s/1N1QqlSDH85mqPFGE5NXXPg?pwd=uwfd 提取码: uwfd 复制这段内容后打开百度网盘手机App,操作更方便哦
[1] 正点原子. 达芬奇之FPGA开发指南
[2] 野火. FPGA+Verilog开发实战指南——基于Xilinx+Spartan6
[3] 小梅哥.小梅哥Xilinx ACX720 FPGA开发板自学教程V3.1.2
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。