上一节咱们学习了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。

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

  1. python代码示例图形-纯干货:手把手教你用Python做数据可视化(附代码)

    原标题:纯干货:手把手教你用Python做数据可视化(附代码) 导读:制作提供信息的可视化(有时称为绘图)是数据分析中的最重要任务之一.可视化可能是探索过程的一部分,例如,帮助识别异常值或所需的数据转 ...

  2. python画图代码大全-纯干货:手把手教你用Python做数据可视化(附代码)

    原标题:纯干货:手把手教你用Python做数据可视化(附代码) 导读:制作提供信息的可视化(有时称为绘图)是数据分析中的最重要任务之一.可视化可能是探索过程的一部分,例如,帮助识别异常值或所需的数据转 ...

  3. 手把手教你写一个生成对抗网络

    成对抗网络代码全解析, 详细代码解析(TensorFlow, numpy, matplotlib, scipy) 那么,什么是 GANs? 用 Ian Goodfellow 自己的话来说: " ...

  4. 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫

    系列教程 手把手教你写电商爬虫-第一课 找个软柿子捏捏 如果没有看过第一课的朋友,请先移步第一课,第一课讲了一些基础性的东西,通过软柿子"切糕王子"这个电商网站好好的练了一次手,相 ...

  5. php注册程序,[PHP初级]手把手教你写注册程序 1

    [PHP初级]手把手教你写注册程序 1 实例内容 在此教程,我们将通过写一个用户注册程序,学习以下内容: 数据的传输与获取 信息的验证 pdo方式操作数据库 事务处理 前台显示文件:index.php ...

  6. python k线合成_手把手教你写一个Python版的K线合成函数

    手把手教你写一个Python版的K线合成函数 在编写.使用策略时,经常会使用一些不常用的K线周期数据.然而交易所.数据源又没有提供这些周期的数据.只能通过使用已有周期的数据进行合成.合成算法已经有一个 ...

  7. 手把手教你写Linux I2C设备驱动

    手把手教你写Linux I2C设备驱动 标签:Linux 设备 驱动 详解 i2c 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http:/ ...

  8. 手把手教你写网站:Python WEB开发技术实战

    摘要:本文详细介绍了Python WEB开发的基础入门.以一个博客站点的开发为例讲解了基于Django框架开发WEB站点的全过程.通过本文的学习可以快速掌握基于Django的Python WEB的开发 ...

  9. hecto - 手把手教你写个 文本编辑器

    https://www.philippflenker.com/hecto/ 的阅读笔记. 我是学完[[Rust权威指南]] 之后才看的这个. 手把手教你写个 文本编辑器(1300+行代码). 作者是真 ...

  10. 手把手教你写一个Matlab App(一)

    对于传统工科的学生用的最多的编程软件应该就是matlab,其集成度高,计算能力强,容易上手,颇受大众青睐.今天挖的这个新坑,主要是分享用matlab app designer设计GUI界面的一些方法和 ...

最新文章

  1. Artistic Style在windows下的使用(C/C++)
  2. 为何需要将jupyter notebook转为脚本
  3. pringboot 单元测试 空指针_单元测试中的 FIRST 原则
  4. android sqlite 自增长序列号归0
  5. 第八篇、盒子模型和距中的设置方法
  6. Java虚拟机-内存分配策略
  7. 老板总说你的报表没价值?这个动态报表神器,还有很多人不知道
  8. 求C n m(从n个数中选m个数,有多少种组合?问题)暴力—递归——回归数学公式,三种方法,层层优化!
  9. mongodb的架构 副本集搭建
  10. OpenHarmony移植:XTS子系统之应用兼容性测试套件
  11. NoiseAsh Rule Tec All Collection for Mac - 无源均衡器
  12. cocos2d-x打飞机实例总结
  13. excel如何转化成word文件
  14. HCNE实验操作视频   迅雷下载
  15. hdu 2545 树上战争
  16. 金蝶KIS迷你版标准版年度结账
  17. (轉貼)《程序员》推荐C++ 图书三人谈 (C/C++)
  18. 一张照片生成3D头像!苹果新模型击败StyleGAN2,表情光线都能调,网友:要用于MR?...
  19. Google体系地图纠偏算法
  20. Unraid安装Transmission 超简单(2022/10/21)

热门文章

  1. android身高控件_Android 滑动选择身高体重控件——RulerView
  2. 阿里云服务器上海地域和杭州节点区别对比
  3. CUDA安装成功测试
  4. 力扣周赛 第280场 Java题解
  5. ShinyApp中的表格:第三天笔记
  6. 7zip打不开wim文件
  7. 汽车数据流分析常采用哪些方法?
  8. 钉钉机器人+Mac定时Launchctl
  9. Lua 源码学习笔记(2)字符串
  10. 基于位置的 AR 应用程序开发最完整指南