手把手教你写Ov7725摄像头数据采集模块(带Verilog代码)

article/2025/9/13 20:47:25

        上一节咱们学习了OV7725的VGA传输协议,对于数据传输的特点有了初步了解,这篇博客主要目的在于使用Verilog实现一个OV7725摄像头的数据采集模块,与咱们这个模块对接的是后一级的SDRAM存储器,其将作为数据的缓存仓库,以便后续对图像进行处理。

        学习目标

  • 搭建一个提取摄像头数据的模块与SDRAM对接
  • 学习分析和设计数字模块的方法

信息汇总 

  1. 前10帧图像不稳定,需要丢弃。
  2. Ov7725输出管脚有四个:                     其中PCLK的时钟频率为24MHz。
  3. 帧有效 VSYNC为低电平0,行有效 HREF为高电平1,PCLK上升沿数据有效。
  4. 一个PCLK上升沿传递一个字节,一个像素点数据需要两个PCLK周期。

 问题分析

        首先我们要确定模块的接口数目,除了上面提到的四个输入管脚,我们需要复位信号,以及输出一个16bit的数据,并且由于我们的后级连接的是SDRAM模块,因此我们还需要一个写信号标志位,高电平有效,并且与输出的数据同步,占时两个PCLK周期。

module ov_data(
input wire Pclk, //像素时钟信号
input wire sys_reset_n, //复位信号
input wire Href, // 行有效信号
input wire Vsync, // 场同步信号
input wire [7:0] ov_data_in, // 输入信号8bitoutput wire data_wr_en, // 输出写有效信号
output wire [15:0] ov_data_out // 输出数据16bit
);endmodule

        下一步自然而然我们就要分析如何采集输入数据信号了。我们很容易想到,数据有效的时候 Vsync 为低电平0,Href为高电平1,然后使用一个Pclk敏感的always内嵌一个if判断即可取到有效数据。

        这就是我们的最初的骨架,但是还有很多不足之处,首先就是前10帧的无效数据该怎么进行滤除?

        思路其实很明确,我们需要一个计数器,每一帧信号计数器加一,从0到9,计数10帧,并且我们还需要一个指示位,来指示帧是否有效。好了,知道了大体的电路组成,咱们就要面对任何计数器都必须要解决的困难:如何确定计数条件?

        这就需要我们分析信号的特点了!

        从上图可以看出,两个高电平之间的低电平是数据,我们可以把VSYNC信号分割开来可以发现,每个周期包含了一个高电平和一个低电平。

        有些同学可能此时会产生一个大胆的想法:就是使用VSYNC作为触发器的触发信号,诶,这样每个上升沿计数一次不就可以计数帧数了吗?其实这种想法特别好,但是呢会有些问题,主要的原因就是VSYNC有可能不稳定。大家注意到没有,一般always的时序电路的触发电平往往是时钟信号,主要原因就是时钟信号比较稳定,振荡较少或者没有,这样触发器往往会比较稳定,不会乱跳。 

        因此我们首选的触发信号还是PCLK信号,这里博主提供一种思路,就是每次PCLK上升沿来临时存储当前的VSYNC,当前一次的值为0,并且后一次的值为1时,则判定为一帧。

        代码补充如下:

module ov_data(
input wire Pclk, //像素时钟信号
input wire sys_reset_n, //复位信号
input wire Href, // 行有效信号
input wire Vsync, // 场同步信号
input wire [7:0] ov_data_in, // 输入信号8bitoutput wire data_wr_en, // 输出写有效信号
output wire [15:0] ov_data_out // 输出数据16bit
);reg [3:0] v_cnt; //帧计数
reg v_temp; //帧存储
reg v_valid; //帧有效指示位always@(posedge Pclk or negedge sys_reset_n)begin
if(sys_reset_n == 1'b0)begin
v_temp <= 1'b0;
v_cnt <= 4'd0;
v_valid <= 1'b0;
end else if(((v_temp == 1'b0)&&(Vsync == 1'b1))(v_cnt == 4'd10))begin// 计数器计数完前10个周期,正准备计数第11个周期时v_cnt <= 4'd0;v_valid <= 1'b1;
end
else if((v_temp == 1'b0)&&(Vsync == 1'b1))begin//如果前一个周期为0,后一个周期为1
v_cnt <= v_cnt+4'd1; //帧计数加1
v_temp <= Vsync;
v_valid <= v_valid;
end else begin //否则保持不变
v_cnt <= v_cnt;
v_temp <= Vsync;
v_valid <= v_valid;
end
endendmodule

总结一下:

  • 计数器的要素:1、计数条件  2、计满条件 3、时钟选择

        好了,一顿操作下,我们解决了前10帧丢弃的问题,现在摆在我们面前的另一个问题是如何把两个八位的数据拼接成16位的数据,并且输出。

        我们细细琢磨一下:我们肯定要有一个拼接的操作,而且,这个拼接的操作是要两个数据拼接一次,所以,拼接操作的时钟就得是2个PCLK,并且后一个PCLK要执行将前一个PCLK的数据与当前PCLK的数据拼接的任务。因此我们明确,一定会有一个数据暂存寄存器,暂存前一个PCLK的数据,并且我们要对PCLK进行二分频。

        这时不少同学肯定会想,诶,二分频,就用计数器做,然后if判断是第一个PCLK还是第二个PCLK,分别执行不同的操作就可以啦!

        思路完全正确,但是二分频可以更加简单,如果我们采用计数的方法,那么这个值肯定是从0到1,这样计数其实就相当于进行一次翻转操作,这样完全可以简化计数器带来的用if语句判断计数是否溢出的操作。因此我们的思路就形成了:PCLK的上升沿,将一个计数器的值翻转,再判断如果是0则存储当前数据,并且最终输出的数据保持不变,如果是1,则进行拼接输出操作。

        补充完整的代码如下:

/*
By WWD,2022/9/4
转载请注明出处
*/module ov_data(
input wire Pclk, //像素时钟信号
input wire sys_reset_n, //复位信号
input wire Href, // 行有效信号
input wire Vsync, // 场同步信号
input wire [7:0] ov_data_in, // 输入信号8bitoutput wire data_wr_en, // 输出写有效信号
output wire [15:0] ov_data_out // 输出数据16bit
);reg [3:0] v_cnt; //帧计数
reg v_temp; //帧存储
reg v_valid; //帧有效指示位reg [7:0] ov_data_reg;//数据暂存寄存器
reg data_cnt;//数据输出计数--2分频
reg wr_en_reg;//读写标志寄存位
reg [15:0] ov_data_out_reg;always@(posedge Pclk or negedge sys_reset_n)begin
if(sys_reset_n == 1'b0)begin
v_temp <= 1'b0;
v_cnt <= 4'd0;
v_valid <= 1'b0;
end else if((v_temp == 1'b0)&&(Vsync == 1'b1)&&(v_cnt == 4'd10))begin// 计数器计数完前10个周期,正准备计数第11个周期时v_cnt <= 4'd0;v_valid <= 1'b1;
end
else if((v_temp == 1'b0)&&(Vsync == 1'b1))begin//如果前一个周期为0,后一个周期为1
v_cnt <= v_cnt+4'd1; //帧计数加1
v_temp <= Vsync;
v_valid <= v_valid;
end else begin //否则保持不变
v_cnt <= v_cnt;
v_temp <= Vsync;
v_valid <= v_valid;
end
endalways@(posedge Pclk or negedge sys_reset_n)begin
if(sys_reset_n == 1'b0)beginov_data_reg <= 8'b0;data_cnt <= 1'b0;ov_data_out_reg <= 16'b0;wr_en_reg <= 1'b0;
end else if((Href == 1'b0)&&(v_valid == 1'b1))begin//Href有效时ov_data_reg <= ov_data_in;data_cnt <= ~data_cnt;if(data_cnt == 1'b0)beginov_data_out_reg <= ov_data_out_reg; //还未到时候,保持住数据wr_en_reg <= 1'b0; //读写标志保持0end else beginov_data_out_reg <= {ov_data_reg,ov_data_in}; //到时候了,开始拼接!wr_en_reg <= 1'b1; //读写标志置1end
end else begin //Href无效时ov_data_reg <= 8'b0;data_cnt <= 1'b0;wr_en_reg <= 1'b0;ov_data_out_reg <= ov_data_out_reg;
end
endassign data_wr_en = wr_en_reg; 
assign ov_data_out = ov_data_out_reg;endmodule

仿真测试

TB文件:

        我们使用野火电子的TB文件进行测试(模块接口兼容)

`timescale  1ns/1ns// Author        : EmbedFire
// Create Date   : 2019/09/25
// Module Name   : tb_ov7725_data
// Project Name  : ov7725_vga_640x480
// Target Devices: Altera EP4CE10F17C8N
// Tool Versions : Quartus 13.0
// Description   : OV7725摄像头图像数据采集模块仿真文件
// 
// Revision      : V1.0
// Additional Comments:
// 
// 实验平台: 野火_征途Pro_FPGA开发板
// 公司    : http://www.embedfire.com
// 论坛    : http://www.firebbs.cn
// 淘宝    : https://fire-stm32.taobao.commodule  tb_ov_data();//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter   H_VALID   =   10'd640 ,   //行有效数据H_TOTAL   =   10'd784 ;   //行扫描周期
parameter   V_SYNC    =   10'd4   ,   //场同步V_BACK    =   10'd18  ,   //场时序后沿V_VALID   =   10'd480 ,   //场有效数据V_FRONT   =   10'd8   ,   //场时序前沿V_TOTAL   =   10'd510 ;   //场扫描周期//wire  define
wire            ov7725_wr_en    ;   //有效图像使能信号
wire    [15:0]  ov7725_data_out ;   //有效图像数据
wire            ov7725_href     ;   //行同步信号
wire            ov7725_vsync    ;   //场同步信号//reg   define
reg             sys_clk         ;   //模拟时钟信号
reg             sys_rst_n       ;   //模拟复位信号
reg     [7:0]   ov7725_data     ;   //模拟摄像头采集图像数据
reg     [11:0]  cnt_h           ;   //行同步计数器
reg     [9:0]   cnt_v           ;   //场同步计数器//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//时钟、复位信号
initialbeginsys_clk     =   1'b1  ;sys_rst_n   <=  1'b0  ;#200sys_rst_n   <=  1'b1  ;endalways  #20 sys_clk = ~sys_clk;//cnt_h:行同步信号计数器
always@(posedge sys_clk or  negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_h   <=  12'd0   ;else    if(cnt_h == ((H_TOTAL * 2) - 1'b1))cnt_h   <=  12'd0   ;elsecnt_h   <=  cnt_h + 1'd1   ;//ov7725_href:行同步信号
assign  ov7725_href = (((cnt_h >= 0)&& (cnt_h <= ((H_VALID * 2) - 1'b1)))&& ((cnt_v >= (V_SYNC + V_BACK))&& (cnt_v <= (V_SYNC + V_BACK + V_VALID - 1'b1))))? 1'b1 : 1'b0  ;//cnt_v:场同步信号计数器
always@(posedge sys_clk or  negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_v   <=  10'd0 ;else    if((cnt_v == (V_TOTAL - 1'b1))&& (cnt_h == ((H_TOTAL * 2) - 1'b1)))cnt_v   <=  10'd0 ;else    if(cnt_h == ((H_TOTAL * 2) - 1'b1))cnt_v   <=  cnt_v + 1'd1 ;elsecnt_v   <=  cnt_v ;//vsync:场同步信号
assign  ov7725_vsync = (cnt_v  <= (V_SYNC - 1'b1)) ? 1'b1 : 1'b0  ;//ov7725_data:模拟摄像头采集图像数据
always@(posedge sys_clk or  negedge sys_rst_n)if(sys_rst_n == 1'b0)ov7725_data <=  8'd0;else    if(ov7725_href == 1'b1)ov7725_data <=  ov7725_data + 1'b1;elseov7725_data <=  8'd0;//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------- ov7725_data_inst -------------
ov_data ov7725_data_inst
(.sys_reset_n          (sys_rst_n      ),  //复位信号.Pclk                 (sys_clk        ),  //摄像头像素时钟.Href                 (ov7725_href    ),  //摄像头行同步信号.Vsync                (ov7725_vsync   ),  //摄像头场同步信号.ov_data_in           (ov7725_data    ),  //摄像头图像数据.data_wr_en           (ov7725_wr_en   ),  //图像数据有效使能信号.ov_data_out          (ov7725_data_out)   //图像数据
);endmodule

波形验证

 我们首先可以看到,Valid信号省略了前10帧信号,完全正确。

 起始段的拼接与写使能信号也完全正确。

 结束段也符合要求。

        至此,本模块仿真完美实现!

总结

        本节文章带领大家体验了一把如何进行数字模块设计的过程,其实无外乎就是不断地分析,不断地将大的模块分解为小的模块的过程,作为RTL级的设计,无外乎围绕的就是 触发器,选择器,计数器,等等子模块,体现在Verilog上就是 always块,if语句,赋值语句等等,来实现最终的功能。

        我们要培养的就是分析问题的能力,要有敏锐的判断力,去判断子模块的选择,并且大脑中要有电路,要会“自我仿真”,“自我纠错”。

        下一节,我们将介绍如何使用SDRAM。


http://chatgpt.dhexx.cn/article/fdEvaLTZ.shtml

相关文章

STM32+ov7725+ESP8266实现无线图传-完成上位机图像显示

一、需求 stm32f407探索者开发板和STM32F103ZET6战舰开发板。接正点原子ov5640、OV7725、OV2640摄像头,通过esp8266Wi-Fi模块(透传模式)将摄像头采集到的rgb565格式图片通过tcp/ip协议上传到上位机显示。 二、设计思路 【1】使用QT开发上位机,建立TCP服务器,接收ESP8266…

FPGA 20个例程篇:19.OV7725摄像头实时采集送HDMI显示(二)

第七章 实战项目提升&#xff0c;完善简历 19.OV7725摄像头实时采集送HDMI显示&#xff08;二&#xff09; 在正式介绍OV7725 CMOS Sensor视频采集前&#xff0c;首先需要去详细说明OV7725的寄存器配置接口&#xff0c;这里有OmniVision公司推出的官方手册 “OV7725_software_…

OV7725寄存器配置

OV7725寄存器配置&#xff08;为了替换NT99141研究了很长一段时间&#xff09; 部分参考链接&#xff1a; OV7725 电器特性和时序图&#xff1a;https://www.cnblogs.com/raymon-tec/p/5087088.html OV7725摄像头的彩色图像采集原理与液晶显示(有必要了解框图)&#xff1a;htt…

基于STM32的OV7725摄像头拍照实验

平台&#xff1a;STM32ZET6&#xff08;核心板&#xff09;ST-LINK/V2SD卡USB串口线鹰眼OV7725摄像头&#xff08;注意&#xff0c;为了减少摄像头连线的麻烦&#xff0c;建议初学者选取单片机时选用带有摄像头接口的板子&#xff09; 工程介绍&#xff1a;需要移植FatFs文件系…

OV7725鹰眼摄像头

OV7725鹰眼摄像头如何使用&#xff1f; 目前的ov7725鹰眼摄像头&#xff0c;基本上用的都是山外的库&#xff0c;所以今天我们主要根据山外的库&#xff0c;基于k60芯片&#xff0c;给大家具体的讲解。 1.摄像头初始化 首先是摄像头的第一步就是初始化&#xff0c;这个我们直…

OV7725摄像头图像采集基础知识

目前FPGA用于图像采集 传输 处理 显示应用越来越多&#xff0c;主要原因是图像处理领域的火热以及FPGA强大的并行处理能力。本文以OV7725为例&#xff0c;对摄像头使用方面的基础知识做个小的总结&#xff0c;为后续做个铺垫。 XCLK:工作时钟输入&#xff0c;由主控器产生&…

FPGA--OV7725摄像头采集与VGA显示实验--1--OV7725使用与驱动协议

目录 前言 OV7725引脚及功能框图 参数指标 引脚 功能框图 SCCB时序及读写操作 SCCB时序特点 读写实现 OV7725寄存器常用配置参数 前言 摄像头采集是图像处理的第一步&#xff0c;本章节分为多部分&#xff0c;旨在让大家学会如何使用OV7725采集图像&#xff0c;并且…

【FPGA的基础快速入门22-------OV7725摄像头模块】

OV7725摄像头模块 OV7725是Omni Vision&#xff08;豪威科技&#xff09;公司生产的CMOS图像传感器&#xff0c;该传感器功耗低、可靠性高以及采集速率快&#xff0c;主要应用在玩具、安防监控、电脑多媒体等领域。 OV7725感光阵列达到640*480&#xff0c;能实现最快60fps VGA…

STM32 OV7725 传感器

目录 OV7725 传感器1、选择输出格式2、选择输出分辨率3、帧率调整4、夜间模式5、消除光带6、白平衡7、缺陷像素矫正8、黑电平矫正9、视频模式10、数字缩减 OV7725 函数1、光模式2、颜色饱和度3、亮度4、对比度5、特效 OV7725 设置参考 OV7725 传感器 OV7725摄像机™ 图像传感器…

OV7725摄像头之OV7725芯片

近日入手了一块正点原子家的OV7725摄像头模块&#xff0c;秉着小白尽可能学得透彻些的想法&#xff0c;选择了野火家的相同摄像头教学视频。链接如下&#xff1a;【单片机】野火STM32F103教学视频 (配套霸道/指南者/MINI)【全】(刘火良老师出品) (无字幕)_哔哩哔哩_bilibili 现…

二进制加法运算

两个二进制整数相加时&#xff0c;是位对位处理的&#xff0c;从最低的一对位&#xff08;右边&#xff09;开始&#xff0c;依序将每一对位进行加法运算。两个二进制数字相加&#xff0c;有四种结果&#xff0c;如下所示&#xff1a; 0 0 00 1 11 0 1 1 1 10 1 与 1 …

二进制加减法编程

1、二进制加法基本指令 &#xff08;1&#xff09;ADD指令 格式&#xff1a;ADD DST&#xff0c;SRC 该指令把源操作数&#xff08;SRC&#xff09;指向的数据与目的操作数&#xff08;DST&#xff09;相加后&#xff0c;将结果放到目的操作数&#xff08;DST&#xff09;中…

二进制乘除法运算原理

二进制乘除法原理 计算机所能完成的最基本操作是加减法和左右移。 虽然ISA中一般都有MUL类指令&#xff0c;但是这些经过译码之后最终的元操作还是加法和移位指令。 二进制乘法 假设不能使用乘除运算求ab的结果&#xff0c;当ab123时&#xff0c;最直接的方法是通过88个88相…

二进制加法计算

读计算机原理这本书的的时候涉及到二进制数的加法&#xff0c;个人做个直观的记录&#xff0c;防止遗忘。 计算时&#xff0c;先把两个二进制数对齐&#xff08;如同十进制一样&#xff0c; 从右向左&#xff09;11为10&#xff0c;此时向上一位进1,0写在本位&#xff08;如同十…

二进制加减乘除

最重要的,理解十进制的借位和进位.十进制中,由于一个循环是10,所以借1位,就相当于从高位借过来10,也就是常说的借1当10,同时,高位需要减去10(外在体现上是减去1,因为高位已经在高位了).反过来,进1,就等于高位加上10,但因为高位已经在高位了,所以去掉位数考虑,高位实际是加1,也就…

二进制加法

计算机中二进制相关运算 有符号二进制与无符号二进制数之间的区别有符号二进制加法运算讨论关于有符号二进制数算术的溢出问题&#xff08;重点&#xff09;无符号二进制运算 有符号二进制与无符号二进制数之间的区别 对于一个有符号二进制数来讲&#xff0c;最高位是该数的符…

二进制加减法计算

二进制加减法&#xff1a;原码、反码、补码 1.十进制下的计算1.模数2.补数 2.二进制数的存储1.计算机计数2.原码3.反码4.补码 3.二进制计算1.中位对称2.循环进位3.二进制减法推算4.结论 1.十进制下的计算 1.模数 假设下文【模】定义如下&#xff1a;某个可度量系统的度量范围…

二进制数的运算方法

1&#xff0e;二进制数的算术运算 二进制数的算术运算包括&#xff1a;加、减、乘、除四则运算&#xff0c;下面分别予以介绍。 &#xff08;1&#xff09;二进制数的加法 根据“逢二进一”规则&#xff0c;二进制数加法的法则为&#xff1a;0&#xff0b;0&#xff1d;00&…

简单计算二进制的加减法

二进制就是计算机技术中广泛采用的一种数制&#xff0c;由&#xff08;0&#xff0c;1&#xff09;组成。你可以广泛的认为&#xff0c;每一个 0&#xff0c;1 都&#xff08;存储在&#xff09;对应着一个比特位&#xff08;bit&#xff09;,而由这些由例如&#xff1a;010010…

二进制的加减乘除法全了看一下叭

看这里~ 前言一、二进制加法二、二进制减法三、二进制乘法四、二进制除法总结 前言 最近在学习通信网络中的CRC算法&#xff0c;所以积累一下二进制的各种算法&#xff0c;如果对您有用&#xff0c;记得点个赞喔&#xff01;谢谢啦。 一、二进制加法 对于二进制加法和十进制的…