超级版主
 
- 积分
- 5979
- 金钱
- 5979
- 注册时间
- 2019-5-8
- 在线时间
- 1683 小时
|
本帖最后由 正点原子运营 于 2021-1-26 11:02 编辑
1)实验平台:正点原子超越者FPGA开发板
2)平台购买地址:https://item.taobao.com/item.htm?&id=631660290421
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz-chaoyuezhe.html
4)本章实例源码下载:
43_ov5640_hdmi_scale.rar
(14.39 MB, 下载次数: 29)
5)正点原子FPGA交流群:905624739点击加入:![]()
6)关注正点原子公众号,获取最新资料更新
![]()
![]()
第五十章基于OV5640摄像头HDMI缩放实验
图像缩放是一种常用的图像处理技术,其基本原理就是根据原图像的像素值通过一定的规则计算得到目标图像的像素值。常用的图像缩放算法有最近邻算法、双线性插值法和双三次插值法等。双线性插值即在两个方向分别进行一次线性插值,通过四个相邻像素插值得到待求像素。双线性插值法是最常见,最通用的算法之一,效果比最近邻插值法好。计算量较小,运算速度较快。图像连续性较好。双线性插值法效果要好于最近邻插值,只是计算量稍大一些,算法复杂些,程序运行时间也稍长些,但缩放后图像质量高,基本克服了最近邻插值灰度值不连续的特点,因为它考虑了待测采样点周围四个直接邻点对该采样点的相关性影响。在本章实验中将进行基于双线性插值法的图像缩放实验。
本章包括以下几个部分:
5050.1简介
50.2实验任务
50.3硬件设计
50.4程序设计
50.5下载验证
50.1简介
图像缩放的目的是将一种分辨率的图像变为另一种目标分辨率图像的处理方式,其核心就是图像插值算法,图像插值算法的好坏将直接影响图像的缩放质量,因此对图像插值算法的研究非常重要。
目前,常用的图像插值算法有最近邻算法、双线性插值法和双三次插值法等,下面将对这三种插值算法进行详细介绍。
(1). 最近邻算法
最近邻算法的插值基函数如下所示:
图 50.1.1 最近邻算法的插值基函数
最近邻算法实际上是简单的像素复制,待插值点的灰度值取与其距离最近的像素点的灰度值,如下图所示:
图 50.1.2 最近邻插值
待插值点映射到原图像坐标系坐标为(x,y),其周围2x2个像素点坐标分别为(i,j)、(i+1,j)、(i,j+1)和(i+1,j+1),令该2x2像素点的已知的灰度值分别为f(i,j)、f(i+1,j)、f(i,j+1)和f(i+1,j+1),令待插值点灰度值为f(x,y)。
图 50.1.3 最近邻插值坐标
最近邻算法是将待求像素在待插值图中的坐标位置进行四舍五入处理,对应坐标的像素值即为待求像素的值,其优点是最常见,最通用的算法之一,计算量很小,算法简单,因此运算速度较快,但它的缺点也很明显,经过最近邻算法缩放后的图像效果不好,放大图像时会出现严重的马赛克,缩小图像则会严重失真。它仅使用离待测采样点最近的像素的灰度值作为该采样点的灰度值,而没考虑其他相邻像素点的影响,因而重新采样后灰度值有明显的不连续性,图像质量损失较大,会产生明显的马赛克和锯齿现象。
(2). 双线性插值法
双线性插值,又称为双线性内插。在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值。
图 50.1.4 双线性插值法坐标
假设有一个二元函数 z=f(x,y) , 已知曲面上的四点,A 、B 、C、D的坐标分别为 (x0,y0) 、(x1,y0)、(x1,y1)、(x0,y1) 。现在要在A 、B 、C、D之间通过插值计算出一个点 P ,若已知 P点的坐标 (x,y),那么如何求出 P点的函数值坐标 z呢?
首先计算 y 轴方向的插值点 P0和 P1,P0和 P1分别与AD和BC成线性关系,可得到下面的表达式:
图 50.1.5 双线性插值法表达式1
这里令:
图 50.1.6 双线性插值法表达式2
这里令P0 的取值 z0,P1 的取值 z1,于是就可以得到P0和 P1纵坐标的表达式:
图 50.1.7 双线性插值法表达式3
图 50.1.8 双线性插值法表达式4
再计算 x 轴方向的插值点 P,令:
图 50.1.9 双线性插值法表达式5
则 P 的取值 z为:
图 50.1.10 双线性插值法表达式6
整理得到下面的式子:
图 50.1.11 双线性插值法表达式7
双线性插值是最常见,最通用的算法之一,效果比最近邻插值法好。计算量较小,运算速度较快。图像连续性较好。双线性插值法效果要好于最近邻插值,只是计算量稍大一些,算法复杂些,程序运行时间也稍长些,但缩放后图像质量高,基本克服了最近邻插值灰度值不连续的特点,因为它考虑了待测采样点周围四个直接邻点对该采样点的相关性影响。但是其也存在一些缺点,其缺点是放大时图像较为模糊,细节损失较严重。它仅考虑待测样点周围四个直接邻点灰度值的影响, 而未考虑到各邻点间灰度值变化率的影响, 因此具有低通滤波器的性质, 从而导致缩放后图像的高频分量受到损失, 图像边缘在一定程度上变得较为模糊。用此方法缩放后的输出图像与输入图像相比, 存在由于插值函数设计考虑不周而产生的图像质量受损与计算精度不高的问题。
(3). 双三次插值法
用三次多项式近似Sinc函数进行内插的方法称为三次插值法,也叫立方插值法。因计算时要分别对纵横方向进行立方插值计算,所以叫双三次插值法。一般来说,其插值计算效果要比传统的低阶插值函数好。其插值基函数如下所示:
图 50.1.12 双三次插值法表达式1
上式有一个变量a,一般而言,由于目前大部分数字图像能量均为主要集中在低频部分,所以当a取0.5时为最优解,取a=0.5也是最常用的双三次插值基函数,取a=0.5时的双三次插值基函数的表达式如下所示:
图 50.1.13 双三次插值法表达式2
缩放系数是在插值计算中直接与像素灰度值进行相应计算的值,上式中的h(x)即为我们所需的缩放系数,x为目标像素坐标系映射到原像素坐标系中目标像素与原像素坐标在水平或垂直方向上的偏移值,当目标像素与原像素重合时x=0,原像素坐标系相邻像素距离单位化为1。
图 50.1.14 双三次插值算法像素坐标映射示意图
双三次插值算法像素坐标映射示意图如图 50.1.14所示,目标像素点的灰度值与其周围4x4个原像素点灰度值有关,目标映射到原坐标系内,与其左上最近像素点横向距离为△x,纵向距离为△y,原图像矩阵内相邻像素点距离为1,目标像素点P的灰度值由公式P(x,y)=ABC求得,其中A、B、C均为矩阵,其公式如下所示:
图 50.1.15 双三次插值法表达式3
其中A、C矩阵内的h(x)表示缩放系数,B矩阵内的f(i,j)表示原图像4x4矩阵内各个像素的灰度值。
由图 50.1.15的三个公式可知,双三次插值计算可以分别在行和场方向上分别计算,行场方向上的计算顺序对最后的结果没有影响,即可先进行行方向上的插值再进行场方向上的插值,也可进行场方向上的插值再进行行方向上的插值。
若先进行场方向的插值,则需要相邻四行的原像素值参与计算,也就是每次同时取在同一列的四个原像素与相应场缩放系数进行计算,得到经过场方向插值的4个临时像素点P1、P2、P3、P4,这四个临时像素点的计算表达式分别如下所示:
图 50.1.16 双三次插值法表达式4
场方向插值得到的4个临时像素值,用这4个临时像素点与相应行缩放系数进行行方向插值计算得到最后的表达式,表达式如下所示:
图 50.1.17 双三次插值法表达式5
其中P(x,y)就是所求的目标像素点的灰度值。
双三次插值算法计算量最大,算法也是最为复杂的。在几何运算中,双线性内插法的平滑作用可能会使图像的细节产生退化,在进行放大处理时,这种影响更为明显。在其他应用中,双线性插值的斜率不连续性会产生不好的结果。立方卷积插值不仅考虑到周围四个直接相邻像素点灰度值的影响,还考虑到它们灰度值变化率的影响。因此克服了最近邻算法和双线性算法的不足之处,能够产生比双线性插值更为平滑的边缘,计算精度很高,处理后的图像像质损失最少,效果是最佳的。因此双三次插值算法计算量较大,算法相较双线性算法更为复杂。
本次实验中采用的是双线性插值算法,使得代码中算法不是太复杂,缩放后的效果也不是太差。
50.2实验任务
本节实验任务是使用超越者开发板及OV5640摄像头实现图像缩放,并通过按键在原图、缩小图和放大图之间切换,最后通过HDMI接口驱动HDMI显示器,并实时显示出图像。
50.3硬件设计
摄像头扩展接口原理图及OV5640模块说明与“OV5640摄像头TFT-LCD显示实验”完全相同,请参考“OV5640摄像头TFT-LCD显示实验”硬件设计部分。HDMI接口部分的硬件设计请参考“HDMI彩条显示实验”中的硬件设计部分。按键原理图请参考“按键控制LED灯实验”中的硬件设计部分
由于OV5640、HDMI接口和DDR3引脚数目较多且在前面相应的章节中已经给出它们的管脚列表,这里不再列出管脚分配。
50.4程序设计
根据实验任务,首先我们设计如图 50.4.1所示的系统框图,本章实验的系统框架延续了“OV5640摄像头HDMI显示实验”的整体架构。本次实验包括以下模块:时钟模块、DDR控制器模块、摄像头驱动模块、缩放模块、参数切换模块、按键控制模块和HDMI顶层模块。其中时钟模块、DDR控制器模块和摄像头驱动模块我们没有做任何修改,这些模块在“OV5640摄像头TFT-LCD显示实验”中已经说明过,这里不再详述。本次实验对HDMI顶层模块做了修改,在HDMI顶层模块下面添加了一个显示模块的子模块,因为要保证最后输出的分辨率为放大后的分辨率,而原始图像和缩小后的图像都比输出分辨率小,所以要填充黑边,显示模块就是起到这个作用。本次实验还添加了缩放模块、参数切换模块和按键控制模块,其中按键控制模块是产生各个模块所需的的一些参数信号;参数切换模块是根据按键切换信号来切换模块参数;缩放模块是将原始图像进行缩放并输出缩放后的数据。
OV5640摄像头HDMI缩放实验系统框图如下图所示:
图 50.4.1 顶层系统框图
由上图可知,时钟模块(clock)为HDMI顶层模块、DDR控制模块、参数切换模块、按键控制模块、缩放模块以及OV5640驱动模块提供驱动时钟。OV5640驱动模块控制着传感器初始化的开始与结束,传感器初始化完成后将采集到的数据输出到缩放模块,根据按键控制模块的切换使能来判断最后写入各个模块的参数。HDMI顶层模块从DDR控制模块中读出数据并驱动显示器显示,这时整个系统才完成了数据的采集、缓存与显示。需要注意的是图像数据采集模块是在DDR3和传感器都初始化完成之后才开始输出数据的,避免了在DDR3初始化过程中向里面写入数据。
顶层模块代码如下:
- 1 module ov5640_hdmi_scale(
- 2 input sys_clk, //系统时钟
- 3 input sys_rst_n, //系统复位,低电平有效
- 4 input key0, //按键key0
- 5 //摄像头接口
- 6 input cam_pclk, //cmos 数据像素时钟
- 7 input cam_vsync, //cmos 场同步信号
- 8 input cam_href, //cmos 行同步信号
- 9 input [7:0] cam_data, //cmos 数据
- 10 output cam_rst_n, //cmos 复位信号,低电平有效
- 11 output cam_pwdn , //电源休眠模式选择 0:正常模式 1:电源休眠模式
- 12 output cam_scl, //cmos SCCB_SCL线
- 13 inout cam_sda, //cmos SCCB_SDA线
- 14 //ddr3 port
- 15 inout [15:0] mcb3_dram_dq,
- 16 output [13:0] mcb3_dram_a,
- 17 output [2:0] mcb3_dram_ba,
- 18 output mcb3_dram_ras_n,
- 19 output mcb3_dram_cas_n,
- 20 output mcb3_dram_we_n,
- 21 output mcb3_dram_reset_n,
- 22 output mcb3_dram_cke,
- 23 output mcb3_dram_dm,
- 24 inout mcb3_dram_udqs,
- 25 inout mcb3_dram_udqs_n,
- 26 inout mcb3_rzq,
- 27 inout mcb3_zio,
- 28 output mcb3_dram_udm,
- 29 inout mcb3_dram_dqs,
- 30 inout mcb3_dram_dqs_n,
- 31 output mcb3_dram_ck,
- 32 output mcb3_dram_ck_n,
- 33
- 34 output init_calib_complete,
- 35 //hdmi接口
- 36 output tmds_clk_p, // TMDS 时钟通道
- 37 output tmds_clk_n,
- 38 output [2:0] tmds_data_p, // TMDS 数据通道
- 39 output [2:0] tmds_data_n
- 40 );
- 41
- 42 //parameter define
- 43 parameter V_CMOS_DISP = 11'd480; //CMOS分辨率--行
- 44 parameter H_CMOS_DISP = 11'd720; //CMOS分辨率--列
- 45 parameter TOTAL_H_PIXEL = H_CMOS_DISP + 12'd1216;
- 46 parameter TOTAL_V_PIXEL = V_CMOS_DISP + 12'd504;
- 47
- 48 //wire define
- 49 wire clk_50m ; //50mhz时钟
- 50 wire locked_100m ; //时钟锁定信号
- 51 wire locked_300m ; //时钟锁定信号
- 52 wire rst_n ; //全局复位
- 53 wire wr_en ; //DDR3控制器模块写使能
- 54 wire [15:0] wr_data ; //DDR3控制器模块写数据
- 55 wire rdata_req ; //DDR3控制器模块读使能
- 56 wire [15:0] rd_data ; //DDR3控制器模块读数据
- 57 wire cmos_frame_valid ; //数据有效使能信号
- 58 wire init_calib_complete ; //DDR3初始化完成init_calib_complete
- 59 wire sys_init_done ; //系统初始化完成(DDR初始化+摄像头初始化)
- 60 wire cmos_frame_vsync ; //输出帧有效场同步信号
- 61 wire lcd_de ; //LCD 数据输入使能
- 62 wire cmos_frame_href ; //输出帧有效行同步信号
- 63 wire [10:0] h_disp ; //水平分辨率
- 64 wire [10:0] v_disp ; //垂直分辨率
- 65 wire [2:0] change_en ; //按键切换信号
- 66 wire [15:0] pixel_data_w ; //摄像头采集16bit数据
- 67 wire [10:0] t_width_wr ; //写端缩放后列分辨率
- 68 wire [10:0] t_height_wr ; //写端缩放后行分辨率
- 69 wire [10:0] t_width_rd ; //读端缩放后列分辨率
- 70 wire [10:0] t_height_rd ; //读端缩放后行分辨率
- 71 wire [2:0] change_en_wr ; //写端切换信号
- 72 wire [2:0] change_en_rd ; //读端切换信号
- 73 wire [15:0] h_scale_k ; //列缩放因子
- 74 wire [15:0] v_scale_k ; //行缩放因子
- 75 wire [27:0] ddr_addr_rd_max ; //读DDR3的结束地址
- 76 wire [7:0] rd_bust_len ; //从DDR3中读数据时的突发长度
- 77 wire [27:0] ddr_addr_wr_max ; //写DDR3的结束地址
- 78 wire [7:0] wr_bust_len ; //从DDR3中写数据时的突发长度
- 79 wire [15:0] scale_data ; //缩放后的数据
- 80 wire scale_data_valid ; //缩放后的数据使能
- 81 wire para_finish;
- 82 wire pixel_valid;
- 83
- 84 //*****************************************************
- 85 //** main code
- 86 //*****************************************************
- 87 //待时钟锁定后产生复位结束信号
- 88 assign rst_n = sys_rst_n & locked_100m & locked_300m;
- 89
- 90 //系统初始化完成:DDR3初始化完成
- 91 assign sys_init_done = init_calib_complete;
- 92
- 93 //ov5640 驱动
- 94 ov5640_dri u_ov5640_dri(
- 95 .clk (clk_50m),
- 96 .rst_n (rst_n),
- 97
- 98 .cam_pclk (cam_pclk ),
- 99 .cam_vsync (cam_vsync),
- 100 .cam_href (cam_href ),
- 101 .cam_data (cam_data ),
- 102 .cam_rst_n (cam_rst_n),
- 103 .cam_pwdn (cam_pwdn ),
- 104 .cam_scl (cam_scl ),
- 105 .cam_sda (cam_sda ),
- 106
- 107 .capture_start (init_calib_complete),
- 108 .cmos_h_pixel (H_CMOS_DISP),
- 109 .cmos_v_pixel (V_CMOS_DISP),
- 110 .total_h_pixel (TOTAL_H_PIXEL),
- 111 .total_v_pixel (TOTAL_V_PIXEL),
- 112 .cmos_frame_vsync (cmos_frame_vsync),
- 113 .cmos_frame_href (cmos_frame_href),
- 114 .cmos_frame_valid (cmos_frame_valid),
- 115 .cmos_frame_data (pixel_data_w)
- 116 );
- 117
- 118 //图像缩放顶层封装模块
- 119 scale_top u_scale_top(
- 120 .pixel_clk (cam_pclk), //原始图像采集时钟
- 121 .sram_clk (clk_100m), //缩放后图像采集时钟
- 122 .sys_rst_n (rst_n), //复位信号
- 123 .hs (cmos_frame_href), //输出帧有效行同步信号
- 124 .vs (cmos_frame_vsync), //输出帧有效场同步信号
- 125 .de (cmos_frame_valid), //输出帧数据有效使能
- 126 .pixel_data (pixel_data_w), //输出帧数据
- 127
- 128 .s_width (H_CMOS_DISP), //缩放前宽度
- 129 .s_height (V_CMOS_DISP), //缩放前高度
- 130 .t_width (t_width_wr), //缩放后宽度
- 131 .t_height (t_height_wr), //缩放后高度
- 132 .h_scale_k (h_scale_k), //列缩放因子
- 133 .v_scale_k (v_scale_k), //行缩放因子
- 134
- 135 .sram_data_out (scale_data), //缩放后数据有效使能
- 136 .data_valid (scale_data_valid) //缩放后输出数据
- 137 );
- 138
- 139 //按键控制模块
- 140 key_ctrl u_key_ctrl(
- 141 //input
- 142 .sys_clk (clk_50m), //时钟信号50Mhz
- 143 .sys_rst_n (rst_n), //复位信号
- 144 .touch_key (key0), //按键
- 145
- 146 .change_en (change_en) //切换使能
- 147 );
- 148
- 149 //参数切换模块
- 150 para_change u_para_change(
- 151 .clk_wr (clk_100m),
- 152 .clk_rd (clk_100m),
- 153 .change_en (change_en), //切换使能
- 154 .rst_n (rst_n ),
- 155
- 156 .rd_vsync (video_vs), //HDMI场信号
- 157 .wr_vsync (cmos_frame_vsync), //输出帧有效场同步信号
- 158 .s_width (H_CMOS_DISP), //缩放前宽度
- 159 .s_height (V_CMOS_DISP), //缩放前高度
- 160 .t_width_wr (t_width_wr), //写端缩放后宽度
- 161 .t_height_wr (t_height_wr), //写端缩放后高度
- 162 .t_width_rd (t_width_rd), //读端缩放后宽度
- 163 .t_height_rd (t_height_rd), //读端缩放后高度
- 164 .h_scale_k (h_scale_k), //列缩放因子
- 165 .v_scale_k (v_scale_k), //行缩放因子
- 166
- 167 .change_en_wr (change_en_wr), //写端切换信号
- 168 .change_en_rd (change_en_rd), //读端切换信号
- 169 .app_addr_rd_max (ddr_addr_rd_max), //读ddr3的结束地址
- 170 .rd_bust_len (rd_bust_len), //从ddr3中读数据时的突发长度
- 171 .app_addr_wr_max (ddr_addr_wr_max), //读ddr3的结束地址
- 172 .wr_bust_len (wr_bust_len) //从ddr3中读数据时的突发长度
- 173
- 174 );
- 175
- 176 //ddr控制器
- 177 ddr3_control
- 178 #(
- 179 .C3_NUM_DQ_PINS (16),
- 180 .C3_MEM_ADDR_WIDTH (14),
- 181 .C3_MEM_BANKADDR_WIDTH (3)
- 182 )
- 183 u_ddr3_control(
- 184 .clk_ddr (clk_ddr),
- 185 .clk_100m (clk_100m),
- 186 .rst_n (sys_rst_n),
- 187 .mcb3_dram_dq (mcb3_dram_dq ),
- 188 .mcb3_dram_a (mcb3_dram_a ),
- 189 .mcb3_dram_ba (mcb3_dram_ba ),
- 190 .mcb3_dram_ras_n (mcb3_dram_ras_n ),
- 191 .mcb3_dram_cas_n (mcb3_dram_cas_n ),
- 192 .mcb3_dram_we_n (mcb3_dram_we_n ),
- 193 .mcb3_dram_reset_n (mcb3_dram_reset_n),
- 194 .mcb3_dram_cke (mcb3_dram_cke ),
- 195 .mcb3_dram_dm (mcb3_dram_dm ),
- 196 .mcb3_dram_udqs (mcb3_dram_udqs ),
- 197 .mcb3_dram_udqs_n (mcb3_dram_udqs_n ),
- 198 .mcb3_rzq (mcb3_rzq ),
- 199 .mcb3_zio (mcb3_zio ),
- 200 .mcb3_dram_udm (mcb3_dram_udm ),
- 201 .mcb3_dram_dqs (mcb3_dram_dqs ),
- 202 .mcb3_dram_dqs_n (mcb3_dram_dqs_n ),
- 203 .mcb3_dram_ck (mcb3_dram_ck ),
- 204 .mcb3_dram_ck_n (mcb3_dram_ck_n ),
- 205 .c3_calib_done (init_calib_complete),
- 206
- 207 .ddr3_read_valid (1'b1 ),
- 208 .ddr3_pingpang_en (1'b1),
- 209
- 210 //摄像头
- 211 .clk_write (clk_100m), //写时钟
- 212 .wr_en (scale_data_valid), //写使能
- 213 .wr_data (scale_data), //写数据
- 214 .wr_load (cmos_frame_vsync), //写端更新信号
- 215 .wr_length (wr_bust_len), //一次写长度
- 216 .wr_max_addr (ddr_addr_wr_max), //写最大地址
- 217 .wr_min_addr (0), //写最小地址
- 218
- 219 .clk_read (pixel_clk), //读时钟
- 220 .rd_load (video_vs), //读端更新信号
- 221 .rd_en (rdata_req), //读使能
- 222 .rd_max_addr (ddr_addr_rd_max), //读最大地址
- 223 .rd_min_addr (0), //读最小地址
- 224 .rd_length (rd_bust_len), //一次读长度
- 225 .rd_data (rd_data) //读数据
- 226
- 227 );
- 228
- 229 //时钟模块
- 230 clock u_clock(
- 231 .sys_clk (sys_clk), //系统输入时钟
- 232
- 233 .clk_ddr (clk_ddr), //ddr运行时钟
- 234 .clk_50m (clk_50m), //50m时钟
- 235 .clk_100m (clk_100m), //100m时钟
- 236 .locked_100m (locked_100m), //时钟稳定信号
- 237 .locked_300m (locked_300m) //时钟稳定信号
- 238
- 239 );
- 240
- 241 //HDMI顶层模块
- 242 hdmi_top u_hdmi_top(
- 243 .clk_50m (clk_50m),
- 244 .pixel_clk (pixel_clk),
- 245 .sys_rst_n (sys_init_done & rst_n ),
- 246 //hdmi接口
- 247 .tmds_clk_p (tmds_clk_p ), // TMDS 时钟通道
- 248 .tmds_clk_n (tmds_clk_n ),
- 249 .tmds_data_p (tmds_data_p ), // TMDS 数据通道
- 250 .tmds_data_n (tmds_data_n ),
- 251 //用户接口
- 252 .change_en (change_en_rd), //切换信号,1为放大,2为缩小,4为原图
- 253 .h_disp (),
- 254 .v_disp (),
- 255 .t_width (t_width_rd), //读端缩放后宽度
- 256 .t_height (t_height_rd), //读端缩放后高度
- 257 .s_width (H_CMOS_DISP), //缩放前宽度
- 258 .s_height (V_CMOS_DISP), //缩放前高度
- 259 .video_vs (video_vs),
- 260 .pixel_xpos (),
- 261 .pixel_ypos (),
- 262 .data_in (rd_data), //数据输入
- 263 .data_req (rdata_req) //请求数据输入
- 264 );
- 265
- 266 endmodule
复制代码
FPGA顶层模块(ov5640_hdmi_scale)例化了以下七个模块:时钟模块(clock)、OV5640驱动模块(ov5640_dri)、图像缩放顶层封装模块(scale_top)、按键控制模块(key_ctrl)、参数切换模块(para_change)、DDR控制模块(ddr3_control)和HDMI顶层模块(hdmi_top)。
时钟模块(clock):时钟模块通过调用DCP IP核实现,共输出3个时钟,频率分别为100M时钟、50Mhz时钟和300M时钟。300Mhz时钟作为DDR存储器的运行时钟;100M时钟作为DDR控制模块的驱动时钟;50Mhz时钟作为OV5640驱动模块、按键控制模块和HDMI顶层模块的驱动时钟;
OV5640驱动模块(ov5640_dri):OV5640驱动模块负责驱动OV5640 SCCB接口总线,将像素时钟驱动下的传感器输出的场同步信号、行同步信号以及8位数据转换成DDR读写控制模块的写使能信号和16位写数据信号,完成对OV5640传感器图像的采集。
图像缩放顶层封装模块(scale_top):根据行场缩放因子对输入的原始图像进行缩放,最后输出我们所需要分辨率的图像数据。
按键控制模块(key_ctrl):根据用户是否按下按键来判断是改变转切换信号。
参数切换模块(para_change):根据切换信号来切换输出到各个模块的参数。
DDR控制模块(ddr3_control):DDR读写控制器模块负责驱动DDR片外存储器,缓存图像传感器输出的图像数据。该模块将MIG IP核复杂的读写操作封装成类似FIFO的用户接口,非常方便用户的使用。有关DDR控制模块的详细介绍请大家参考“DDR读写测试实验”章节。
HDMI顶层模块(hdmi_top):HDMI顶层模块负责驱动HDMI显示器的驱动信号的输出,同时为其他模块提供显示器参数、场同步信号和数据请求信号。
本次实验的摄像头采集的分辨率为720x480,代码43和43行定义了摄像头的分辨率。
代码119行至137行,是对图像缩放顶层封装模块的例化,这个模块是将原始图像通过行场缩放系数进行缩放,再通过双线性插值法进行运算最后得到缩放后的数据。
代码140行至147行,是对按键控制模块的例化,该模块是通过判断按键是否按下来翻转切换信号。
代码150行至174行,是对参数切换模块的例化。因为原始图像的分辨率和缩放后的分辨率是不同的,所以输出到各个模块的参数是变化的,因此必须通过切换信号change_en来切换写入各个模块的参数。
代码242行至264行,是对hdmi顶层模块的例化。相对于“OV5640摄像头HDMI显示实验”中的hdmi顶层模块的端口,本次实验只是添加了change_en、s_width、s_height、t_width和t_height这五个信号,其中change_en是为了切换读请求使能和有效数据,以及输出到显示模块的参数。
下面是图像缩放顶层封装模块的代码:
- 1 module scale_top(
- 2 input pixel_clk, //原始像素时钟
- 3 input sram_clk, //缩放后像素时钟
- 4 input sys_rst_n, //复位信号
- 5 input hs, //行信号
- 6 input vs, //场信号
- 7 input de, //数据使能信号
- 8 input [11:0] s_width, //缩放前宽度
- 9 input [11:0] s_height, //缩放前高度
- 10 input [11:0] t_width, //缩放后宽度
- 11 input [11:0] t_height, //缩放后高度
- 12 input [15:0] h_scale_k, //列缩放因子
- 13 input [15:0] v_scale_k, //行缩放因子
- 14 input [15:0] pixel_data, //缩放前数据
- 15 output [15:0] sram_data_out, //缩放后数据
- 16 output data_valid //缩放后数据有效信号
- 17 );
- 18
- 19 //wire define
- 20 wire [15:0] sram_data_out; //缩放后的RGB数据
- 21 wire [7:0] data_out_r; //缩放后的红色数据
- 22 wire [7:0] data_out_g; //缩放后的绿色数据
- 23 wire [7:0] data_out_b; //缩放后的蓝色数据
- 24 wire data_valid; //缩放后数据有效信号
- 25
- 26 //*****************************************************
- 27 //** main code
- 28 //*****************************************************
- 29
- 30 //将缩放后的数据拼成16bit数据输出
- 31 assign sram_data_out = data_valid ? {data_out_r[7:3],data_out_g[7:2],
- 32 data_out_b[7:3]} : 16'b0;
- 33 assign fifo_rden = ~fifo_empty_r && ~fifo_empty_g && ~fifo_empty_b;
- 34
- 35 //红色数据缩放模块
- 36 vin_scale_down u_vin_scale_down_r(
- 37 .pixel_clk (pixel_clk),
- 38 .sram_clk (sram_clk),
- 39 .sys_rst_n (sys_rst_n),
- 40 .hs (hs),
- 41 .vs (vs),
- 42 .de (de),
- 43 .s_width (s_width),
- 44 .s_height (s_height),
- 45 .t_width (t_width),
- 46 .t_height (t_height),
- 47 .h_scale_k (h_scale_k),
- 48 .v_scale_k (v_scale_k),
- 49 .fifo_scale_rden (fifo_rden),
- 50 .pixel_data ({pixel_data[15:11],3'b0}),
- 51 .sram_data_out (data_out_r),
- 52 .data_valid (data_valid),
- 53 .fifo_scale_rdempty (fifo_empty_r)
- 54 );
- 55
- 56 //绿色数据缩放模块
- 57 vin_scale_down u_vin_scale_down_g(
- 58 .pixel_clk (pixel_clk),
- 59 .sram_clk (sram_clk),
- 60 .sys_rst_n (sys_rst_n),
- 61 .hs (hs),
- 62 .vs (vs),
- 63 .de (de),
- 64 .s_width (s_width),
- 65 .s_height (s_height),
- 66 .t_width (t_width),
- 67 .t_height (t_height),
- 68 .h_scale_k (h_scale_k),
- 69 .v_scale_k (v_scale_k),
- 70 .fifo_scale_rden (fifo_rden),
- 71 .pixel_data ({pixel_data[10:5],2'b0}),
- 72 .sram_data_out (data_out_g),
- 73 .data_valid (),
- 74 .fifo_scale_rdempty (fifo_empty_g)
- 75 );
- 76
- 77 //蓝色数据缩放模块
- 78 vin_scale_down u_vin_scale_down_b(
- 79 .pixel_clk (pixel_clk),
- 80 .sram_clk (sram_clk),
- 81 .sys_rst_n (sys_rst_n),
- 82 .hs (hs),
- 83 .vs (vs),
- 84 .de (de),
- 85 .s_width (s_width),
- 86 .s_height (s_height),
- 87 .t_width (t_width),
- 88 .t_height (t_height),
- 89 .h_scale_k (h_scale_k),
- 90 .v_scale_k (v_scale_k),
- 91 .fifo_scale_rden (fifo_rden),
- 92 .pixel_data ({pixel_data[4:0],3'b0}),
- 93 .sram_data_out (data_out_b),
- 94 .data_valid (),
- 95 .fifo_scale_rdempty (fifo_empty_b)
- 96 );
- 97
- 98 endmodule
复制代码
代码第31行和32行是将24bit缩放后的数据拼接成RGB565格式的16bit颜色数据,因为输入的原始数据是16bit,所以输出的数据也必须是16bit数据。
代码第33行表示当RGB三个缩放后的fifo都不为空时拉高读使能,以便RGB颜色数据的同步。
代码第36行至96行是红绿蓝三种颜色的缩放顶层模块的运算,其中50行、71行和92行分别是将RGB565的数据拼成RGB888的数据位宽。因为缩放顶层模块的输入数据是8bit位宽,所以要将位宽拼成8bit。
缩放顶层模块代码如下:
- 1 module vin_scale_down(
- 2 input pixel_clk,
- 3 input sram_clk,
- 4 input sys_rst_n, //复位信号
- 5 input hs, //行信号
- 6 input vs, //场信号
- 7 input de, //数据使能信号
- 8 input [11:0] s_width, //缩放前宽度
- 9 input [11:0] s_height, //缩放前高度
- 10 input [11:0] t_width, //缩放后宽度
- 11 input [11:0] t_height, //缩放后高度
- 12 input [7:0] pixel_data, //缩放前数据
- 13 input [15:0] h_scale_k, //列缩放因子
- 14 input [15:0] v_scale_k, //行缩放因子
- 15 input fifo_scale_rden, //缩放后fifo读使能
- 16 output [7:0] sram_data_out, //缩放后数据
- 17 output data_valid, //缩放后数据有效信号
- 18 output fifo_scale_rdempty //缩放后fifo空信号
- 19 );
- 20
- 21 //reg define
- 22 reg de_d0;
- 23 reg vs_d0;
- 24 reg [15:0] vs_d;
- 25 reg frame_flag; //帧头标志信号
- 26 reg [7:0] pixel_data_d0;
- 27 reg data_valid; //缩放后数据有效信号
- 28 reg data_ready; //fifo一行数据准备完成信号
- 29
- 30 //wire define
- 31 wire scaler_rd_req; //缩放前数据请求信号
- 32 wire fifo_rdempty; //缩放前fifo的空信号
- 33 wire[7:0] fifo_data; //缩放前fifo的输出数据
- 34 wire[7:0] scaler_data_out; //缩放后数据
- 35 wire scaler_wren; //缩放后fifo写使能
- 36 wire fifo_scale_rdempty; //缩放后fifo空信号
- 37 wire[10:0] rd_data_count; //fifo缓存计数器
- 38
- 39 //*****************************************************
- 40 //** main code
- 41 //*****************************************************
- 42
- 43 //对信号打拍
- 44 always@(posedge pixel_clk)begin
- 45 de_d0 <= de;
- 46 pixel_data_d0 <= pixel_data;
- 47 end
- 48
- 49 //产生数据有效信号
- 50 always@(posedge sram_clk)begin
- 51 data_valid <= fifo_scale_rden;
- 52 vs_d0 <= vs;
- 53 vs_d <= {vs_d[14:0], vs_d0};
- 54 frame_flag <= vs_d[15] && ~vs_d[0];
- 55 end
- 56
- 57 //产生fifo一行数据准备完成信号
- 58 always@(posedge sram_clk)begin
- 59 if(frame_flag)
- 60 data_ready <= 1'b0;
- 61 else if(rd_data_count >= s_width - 3)
- 62 data_ready <= 1'b1;
- 63 else
- 64 data_ready <= 1'b0;
- 65 end
- 66
- 67 //缓存缩放前数据
- 68 fifo_2048x8 u_fifo_data_in(
- 69 .rst (frame_flag || !sys_rst_n ),
- 70 .wr_clk (pixel_clk),
- 71 .rd_clk (sram_clk),
- 72 .din (pixel_data_d0),
- 73 .wr_en (de_d0),
- 74 .rd_en (scaler_rd_req),
- 75 .dout (fifo_data),
- 76 .full (),
- 77 .empty (fifo_rdempty),
- 78 .rd_data_count (rd_data_count),
- 79 .wr_data_count ()
- 80 );
- 81
- 82 //缓存缩放后数据
- 83 fifo_2048x8 u_lite_scale_data(
- 84 .rst (frame_flag || !sys_rst_n ),
- 85 .wr_clk (sram_clk),
- 86 .rd_clk (sram_clk),
- 87 .din (scaler_data_out),
- 88 .wr_en (scaler_wren),
- 89 .rd_en (fifo_scale_rden),
- 90 .dout (sram_data_out),
- 91 .full (),
- 92 .empty (fifo_scale_rdempty),
- 93 .rd_data_count (),
- 94 .wr_data_count ()
- 95 );
- 96
- 97 //缩放模块
- 98 scaler u_scaler(
- 99 .sys_clk(sram_clk),
- 100 .rst_n(sys_rst_n),
- 101 //data in
- 102 .fifo_rdempty(fifo_rdempty),
- 103 .scaler_rd_req(scaler_rd_req),
- 104 .data_in(fifo_data),
- 105 .frame_flag(frame_flag),
- 106 .data_ready(data_ready),
- 107 //data out
- 108 .scaler_wren(scaler_wren),
- 109 .data_out(scaler_data_out),
- 110 //config
- 111 .s_width(s_width),
- 112 .s_height(s_height),
- 113 .t_width(t_width),
- 114 .t_height(t_height),
- 115 .k_h(h_scale_k),
- 116 .k_v(v_scale_k)
- 117 );
- 118
- 119 endmodule
复制代码
代码第44行至47行,是对输入信号做一个打拍寄存,是为了防止信号的扇出太大。
代码第50行至55行,是将输入信号在读时钟下打拍,是为了同步时钟域,避免信号跨时钟域导致时序不满足。代码第51行,是为了产生缩放后数据有效信号。本次实验采用的是标准fifo,所以fifo读使能的下一个时钟周期的数据才是有效数据,所以缩放后数据有效信号是由fifo读使能打一拍后得到的。其中54行将帧头的电平宽度加宽,是为了满足fifo复位的时序。
代码第58行至65行是产生fifo一行数据准备完成信号。当fifo的数据计数器rd_data_count接近于缩放前宽度时,此时拉高data_ready信号,则认为fifo中已经存有一行数据。
缩放模块的代码如下:
- 1 module scaler
- 2 (
- 3 input sys_clk,
- 4 input rst_n,
- 5 //data in
- 6 input fifo_rdempty, //缩放前fifo的将空标志
- 7 output scaler_rd_req, //fifo读请求
- 8 input [7:0] data_in, //缩放前数据
- 9 input frame_flag, //帧头标志信号
- 10 input data_ready, //fifo一行数据准备完成信号
- 11 //data out
- 12 output scaler_wren, //缩放后fifo写使能
- 13 output [7:0] data_out, //缩放后数据
- 14 //config
- 15 input [11:0] s_width, //缩放前宽度
- 16 input [11:0] s_height, //缩放前高度
- 17 input [11:0] t_width, //缩放后宽度
- 18 input [11:0] t_height, //缩放后高度
- 19 input [15:0] k_h, //列缩放因子
- 20 input [15:0] k_v //行缩放因子
- 21 );
- 22
- 23 //reg define
- 24 reg [15:0] k_h_reg;
- 25 reg [15:0] k_v_reg;
- 26 reg wr_buf_sel; //ram写切换使能
- 27 reg [11:0] wr_lines; //读出fifo数据的行数
- 28 reg [19:0] scale_map_v; //行缩放因子累加寄存器
- 29 reg [7:0] line0_pixel_data; //将要进行行运算的像素数据
- 30 reg [7:0] line1_pixel_data; //将要进行行运算的像素数据
- 31 reg [11:0] ram_v_raddr; //读ram地址
- 32 reg [10:0] ram_v_waddr; //ram写地址
- 33 reg [10:0] ram_v_waddr_d0;
- 34 reg [10:0] ram_v_waddr_d1;
- 35 reg [10:0] ram_v_raddr_d0;
- 36 reg [10:0] ram_v_raddr_d1;
- 37 reg [10:0] ram_h_waddr_d0;
- 38 reg [10:0] ram_h_waddr_d1;
- 39 reg [10:0] ram_h_raddr_d0;
- 40 reg [10:0] ram_h_raddr_d1;
- 41 reg pixel_h_data_read_en_d0;
- 42 reg pixel_h_data_read_en_d1;
- 43 reg pixel_v_data_read_en_d0;
- 44 reg pixel_v_data_read_en_d1;
- 45 reg fifo_line_finish ; //fifo一行读完信号
- 46 reg ram_v_wren ; //行ram写使能
- 47 reg [11:0] scale_line; //缩放后的行计数
- 48 reg one_line_data_ready ; //首行数据写入行ram的标志信号
- 49 reg pixel_v_data_read_en ; //行ram数据可读使能
- 50 reg scale_line_finish ; //行缩放完成信号
- 51 reg calu_v_data_in_en ; //产生行运算输入使能有效信号
- 52 reg [7:0] a_coff_v; //行加权系数输入
- 53 reg [7:0] b_coff_v; //行加权系数输入
- 54 reg [15:0] scale_line_finish_d;
- 55 reg [15:0] scale_h_finish_d;
- 56 reg [7:0] ram_h_data_in; //列ram输入数据
- 57 reg ram_h_wren; //列ram写使能
- 58 reg [10:0] ram_h_waddr; //列ram写地址
- 59 reg pixel_h_data_read_en; //列ram数据可读使能
- 60 reg [10:0] ram_h_raddr; //列ram读地址
- 61 reg [19:0] scale_map_h; //列缩放因子累加寄存器
- 62 reg scaler_wren; //缩放后fifo写使能
- 63 reg [7:0] data_out; //缩放后数据
- 64 reg [7:0] col0_pixel_data;
- 65 reg [7:0] col1_pixel_data;
- 66 reg calu_h_data_in_en; //列运算输入使能有效信号
- 67 reg [7:0] a_coff_h; //行加权系数输入
- 68 reg [7:0] b_coff_h; //行加权系数输入
- 69 reg [19:0] scale_map_v_d0;
- 70 reg [19:0] scale_map_h_d0;
- 71 reg scaler_up; //缩放标志 1表示放大,0表示缩小
- 72 reg scale_h_line_valid_cont; //有效行缩放标志持续信号
- 73 reg scale_h_finish ; //列缩放完成信号
- 74 reg scale_h_not_end ; //列缩放未结束信号
- 75 reg [10:0] cnt_h_pixel; //列缩放计数
- 76 reg rd_v_not_need_data; //行缩放不需要请求数据标志
- 77 reg scale_v_end_line; //最后一行标志信号
- 78 reg frame_flag_d0;
- 79 reg calu_h_data_in_en_d0;
- 80 reg scaler_rd_req; //fifo读请求
- 81 reg [10:0] cnt_rd_req; //fifo读请求计数
- 82
- 83 //wire define
- 84 wire[7:0] ram_v_data_out0; //ram0输出数据
- 85 wire[7:0] ram_v_data_out1; //ram1输出数据
- 86 wire data_en_out_calu_v; //行运算输出使能
- 87 wire[7:0] c_v; //行运算输出数据
- 88 wire calu_h_data_out_en; //列运算输出使能
- 89 wire[7:0] c_h; //列运算输出数据
- 90 wire[7:0] ram_h_data_out; //列ram输出数据
- 91 wire[19:0] scale_map_h_next; //下一次列缩放因子累加寄存器
- 92 wire scale_h_line_valid; //有效列缩放标志
- 93 wire[19:0] scale_map_v_next; //下一次行缩放因子累加寄存器
- 94 wire scale_v_line_valid; //有效行缩放标志
- 95
- 96 //*****************************************************
- 97 //** main code
- 98 //*****************************************************
- 99
- 100 //产生下一次行缩放因子累加寄存器
- 101 assign scale_map_v_next = scale_map_v + {4'd0,k_v_reg};
- 102
- 103 //产生下一次列缩放因子累加寄存器
- 104 assign scale_map_h_next =scale_map_h + {4'd0,k_h_reg};
- 105
- 106 //产生有效行缩放标志
- 107 assign scale_v_line_valid = (scale_map_v[19:8] == wr_lines - 12'd1) || rd_v_not_need_data;
- 108
- 109 //产生有效列缩放标志
- 110 assign scale_h_line_valid = ((scale_map_h[19:8] == scale_map_h_next[19:8])
- 111 && scaler_up && pixel_h_data_read_en_d1) ? 1 : 0;
- 112
- 113 //产生fifo读请求
- 114 always@(posedge sys_clk or negedge rst_n )begin
- 115 if(~rst_n || frame_flag)
- 116 scaler_rd_req <= 0;
- 117 else if(cnt_rd_req>= s_width - 1)
- 118 scaler_rd_req <= 0;
- 119 else if(data_ready)
- 120 if(!rd_v_not_need_data)
- 121 scaler_rd_req <= 1;
- 122 else
- 123 scaler_rd_req <= 0;
- 124 else
- 125 scaler_rd_req <= scaler_rd_req;
- 126 end
- 127
- 128 //对fifo读请求进行计数
- 129 always@(posedge sys_clk or negedge rst_n )begin
- 130 if(~rst_n || frame_flag)
- 131 cnt_rd_req <= 0;
- 132 else if(scaler_rd_req)
- 133 cnt_rd_req <= cnt_rd_req + 1;
- 134 else
- 135 cnt_rd_req <= 0;
- 136 end
- 137
- 138 always@(posedge sys_clk or negedge rst_n )begin
- 139 if(!rst_n)begin
- 140 ram_v_waddr_d0 <= 0;
- 141 ram_v_waddr_d1 <= 0;
- 142 ram_v_raddr_d0 <= 0;
- 143 ram_v_raddr_d1 <= 0;
- 144 ram_h_waddr_d0 <= 0;
- 145 ram_h_waddr_d1 <= 0;
- 146 ram_h_raddr_d0 <= 0;
- 147 ram_h_raddr_d1 <= 0;
- 148 pixel_v_data_read_en_d0 <= 0;
- 149 pixel_v_data_read_en_d1 <= 0;
- 150 pixel_h_data_read_en_d0 <= 0;
- 151 pixel_h_data_read_en_d1 <= 0;
- 152 scale_line_finish_d <= 0;
- 153 scale_h_finish_d <= 0;
- 154 scale_map_v_d0 <= 0;
- 155 frame_flag_d0 <= 0;
- 156 end
- 157 else begin
- 158 ram_v_waddr_d0 <= ram_v_waddr;
- 159 ram_v_waddr_d1 <= ram_v_waddr_d0;
- 160 ram_v_raddr_d0 <= ram_v_raddr;
- 161 ram_v_raddr_d1 <= ram_v_raddr_d0;
- 162 ram_h_waddr_d0 <= ram_h_waddr;
- 163 ram_h_waddr_d1 <= ram_h_waddr_d0;
- 164 ram_h_raddr_d0 <= ram_h_raddr;
- 165 ram_h_raddr_d1 <= ram_h_raddr_d0;
- 166 pixel_v_data_read_en_d0 <= pixel_v_data_read_en;
- 167 pixel_h_data_read_en_d0 <= pixel_h_data_read_en;
- 168 pixel_h_data_read_en_d1 <= pixel_h_data_read_en_d0;
- 169 pixel_v_data_read_en_d1 <= pixel_v_data_read_en_d0;
- 170 scale_line_finish_d <= {scale_line_finish_d[14:0],scale_line_finish};
- 171 scale_h_finish_d <= {scale_h_finish_d[14:0],scale_h_finish};
- 172 scale_map_v_d0 <= scale_map_v;
- 173 scale_map_h_d0 <= scale_map_h;
- 174 frame_flag_d0 <= frame_flag;
- 175 end
- 176 end
- 177
- 178 //将行列缩放因子进行寄存
- 179 always@(posedge sys_clk )begin
- 180 k_h_reg <= k_h;
- 181 k_v_reg <= k_v;
- 182 end
- 183
- 184 //判断行是放大还是缩小
- 185 always@(posedge sys_clk)begin
- 186 if(k_h_reg[15:8] == 8'd0)
- 187 scaler_up <= 1;
- 188 else
- 189 scaler_up <= 0;
- 190 end
- 191
- 192 //fifo一行读完信号
- 193 always@(posedge sys_clk or negedge rst_n )begin
- 194 if(~rst_n)
- 195 fifo_line_finish <= 0;
- 196 else if((ram_v_waddr_d0 == s_width) && (ram_v_waddr_d1 == s_width - 1))
- 197 fifo_line_finish <= 1;
- 198 else
- 199 fifo_line_finish <= 0;
- 200 end
- 201
- 202 //产生行ram写地址
- 203 always@(posedge sys_clk or negedge rst_n )begin
- 204 if(~rst_n)
- 205 ram_v_waddr <= 0;
- 206 else if(frame_flag || fifo_line_finish)
- 207 ram_v_waddr <= 0;
- 208 else if(ram_v_wren)
- 209 ram_v_waddr <= ram_v_waddr + 1;
- 210 else
- 211 ram_v_waddr <= ram_v_waddr;
- 212 end
- 213
- 214 //产生行ram写使能
- 215 always@(posedge sys_clk or negedge rst_n )begin
- 216 if(~rst_n)
- 217 ram_v_wren <= 0;
- 218 else if(frame_flag || fifo_line_finish)
- 219 ram_v_wren <= 0;
- 220 else if(scaler_rd_req)
- 221 ram_v_wren <= 1;
- 222 else
- 223 ram_v_wren <= 0;
- 224 end
- 225
- 226 //产生ram的写切换信号
- 227 always@(posedge sys_clk or negedge rst_n)begin
- 228 if(~rst_n || frame_flag)
- 229 wr_buf_sel <= 1'b0;
- 230 else if(fifo_line_finish)
- 231 wr_buf_sel <= ~wr_buf_sel;
- 232 else
- 233 wr_buf_sel <= wr_buf_sel;
- 234 end
- 235
- 236 //对fifo读出数据的行数计数
- 237 always@(posedge sys_clk or negedge rst_n)begin
- 238 if(~rst_n)
- 239 wr_lines <= 12'd0;
- 240 else if(frame_flag)
- 241 wr_lines <= 12'd0;
- 242 else if(fifo_line_finish)
- 243 wr_lines <= wr_lines + 12'd1;
- 244 else
- 245 wr_lines <= wr_lines;
- 246 end
- 247
- 248 //对行缩放因子进行累加
- 249 always@(posedge sys_clk or negedge rst_n)begin
- 250 if(~rst_n)
- 251 scale_map_v <= 20'd0;
- 252 else if(frame_flag )
- 253 scale_map_v <= 20'd0;
- 254 else if(scale_line_finish && (scale_line < t_height - 1))
- 255 scale_map_v <= scale_map_v + {4'd0,k_v_reg};
- 256 else
- 257 scale_map_v <= scale_map_v;
- 258 end
- 259
- 260 //产生行缩放不需要请求数据标志
- 261 always@(posedge sys_clk or negedge rst_n)begin
- 262 if(~rst_n)
- 263 rd_v_not_need_data <= 1'd0;
- 264 else if(frame_flag )
- 265 rd_v_not_need_data <= 1'd0;
- 266 else if(scale_line_finish && one_line_data_ready && ~scale_v_end_line)
- 267 if(scale_map_v[19:8] == scale_map_v_next[19:8])
- 268 rd_v_not_need_data <= 1;
- 269 else
- 270 rd_v_not_need_data <= 0;
- 271 else
- 272 rd_v_not_need_data <= rd_v_not_need_data;
- 273 end
- 274
- 275 //对缩放后的行进行计数
- 276 always@(posedge sys_clk or negedge rst_n)begin
- 277 if(~rst_n)
- 278 scale_line <= 12'd0;
- 279 else if( frame_flag )
- 280 scale_line <= 12'd0;
- 281 else if(scale_line_finish)
- 282 scale_line <= scale_line + 12'd1;
- 283 else
- 284 scale_line <= scale_line;
- 285 end
- 286
- 287 //产生ram数据首行写入使能
- 288 always@(posedge sys_clk)begin
- 289 if(~rst_n)
- 290 one_line_data_ready <= 0;
- 291 else if(frame_flag)
- 292 one_line_data_ready <= 0;
- 293 else if(fifo_line_finish)
- 294 one_line_data_ready <= 1;
- 295 else
- 296 one_line_data_ready <= one_line_data_ready;
- 297 end
- 298
- 299 //产生ram数据可读使能
- 300 always@(posedge sys_clk)begin
- 301 if(~rst_n)
- 302 pixel_v_data_read_en <= 0;
- 303 else if(frame_flag)
- 304 pixel_v_data_read_en <= 0;
- 305 else if(scale_h_not_end && scaler_up)
- 306 pixel_v_data_read_en <= 0;
- 307 else if( ((ram_v_waddr >= ram_v_raddr + 2) && scale_v_line_valid )
- 308 || (rd_v_not_need_data && ram_v_raddr< s_width - 1)
- 309 || (scale_v_end_line && scale_v_line_valid && (ram_v_raddr< s_width - 1) ) )
- 310 pixel_v_data_read_en <= 1;
- 311 else
- 312 pixel_v_data_read_en <= 0;
- 313 end
- 314
- 315 //产生最后一行标志信号
- 316 always@(posedge sys_clk or negedge rst_n )begin
- 317 if(~rst_n)
- 318 scale_v_end_line <= 0;
- 319 else if(frame_flag )
- 320 scale_v_end_line <= 0;
- 321 else if((scale_line == t_height - 1) && scale_line_finish_d[10] )
- 322 scale_v_end_line <= 1;
- 323 else
- 324 scale_v_end_line <= scale_v_end_line;
- 325
- 326 end
- 327
- 328 //产生行ram读地址
- 329 always@(posedge sys_clk or negedge rst_n )begin
- 330 if(~rst_n)
- 331 ram_v_raddr <= 0;
- 332 else if(frame_flag || (scale_line_finish_d[8] && ~scale_v_end_line))
- 333 ram_v_raddr <= 0;
- 334 else if(pixel_v_data_read_en )
- 335 ram_v_raddr <= ram_v_raddr + 1;
- 336 else
- 337 ram_v_raddr <= ram_v_raddr;
- 338 end
- 339
- 340 //产生行运算输入使能有效信号
- 341 always@(posedge sys_clk or negedge rst_n )begin
- 342 if(~rst_n)
- 343 calu_v_data_in_en <= 0;
- 344 else if(pixel_v_data_read_en_d0)
- 345 calu_v_data_in_en <= 1;
- 346 else
- 347 calu_v_data_in_en <= 0;
- 348 end
- 349
- 350 //产生行缩放完成信号
- 351 always@(posedge sys_clk or negedge rst_n )begin
- 352 if(~rst_n)
- 353 scale_line_finish <= 0;
- 354 else if(frame_flag )
- 355 scale_line_finish <= 0;
- 356 else if((ram_v_raddr_d0 == s_width - 1) && (ram_v_raddr_d1 == s_width - 2))
- 357 scale_line_finish <= 1;
- 358 else
- 359 scale_line_finish <= 0;
- 360 end
- 361
- 362 //对相邻的2行进行权重赋值
- 363 always@(posedge sys_clk)begin
- 364 if(~rst_n)begin
- 365 a_coff_v <= 0;
- 366 b_coff_v <= 0;
- 367 end
- 368 else begin
- 369 a_coff_v <= scale_map_v[7:0];
- 370 b_coff_v <= ~scale_map_v[7:0];
- 371 end
- 372 end
- 373
- 374 //根据ram写切换使能对对应的行寄存器赋值
- 375 always@(posedge sys_clk)begin
- 376 if(~rst_n)begin
- 377 line0_pixel_data <= 0;
- 378 line1_pixel_data <= 0;
- 379 end
- 380 else if(wr_buf_sel)begin
- 381 line0_pixel_data <= ram_v_data_out0;
- 382 line1_pixel_data <= ram_v_data_out1;
- 383 end
- 384 else begin
- 385 line0_pixel_data <= ram_v_data_out1;
- 386 line1_pixel_data <= ram_v_data_out0;
- 387 end
- 388 end
- 389
- 390 //对读出的fifo数据通过ram进行存储
- 391 ram_scaler u_ram_scaler_v_0(
- 392 .clka (sys_clk),
- 393 .wea (ram_v_wren & wr_buf_sel),
- 394 .addra (ram_v_waddr),
- 395 .dina (data_in),
- 396 .clkb (sys_clk),
- 397 .addrb (ram_v_raddr),
- 398 .doutb (ram_v_data_out0)
- 399 );
- 400
- 401 //对读出的fifo数据通过ram进行存储
- 402 ram_scaler u_ram_scaler_v_1(
- 403 .clka (sys_clk),
- 404 .wea (ram_v_wren & ~wr_buf_sel),
- 405 .addra (ram_v_waddr),
- 406 .dina (data_in),
- 407 .clkb (sys_clk),
- 408 .addrb (ram_v_raddr),
- 409 .doutb (ram_v_data_out1)
- 410 );
- 411
- 412 //对行进行加权运算
- 413 calu calu_v(
- 414 .sys_clk(sys_clk),
- 415 .a(line0_pixel_data),
- 416 .b(line1_pixel_data),
- 417 .a_coff(a_coff_v),
- 418 .b_coff(b_coff_v),
- 419 .c(c_v),
- 420 .data_en_in(calu_v_data_in_en),
- 421 .data_en_out(data_en_out_calu_v)
- 422 );
复制代码
代码第101行定义了一个产生下一次行缩放因子累加寄存器,这个信号的作用是与行缩放因子累加寄存器进行对比,以此来判断是否需要从缩放前的fifo读数据。
代码第104行定义了一个产生下一次列缩放因子累加寄存器,这个信号的作用是与列缩放因子累加寄存器进行对比,以此来产生有效列缩放标志。
代码第107行是产生有效行缩放标志。当这个信号为高时,表示此时存在ram中的2行数据需要进行缩放处理。
代码第110行和111行是产生有效列缩放标志。当这个信号为高时,表示此时从列ram读出来的数据也是下一个时钟周期需要运算的数据,因此读ram地址此时不累加。
代码第114行和126行是产生fifo读请求。当data_ready拉高表明fifo中已经存有一行数据,rd_v_not_need_data拉高则表示此时2个场ram不能写入数据,所以fifo读请求只有在data_ready为高并且rd_v_not_need_data为低时才能拉高。
代码第185行至190行,此处判断此次缩放是放大还是缩小,当信号scaler_up为高时为放大,反之为缩小。
代码第193行至200行,产生了fifo一行读完信号信号。当场ram的写地址等于原图像的场分辨率时,表示此次从fifo中已经读出一行的数据。
代码第227行至234行,产生ram的写切换信号。本次实验采用的是双线性插值法,所以需要缓存2行,因此写切换信号是为了将数据乒乓写入场ram。
代码第249行至258行,是对行缩放因子进行累加。当场ram一行数据读完以后,对行缩放因子进行累加,在帧头到来时对信号进行清零。
代码第261行至273行,是产生行缩放不需要请求数据标志。当行缩放因子累加值的高12位与下一行的行缩放因子累加值高12位相等时,表示下次插值的2行还是现在存在场ram的2行,因此不需要从fifo里面读出数据。波形如下所示:
图 50.4.2 缩放时序1
代码第288行至297行,是产生ram数据首行写入使能,当这个信号拉高时,表示新的一帧中场ram中已经写入了一行数据,下一行可以开始进行插值运算了。
代码第300行至313行,是产生ram数据可读使能。当这个信号拉高时,场ram的读地址开始累加。当行缩放没有处理完时,该信号不能拉高。这里设置写地址大于读地址这个条件,是为了避免读超过写造成读出的数据错误。当信号rd_v_not_need_data拉高时,表示此次不需要写数据,所以只要避免读地址超出原图的行分辨率即可。这里添加最后一行标志信号(scale_v_end_line)为高时的判断条件是为了避免最后一行没有参与插值运算。
代码第316行至326行,是产生最后一行标志信号。当这个信号被拉高时,表示此次是进行这帧的最后一行的缩放。
代码第341行至348行,产生行运算数据有效使能 。这里相对于信号pixel_v_data_read_en延迟了2拍,因为ram数据是晚于数据一个周期出现的并且ram出来的数据还要经过信号wr_buf_sel做个切换才能输入到行运算模块,所以这里要延迟2拍以同步数据。
代码第363行至372行,是进行权重赋值。根据双线性插值法公式中对权重的定义分别是β和(1-β),这里的scale_map_v[7:0]就相当于β,~scale_map_v[7:0]相当于(1-β)。
代码第375行至388行,是根据ram写切换使能对对应的行寄存器赋值。当信号wr_buf_sel为高时,此时场ram0的输出数据为上一行数据,场ram1的输出数据为现在这行的输出数据;当信号wr_buf_sel为低时,此时场ram1的输出数据为上一行数据,场ram0的输出数据为现在这行的输出数据。
- 425 //产生列ram的写使能和数据
- 426 always@(posedge sys_clk)begin
- 427 if(~rst_n)begin
- 428 ram_h_data_in <= 0;
- 429 ram_h_wren <= 0;
- 430 end
- 431 else if(frame_flag_d0)begin
- 432 ram_h_data_in <= 0;
- 433 ram_h_wren <= 0;
- 434 end
- 435 else if(data_en_out_calu_v)begin
- 436 ram_h_data_in <= c_v;
- 437 ram_h_wren <= data_en_out_calu_v;
- 438 end
- 439 else begin
- 440 ram_h_data_in <= 0;
- 441 ram_h_wren <= 0;
- 442 end
- 443 end
- 444
- 445 //产生列ram的写地址
- 446 always@(posedge sys_clk or negedge rst_n )begin
- 447 if(~rst_n)
- 448 ram_h_waddr <= 0;
- 449 else if(frame_flag_d0)
- 450 ram_h_waddr <= 0;
- 451 else if(scale_h_finish )
- 452 ram_h_waddr <= 0;
- 453 else if(ram_h_wren )
- 454 ram_h_waddr <= ram_h_waddr + 1;
- 455 else
- 456 ram_h_waddr <= ram_h_waddr;
- 457 end
- 458
- 459 //产生列ram数据可读使能
- 460 always@(posedge sys_clk)begin
- 461 if(~rst_n)
- 462 pixel_h_data_read_en <= 0;
- 463 else if(frame_flag_d0)
- 464 pixel_h_data_read_en <= 0;
- 465 else if(scale_h_finish )
- 466 pixel_h_data_read_en <= 0;
- 467 else if(ram_h_waddr >= ram_h_raddr + 2)
- 468 pixel_h_data_read_en <= 1;
- 469 else
- 470 pixel_h_data_read_en <= 0;
- 471 end
- 472
- 473 //产生列ram的读地址
- 474 always@(posedge sys_clk or negedge rst_n )begin
- 475 if(~rst_n)
- 476 ram_h_raddr <= 0;
- 477 else if(frame_flag_d0 || scale_h_finish )
- 478 ram_h_raddr <= 0;
- 479 else if((pixel_h_data_read_en == 1) && (pixel_h_data_read_en_d0 == 0))
- 480 ram_h_raddr <= 0;
- 481 else if(scale_h_line_valid && scaler_up)
- 482 ram_h_raddr <= ram_h_raddr;
- 483 else if(pixel_h_data_read_en)
- 484 ram_h_raddr <= ram_h_raddr + 1;
- 485 else
- 486 ram_h_raddr <= ram_h_raddr;
- 487 end
- 488
- 489 //对列缩放因子进行累加
- 490 always@(posedge sys_clk or negedge rst_n)begin
- 491 if(~rst_n)
- 492 scale_map_h <= 20'd0;
- 493 else if(frame_flag_d0 || scale_h_finish)
- 494 scale_map_h <= 20'd0;
- 495 else if(pixel_h_data_read_en_d1 && (ram_h_raddr> scale_map_h[19:8]))
- 496 scale_map_h <= scale_map_h + {4'd0,k_h_reg};
- 497 else
- 498 scale_map_h <= scale_map_h;
- 499 end
- 500
- 501 //产生列运算输入使能有效信号
- 502 always@(posedge sys_clk or negedge rst_n)begin
- 503 if(~rst_n)
- 504 calu_h_data_in_en <= 1'd0;
- 505 else if(frame_flag_d0 )
- 506 calu_h_data_in_en <= 1'd0;
- 507 else if(ram_h_raddr >= s_width && !scaler_up && cnt_h_pixel>= t_width - 1)
- 508 calu_h_data_in_en <= 1'd0;
- 509 else if(pixel_h_data_read_en_d0 && (cnt_h_pixel <= t_width - 1)
- 510 && (((scale_map_h[19:8] + 1) == ram_h_raddr) && !scaler_up) )
- 511 calu_h_data_in_en <= 1;
- 512 else if(pixel_h_data_read_en_d0 && (cnt_h_pixel <= t_width - 2) && scaler_up )
- 513 calu_h_data_in_en <= 1;
- 514 else
- 515 calu_h_data_in_en <= 0;
- 516 end
- 517
- 518 //产生列缩放完成信号
- 519 always@(posedge sys_clk or negedge rst_n )begin
- 520 if(~rst_n)
- 521 scale_h_finish <= 0;
- 522 else if(frame_flag_d0 )
- 523 scale_h_finish <= 0;
- 524 else if((ram_h_raddr_d0 == s_width-1) && (ram_h_raddr_d1 == s_width - 2))
- 525 scale_h_finish <= 1;
- 526 else
- 527 scale_h_finish <= 0;
- 528 end
- 529
- 530 //产生列缩放未结束信号
- 531 always@(posedge sys_clk or negedge rst_n )begin
- 532 if(~rst_n)
- 533 scale_h_not_end <= 0;
- 534 else if(frame_flag_d0 || scale_h_finish_d[4])
- 535 scale_h_not_end <= 0;
- 536 else if(ram_h_waddr == s_width - 1 )
- 537 scale_h_not_end <= 1;
- 538 else
- 539 scale_h_not_end <= scale_h_not_end;
- 540 end
- 541
- 542 //对列运算的数据赋值
- 543 always@(posedge sys_clk)begin
- 544 if(~rst_n)begin
- 545 col0_pixel_data <= 0;
- 546 col1_pixel_data <= 0;
- 547 calu_h_data_in_en_d0 <= 0;
- 548 end
- 549 else begin
- 550 col0_pixel_data <= ram_h_data_out;
- 551 col1_pixel_data <= col0_pixel_data;
- 552 calu_h_data_in_en_d0 <= calu_h_data_in_en;
- 553 end
- 554 end
- 555
- 556 //对相邻的2列进行权重赋值
- 557 always@(posedge sys_clk)begin
- 558 if(~rst_n)begin
- 559 a_coff_h <= 0;
- 560 b_coff_h <= 0;
- 561 end
- 562 else begin
- 563 a_coff_h <= scale_map_h[7:0];
- 564 b_coff_h <= ~scale_map_h[7:0];
- 565 end
- 566 end
- 567
- 568 //对列运算后的信号进行输出寄存
- 569 always@(posedge sys_clk)begin
- 570 if(~rst_n)begin
- 571 scaler_wren <= 0;
- 572 data_out <= 0;
- 573 end
- 574 else begin
- 575 scaler_wren <= calu_h_data_out_en;
- 576 data_out <= c_h;
- 577 end
- 578 end
- 579
- 580 //对列运算输入使能进行计数
- 581 always@(posedge sys_clk)begin
- 582 if(~rst_n || frame_flag_d0)begin
- 583 cnt_h_pixel <= 0;
- 584 end
- 585 else begin
- 586 if(scale_h_finish)
- 587 cnt_h_pixel <= 0;
- 588 else if(calu_h_data_in_en)
- 589 cnt_h_pixel <= cnt_h_pixel + 1;
- 590 else
- 591 cnt_h_pixel <= cnt_h_pixel;
- 592 end
- 593 end
- 594
- 595 //存放行缩放后的数据
- 596 ram_scaler u_ram_scaler_h(
- 597 .clka (sys_clk),
- 598 .wea (ram_h_wren),
- 599 .addra (ram_h_waddr),
- 600 .dina (ram_h_data_in),
- 601 .clkb (sys_clk),
- 602 .addrb (ram_h_raddr),
- 603 .doutb (ram_h_data_out)
- 604 );
- 605
- 606 //对列进行加权运算
- 607 calu calu_h(
- 608 .sys_clk(sys_clk),
- 609 .a(col0_pixel_data),
- 610 .b(col1_pixel_data),
- 611 .a_coff(a_coff_h),
- 612 .b_coff(b_coff_h),
- 613 .c(c_h),
- 614 .data_en_in(calu_h_data_in_en_d0),
- 615 .data_en_out(calu_h_data_out_en)
- 616 );
- 617
- 618 endmodule
复制代码
代码第426行至443行,是产生列ram的写使能和数据。在行运算数据输出使能有效的情况下,将行运算模块的输出信号分别赋给列ram的写使能和数据。
代码第460行至471行是产生ram数据可读使能。当这个信号拉高时,场ram的读地址开始累加。这里设置写地址大于读地址这个条件,是为了避免读超过写造成读出的数据错误。当帧头到来或者一行列缩放结束,对信号ram数据可读使能进行清零。波形如下所示:
图 50.4.3 缩放时序2
代码第474行至487行是产生ram读地址。当处于图像放大时,会出现原图像的2个点进行多次插值的现象,因此出现这种情况的时候,ram中的读地址是不需要累加的。波形如下所示:
图 50.4.4 缩放时序3
代码第490行至499行是对列缩放因子进行累加。当场ram一行数据读完以后,对行缩放因子进行累加,在帧头到来时对信号进行清零。
代码第502行至516行是产生列运算输入使能有效信号。该信号在图像放大的情况下是连续的,波形如图 50.4.5所示。因为图像若是处于放大的情况下,则原图的每个像素点都要被插值,所以列运算输入使能有效信号是连续的;图 50.4.6是处于图像缩小情况下的列运算输入使能有效信号的波形,当图像处于缩小的情况下,原图中的不是每个像素点都要被插值,所以该信号是不连续的。
图 50.4.5 缩放时序4
图 50.4.6 缩放时序5
代码第502行至516行是产生列缩放未结束信号。该信号的作用就是限制场ram,在行缩放还未结束前不要再从ram中读数据,防止出错。波形如下所示:
图 50.4.7 缩放时序6
代码第557行至566行是对相邻的2列进行权重赋值。根据双线性插值法公式中对权重的定义分别是β和(1-β),这里的scale_map_h[7:0]就相当于β,~scale_map_h[7:0]相当于(1-β)。
插值运算模块的代码如下:
- 1 module calu#
- 2 (
- 3 parameter DATA_WIDTH = 8)
- 4 (
- 5 input sys_clk,
- 6 input [DATA_WIDTH - 1:0] a, //像素数据
- 7 input [DATA_WIDTH - 1:0] b, //像素数据
- 8 input [7:0] a_coff, //行权重系数
- 9 input [7:0] b_coff, //行权重系数
- 10 output [DATA_WIDTH - 1:0] c, //缩放后的像素数据
- 11 input [7:0] a_coff_next, //下级权重系数输入
- 12 input [7:0] b_coff_next, //下级权重系数输入
- 13 output [7:0] a_coff_next_out,//下级权重系数输出
- 14 output [7:0] b_coff_next_out,//下级权重系数输出
- 15 input data_en_in, //数据有效使能输入
- 16 input scale_en_in, //数据缩放有效使能输入
- 17 output data_en_out, //数据有效使能输出
- 18 output scale_en_out //数据缩放有效使能输出
- 19 );
- 20
- 21 //reg define
- 22 reg[DATA_WIDTH - 1:0] a_reg;
- 23 reg[DATA_WIDTH - 1:0] b_reg;
- 24 reg[7:0] a_coff_reg;
- 25 reg[7:0] b_coff_reg;
- 26 reg[7:0] a_coff_next_out;//下级系数
- 27 reg[7:0] b_coff_next_out;//下级系数
- 28 reg data_en_out;
- 29 reg scale_en_out;
- 30 reg[15:0] a_mult0;
- 31 reg[15:0] b_mult0;
- 32 reg[15:0] add0;
- 33 reg[8:0] add_tmp0;
- 34 reg data_en_in_d0;
- 35 reg data_en_in_d1;
- 36 reg data_en_in_d2;
- 37 reg scale_en_in_d0;
- 38 reg scale_en_in_d1;
- 39 reg scale_en_in_d2;
- 40 reg[7:0] a_coff_next_d0;
- 41 reg[7:0] a_coff_next_d1;
- 42 reg[7:0] a_coff_next_d2;
- 43 reg[7:0] b_coff_next_d0;
- 44 reg[7:0] b_coff_next_d1;
- 45 reg[7:0] b_coff_next_d2;
- 46
- 47 //*****************************************************
- 48 //** main code
- 49 //*****************************************************
- 50
- 51 assign c = add_tmp0[8] ? 8'hf0 : add_tmp0[7:0];
- 52
- 53 //打拍寄存
- 54 always@(posedge sys_clk)begin
- 55 a_reg <= a;
- 56 b_reg <= b;
- 57 a_coff_reg <= a_coff;
- 58 b_coff_reg <= b_coff;
- 59 data_en_in_d0 <= data_en_in;
- 60 scale_en_in_d0 <= scale_en_in;
- 61 a_coff_next_d0 <= a_coff_next;
- 62 b_coff_next_d0 <= b_coff_next;
- 63 end
- 64
- 65 //对像素点进行加权运算
- 66 always@(posedge sys_clk)begin
- 67 a_mult0 <= a_reg[7:0] * a_coff_reg;
- 68 b_mult0 <= b_reg[7:0] * b_coff_reg;
- 69 add0 <= a_mult0 + b_mult0;
- 70 add_tmp0 <= {1'b0,add0[15:8]} + {8'd0,{(add0[7:0] > 8'h66)}};
- 71
- 72 end
- 73
- 74 //对信号进行延时
- 75 always@(posedge sys_clk)begin
- 76 data_en_in_d1 <= data_en_in_d0;
- 77 scale_en_in_d1 <= scale_en_in_d0;
- 78 a_coff_next_d1 <= a_coff_next_d0;
- 79 b_coff_next_d1 <= b_coff_next_d0;
- 80
- 81 data_en_in_d2 <= data_en_in_d1;
- 82 scale_en_in_d2 <= scale_en_in_d1;
- 83 a_coff_next_d2 <= a_coff_next_d1;
- 84 b_coff_next_d2 <= b_coff_next_d1;
- 85
- 86 data_en_out <= data_en_in_d2;
- 87 scale_en_out <= scale_en_in_d2;
- 88 a_coff_next_out <= a_coff_next_d2;
- 89 b_coff_next_out <= b_coff_next_d2;
- 90 end
- 91
- 92 endmodule
复制代码
代码第51行,用add_tmp0的第8位来做判断,是为了防止缩放后的数据超出255。
代码第54行至63行,将所有的输入信号进行打拍寄存,是为了减少同一个信号的输出扇出,优化代码的时序。
代码第66行至72行,是对像素点进行加权运算。根据公式P = (1 -β)*P1 +β*P2,这里的信号a_reg和b_reg相当于公式中的P1和P2,a_coff_reg和b_coff_reg相当于公式中的(1 -β)和β,信号add0相当于公式中的P。因为之前将缩放因子扩大了256倍,所以最后加权得到的信号要除以256,并且当小数部分大于102时,整数部分要加1。
代码第75行至90行,是对信号进行打拍。上面的加权运算总共用了三个时钟周期,所以这里将输入的使能信号打三拍后,才能使输出的数据与输出的数据有效使能一一对应上。
下面是按键控制模块的代码:
- 1 module key_ctrl(
- 2 //input
- 3 input sys_clk, //时钟信号50Mhz
- 4 input sys_rst_n, //复位信号
- 5 input touch_key, //按键
- 6
- 7 //output
- 8 output [2:0] change_en //切换信号
- 9 );
- 10
- 11 //reg define
- 12 reg touch_key_d0;
- 13 reg touch_key_d1;
- 14 reg vs_in_d0;
- 15 reg touch_en;
- 16 reg [23:0] cnt_time;
- 17 reg [2:0] change_en ;
- 18
- 19 //*****************************************************
- 20 //** main code
- 21 //*****************************************************
- 22
- 23 //对触摸按键端口的数据延迟两个时钟周期
- 24 always [url=home.php?mod=space&uid=95564]@[/url] (posedge sys_clk or negedge sys_rst_n) begin
- 25 if(!sys_rst_n) begin
- 26 touch_key_d0 <= 1'b0;
- 27 touch_key_d1 <= 1'b0;
- 28 end
- 29 else begin
- 30 touch_key_d0 <= touch_key;
- 31 touch_key_d1 <= touch_key_d0;
- 32 end
- 33 end
- 34
- 35 //产生切换信号
- 36 always @ (posedge sys_clk or negedge sys_rst_n) begin
- 37 if(!sys_rst_n) begin
- 38 change_en <= 3'd4;
- 39 end
- 40 else begin
- 41 if(cnt_time == 500000)
- 42 if(change_en == 3'b100)
- 43 change_en <= 3'b1;
- 44 else
- 45 change_en <= {change_en[1:0],1'b0};
- 46 else
- 47 change_en <= change_en ;
- 48 end
- 49 end
- 50
- 51 //对按键的低电平时间进行计数
- 52 always @ (posedge sys_clk or negedge sys_rst_n) begin
- 53 if(!sys_rst_n) begin
- 54 cnt_time <= 24'b0;
- 55 end
- 56 else begin
- 57 if(touch_key_d1)
- 58 cnt_time <= 24'b0;
- 59 else if(cnt_time >= 2500000)
- 60 cnt_time <= cnt_time;
- 61 else
- 62 cnt_time <= cnt_time + 1;
- 63 end
- 64 end
- 65
- 66 endmodule
复制代码
代码第24至33行,是将输入的按键信号进行打拍,以减少信号的毛刺。
代码第36至49行,是对按键进行消抖处理。当按键按下的时间不超过10ms,则认为这是毛刺,此次的按键是无效的。当超过10ms时,将信号change_en进行移位。
代码第52至64行,是对按键按下的时间进行记时。当按键未按下时对计数器进行清零,按键按下的时间超过50ms后计数器不再累加。
下面是ddr参数切换模块的代码:
- 1 module para_change(
- 2 input clk_wr, //写时钟
- 3 input clk_rd, //读时钟
- 4 input [2:0] change_en, //切换信号,1为放大,2为缩小,4为原图
- 5 input rst_n, //复位信号
- 6
- 7 input rd_vsync, //读端场信号
- 8 input wr_vsync, //写端场信号
- 9 input [10:0] s_width, //缩放前宽度
- 10 input [10:0] s_height, //缩放前高度
- 11
- 12 output [10:0] t_width_wr, //写端缩放后宽度
- 13 output [10:0] t_height_wr, //写端缩放后高度
- 14 output [10:0] t_width_rd, //读端缩放后宽度
- 15 output [10:0] t_height_rd, //读端缩放后高度
- 16 output [15:0] h_scale_k, //列缩放因子
- 17 output [15:0] v_scale_k, //行缩放因子
- 18 output [2:0] change_en_wr, //写端切换信号
- 19 output [2:0] change_en_rd, //读端切换信号
- 20 output [27:0] app_addr_rd_max, //存入DDR3的最大读地址
- 21 output [7:0] rd_bust_len , //从DDR3中读数据时的突发长度
- 22 output [27:0] app_addr_wr_max, //存入DDR3的最大写地址
- 23 output [7:0] wr_bust_len //从DDR3中写数据时的突发长度
- 24
- 25 );
- 26
- 27 //reg define
- 28 reg [27:0] app_addr_rd_max;
- 29 reg [7:0] rd_bust_len;
- 30 reg [27:0] app_addr_wr_max;
- 31 reg [7:0] wr_bust_len;
- 32 reg [7:0] rd_vsync_d;
- 33 reg [7:0] wr_vsync_d;
- 34 reg [10:0] t_width_wr;
- 35 reg [10:0] t_height_wr;
- 36 reg [10:0] t_width_rd;
- 37 reg [10:0] t_height_rd;
- 38 reg [10:0] s_width_wr;
- 39 reg [10:0] s_height_wr;
- 40 reg [10:0] s_width_rd;
- 41 reg [10:0] s_height_rd;
- 42 reg [2:0] change_wr_en_d1;
- 43 reg [2:0] change_en_wr_d0;
- 44 reg [2:0] change_en_wr;
- 45 reg [2:0] change_en_rd;
- 46 reg [2:0] change_wr_en_d0;
- 47 reg delay_frame; //切换后写入一帧标志信号
- 48 reg [2:0] change_rd_en_d0;
- 49 reg [2:0] change_rd_en_d1;
- 50 reg delay_frame_d0;
- 51 reg [15:0] h_scale_k; //(s_width * 256)/ t_width
- 52 reg [15:0] v_scale_k; //(s_height * 256)/ t_height
- 53 reg [3:0] cmos_ps_cnt;
- 54
- 55 //wire define
- 56 wire [10:0] t_width_up; //放大后图像的宽度
- 57 wire [10:0] t_height_up; //放大后图像的高度
- 58 wire [10:0] t_width_down; //缩小后图像的宽度
- 59 wire [10:0] t_height_down; //缩小后图像的高度
- 60 wire para_finish;
- 61
- 62 //*****************************************************
- 63 //** main code
- 64 //*****************************************************
- 65 //放大后图像的分辨率
- 66 assign t_width_up = 11'd1024;
- 67 assign t_height_up = 11'd768;
- 68 //缩小后图像的分辨率
- 69 assign t_width_down = 11'd560;
- 70 assign t_height_down = 11'd400;
- 71
- 72 //对写端输入信号进行寄存
- 73 always@(posedge clk_wr or negedge rst_n)begin
- 74 if(~rst_n)begin
- 75 wr_vsync_d <= 4'd0;
- 76 change_wr_en_d0 <= 1'b0;
- 77 change_en_wr_d0 <= 1'b0;
- 78 s_width_wr <= 11'b0;
- 79 s_height_wr <= 11'b0;
- 80 end
- 81 else begin
- 82 wr_vsync_d <= {wr_vsync_d[6:0],wr_vsync };
- 83 s_width_wr <= s_width;
- 84 s_height_wr <= s_height;
- 85 change_wr_en_d0 <= change_en;
- 86 change_wr_en_d1 <= change_wr_en_d0;
- 87 change_en_wr_d0 <= change_en_wr;
- 88 end
- 89 end
- 90
- 91 //对读端输入信号进行寄存
- 92 always@(posedge clk_rd or negedge rst_n)begin
- 93 if(~rst_n)begin
- 94 rd_vsync_d <= 4'd0;
- 95 change_rd_en_d0 <= 1'b0;
- 96 delay_frame_d0 <= 1'b0;
- 97 s_width_rd <= 11'b0;
- 98 s_height_rd <= 11'b0;
- 99 end
- 100 else begin
- 101 rd_vsync_d <= {rd_vsync_d[6:0],rd_vsync };
- 102 s_width_rd <= s_width;
- 103 s_height_rd <= s_height;
- 104 change_rd_en_d0 <= change_en;
- 105 change_rd_en_d1 <= change_rd_en_d0;
- 106 delay_frame_d0 <= delay_frame;
- 107 end
- 108 end
- 109
- 110 //产生写端切换使能
- 111 always@(posedge clk_wr or negedge rst_n)begin
- 112 if(~rst_n)
- 113 change_en_wr <= 1'b0;
- 114 else if(wr_vsync_d[1] && ~wr_vsync_d[0])
- 115 change_en_wr <= change_wr_en_d0;
- 116 else
- 117 change_en_wr <= change_en_wr;
- 118 end
- 119
- 120 //写端缩放后的分辨率
- 121 always@(posedge clk_wr or negedge rst_n)begin
- 122 if(~rst_n)begin
- 123 t_width_wr <= 11'b0;
- 124 t_height_wr <= 11'b0;
- 125 end
- 126 else if(wr_vsync_d[2] && ~wr_vsync_d[1])begin
- 127 if(change_en_wr[0]) begin
- 128 t_width_wr <= t_width_up;
- 129 t_height_wr <= t_height_up;
- 130 end
- 131 else if(change_en_wr[1]) begin
- 132 t_width_wr <= t_width_down;
- 133 t_height_wr <= t_height_down;
- 134 end
- 135 else begin
- 136 t_width_wr <= s_width;
- 137 t_height_wr <= s_height;
- 138 end
- 139 end
- 140 else begin
- 141 t_width_wr <= t_width_wr;
- 142 t_height_wr <= t_height_wr;
- 143 end
- 144 end
- 145
- 146 //产生行场缩放因子
- 147 always@(posedge clk_wr or negedge rst_n)begin
- 148 if(~rst_n)begin
- 149 h_scale_k <= 16'b0;
- 150 v_scale_k <= 16'b0;
- 151 end
- 152 else if(wr_vsync_d[3] && ~wr_vsync_d[2]) begin
- 153 if(change_en_wr[0]) begin
- 154 h_scale_k <= 16'd180; //(s_width * 256)/ t_width
- 155 v_scale_k <= 16'd160; //(s_height * 256)/ t_height
- 156 end
- 157 else if(change_en_wr[1]) begin
- 158 h_scale_k <= 16'd329;
- 159 v_scale_k <= 16'd307;
- 160 end
- 161 else begin
- 162 h_scale_k <= 16'd256;
- 163 v_scale_k <= 16'd256;
- 164 end
- 165 end
- 166 else begin
- 167 h_scale_k <= h_scale_k;
- 168 v_scale_k <= v_scale_k;
- 169 end
- 170 end
- 171
- 172 //产生切换后写入一帧标志信号
- 173 always@(posedge clk_wr or negedge rst_n)begin
- 174 if(~rst_n)
- 175 delay_frame <= 1'b0;
- 176 else if(change_wr_en_d0 != change_wr_en_d1)
- 177 delay_frame <= 1'b1;
- 178 else if((wr_vsync_d[5] && ~wr_vsync_d[4]) && (change_wr_en_d0 == change_en_wr))
- 179 delay_frame <= 1'b0;
- 180 else
- 181 delay_frame <= delay_frame;
- 182 end
- 183
- 184 //产生写端的ddr参数
- 185 always@(posedge clk_wr or negedge rst_n)begin
- 186 if(~rst_n)begin
- 187 wr_bust_len <= s_width[10:3];
- 188 app_addr_wr_max <= s_width * s_height;
- 189 end
- 190 else if(change_en_wr[2] )begin
- 191 if(wr_vsync_d[5] && ~wr_vsync_d[4])begin
- 192 wr_bust_len <= s_width_wr[10:3];
- 193 app_addr_wr_max <= s_width_wr * s_height_wr;
- 194 end
- 195 else begin
- 196 wr_bust_len <= wr_bust_len;
- 197 app_addr_wr_max <= app_addr_wr_max;
- 198 end
- 199 end
- 200 else begin
- 201 if(wr_vsync_d[5] && ~wr_vsync_d[4])begin
- 202 wr_bust_len <= t_width_wr[10:3];
- 203 app_addr_wr_max <= t_width_wr * t_height_wr;
- 204 end
- 205 else begin
- 206 wr_bust_len <= wr_bust_len;
- 207 app_addr_wr_max <= app_addr_wr_max;
- 208 end
- 209 end
- 210 end
- 211
- 212 //产生读端切换使能
- 213 always@(posedge clk_rd or negedge rst_n)begin
- 214 if(~rst_n)
- 215 change_en_rd <= 1'b0;
- 216 else if(rd_vsync_d[1] && ~rd_vsync_d[0] && ~delay_frame_d0)
- 217 change_en_rd <= change_rd_en_d0;
- 218 else
- 219 change_en_rd <= change_en_rd;
- 220 end
- 221
- 222 //读端缩放后的分辨率
- 223 always@(posedge clk_wr or negedge rst_n)begin
- 224 if(~rst_n)begin
- 225 t_width_rd <= 11'b0;
- 226 t_height_rd <= 11'b0;
- 227 end
- 228 else if( rd_vsync_d[3] && ~rd_vsync_d[2] && ~delay_frame_d0)begin
- 229 if(change_en_rd[0] ) begin
- 230 t_width_rd <= t_width_up;
- 231 t_height_rd <= t_height_up;
- 232 end
- 233 else if(change_en_rd[1]) begin
- 234 t_width_rd <= t_width_down;
- 235 t_height_rd <= t_height_down;
- 236 end
- 237 else begin
- 238 t_width_rd <= s_width;
- 239 t_height_rd <= s_height;
- 240 end
- 241 end
- 242 else begin
- 243 t_width_rd <= t_width_rd;
- 244 t_height_rd <= t_height_rd;
- 245 end
- 246 end
- 247
- 248 //产生读端的ddr参数
- 249 always@(posedge clk_rd or negedge rst_n)begin
- 250 if(~rst_n)begin
- 251 rd_bust_len <= s_width[10:3];
- 252 app_addr_rd_max <= s_width * s_height;
- 253 end
- 254 else if(change_en_rd[2] )begin
- 255 if(rd_vsync_d[5] && ~rd_vsync_d[4] && ~delay_frame_d0)begin
- 256 rd_bust_len <= s_width_rd[10:3];
- 257 app_addr_rd_max <= s_width_rd * s_height_rd;
- 258 end
- 259 else begin
- 260 rd_bust_len <= rd_bust_len;
- 261 app_addr_rd_max <= app_addr_rd_max;
- 262 end
- 263 end
- 264 else begin
- 265 if(rd_vsync_d[5] && ~rd_vsync_d[4] && ~delay_frame_d0)begin
- 266 rd_bust_len <= t_width_rd[10:3];
- 267 app_addr_rd_max <= t_width_rd * t_height_rd;
- 268 end
- 269 else begin
- 270 rd_bust_len <= rd_bust_len;
- 271 app_addr_rd_max <= app_addr_rd_max;
- 272 end
- 273 end
- 274 end
- 275
- 276 endmodule
复制代码
代码第66至67行,分别设置了放大后图像的分辨率,本次实验将该分辨率设为1024x768。代码第69至70行,分别设置了缩小后图像的分辨率,本次实验将该分辨率设为560x400。这里有个地方需要大家注意下,本次实验中在ddr控制器模块是将16bit数据拼成128bit数据,所以这里缩放所设的列分辨率必须为8的倍数,否则显示图像会出错。
代码第111至118行,是产生写端的切换信号,至于为什么在场下降沿切换,这是为了避免在切换参数时,导致其他模块的时序出现错乱。
代码第121至144行,是产生缩放后的写分辨率。当写端场下降沿到来时,如果信号change_en_wr为1,代表此次的缩放为图像放大,所以将放大后图像的分辨率输出到缩放模块;如果信号change_en_wr为2,代表此次的缩放为图像缩小,所以将缩小后图像的分辨率输出到缩放模块;如果信号change_en_wr为4,代表此次的缩放为原图输出,所以将原图像的分辨率输出到缩放模块。
代码第147至170行,是产生产生行场缩放因子,其计算公式为:缩放因子=(原图分辨率*256)/缩放后的分辨率。这里之所以乘以256,是因为fpga不支持小数运算,所以这里将原图分辨率乘以256。
代码第173至182行,是产生一个切换后写入一帧的标志信号。按键按下后,写入ddr控制器的数据量是不同的,所以要先切换写端的参数,当至少写入一帧数据后再来切换读端的参数,这是为了在切换输入源时,屏幕显示出现花屏的现象。
代码第185至210行,是产生写端的ddr参数 。当切换信号change_en_wr[2]为高时并且场信号到来时,将原图的参数写入ddr控制器,反之就将缩放后的参数写入ddr控制器。
代码第223至246行,是产生缩放后的读分辨率。当读端场下降沿到来时且写端已经写入切换后的至少一帧数据,如果信号change_en_rd为1,代表此次的缩放为图像放大,所以将放大后图像的分辨率输出到hdmi顶层模块;如果信号change_en_rd为2,代表此次的缩放为图像缩小,所以将缩小后图像的分辨率输出到hdmi顶层模块;如果信号change_en_rd为4,代表此次的缩放为原图输出,所以将原图像的分辨率输出到hdmi顶层模块。
代码第249至274行,是产生读端的ddr参数 。当切换信号change_en_rd [2]为高时,场信号到来并且写端已经写入切换后的至少一帧数据时,将原图的参数写入ddr控制器,反之就将缩放后的参数写入ddr控制器。
相对于“OV5640摄像头HDMI显示实验”的HDMI顶层模块,本次实验的HDMI顶层模块做了下面的修改:
- 14 input [10:0] t_width, //缩放后宽度
- 15 input [10:0] t_height, //缩放后高度
- 16 input [10:0] s_width, //缩放前宽度
- 17 input [10:0] s_height, //缩放前高度
- 18 input [2:0] change_en, //切换信号,1为放大,2为缩小,4为原图
复制代码
添加了上面的五个输入端口,这五个信号是为了切换读请求使能和有效数据,以及输出到显示模块的参数。
- 60 assign video_rgb_565 = change_en[0] ? video_rgb_dri : video_rgb_dis;
- 61 assign data_req = change_en[0] ? data_req_dri : data_req_dis;
- 62 assign video_width = change_en[2] ? s_width : t_width ;
- 63 assign video_height = change_en[2] ? s_height : t_height ;
复制代码
当显示的数据为放大后的数据时,请求信号和数据由驱动模块输出,反之由显示模块输出。当最后输出的图像为原图输出时,将原图的分辨率输出到显示模块,否则将缩放后的分辨率输出到显示模块。
- 83 //显示模块
- 84 lcd_display u_lcd_display(
- 85 .lcd_clk (pixel_clk), //lcd驱动时钟
- 86 .sys_rst_n (sys_rst_n), //复位信号
- 87
- 88 .pixel_xpos(pixel_xpos), //像素点横坐标
- 89 .pixel_ypos(pixel_ypos), //像素点纵坐标
- 90 .data_in (data_in), //像素点数据
- 91 .h_disp (h_disp), //LCD屏水平分辨率
- 92 .v_disp (v_disp), //LCD屏垂直分辨率
- 93 .t_width (video_width), //有效显示数据的宽度
- 94 .t_height (video_height), //有效显示数据的高度
- 95
- 96 .lcd_data (video_rgb_dis), //LCD像素点数据
- 97 .data_req (data_req_dis) //请求像素点颜色数据输入
- 98 );
复制代码
添加了一个显示模块。当显示数据为缩小后和原图的数据时,该数据不能显示全屏,所以需要显示模块来填充黑边来满足全屏显示的数据。
50.5下载验证
编译完工程之后我们就可以开始下载程序了。将OV5640摄像头模块插在超越者开发板的“OLED/CAMERA”插座上,并将HDMI电缆一端连接到开发板上的HDMI插座、另一端连接到显示器。将下载器一端连电脑,另一端与开发板上的JTAG端口连接,连接电源线并打开电源开关。接下来我们下载程序,验证OV5640 HDMI缩放显示功能。下载完成后观察HDMI显示器显示的图案如下图所示,说明OV5640摄像头HDMI缩放实验程序下载验证成功。
图 50.5.1 缩放实验HDMI实时显示原图图像
图 50.5.2 缩放实验HDMI实时显示缩小图像
图 50.5.3 缩放实验HDMI实时显示放大图像
|
|