OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 12768|回复: 0
打印 上一主题 下一主题

[XILINX] 【正点原子FPGA连载】第五十章基于OV5640摄像头HDMI缩放实验--摘自【正点原子】超越者之FPGA开发指南

[复制链接]

1402

主题

1418

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
5979
金钱
5979
注册时间
2019-5-8
在线时间
1683 小时
跳转到指定楼层
楼主
发表于 2021-1-26 11:01:12 | 只看该作者 |只看大图 回帖奖励 |正序浏览 |阅读模式
本帖最后由 正点原子运营 于 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. 1   module ov5640_hdmi_scale(   
  2. 2       input                 sys_clk,        //系统时钟
  3. 3       input                 sys_rst_n,      //系统复位,低电平有效
  4. 4       input                 key0,           //按键key0  
  5. 5       //摄像头接口                          
  6. 6       input                 cam_pclk,       //cmos 数据像素时钟
  7. 7       input                 cam_vsync,      //cmos 场同步信号
  8. 8       input                 cam_href,        //cmos 行同步信号
  9. 9       input   [7:0]         cam_data,        //cmos 数据
  10. 10      output                cam_rst_n,       //cmos 复位信号,低电平有效
  11. 11      output                cam_pwdn ,       //电源休眠模式选择 0:正常模式 1:电源休眠模式
  12. 12      output                cam_scl,         //cmos SCCB_SCL线
  13. 13      inout                 cam_sda,         //cmos SCCB_SDA线      
  14. 14    //ddr3 port
  15. 15      inout   [15:0]        mcb3_dram_dq,     
  16. 16      output  [13:0]        mcb3_dram_a,      
  17. 17      output  [2:0]         mcb3_dram_ba,     
  18. 18      output                mcb3_dram_ras_n,  
  19. 19      output                mcb3_dram_cas_n,   
  20. 20      output                mcb3_dram_we_n,   
  21. 21      output                mcb3_dram_reset_n,
  22. 22      output                mcb3_dram_cke,   
  23. 23      output                mcb3_dram_dm,     
  24. 24      inout                 mcb3_dram_udqs,   
  25. 25      inout                 mcb3_dram_udqs_n,
  26. 26      inout                 mcb3_rzq,         
  27. 27      inout                 mcb3_zio,         
  28. 28      output                mcb3_dram_udm,   
  29. 29      inout                 mcb3_dram_dqs,   
  30. 30      inout                 mcb3_dram_dqs_n,  
  31. 31      output                mcb3_dram_ck,     
  32. 32      output                mcb3_dram_ck_n,      
  33. 33  
  34. 34      output                init_calib_complete,   
  35. 35      //hdmi接口                              
  36. 36      output                tmds_clk_p,         // TMDS 时钟通道
  37. 37      output                tmds_clk_n,
  38. 38      output  [2:0]         tmds_data_p,        // TMDS 数据通道
  39. 39      output  [2:0]         tmds_data_n  
  40. 40      );
  41. 41  
  42. 42  //parameter define
  43. 43  parameter  V_CMOS_DISP = 11'd480;                  //CMOS分辨率--行
  44. 44  parameter  H_CMOS_DISP = 11'd720;                  //CMOS分辨率--列
  45. 45  parameter  TOTAL_H_PIXEL = H_CMOS_DISP + 12'd1216;
  46. 46  parameter  TOTAL_V_PIXEL = V_CMOS_DISP + 12'd504;      
  47. 47                                                                     
  48. 48  //wire define                          
  49. 49  wire         clk_50m                   ;  //50mhz时钟
  50. 50  wire         locked_100m               ;  //时钟锁定信号
  51. 51  wire         locked_300m               ;  //时钟锁定信号
  52. 52  wire         rst_n                     ;  //全局复位                                                               
  53. 53  wire         wr_en                     ;  //DDR3控制器模块写使能
  54. 54  wire  [15:0] wr_data                   ;  //DDR3控制器模块写数据
  55. 55  wire         rdata_req                 ;  //DDR3控制器模块读使能
  56. 56  wire  [15:0] rd_data                   ;  //DDR3控制器模块读数据
  57. 57  wire         cmos_frame_valid          ;  //数据有效使能信号
  58. 58  wire         init_calib_complete       ;  //DDR3初始化完成init_calib_complete
  59. 59  wire         sys_init_done             ;  //系统初始化完成(DDR初始化+摄像头初始化)
  60. 60  wire         cmos_frame_vsync          ;  //输出帧有效场同步信号
  61. 61  wire         lcd_de                    ;  //LCD 数据输入使能
  62. 62  wire         cmos_frame_href           ;  //输出帧有效行同步信号
  63. 63  wire  [10:0] h_disp                    ;  //水平分辨率
  64. 64  wire  [10:0] v_disp                    ;  //垂直分辨率     
  65. 65  wire  [2:0]  change_en                 ;  //按键切换信号
  66. 66  wire  [15:0] pixel_data_w              ;  //摄像头采集16bit数据
  67. 67  wire  [10:0] t_width_wr                ;  //写端缩放后列分辨率  
  68. 68  wire  [10:0] t_height_wr               ;  //写端缩放后行分辨率
  69. 69  wire  [10:0] t_width_rd                ;  //读端缩放后列分辨率   
  70. 70  wire  [10:0] t_height_rd               ;  //读端缩放后行分辨率
  71. 71  wire  [2:0]  change_en_wr              ;  //写端切换信号
  72. 72  wire  [2:0]  change_en_rd              ;  //读端切换信号
  73. 73  wire  [15:0] h_scale_k                 ;  //列缩放因子
  74. 74  wire  [15:0] v_scale_k                 ;  //行缩放因子
  75. 75  wire  [27:0] ddr_addr_rd_max           ;  //读DDR3的结束地址
  76. 76  wire  [7:0]  rd_bust_len               ;  //从DDR3中读数据时的突发长度
  77. 77  wire  [27:0] ddr_addr_wr_max           ;  //写DDR3的结束地址
  78. 78  wire  [7:0]  wr_bust_len               ;  //从DDR3中写数据时的突发长度
  79. 79  wire  [15:0] scale_data                ;  //缩放后的数据
  80. 80  wire         scale_data_valid          ;  //缩放后的数据使能
  81. 81  wire         para_finish;
  82. 82  wire         pixel_valid;
  83. 83  
  84. 84  //*****************************************************
  85. 85  //**                    main code
  86. 86  //*****************************************************
  87. 87  //待时钟锁定后产生复位结束信号
  88. 88  assign  rst_n = sys_rst_n & locked_100m & locked_300m;
  89. 89  
  90. 90  //系统初始化完成:DDR3初始化完成
  91. 91  assign  sys_init_done = init_calib_complete;
  92. 92  
  93. 93   //ov5640 驱动
  94. 94  ov5640_dri u_ov5640_dri(
  95. 95      .clk               (clk_50m),
  96. 96      .rst_n             (rst_n),
  97. 97  
  98. 98      .cam_pclk          (cam_pclk ),
  99. 99      .cam_vsync         (cam_vsync),
  100. 100     .cam_href          (cam_href ),
  101. 101     .cam_data          (cam_data ),
  102. 102     .cam_rst_n         (cam_rst_n),
  103. 103     .cam_pwdn          (cam_pwdn ),
  104. 104     .cam_scl           (cam_scl  ),
  105. 105     .cam_sda           (cam_sda  ),
  106. 106     
  107. 107     .capture_start     (init_calib_complete),
  108. 108     .cmos_h_pixel      (H_CMOS_DISP),
  109. 109     .cmos_v_pixel      (V_CMOS_DISP),
  110. 110     .total_h_pixel     (TOTAL_H_PIXEL),
  111. 111     .total_v_pixel     (TOTAL_V_PIXEL),
  112. 112     .cmos_frame_vsync  (cmos_frame_vsync),
  113. 113     .cmos_frame_href   (cmos_frame_href),
  114. 114     .cmos_frame_valid  (cmos_frame_valid),
  115. 115     .cmos_frame_data   (pixel_data_w)
  116. 116     );
  117. 117
  118. 118 //图像缩放顶层封装模块   
  119. 119  scale_top u_scale_top(
  120. 120     .pixel_clk        (cam_pclk),          //原始图像采集时钟  
  121. 121     .sram_clk         (clk_100m),          //缩放后图像采集时钟
  122. 122     .sys_rst_n        (rst_n),             //复位信号   
  123. 123     .hs               (cmos_frame_href),   //输出帧有效行同步信号  
  124. 124     .vs               (cmos_frame_vsync),  //输出帧有效场同步信号
  125. 125     .de               (cmos_frame_valid),  //输出帧数据有效使能
  126. 126     .pixel_data       (pixel_data_w),      //输出帧数据
  127. 127     
  128. 128     .s_width          (H_CMOS_DISP),       //缩放前宽度
  129. 129     .s_height         (V_CMOS_DISP),       //缩放前高度
  130. 130     .t_width          (t_width_wr),        //缩放后宽度
  131. 131     .t_height         (t_height_wr),       //缩放后高度  
  132. 132     .h_scale_k        (h_scale_k),         //列缩放因子
  133. 133     .v_scale_k        (v_scale_k),         //行缩放因子
  134. 134
  135. 135     .sram_data_out    (scale_data),        //缩放后数据有效使能
  136. 136     .data_valid       (scale_data_valid)   //缩放后输出数据
  137. 137 );  
  138. 138
  139. 139 //按键控制模块
  140. 140 key_ctrl u_key_ctrl(
  141. 141     //input
  142. 142     .sys_clk          (clk_50m),           //时钟信号50Mhz
  143. 143     .sys_rst_n        (rst_n),             //复位信号
  144. 144     .touch_key        (key0),              //按键
  145. 145                                            
  146. 146     .change_en        (change_en)          //切换使能  
  147. 147 );
  148. 148
  149. 149 //参数切换模块
  150. 150 para_change u_para_change(
  151. 151     .clk_wr           (clk_100m),
  152. 152     .clk_rd           (clk_100m),   
  153. 153     .change_en        (change_en),         //切换使能
  154. 154     .rst_n            (rst_n ),            
  155. 155                                            
  156. 156     .rd_vsync         (video_vs),          //HDMI场信号
  157. 157     .wr_vsync         (cmos_frame_vsync),  //输出帧有效场同步信号
  158. 158     .s_width          (H_CMOS_DISP),       //缩放前宽度
  159. 159     .s_height         (V_CMOS_DISP),       //缩放前高度
  160. 160     .t_width_wr       (t_width_wr),        //写端缩放后宽度
  161. 161     .t_height_wr      (t_height_wr),       //写端缩放后高度
  162. 162     .t_width_rd       (t_width_rd),        //读端缩放后宽度
  163. 163     .t_height_rd      (t_height_rd),       //读端缩放后高度  
  164. 164     .h_scale_k        (h_scale_k),         //列缩放因子
  165. 165     .v_scale_k        (v_scale_k),         //行缩放因子
  166. 166         
  167. 167     .change_en_wr     (change_en_wr),      //写端切换信号
  168. 168     .change_en_rd     (change_en_rd),      //读端切换信号
  169. 169     .app_addr_rd_max  (ddr_addr_rd_max),   //读ddr3的结束地址
  170. 170     .rd_bust_len      (rd_bust_len),       //从ddr3中读数据时的突发长度
  171. 171     .app_addr_wr_max  (ddr_addr_wr_max),   //读ddr3的结束地址
  172. 172     .wr_bust_len      (wr_bust_len)        //从ddr3中读数据时的突发长度      
  173. 173     
  174. 174     );  
  175. 175     
  176. 176 //ddr控制器
  177. 177 ddr3_control
  178. 178  #(
  179. 179    .C3_NUM_DQ_PINS         (16),
  180. 180    .C3_MEM_ADDR_WIDTH      (14),
  181. 181    .C3_MEM_BANKADDR_WIDTH  (3)
  182. 182    )
  183. 183  u_ddr3_control(
  184. 184   .clk_ddr                (clk_ddr),                              
  185. 185   .clk_100m               (clk_100m),                             
  186. 186   .rst_n                  (sys_rst_n),                           
  187. 187   .mcb3_dram_dq           (mcb3_dram_dq     ),                    
  188. 188   .mcb3_dram_a            (mcb3_dram_a      ),                    
  189. 189   .mcb3_dram_ba           (mcb3_dram_ba     ),                    
  190. 190   .mcb3_dram_ras_n        (mcb3_dram_ras_n  ),                    
  191. 191   .mcb3_dram_cas_n        (mcb3_dram_cas_n  ),                    
  192. 192   .mcb3_dram_we_n         (mcb3_dram_we_n   ),                    
  193. 193   .mcb3_dram_reset_n      (mcb3_dram_reset_n),                    
  194. 194   .mcb3_dram_cke          (mcb3_dram_cke    ),                    
  195. 195   .mcb3_dram_dm           (mcb3_dram_dm     ),                    
  196. 196   .mcb3_dram_udqs         (mcb3_dram_udqs   ),                    
  197. 197   .mcb3_dram_udqs_n       (mcb3_dram_udqs_n ),                    
  198. 198   .mcb3_rzq               (mcb3_rzq         ),                    
  199. 199   .mcb3_zio               (mcb3_zio         ),                    
  200. 200   .mcb3_dram_udm          (mcb3_dram_udm    ),                    
  201. 201   .mcb3_dram_dqs          (mcb3_dram_dqs    ),                    
  202. 202   .mcb3_dram_dqs_n        (mcb3_dram_dqs_n  ),                    
  203. 203   .mcb3_dram_ck           (mcb3_dram_ck     ),                    
  204. 204   .mcb3_dram_ck_n         (mcb3_dram_ck_n   ),                    
  205. 205   .c3_calib_done          (init_calib_complete),                  
  206. 206                                                                  
  207. 207   .ddr3_read_valid        (1'b1 ),                                
  208. 208   .ddr3_pingpang_en       (1'b1),                                 
  209. 209                                                                  
  210. 210   //摄像头   
  211. 211   .clk_write              (clk_100m),              //写时钟                          
  212. 212   .wr_en                  (scale_data_valid),      //写使能                        
  213. 213   .wr_data                (scale_data),            //写数据   
  214. 214   .wr_load                (cmos_frame_vsync),      //写端更新信号   
  215. 215   .wr_length              (wr_bust_len),           //一次写长度                          
  216. 216   .wr_max_addr            (ddr_addr_wr_max),       //写最大地址   
  217. 217   .wr_min_addr            (0),                     //写最小地址                        
  218. 218                                                                             
  219. 219   .clk_read               (pixel_clk),             //读时钟   
  220. 220   .rd_load                (video_vs),              //读端更新信号   
  221. 221   .rd_en                  (rdata_req),             //读使能                        
  222. 222   .rd_max_addr            (ddr_addr_rd_max),       //读最大地址   
  223. 223   .rd_min_addr            (0),                     //读最小地址                        
  224. 224   .rd_length              (rd_bust_len),           //一次读长度        
  225. 225   .rd_data                (rd_data)                //读数据                        
  226. 226                                                                  
  227. 227   );
  228. 228
  229. 229 //时钟模块
  230. 230 clock u_clock(
  231. 231     .sys_clk      (sys_clk),       //系统输入时钟
  232. 232
  233. 233     .clk_ddr      (clk_ddr),       //ddr运行时钟
  234. 234     .clk_50m      (clk_50m),       //50m时钟
  235. 235     .clk_100m     (clk_100m),      //100m时钟
  236. 236     .locked_100m  (locked_100m),   //时钟稳定信号
  237. 237     .locked_300m  (locked_300m)    //时钟稳定信号
  238. 238     
  239. 239 );
  240. 240
  241. 241 //HDMI顶层模块   
  242. 242 hdmi_top u_hdmi_top(
  243. 243     .clk_50m              (clk_50m),  
  244. 244     .pixel_clk            (pixel_clk),  
  245. 245     .sys_rst_n            (sys_init_done & rst_n ),
  246. 246     //hdmi接口                  
  247. 247     .tmds_clk_p           (tmds_clk_p   ),   // TMDS 时钟通道
  248. 248     .tmds_clk_n           (tmds_clk_n   ),
  249. 249     .tmds_data_p          (tmds_data_p  ),   // TMDS 数据通道
  250. 250     .tmds_data_n          (tmds_data_n  ),
  251. 251     //用户接口
  252. 252     .change_en            (change_en_rd),    //切换信号,1为放大,2为缩小,4为原图
  253. 253     .h_disp               (),     
  254. 254     .v_disp               (),      
  255. 255     .t_width              (t_width_rd),      //读端缩放后宽度
  256. 256     .t_height             (t_height_rd),     //读端缩放后高度   
  257. 257     .s_width              (H_CMOS_DISP),     //缩放前宽度
  258. 258     .s_height             (V_CMOS_DISP),     //缩放前高度
  259. 259     .video_vs             (video_vs),   
  260. 260     .pixel_xpos           (),
  261. 261     .pixel_ypos           (),      
  262. 262     .data_in              (rd_data),         //数据输入     
  263. 263     .data_req             (rdata_req)        //请求数据输入   
  264. 264 );
  265. 265
  266. 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. 1  module scale_top(
  2. 2   input           pixel_clk,     //原始像素时钟
  3. 3   input           sram_clk,      //缩放后像素时钟
  4. 4   input           sys_rst_n,     //复位信号   
  5. 5   input           hs,            //行信号
  6. 6   input           vs,            //场信号
  7. 7   input           de,            //数据使能信号
  8. 8   input   [11:0]  s_width,       //缩放前宽度
  9. 9   input   [11:0]  s_height,      //缩放前高度
  10. 10  input   [11:0]  t_width,       //缩放后宽度
  11. 11  input   [11:0]  t_height,      //缩放后高度
  12. 12  input   [15:0]  h_scale_k,     //列缩放因子
  13. 13  input   [15:0]  v_scale_k,     //行缩放因子   
  14. 14  input   [15:0]  pixel_data,    //缩放前数据
  15. 15  output  [15:0]  sram_data_out, //缩放后数据
  16. 16  output          data_valid     //缩放后数据有效信号
  17. 17     );
  18. 18
  19. 19 //wire define   
  20. 20 wire [15:0] sram_data_out;  //缩放后的RGB数据
  21. 21 wire [7:0]  data_out_r;     //缩放后的红色数据
  22. 22 wire [7:0]  data_out_g;     //缩放后的绿色数据
  23. 23 wire [7:0]  data_out_b;     //缩放后的蓝色数据
  24. 24 wire        data_valid;     //缩放后数据有效信号
  25. 25
  26. 26 //*****************************************************
  27. 27 //**                    main code
  28. 28 //*****************************************************
  29. 29
  30. 30 //将缩放后的数据拼成16bit数据输出   
  31. 31 assign  sram_data_out = data_valid ? {data_out_r[7:3],data_out_g[7:2],
  32. 32                                       data_out_b[7:3]} : 16'b0;
  33. 33 assign  fifo_rden = ~fifo_empty_r && ~fifo_empty_g && ~fifo_empty_b;   
  34. 34
  35. 35 //红色数据缩放模块   
  36. 36  vin_scale_down u_vin_scale_down_r(
  37. 37  .pixel_clk          (pixel_clk),
  38. 38  .sram_clk           (sram_clk),
  39. 39  .sys_rst_n          (sys_rst_n),
  40. 40  .hs                 (hs),
  41. 41  .vs                 (vs),
  42. 42  .de                 (de),
  43. 43  .s_width            (s_width),
  44. 44  .s_height           (s_height),
  45. 45  .t_width            (t_width),
  46. 46  .t_height           (t_height),
  47. 47  .h_scale_k          (h_scale_k),
  48. 48  .v_scale_k          (v_scale_k),   
  49. 49  .fifo_scale_rden    (fifo_rden),
  50. 50  .pixel_data         ({pixel_data[15:11],3'b0}),
  51. 51  .sram_data_out      (data_out_r),
  52. 52  .data_valid         (data_valid),
  53. 53  .fifo_scale_rdempty (fifo_empty_r)
  54. 54 );      
  55. 55
  56. 56 //绿色数据缩放模块      
  57. 57  vin_scale_down u_vin_scale_down_g(
  58. 58  .pixel_clk          (pixel_clk),
  59. 59  .sram_clk           (sram_clk),
  60. 60  .sys_rst_n          (sys_rst_n),   
  61. 61  .hs                 (hs),
  62. 62  .vs                 (vs),
  63. 63  .de                 (de),
  64. 64  .s_width            (s_width),
  65. 65  .s_height           (s_height),
  66. 66  .t_width            (t_width),
  67. 67  .t_height           (t_height),
  68. 68  .h_scale_k          (h_scale_k),
  69. 69  .v_scale_k          (v_scale_k),        
  70. 70  .fifo_scale_rden    (fifo_rden),   
  71. 71  .pixel_data         ({pixel_data[10:5],2'b0}),
  72. 72  .sram_data_out      (data_out_g),
  73. 73  .data_valid         (),
  74. 74     .fifo_scale_rdempty (fifo_empty_g)
  75. 75 );     
  76. 76
  77. 77 //蓝色数据缩放模块
  78. 78  vin_scale_down u_vin_scale_down_b(
  79. 79  .pixel_clk          (pixel_clk),
  80. 80  .sram_clk           (sram_clk),
  81. 81  .sys_rst_n          (sys_rst_n),   
  82. 82  .hs                 (hs),
  83. 83  .vs                 (vs),
  84. 84  .de                 (de),
  85. 85  .s_width            (s_width),
  86. 86  .s_height           (s_height),
  87. 87  .t_width            (t_width),
  88. 88  .t_height           (t_height),
  89. 89  .h_scale_k          (h_scale_k),
  90. 90  .v_scale_k          (v_scale_k),   
  91. 91  .fifo_scale_rden    (fifo_rden),        
  92. 92  .pixel_data         ({pixel_data[4:0],3'b0}),
  93. 93  .sram_data_out      (data_out_b),
  94. 94  .data_valid         (),
  95. 95  .fifo_scale_rdempty (fifo_empty_b)
  96. 96 );   
  97. 97  
  98. 98 endmodule
复制代码

代码第31行和32行是将24bit缩放后的数据拼接成RGB565格式的16bit颜色数据,因为输入的原始数据是16bit,所以输出的数据也必须是16bit数据。
代码第33行表示当RGB三个缩放后的fifo都不为空时拉高读使能,以便RGB颜色数据的同步。
代码第36行至96行是红绿蓝三种颜色的缩放顶层模块的运算,其中50行、71行和92行分别是将RGB565的数据拼成RGB888的数据位宽。因为缩放顶层模块的输入数据是8bit位宽,所以要将位宽拼成8bit。
缩放顶层模块代码如下:
  1. 1   module vin_scale_down(
  2. 2       input           pixel_clk,
  3. 3       input           sram_clk,
  4. 4       input           sys_rst_n,          //复位信号      
  5. 5       input           hs,                 //行信号
  6. 6       input           vs,                 //场信号
  7. 7       input           de,                 //数据使能信号
  8. 8       input   [11:0]  s_width,            //缩放前宽度
  9. 9       input   [11:0]  s_height,           //缩放前高度
  10. 10      input   [11:0]  t_width,            //缩放后宽度
  11. 11      input   [11:0]  t_height,           //缩放后高度      
  12. 12      input   [7:0]   pixel_data,         //缩放前数据
  13. 13      input   [15:0]  h_scale_k,          //列缩放因子
  14. 14      input   [15:0]  v_scale_k,          //行缩放因子
  15. 15      input           fifo_scale_rden,    //缩放后fifo读使能
  16. 16      output  [7:0]   sram_data_out,      //缩放后数据
  17. 17      output          data_valid,         //缩放后数据有效信号
  18. 18      output          fifo_scale_rdempty  //缩放后fifo空信号
  19. 19  );
  20. 20  
  21. 21  //reg define
  22. 22  reg         de_d0;
  23. 23  reg         vs_d0;
  24. 24  reg [15:0]  vs_d;
  25. 25  reg         frame_flag;           //帧头标志信号
  26. 26  reg [7:0]   pixel_data_d0;        
  27. 27  reg         data_valid;           //缩放后数据有效信号
  28. 28  reg         data_ready;           //fifo一行数据准备完成信号
  29. 29                                    
  30. 30  //wire define                     
  31. 31  wire        scaler_rd_req;        //缩放前数据请求信号
  32. 32  wire        fifo_rdempty;         //缩放前fifo的空信号
  33. 33  wire[7:0]   fifo_data;            //缩放前fifo的输出数据
  34. 34  wire[7:0]   scaler_data_out;      //缩放后数据
  35. 35  wire        scaler_wren;          //缩放后fifo写使能
  36. 36  wire        fifo_scale_rdempty;   //缩放后fifo空信号
  37. 37  wire[10:0]  rd_data_count;        //fifo缓存计数器
  38. 38  
  39. 39  //*****************************************************
  40. 40  //**                    main code
  41. 41  //*****************************************************
  42. 42  
  43. 43  //对信号打拍
  44. 44  always@(posedge pixel_clk)begin
  45. 45      de_d0 <= de;
  46. 46      pixel_data_d0 <= pixel_data;
  47. 47  end
  48. 48  
  49. 49  //产生数据有效信号
  50. 50  always@(posedge sram_clk)begin
  51. 51      data_valid <= fifo_scale_rden;
  52. 52      vs_d0 <= vs;
  53. 53      vs_d <= {vs_d[14:0], vs_d0};
  54. 54      frame_flag <= vs_d[15] && ~vs_d[0];
  55. 55  end
  56. 56  
  57. 57  //产生fifo一行数据准备完成信号
  58. 58  always@(posedge sram_clk)begin
  59. 59      if(frame_flag)
  60. 60         data_ready <= 1'b0;
  61. 61      else if(rd_data_count >= s_width - 3)
  62. 62         data_ready <= 1'b1;  
  63. 63      else
  64. 64         data_ready <= 1'b0;      
  65. 65  end
  66. 66  
  67. 67  //缓存缩放前数据
  68. 68  fifo_2048x8 u_fifo_data_in(
  69. 69      .rst      (frame_flag || !sys_rst_n ),
  70. 70      .wr_clk   (pixel_clk),
  71. 71      .rd_clk   (sram_clk),
  72. 72      .din      (pixel_data_d0),
  73. 73      .wr_en    (de_d0),
  74. 74      .rd_en    (scaler_rd_req),
  75. 75      .dout     (fifo_data),
  76. 76      .full     (),
  77. 77      .empty    (fifo_rdempty),
  78. 78      .rd_data_count (rd_data_count),
  79. 79      .wr_data_count ()   
  80. 80  );      
  81. 81  
  82. 82  //缓存缩放后数据
  83. 83  fifo_2048x8 u_lite_scale_data(  
  84. 84      .rst           (frame_flag || !sys_rst_n ),
  85. 85      .wr_clk        (sram_clk),
  86. 86      .rd_clk        (sram_clk),
  87. 87      .din           (scaler_data_out),
  88. 88      .wr_en         (scaler_wren),
  89. 89      .rd_en         (fifo_scale_rden),
  90. 90      .dout          (sram_data_out),
  91. 91      .full          (),
  92. 92      .empty         (fifo_scale_rdempty),
  93. 93      .rd_data_count (),
  94. 94      .wr_data_count ()   
  95. 95  );  
  96. 96  
  97. 97  //缩放模块
  98. 98  scaler u_scaler(
  99. 99      .sys_clk(sram_clk),
  100. 100     .rst_n(sys_rst_n),
  101. 101     //data in
  102. 102     .fifo_rdempty(fifo_rdempty),
  103. 103     .scaler_rd_req(scaler_rd_req),
  104. 104     .data_in(fifo_data),
  105. 105     .frame_flag(frame_flag),
  106. 106     .data_ready(data_ready),
  107. 107     //data out
  108. 108     .scaler_wren(scaler_wren),
  109. 109     .data_out(scaler_data_out),
  110. 110     //config
  111. 111     .s_width(s_width),
  112. 112     .s_height(s_height),
  113. 113     .t_width(t_width),
  114. 114     .t_height(t_height),
  115. 115     .k_h(h_scale_k),
  116. 116     .k_v(v_scale_k)
  117. 117 );
  118. 118
  119. 119 endmodule
复制代码

代码第44行至47行,是对输入信号做一个打拍寄存,是为了防止信号的扇出太大。
代码第50行至55行,是将输入信号在读时钟下打拍,是为了同步时钟域,避免信号跨时钟域导致时序不满足。代码第51行,是为了产生缩放后数据有效信号。本次实验采用的是标准fifo,所以fifo读使能的下一个时钟周期的数据才是有效数据,所以缩放后数据有效信号是由fifo读使能打一拍后得到的。其中54行将帧头的电平宽度加宽,是为了满足fifo复位的时序。
代码第58行至65行是产生fifo一行数据准备完成信号。当fifo的数据计数器rd_data_count接近于缩放前宽度时,此时拉高data_ready信号,则认为fifo中已经存有一行数据。
缩放模块的代码如下:
  1. 1   module scaler
  2. 2   (
  3. 3       input                   sys_clk,
  4. 4       input                   rst_n,
  5. 5       //data in
  6. 6       input                   fifo_rdempty,     //缩放前fifo的将空标志
  7. 7       output                  scaler_rd_req,    //fifo读请求
  8. 8       input  [7:0]            data_in,          //缩放前数据
  9. 9       input                   frame_flag,       //帧头标志信号
  10. 10      input                   data_ready,       //fifo一行数据准备完成信号
  11. 11      //data out
  12. 12      output                  scaler_wren,      //缩放后fifo写使能
  13. 13      output [7:0]            data_out,         //缩放后数据
  14. 14      //config
  15. 15      input  [11:0]           s_width,          //缩放前宽度
  16. 16      input  [11:0]           s_height,         //缩放前高度
  17. 17      input  [11:0]           t_width,          //缩放后宽度
  18. 18      input  [11:0]           t_height,         //缩放后高度
  19. 19      input  [15:0]           k_h,              //列缩放因子
  20. 20      input  [15:0]           k_v               //行缩放因子
  21. 21  );
  22. 22  
  23. 23  //reg define
  24. 24  reg [15:0]             k_h_reg;               
  25. 25  reg [15:0]             k_v_reg;  
  26. 26  reg                    wr_buf_sel;              //ram写切换使能
  27. 27  reg [11:0]             wr_lines;                //读出fifo数据的行数
  28. 28  reg [19:0]             scale_map_v;             //行缩放因子累加寄存器
  29. 29  reg [7:0]              line0_pixel_data;        //将要进行行运算的像素数据
  30. 30  reg [7:0]              line1_pixel_data;        //将要进行行运算的像素数据
  31. 31  reg [11:0]             ram_v_raddr;             //读ram地址
  32. 32  reg [10:0]             ram_v_waddr;             //ram写地址
  33. 33  reg [10:0]             ram_v_waddr_d0;  
  34. 34  reg [10:0]             ram_v_waddr_d1;   
  35. 35  reg [10:0]             ram_v_raddr_d0;  
  36. 36  reg [10:0]             ram_v_raddr_d1;
  37. 37  reg [10:0]             ram_h_waddr_d0;  
  38. 38  reg [10:0]             ram_h_waddr_d1;   
  39. 39  reg [10:0]             ram_h_raddr_d0;  
  40. 40  reg [10:0]             ram_h_raddr_d1;
  41. 41  reg                    pixel_h_data_read_en_d0;   
  42. 42  reg                    pixel_h_data_read_en_d1;   
  43. 43  reg                    pixel_v_data_read_en_d0;  
  44. 44  reg                    pixel_v_data_read_en_d1;  
  45. 45  reg                    fifo_line_finish ;       //fifo一行读完信号   
  46. 46  reg                    ram_v_wren ;             //行ram写使能
  47. 47  reg [11:0]             scale_line;              //缩放后的行计数   
  48. 48  reg                    one_line_data_ready ;    //首行数据写入行ram的标志信号
  49. 49  reg                    pixel_v_data_read_en ;   //行ram数据可读使能
  50. 50  reg                    scale_line_finish ;      //行缩放完成信号
  51. 51  reg                    calu_v_data_in_en ;      //产生行运算输入使能有效信号
  52. 52  reg [7:0]              a_coff_v;                //行加权系数输入
  53. 53  reg [7:0]              b_coff_v;                //行加权系数输入  
  54. 54  reg [15:0]             scale_line_finish_d;
  55. 55  reg [15:0]             scale_h_finish_d;
  56. 56  reg [7:0]              ram_h_data_in;           //列ram输入数据
  57. 57  reg                    ram_h_wren;              //列ram写使能
  58. 58  reg [10:0]             ram_h_waddr;             //列ram写地址
  59. 59  reg                    pixel_h_data_read_en;    //列ram数据可读使能
  60. 60  reg [10:0]             ram_h_raddr;             //列ram读地址
  61. 61  reg [19:0]             scale_map_h;             //列缩放因子累加寄存器
  62. 62  reg                    scaler_wren;             //缩放后fifo写使能
  63. 63  reg [7:0]              data_out;                //缩放后数据
  64. 64  reg [7:0]              col0_pixel_data;
  65. 65  reg [7:0]              col1_pixel_data;
  66. 66  reg                    calu_h_data_in_en;       //列运算输入使能有效信号
  67. 67  reg [7:0]              a_coff_h;                //行加权系数输入
  68. 68  reg [7:0]              b_coff_h;                //行加权系数输入
  69. 69  reg [19:0]             scale_map_v_d0;
  70. 70  reg [19:0]             scale_map_h_d0;
  71. 71  reg                    scaler_up;               //缩放标志  1表示放大,0表示缩小
  72. 72  reg                    scale_h_line_valid_cont; //有效行缩放标志持续信号
  73. 73  reg                    scale_h_finish ;         //列缩放完成信号
  74. 74  reg                    scale_h_not_end ;        //列缩放未结束信号
  75. 75  reg [10:0]             cnt_h_pixel;             //列缩放计数
  76. 76  reg                    rd_v_not_need_data;      //行缩放不需要请求数据标志
  77. 77  reg                    scale_v_end_line;        //最后一行标志信号
  78. 78  reg                    frame_flag_d0;
  79. 79  reg                    calu_h_data_in_en_d0;
  80. 80  reg                    scaler_rd_req;           //fifo读请求
  81. 81  reg [10:0]             cnt_rd_req;              //fifo读请求计数  
  82. 82  
  83. 83  //wire define
  84. 84  wire[7:0]              ram_v_data_out0;         //ram0输出数据
  85. 85  wire[7:0]              ram_v_data_out1;         //ram1输出数据
  86. 86  wire                   data_en_out_calu_v;      //行运算输出使能
  87. 87  wire[7:0]              c_v;                     //行运算输出数据
  88. 88  wire                   calu_h_data_out_en;      //列运算输出使能
  89. 89  wire[7:0]              c_h;                     //列运算输出数据
  90. 90  wire[7:0]              ram_h_data_out;          //列ram输出数据
  91. 91  wire[19:0]             scale_map_h_next;        //下一次列缩放因子累加寄存器  
  92. 92  wire                   scale_h_line_valid;      //有效列缩放标志
  93. 93  wire[19:0]             scale_map_v_next;        //下一次行缩放因子累加寄存器
  94. 94  wire                   scale_v_line_valid;      //有效行缩放标志
  95. 95  
  96. 96  //*****************************************************
  97. 97  //**                    main code
  98. 98  //*****************************************************
  99. 99  
  100. 100 //产生下一次行缩放因子累加寄存器
  101. 101 assign scale_map_v_next = scale_map_v + {4'd0,k_v_reg};
  102. 102
  103. 103 //产生下一次列缩放因子累加寄存器
  104. 104 assign scale_map_h_next =scale_map_h + {4'd0,k_h_reg};
  105. 105
  106. 106 //产生有效行缩放标志
  107. 107 assign scale_v_line_valid = (scale_map_v[19:8] == wr_lines - 12'd1) || rd_v_not_need_data;
  108. 108
  109. 109 //产生有效列缩放标志
  110. 110 assign scale_h_line_valid = ((scale_map_h[19:8] == scale_map_h_next[19:8])
  111. 111                              && scaler_up &&  pixel_h_data_read_en_d1) ? 1 : 0;
  112. 112   
  113. 113 //产生fifo读请求
  114. 114 always@(posedge sys_clk or negedge rst_n )begin
  115. 115     if(~rst_n || frame_flag)
  116. 116         scaler_rd_req <= 0;
  117. 117     else if(cnt_rd_req>= s_width - 1)
  118. 118         scaler_rd_req <= 0;     
  119. 119     else if(data_ready)
  120. 120          if(!rd_v_not_need_data)
  121. 121              scaler_rd_req <= 1;
  122. 122          else
  123. 123              scaler_rd_req <= 0;         
  124. 124     else
  125. 125         scaler_rd_req <= scaler_rd_req;                 
  126. 126 end
  127. 127
  128. 128 //对fifo读请求进行计数
  129. 129 always@(posedge sys_clk or negedge rst_n )begin
  130. 130     if(~rst_n || frame_flag)
  131. 131         cnt_rd_req <= 0;
  132. 132     else if(scaler_rd_req)  
  133. 133         cnt_rd_req <= cnt_rd_req + 1;
  134. 134     else   
  135. 135         cnt_rd_req <= 0;        
  136. 136 end
  137. 137
  138. 138 always@(posedge sys_clk or negedge rst_n )begin
  139. 139     if(!rst_n)begin
  140. 140         ram_v_waddr_d0 <= 0;
  141. 141         ram_v_waddr_d1 <= 0;   
  142. 142         ram_v_raddr_d0 <= 0;
  143. 143         ram_v_raddr_d1 <= 0;        
  144. 144         ram_h_waddr_d0 <= 0;
  145. 145         ram_h_waddr_d1 <= 0;   
  146. 146         ram_h_raddr_d0 <= 0;
  147. 147         ram_h_raddr_d1 <= 0;            
  148. 148         pixel_v_data_read_en_d0 <= 0;
  149. 149         pixel_v_data_read_en_d1 <= 0;   
  150. 150         pixel_h_data_read_en_d0 <= 0;   
  151. 151         pixel_h_data_read_en_d1 <= 0;      
  152. 152         scale_line_finish_d <= 0;   
  153. 153         scale_h_finish_d <= 0;         
  154. 154         scale_map_v_d0 <= 0;   
  155. 155         frame_flag_d0 <= 0;
  156. 156     end
  157. 157     else begin
  158. 158         ram_v_waddr_d0 <= ram_v_waddr;
  159. 159         ram_v_waddr_d1 <= ram_v_waddr_d0;
  160. 160         ram_v_raddr_d0 <= ram_v_raddr;
  161. 161         ram_v_raddr_d1 <= ram_v_raddr_d0;   
  162. 162         ram_h_waddr_d0 <= ram_h_waddr;
  163. 163         ram_h_waddr_d1 <= ram_h_waddr_d0;
  164. 164         ram_h_raddr_d0 <= ram_h_raddr;
  165. 165         ram_h_raddr_d1 <= ram_h_raddr_d0;               
  166. 166         pixel_v_data_read_en_d0 <= pixel_v_data_read_en;
  167. 167         pixel_h_data_read_en_d0 <= pixel_h_data_read_en;        
  168. 168         pixel_h_data_read_en_d1 <= pixel_h_data_read_en_d0;         
  169. 169         pixel_v_data_read_en_d1 <= pixel_v_data_read_en_d0;
  170. 170         scale_line_finish_d <= {scale_line_finish_d[14:0],scale_line_finish};
  171. 171         scale_h_finish_d <= {scale_h_finish_d[14:0],scale_h_finish};        
  172. 172         scale_map_v_d0 <= scale_map_v;  
  173. 173         scale_map_h_d0 <= scale_map_h;      
  174. 174         frame_flag_d0 <= frame_flag;   
  175. 175     end     
  176. 176 end
  177. 177
  178. 178 //将行列缩放因子进行寄存
  179. 179 always@(posedge sys_clk )begin
  180. 180     k_h_reg <= k_h;
  181. 181     k_v_reg <= k_v;
  182. 182 end
  183. 183
  184. 184 //判断行是放大还是缩小
  185. 185 always@(posedge sys_clk)begin
  186. 186     if(k_h_reg[15:8] == 8'd0)
  187. 187         scaler_up <= 1;
  188. 188     else
  189. 189         scaler_up <= 0;
  190. 190 end
  191. 191
  192. 192 //fifo一行读完信号
  193. 193 always@(posedge sys_clk or negedge rst_n )begin
  194. 194     if(~rst_n)
  195. 195         fifo_line_finish <= 0;
  196. 196     else if((ram_v_waddr_d0 == s_width) && (ram_v_waddr_d1 == s_width - 1))
  197. 197         fifo_line_finish <= 1;
  198. 198     else   
  199. 199         fifo_line_finish <= 0;      
  200. 200 end
  201. 201
  202. 202 //产生行ram写地址
  203. 203 always@(posedge sys_clk or negedge rst_n )begin
  204. 204     if(~rst_n)
  205. 205         ram_v_waddr <= 0;
  206. 206     else if(frame_flag || fifo_line_finish)
  207. 207         ram_v_waddr <= 0;           
  208. 208     else if(ram_v_wren)
  209. 209         ram_v_waddr <= ram_v_waddr + 1;
  210. 210     else
  211. 211         ram_v_waddr <= ram_v_waddr;     
  212. 212 end
  213. 213
  214. 214 //产生行ram写使能
  215. 215 always@(posedge sys_clk or negedge rst_n )begin
  216. 216     if(~rst_n)
  217. 217         ram_v_wren <= 0;
  218. 218     else if(frame_flag || fifo_line_finish)
  219. 219         ram_v_wren <= 0;            
  220. 220     else if(scaler_rd_req)
  221. 221         ram_v_wren <= 1;   
  222. 222     else
  223. 223         ram_v_wren <= 0;        
  224. 224 end
  225. 225
  226. 226 //产生ram的写切换信号
  227. 227 always@(posedge sys_clk or negedge rst_n)begin
  228. 228     if(~rst_n || frame_flag)
  229. 229         wr_buf_sel <= 1'b0;
  230. 230     else if(fifo_line_finish)
  231. 231         wr_buf_sel <= ~wr_buf_sel;
  232. 232     else
  233. 233         wr_buf_sel <= wr_buf_sel;
  234. 234 end
  235. 235
  236. 236 //对fifo读出数据的行数计数
  237. 237 always@(posedge sys_clk or negedge rst_n)begin
  238. 238     if(~rst_n)
  239. 239         wr_lines <= 12'd0;
  240. 240     else if(frame_flag)
  241. 241         wr_lines <= 12'd0;
  242. 242     else if(fifo_line_finish)
  243. 243         wr_lines <= wr_lines + 12'd1;
  244. 244     else
  245. 245         wr_lines <= wr_lines;
  246. 246 end
  247. 247
  248. 248 //对行缩放因子进行累加
  249. 249 always@(posedge sys_clk or negedge rst_n)begin
  250. 250     if(~rst_n)
  251. 251         scale_map_v <= 20'd0;
  252. 252     else if(frame_flag )
  253. 253         scale_map_v <= 20'd0;
  254. 254     else if(scale_line_finish && (scale_line < t_height - 1))
  255. 255         scale_map_v <= scale_map_v + {4'd0,k_v_reg};
  256. 256     else
  257. 257         scale_map_v <= scale_map_v;
  258. 258 end
  259. 259
  260. 260 //产生行缩放不需要请求数据标志
  261. 261 always@(posedge sys_clk or negedge rst_n)begin
  262. 262     if(~rst_n)
  263. 263         rd_v_not_need_data <= 1'd0;
  264. 264     else if(frame_flag )
  265. 265         rd_v_not_need_data <= 1'd0;
  266. 266     else if(scale_line_finish && one_line_data_ready && ~scale_v_end_line)
  267. 267         if(scale_map_v[19:8] == scale_map_v_next[19:8])
  268. 268             rd_v_not_need_data <= 1;
  269. 269         else
  270. 270             rd_v_not_need_data <= 0;            
  271. 271     else
  272. 272         rd_v_not_need_data <= rd_v_not_need_data;
  273. 273 end
  274. 274
  275. 275 //对缩放后的行进行计数
  276. 276 always@(posedge sys_clk or negedge rst_n)begin
  277. 277     if(~rst_n)
  278. 278         scale_line <= 12'd0;
  279. 279     else if( frame_flag )
  280. 280         scale_line <= 12'd0;
  281. 281     else if(scale_line_finish)
  282. 282         scale_line <= scale_line + 12'd1;
  283. 283     else
  284. 284         scale_line <= scale_line;
  285. 285 end
  286. 286
  287. 287 //产生ram数据首行写入使能
  288. 288 always@(posedge sys_clk)begin
  289. 289     if(~rst_n)
  290. 290         one_line_data_ready <= 0;
  291. 291     else if(frame_flag)
  292. 292         one_line_data_ready <= 0;
  293. 293     else if(fifo_line_finish)   
  294. 294         one_line_data_ready <= 1;
  295. 295     else
  296. 296         one_line_data_ready <= one_line_data_ready;
  297. 297 end
  298. 298
  299. 299 //产生ram数据可读使能
  300. 300 always@(posedge sys_clk)begin
  301. 301     if(~rst_n)
  302. 302         pixel_v_data_read_en <= 0;
  303. 303     else if(frame_flag)
  304. 304         pixel_v_data_read_en <= 0;
  305. 305     else if(scale_h_not_end && scaler_up)   
  306. 306         pixel_v_data_read_en <= 0;      
  307. 307     else if( ((ram_v_waddr >= ram_v_raddr + 2) && scale_v_line_valid )
  308. 308                || (rd_v_not_need_data && ram_v_raddr< s_width - 1)
  309. 309                || (scale_v_end_line && scale_v_line_valid && (ram_v_raddr< s_width - 1) ) )
  310. 310         pixel_v_data_read_en <= 1;
  311. 311     else
  312. 312         pixel_v_data_read_en <= 0;  
  313. 313 end
  314. 314
  315. 315 //产生最后一行标志信号
  316. 316 always@(posedge sys_clk or negedge rst_n )begin
  317. 317     if(~rst_n)
  318. 318         scale_v_end_line <= 0;
  319. 319     else if(frame_flag   )
  320. 320         scale_v_end_line <= 0;         
  321. 321     else if((scale_line == t_height - 1) && scale_line_finish_d[10] )
  322. 322         scale_v_end_line <= 1;
  323. 323     else
  324. 324         scale_v_end_line <= scale_v_end_line;      
  325. 325
  326. 326 end
  327. 327
  328. 328 //产生行ram读地址
  329. 329 always@(posedge sys_clk or negedge rst_n )begin
  330. 330     if(~rst_n)
  331. 331         ram_v_raddr <= 0;
  332. 332     else if(frame_flag || (scale_line_finish_d[8] && ~scale_v_end_line))
  333. 333         ram_v_raddr <= 0;                  
  334. 334     else if(pixel_v_data_read_en )
  335. 335         ram_v_raddr <= ram_v_raddr + 1;
  336. 336     else
  337. 337         ram_v_raddr <= ram_v_raddr;     
  338. 338 end
  339. 339
  340. 340 //产生行运算输入使能有效信号
  341. 341 always@(posedge sys_clk or negedge rst_n )begin
  342. 342     if(~rst_n)
  343. 343         calu_v_data_in_en <= 0;
  344. 344     else if(pixel_v_data_read_en_d0)
  345. 345         calu_v_data_in_en <= 1;         
  346. 346     else
  347. 347         calu_v_data_in_en <= 0;     
  348. 348 end
  349. 349
  350. 350 //产生行缩放完成信号
  351. 351 always@(posedge sys_clk or negedge rst_n )begin
  352. 352     if(~rst_n)
  353. 353         scale_line_finish <= 0;
  354. 354     else if(frame_flag )
  355. 355         scale_line_finish <= 0;         
  356. 356     else if((ram_v_raddr_d0 == s_width - 1) && (ram_v_raddr_d1 == s_width - 2))
  357. 357         scale_line_finish <= 1;
  358. 358     else
  359. 359         scale_line_finish <= 0;     
  360. 360 end
  361. 361
  362. 362 //对相邻的2行进行权重赋值
  363. 363 always@(posedge sys_clk)begin
  364. 364     if(~rst_n)begin
  365. 365         a_coff_v <= 0;
  366. 366         b_coff_v <= 0;
  367. 367     end
  368. 368     else begin
  369. 369         a_coff_v <= scale_map_v[7:0];
  370. 370         b_coff_v <= ~scale_map_v[7:0];  
  371. 371     end
  372. 372 end
  373. 373
  374. 374 //根据ram写切换使能对对应的行寄存器赋值
  375. 375 always@(posedge sys_clk)begin
  376. 376     if(~rst_n)begin
  377. 377         line0_pixel_data <= 0;
  378. 378         line1_pixel_data <= 0;
  379. 379     end
  380. 380     else if(wr_buf_sel)begin
  381. 381         line0_pixel_data <= ram_v_data_out0;
  382. 382         line1_pixel_data <= ram_v_data_out1;
  383. 383     end
  384. 384     else begin
  385. 385         line0_pixel_data <= ram_v_data_out1;
  386. 386         line1_pixel_data <= ram_v_data_out0;
  387. 387     end
  388. 388 end
  389. 389   
  390. 390 //对读出的fifo数据通过ram进行存储
  391. 391 ram_scaler u_ram_scaler_v_0(
  392. 392     .clka  (sys_clk),
  393. 393     .wea   (ram_v_wren & wr_buf_sel),
  394. 394     .addra (ram_v_waddr),
  395. 395     .dina  (data_in),
  396. 396     .clkb  (sys_clk),
  397. 397     .addrb (ram_v_raddr),
  398. 398     .doutb (ram_v_data_out0)
  399. 399   );
  400. 400  
  401. 401 //对读出的fifo数据通过ram进行存储
  402. 402 ram_scaler u_ram_scaler_v_1(
  403. 403     .clka  (sys_clk),
  404. 404     .wea   (ram_v_wren & ~wr_buf_sel),
  405. 405     .addra (ram_v_waddr),
  406. 406     .dina  (data_in),
  407. 407     .clkb  (sys_clk),
  408. 408     .addrb (ram_v_raddr),
  409. 409     .doutb (ram_v_data_out1)
  410. 410   );
  411. 411  
  412. 412 //对行进行加权运算  
  413. 413 calu calu_v(
  414. 414     .sys_clk(sys_clk),
  415. 415     .a(line0_pixel_data),
  416. 416     .b(line1_pixel_data),
  417. 417     .a_coff(a_coff_v),                 
  418. 418     .b_coff(b_coff_v),
  419. 419     .c(c_v),
  420. 420     .data_en_in(calu_v_data_in_en),
  421. 421     .data_en_out(data_en_out_calu_v)   
  422. 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的输出数据为现在这行的输出数据。
  1. 425 //产生列ram的写使能和数据
  2. 426 always@(posedge sys_clk)begin
  3. 427     if(~rst_n)begin
  4. 428         ram_h_data_in <= 0;
  5. 429         ram_h_wren <= 0;
  6. 430     end
  7. 431     else if(frame_flag_d0)begin
  8. 432         ram_h_data_in <= 0;
  9. 433         ram_h_wren <= 0;
  10. 434     end
  11. 435     else if(data_en_out_calu_v)begin
  12. 436         ram_h_data_in <= c_v;
  13. 437         ram_h_wren <= data_en_out_calu_v;
  14. 438     end
  15. 439     else begin
  16. 440         ram_h_data_in <= 0;
  17. 441         ram_h_wren <= 0;
  18. 442     end
  19. 443 end
  20. 444
  21. 445 //产生列ram的写地址
  22. 446 always@(posedge sys_clk or negedge rst_n )begin
  23. 447     if(~rst_n)
  24. 448         ram_h_waddr <= 0;
  25. 449     else if(frame_flag_d0)
  26. 450         ram_h_waddr <= 0;           
  27. 451     else if(scale_h_finish )
  28. 452         ram_h_waddr <= 0;      
  29. 453     else if(ram_h_wren )
  30. 454         ram_h_waddr <= ram_h_waddr + 1;         
  31. 455     else
  32. 456         ram_h_waddr <= ram_h_waddr;     
  33. 457 end
  34. 458
  35. 459 //产生列ram数据可读使能
  36. 460 always@(posedge sys_clk)begin
  37. 461     if(~rst_n)
  38. 462         pixel_h_data_read_en <= 0;
  39. 463     else if(frame_flag_d0)  
  40. 464         pixel_h_data_read_en <= 0;
  41. 465     else if(scale_h_finish )   
  42. 466         pixel_h_data_read_en <= 0;      
  43. 467     else if(ram_h_waddr >= ram_h_raddr + 2)
  44. 468         pixel_h_data_read_en <= 1;
  45. 469     else
  46. 470         pixel_h_data_read_en <= 0;  
  47. 471 end
  48. 472
  49. 473 //产生列ram的读地址
  50. 474 always@(posedge sys_clk or negedge rst_n )begin
  51. 475     if(~rst_n)
  52. 476         ram_h_raddr <= 0;
  53. 477     else if(frame_flag_d0 || scale_h_finish )
  54. 478         ram_h_raddr <= 0;   
  55. 479     else if((pixel_h_data_read_en == 1) && (pixel_h_data_read_en_d0 == 0))
  56. 480         ram_h_raddr <= 0;               
  57. 481     else if(scale_h_line_valid  && scaler_up)
  58. 482         ram_h_raddr <= ram_h_raddr;                     
  59. 483     else if(pixel_h_data_read_en)
  60. 484         ram_h_raddr <= ram_h_raddr + 1;
  61. 485     else
  62. 486         ram_h_raddr <= ram_h_raddr;     
  63. 487 end
  64. 488
  65. 489 //对列缩放因子进行累加
  66. 490 always@(posedge sys_clk or negedge rst_n)begin
  67. 491     if(~rst_n)
  68. 492         scale_map_h <= 20'd0;
  69. 493     else if(frame_flag_d0 || scale_h_finish)
  70. 494         scale_map_h <= 20'd0;
  71. 495     else if(pixel_h_data_read_en_d1 && (ram_h_raddr> scale_map_h[19:8]))
  72. 496         scale_map_h <= scale_map_h + {4'd0,k_h_reg};
  73. 497     else
  74. 498         scale_map_h <= scale_map_h;
  75. 499 end
  76. 500
  77. 501 //产生列运算输入使能有效信号
  78. 502 always@(posedge sys_clk or negedge rst_n)begin
  79. 503     if(~rst_n)
  80. 504         calu_h_data_in_en <= 1'd0;
  81. 505     else if(frame_flag_d0 )
  82. 506         calu_h_data_in_en <= 1'd0;
  83. 507     else if(ram_h_raddr >= s_width && !scaler_up && cnt_h_pixel>= t_width - 1)
  84. 508         calu_h_data_in_en <= 1'd0;              
  85. 509     else if(pixel_h_data_read_en_d0 && (cnt_h_pixel <= t_width - 1)
  86. 510             && (((scale_map_h[19:8] + 1) == ram_h_raddr) && !scaler_up) )
  87. 511         calu_h_data_in_en <= 1;
  88. 512     else if(pixel_h_data_read_en_d0 && (cnt_h_pixel <= t_width - 2) && scaler_up )
  89. 513         calu_h_data_in_en <= 1;     
  90. 514     else
  91. 515         calu_h_data_in_en <= 0;
  92. 516 end
  93. 517
  94. 518 //产生列缩放完成信号
  95. 519 always@(posedge sys_clk or negedge rst_n )begin
  96. 520     if(~rst_n)
  97. 521         scale_h_finish <= 0;
  98. 522     else if(frame_flag_d0 )
  99. 523         scale_h_finish <= 0;            
  100. 524     else if((ram_h_raddr_d0 == s_width-1) && (ram_h_raddr_d1 == s_width - 2))
  101. 525         scale_h_finish <= 1;   
  102. 526     else
  103. 527         scale_h_finish <= 0;        
  104. 528 end
  105. 529
  106. 530 //产生列缩放未结束信号
  107. 531 always@(posedge sys_clk or negedge rst_n )begin
  108. 532     if(~rst_n)
  109. 533         scale_h_not_end <= 0;
  110. 534     else if(frame_flag_d0 || scale_h_finish_d[4])
  111. 535         scale_h_not_end <= 0;           
  112. 536     else if(ram_h_waddr == s_width - 1 )
  113. 537         scale_h_not_end <= 1;   
  114. 538     else
  115. 539         scale_h_not_end <= scale_h_not_end;     
  116. 540 end
  117. 541
  118. 542 //对列运算的数据赋值
  119. 543 always@(posedge sys_clk)begin
  120. 544     if(~rst_n)begin
  121. 545         col0_pixel_data <= 0;
  122. 546         col1_pixel_data <= 0;
  123. 547         calu_h_data_in_en_d0 <= 0;
  124. 548     end
  125. 549     else begin
  126. 550         col0_pixel_data <= ram_h_data_out;
  127. 551         col1_pixel_data <= col0_pixel_data;
  128. 552         calu_h_data_in_en_d0 <= calu_h_data_in_en;      
  129. 553     end
  130. 554 end
  131. 555
  132. 556 //对相邻的2列进行权重赋值
  133. 557 always@(posedge sys_clk)begin
  134. 558     if(~rst_n)begin
  135. 559         a_coff_h <= 0;
  136. 560         b_coff_h <= 0;
  137. 561     end
  138. 562     else begin
  139. 563         a_coff_h <= scale_map_h[7:0];
  140. 564         b_coff_h <= ~scale_map_h[7:0];  
  141. 565     end
  142. 566 end
  143. 567
  144. 568 //对列运算后的信号进行输出寄存
  145. 569 always@(posedge sys_clk)begin
  146. 570     if(~rst_n)begin
  147. 571         scaler_wren <= 0;
  148. 572         data_out <= 0;
  149. 573     end
  150. 574     else begin
  151. 575         scaler_wren <= calu_h_data_out_en;
  152. 576         data_out <= c_h;   
  153. 577     end
  154. 578 end
  155. 579
  156. 580 //对列运算输入使能进行计数
  157. 581 always@(posedge sys_clk)begin
  158. 582     if(~rst_n || frame_flag_d0)begin
  159. 583         cnt_h_pixel <= 0;
  160. 584     end
  161. 585     else begin
  162. 586         if(scale_h_finish)
  163. 587           cnt_h_pixel <= 0;
  164. 588         else if(calu_h_data_in_en)  
  165. 589           cnt_h_pixel <= cnt_h_pixel + 1;
  166. 590         else
  167. 591           cnt_h_pixel <= cnt_h_pixel;         
  168. 592     end
  169. 593 end
  170. 594
  171. 595 //存放行缩放后的数据
  172. 596 ram_scaler u_ram_scaler_h(
  173. 597     .clka  (sys_clk),
  174. 598     .wea   (ram_h_wren),
  175. 599     .addra (ram_h_waddr),
  176. 600     .dina  (ram_h_data_in),
  177. 601     .clkb  (sys_clk),
  178. 602     .addrb (ram_h_raddr),
  179. 603     .doutb (ram_h_data_out)
  180. 604   );
  181. 605  
  182. 606 //对列进行加权运算
  183. 607 calu calu_h(
  184. 608     .sys_clk(sys_clk),
  185. 609     .a(col0_pixel_data),
  186. 610     .b(col1_pixel_data),
  187. 611     .a_coff(a_coff_h),                 
  188. 612     .b_coff(b_coff_h),
  189. 613     .c(c_h),
  190. 614     .data_en_in(calu_h_data_in_en_d0),
  191. 615     .data_en_out(calu_h_data_out_en)   
  192. 616 );   
  193. 617
  194. 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. 1  module calu#
  2. 2  (   
  3. 3   parameter DATA_WIDTH = 8)
  4. 4  (
  5. 5   input   sys_clk,
  6. 6   input  [DATA_WIDTH - 1:0]  a,              //像素数据
  7. 7   input  [DATA_WIDTH - 1:0]  b,              //像素数据
  8. 8   input  [7:0]               a_coff,         //行权重系数
  9. 9   input  [7:0]               b_coff,         //行权重系数
  10. 10  output [DATA_WIDTH - 1:0]  c,              //缩放后的像素数据
  11. 11  input  [7:0]               a_coff_next,    //下级权重系数输入
  12. 12  input  [7:0]               b_coff_next,    //下级权重系数输入
  13. 13  output [7:0]               a_coff_next_out,//下级权重系数输出
  14. 14  output [7:0]               b_coff_next_out,//下级权重系数输出
  15. 15  input                      data_en_in,     //数据有效使能输入  
  16. 16  input                      scale_en_in,    //数据缩放有效使能输入
  17. 17  output                     data_en_out,    //数据有效使能输出
  18. 18  output                     scale_en_out    //数据缩放有效使能输出
  19. 19 );
  20. 20
  21. 21 //reg define
  22. 22 reg[DATA_WIDTH - 1:0] a_reg;
  23. 23 reg[DATA_WIDTH - 1:0] b_reg;
  24. 24 reg[7:0]              a_coff_reg;
  25. 25 reg[7:0]              b_coff_reg;
  26. 26 reg[7:0]              a_coff_next_out;//下级系数
  27. 27 reg[7:0]              b_coff_next_out;//下级系数
  28. 28 reg                   data_en_out;
  29. 29 reg                   scale_en_out;
  30. 30 reg[15:0]             a_mult0;
  31. 31 reg[15:0]             b_mult0;
  32. 32 reg[15:0]             add0;
  33. 33 reg[8:0]              add_tmp0;
  34. 34 reg                   data_en_in_d0;
  35. 35 reg                   data_en_in_d1;
  36. 36 reg                   data_en_in_d2;
  37. 37 reg                   scale_en_in_d0;
  38. 38 reg                   scale_en_in_d1;
  39. 39 reg                   scale_en_in_d2;
  40. 40 reg[7:0]              a_coff_next_d0;
  41. 41 reg[7:0]              a_coff_next_d1;
  42. 42 reg[7:0]              a_coff_next_d2;
  43. 43 reg[7:0]              b_coff_next_d0;
  44. 44 reg[7:0]              b_coff_next_d1;
  45. 45 reg[7:0]              b_coff_next_d2;
  46. 46
  47. 47 //*****************************************************
  48. 48 //**                    main code
  49. 49 //*****************************************************
  50. 50
  51. 51 assign c = add_tmp0[8] ? 8'hf0 : add_tmp0[7:0];
  52. 52
  53. 53 //打拍寄存
  54. 54 always@(posedge sys_clk)begin
  55. 55     a_reg <= a;
  56. 56     b_reg <= b;
  57. 57     a_coff_reg <= a_coff;
  58. 58     b_coff_reg <= b_coff;
  59. 59     data_en_in_d0 <= data_en_in;
  60. 60     scale_en_in_d0 <= scale_en_in;
  61. 61     a_coff_next_d0 <= a_coff_next;
  62. 62     b_coff_next_d0 <= b_coff_next;
  63. 63 end
  64. 64
  65. 65 //对像素点进行加权运算
  66. 66 always@(posedge sys_clk)begin
  67. 67     a_mult0 <= a_reg[7:0] * a_coff_reg;  
  68. 68     b_mult0 <= b_reg[7:0] * b_coff_reg;
  69. 69     add0 <= a_mult0 + b_mult0;   
  70. 70     add_tmp0 <= {1'b0,add0[15:8]} + {8'd0,{(add0[7:0] > 8'h66)}};   
  71. 71  
  72. 72 end
  73. 73
  74. 74 //对信号进行延时
  75. 75 always@(posedge sys_clk)begin
  76. 76      data_en_in_d1 <= data_en_in_d0;
  77. 77      scale_en_in_d1 <= scale_en_in_d0;
  78. 78      a_coff_next_d1 <= a_coff_next_d0;
  79. 79      b_coff_next_d1 <= b_coff_next_d0;
  80. 80      
  81. 81      data_en_in_d2 <= data_en_in_d1;
  82. 82      scale_en_in_d2 <= scale_en_in_d1;
  83. 83      a_coff_next_d2 <= a_coff_next_d1;
  84. 84      b_coff_next_d2 <= b_coff_next_d1;
  85. 85      
  86. 86      data_en_out <= data_en_in_d2;
  87. 87      scale_en_out <= scale_en_in_d2;
  88. 88      a_coff_next_out <= a_coff_next_d2;
  89. 89      b_coff_next_out <= b_coff_next_d2;  
  90. 90 end
  91. 91
  92. 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. 1  module key_ctrl(
  2. 2      //input
  3. 3      input        sys_clk,      //时钟信号50Mhz
  4. 4      input        sys_rst_n,    //复位信号
  5. 5      input        touch_key,    //按键
  6. 6   
  7. 7      //output
  8. 8      output [2:0] change_en     //切换信号      
  9. 9  );
  10. 10
  11. 11 //reg define
  12. 12 reg        touch_key_d0;
  13. 13 reg        touch_key_d1;
  14. 14 reg        vs_in_d0;
  15. 15 reg        touch_en;
  16. 16 reg [23:0] cnt_time;
  17. 17 reg [2:0]  change_en ;
  18. 18
  19. 19 //*****************************************************
  20. 20 //**                    main code
  21. 21 //*****************************************************
  22. 22
  23. 23 //对触摸按键端口的数据延迟两个时钟周期
  24. 24 always [url=home.php?mod=space&uid=95564]@[/url] (posedge sys_clk or negedge sys_rst_n) begin
  25. 25     if(!sys_rst_n) begin
  26. 26         touch_key_d0 <= 1'b0;
  27. 27         touch_key_d1 <= 1'b0;
  28. 28     end
  29. 29     else begin
  30. 30         touch_key_d0 <= touch_key;
  31. 31         touch_key_d1 <= touch_key_d0;
  32. 32     end
  33. 33 end
  34. 34
  35. 35 //产生切换信号
  36. 36 always @ (posedge sys_clk or negedge sys_rst_n) begin
  37. 37     if(!sys_rst_n) begin
  38. 38         change_en <= 3'd4;
  39. 39     end
  40. 40     else begin
  41. 41         if(cnt_time ==  500000)
  42. 42             if(change_en == 3'b100)
  43. 43                 change_en <= 3'b1;
  44. 44             else                 
  45. 45                 change_en <= {change_en[1:0],1'b0};
  46. 46         else
  47. 47             change_en <= change_en ;                 
  48. 48     end
  49. 49 end
  50. 50
  51. 51 //对按键的低电平时间进行计数
  52. 52 always @ (posedge sys_clk or negedge sys_rst_n) begin
  53. 53     if(!sys_rst_n) begin
  54. 54         cnt_time <= 24'b0;
  55. 55     end
  56. 56     else begin
  57. 57         if(touch_key_d1)
  58. 58             cnt_time <= 24'b0;
  59. 59         else if(cnt_time >= 2500000)  
  60. 60             cnt_time <= cnt_time;
  61. 61         else
  62. 62             cnt_time <= cnt_time + 1;                 
  63. 63     end
  64. 64 end
  65. 65
  66. 66 endmodule
复制代码

代码第24至33行,是将输入的按键信号进行打拍,以减少信号的毛刺。
代码第36至49行,是对按键进行消抖处理。当按键按下的时间不超过10ms,则认为这是毛刺,此次的按键是无效的。当超过10ms时,将信号change_en进行移位。
代码第52至64行,是对按键按下的时间进行记时。当按键未按下时对计数器进行清零,按键按下的时间超过50ms后计数器不再累加。
下面是ddr参数切换模块的代码:
  1. 1   module para_change(
  2. 2       input           clk_wr,            //写时钟
  3. 3       input           clk_rd,            //读时钟
  4. 4       input  [2:0]    change_en,         //切换信号,1为放大,2为缩小,4为原图
  5. 5       input           rst_n,             //复位信号
  6. 6                                          
  7. 7       input           rd_vsync,          //读端场信号
  8. 8       input           wr_vsync,          //写端场信号   
  9. 9       input  [10:0]   s_width,           //缩放前宽度
  10. 10      input  [10:0]   s_height,          //缩放前高度
  11. 11      
  12. 12      output [10:0]   t_width_wr,        //写端缩放后宽度
  13. 13      output [10:0]   t_height_wr,       //写端缩放后高度      
  14. 14      output [10:0]   t_width_rd,        //读端缩放后宽度
  15. 15      output [10:0]   t_height_rd,       //读端缩放后高度      
  16. 16      output [15:0]   h_scale_k,         //列缩放因子   
  17. 17      output [15:0]   v_scale_k,         //行缩放因子                                       
  18. 18      output [2:0]    change_en_wr,      //写端切换信号
  19. 19      output [2:0]    change_en_rd,      //读端切换信号
  20. 20      output [27:0]   app_addr_rd_max,   //存入DDR3的最大读地址
  21. 21      output [7:0]    rd_bust_len    ,   //从DDR3中读数据时的突发长度  
  22. 22      output [27:0]   app_addr_wr_max,   //存入DDR3的最大写地址
  23. 23      output [7:0]    wr_bust_len        //从DDR3中写数据时的突发长度
  24. 24      
  25. 25      );
  26. 26  
  27. 27  //reg define
  28. 28  reg  [27:0]   app_addr_rd_max;   
  29. 29  reg  [7:0]    rd_bust_len;   
  30. 30  reg  [27:0]   app_addr_wr_max;   
  31. 31  reg  [7:0]    wr_bust_len;   
  32. 32  reg  [7:0]    rd_vsync_d;
  33. 33  reg  [7:0]    wr_vsync_d;               
  34. 34  reg  [10:0]   t_width_wr;   
  35. 35  reg  [10:0]   t_height_wr;  
  36. 36  reg  [10:0]   t_width_rd;   
  37. 37  reg  [10:0]   t_height_rd;  
  38. 38  reg  [10:0]   s_width_wr;   
  39. 39  reg  [10:0]   s_height_wr;  
  40. 40  reg  [10:0]   s_width_rd;   
  41. 41  reg  [10:0]   s_height_rd;
  42. 42  reg  [2:0]    change_wr_en_d1;
  43. 43  reg  [2:0]    change_en_wr_d0;
  44. 44  reg  [2:0]    change_en_wr;
  45. 45  reg  [2:0]    change_en_rd;
  46. 46  reg  [2:0]    change_wr_en_d0;
  47. 47  reg           delay_frame;      //切换后写入一帧标志信号
  48. 48  reg  [2:0]    change_rd_en_d0;
  49. 49  reg  [2:0]    change_rd_en_d1;
  50. 50  reg           delay_frame_d0;   
  51. 51  reg  [15:0]   h_scale_k;        //(s_width * 256)/ t_width
  52. 52  reg  [15:0]   v_scale_k;        //(s_height * 256)/ t_height
  53. 53  reg  [3:0]    cmos_ps_cnt;
  54. 54  
  55. 55  //wire define
  56. 56  wire [10:0]   t_width_up;       //放大后图像的宽度
  57. 57  wire [10:0]   t_height_up;      //放大后图像的高度
  58. 58  wire [10:0]   t_width_down;     //缩小后图像的宽度
  59. 59  wire [10:0]   t_height_down;    //缩小后图像的高度
  60. 60  wire          para_finish;
  61. 61  
  62. 62  //*****************************************************
  63. 63  //**                    main code
  64. 64  //*****************************************************
  65. 65  //放大后图像的分辨率
  66. 66  assign t_width_up  = 11'd1024;
  67. 67  assign t_height_up = 11'd768;
  68. 68  //缩小后图像的分辨率
  69. 69  assign t_width_down  = 11'd560;
  70. 70  assign t_height_down = 11'd400;
  71. 71  
  72. 72  //对写端输入信号进行寄存
  73. 73  always@(posedge clk_wr or negedge rst_n)begin
  74. 74      if(~rst_n)begin
  75. 75          wr_vsync_d      <= 4'd0;   
  76. 76          change_wr_en_d0 <= 1'b0;
  77. 77          change_en_wr_d0 <= 1'b0;        
  78. 78          s_width_wr      <= 11'b0;
  79. 79          s_height_wr     <= 11'b0;           
  80. 80      end
  81. 81      else begin
  82. 82          wr_vsync_d      <= {wr_vsync_d[6:0],wr_vsync };
  83. 83          s_width_wr      <= s_width;
  84. 84          s_height_wr     <= s_height;        
  85. 85          change_wr_en_d0 <= change_en;
  86. 86          change_wr_en_d1 <= change_wr_en_d0;     
  87. 87          change_en_wr_d0 <= change_en_wr;            
  88. 88      end
  89. 89  end
  90. 90  
  91. 91  //对读端输入信号进行寄存
  92. 92  always@(posedge clk_rd or negedge rst_n)begin
  93. 93      if(~rst_n)begin
  94. 94          rd_vsync_d       <= 4'd0;   
  95. 95          change_rd_en_d0  <= 1'b0;   
  96. 96          delay_frame_d0   <= 1'b0;
  97. 97          s_width_rd       <= 11'b0;
  98. 98          s_height_rd      <= 11'b0;      
  99. 99      end
  100. 100     else begin
  101. 101         rd_vsync_d       <= {rd_vsync_d[6:0],rd_vsync };   
  102. 102         s_width_rd       <= s_width;
  103. 103         s_height_rd      <= s_height;      
  104. 104         change_rd_en_d0  <= change_en;  
  105. 105         change_rd_en_d1  <= change_rd_en_d0;            
  106. 106         delay_frame_d0   <= delay_frame;                        
  107. 107     end
  108. 108 end
  109. 109
  110. 110 //产生写端切换使能
  111. 111 always@(posedge clk_wr or negedge rst_n)begin
  112. 112     if(~rst_n)
  113. 113         change_en_wr <= 1'b0;                  
  114. 114     else if(wr_vsync_d[1] && ~wr_vsync_d[0])
  115. 115         change_en_wr <= change_wr_en_d0;                        
  116. 116     else
  117. 117         change_en_wr <= change_en_wr;                           
  118. 118 end
  119. 119
  120. 120 //写端缩放后的分辨率
  121. 121 always@(posedge clk_wr or negedge rst_n)begin
  122. 122     if(~rst_n)begin
  123. 123         t_width_wr  <= 11'b0;   
  124. 124         t_height_wr <= 11'b0;           
  125. 125     end
  126. 126     else if(wr_vsync_d[2] && ~wr_vsync_d[1])begin               
  127. 127         if(change_en_wr[0]) begin
  128. 128             t_width_wr  <= t_width_up;  
  129. 129             t_height_wr <= t_height_up;     
  130. 130         end
  131. 131         else if(change_en_wr[1]) begin
  132. 132             t_width_wr  <= t_width_down;   
  133. 133             t_height_wr <= t_height_down;      
  134. 134         end     
  135. 135         else  begin
  136. 136             t_width_wr  <= s_width;
  137. 137             t_height_wr <= s_height;        
  138. 138         end
  139. 139     end
  140. 140     else begin
  141. 141         t_width_wr  <= t_width_wr;  
  142. 142         t_height_wr <= t_height_wr;     
  143. 143     end                        
  144. 144 end
  145. 145
  146. 146 //产生行场缩放因子
  147. 147 always@(posedge clk_wr or negedge rst_n)begin
  148. 148     if(~rst_n)begin
  149. 149         h_scale_k  <= 16'b0;                     
  150. 150         v_scale_k  <= 16'b0;            
  151. 151     end                 
  152. 152     else if(wr_vsync_d[3] && ~wr_vsync_d[2]) begin
  153. 153         if(change_en_wr[0]) begin
  154. 154             h_scale_k  <= 16'd180;                //(s_width * 256)/ t_width
  155. 155             v_scale_k  <= 16'd160;                //(s_height * 256)/ t_height
  156. 156         end
  157. 157         else if(change_en_wr[1]) begin
  158. 158             h_scale_k  <= 16'd329;  
  159. 159             v_scale_k  <= 16'd307;  
  160. 160         end     
  161. 161         else  begin
  162. 162             h_scale_k  <= 16'd256;  
  163. 163             v_scale_k  <= 16'd256;      
  164. 164         end         
  165. 165     end     
  166. 166     else  begin
  167. 167         h_scale_k  <= h_scale_k;   
  168. 168         v_scale_k <= v_scale_k;     
  169. 169     end                        
  170. 170 end
  171. 171
  172. 172 //产生切换后写入一帧标志信号
  173. 173 always@(posedge clk_wr or negedge rst_n)begin
  174. 174     if(~rst_n)
  175. 175         delay_frame <= 1'b0;   
  176. 176     else if(change_wr_en_d0  != change_wr_en_d1)
  177. 177         delay_frame <= 1'b1;                    
  178. 178     else if((wr_vsync_d[5] && ~wr_vsync_d[4]) && (change_wr_en_d0 == change_en_wr))
  179. 179         delay_frame <= 1'b0;                    
  180. 180     else
  181. 181         delay_frame <= delay_frame;                        
  182. 182 end
  183. 183
  184. 184 //产生写端的ddr参数  
  185. 185 always@(posedge clk_wr or negedge rst_n)begin
  186. 186     if(~rst_n)begin
  187. 187         wr_bust_len <= s_width[10:3];
  188. 188         app_addr_wr_max <= s_width * s_height;  
  189. 189     end
  190. 190     else if(change_en_wr[2] )begin
  191. 191         if(wr_vsync_d[5] && ~wr_vsync_d[4])begin
  192. 192             wr_bust_len <= s_width_wr[10:3];
  193. 193             app_addr_wr_max <= s_width_wr * s_height_wr;   
  194. 194         end
  195. 195         else begin
  196. 196             wr_bust_len <= wr_bust_len;
  197. 197             app_addr_wr_max <= app_addr_wr_max;
  198. 198         end     
  199. 199     end
  200. 200     else begin
  201. 201         if(wr_vsync_d[5] && ~wr_vsync_d[4])begin
  202. 202             wr_bust_len <= t_width_wr[10:3];
  203. 203             app_addr_wr_max <= t_width_wr * t_height_wr;            
  204. 204         end
  205. 205         else begin
  206. 206             wr_bust_len <= wr_bust_len;
  207. 207             app_addr_wr_max <= app_addr_wr_max;     
  208. 208         end
  209. 209     end
  210. 210 end
  211. 211
  212. 212 //产生读端切换使能
  213. 213 always@(posedge clk_rd or negedge rst_n)begin
  214. 214     if(~rst_n)
  215. 215         change_en_rd <= 1'b0;                  
  216. 216     else if(rd_vsync_d[1] && ~rd_vsync_d[0] && ~delay_frame_d0)
  217. 217         change_en_rd <= change_rd_en_d0;                        
  218. 218     else
  219. 219         change_en_rd <= change_en_rd;                           
  220. 220 end
  221. 221
  222. 222 //读端缩放后的分辨率
  223. 223 always@(posedge clk_wr or negedge rst_n)begin
  224. 224     if(~rst_n)begin
  225. 225         t_width_rd  <= 11'b0;   
  226. 226         t_height_rd <= 11'b0;           
  227. 227     end
  228. 228     else if( rd_vsync_d[3] && ~rd_vsync_d[2] && ~delay_frame_d0)begin                  
  229. 229         if(change_en_rd[0] ) begin
  230. 230             t_width_rd  <= t_width_up;  
  231. 231             t_height_rd <= t_height_up;     
  232. 232         end
  233. 233         else if(change_en_rd[1]) begin
  234. 234             t_width_rd  <= t_width_down;   
  235. 235             t_height_rd <= t_height_down;      
  236. 236         end     
  237. 237         else  begin
  238. 238             t_width_rd  <= s_width;
  239. 239             t_height_rd <= s_height;        
  240. 240         end
  241. 241     end
  242. 242     else begin
  243. 243         t_width_rd  <= t_width_rd;  
  244. 244         t_height_rd <= t_height_rd;
  245. 245     end                        
  246. 246 end
  247. 247
  248. 248 //产生读端的ddr参数  
  249. 249 always@(posedge clk_rd or negedge rst_n)begin
  250. 250     if(~rst_n)begin
  251. 251         rd_bust_len <= s_width[10:3];
  252. 252         app_addr_rd_max <= s_width * s_height;         
  253. 253     end
  254. 254     else if(change_en_rd[2]  )begin
  255. 255         if(rd_vsync_d[5] && ~rd_vsync_d[4] && ~delay_frame_d0)begin
  256. 256             rd_bust_len <= s_width_rd[10:3];
  257. 257             app_addr_rd_max <= s_width_rd * s_height_rd;   
  258. 258         end
  259. 259         else begin
  260. 260             rd_bust_len <= rd_bust_len;
  261. 261             app_addr_rd_max <= app_addr_rd_max;
  262. 262         end     
  263. 263     end
  264. 264     else begin
  265. 265         if(rd_vsync_d[5] && ~rd_vsync_d[4] && ~delay_frame_d0)begin
  266. 266             rd_bust_len <= t_width_rd[10:3];
  267. 267             app_addr_rd_max <= t_width_rd * t_height_rd;            
  268. 268         end
  269. 269         else begin
  270. 270             rd_bust_len <= rd_bust_len;
  271. 271             app_addr_rd_max <= app_addr_rd_max;     
  272. 272         end
  273. 273     end
  274. 274 end     
  275. 275     
  276. 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顶层模块做了下面的修改:
  1. 14      input   [10:0]  t_width,        //缩放后宽度
  2. 15      input   [10:0]  t_height,       //缩放后高度   
  3. 16      input   [10:0]  s_width,        //缩放前宽度
  4. 17      input   [10:0]  s_height,       //缩放前高度   
  5. 18      input   [2:0]   change_en,      //切换信号,1为放大,2为缩小,4为原图
复制代码

添加了上面的五个输入端口,这五个信号是为了切换读请求使能和有效数据,以及输出到显示模块的参数。                     
  1. 60  assign  video_rgb_565 =  change_en[0] ?  video_rgb_dri :  video_rgb_dis;
  2. 61  assign  data_req      =  change_en[0] ?  data_req_dri :  data_req_dis;  
  3. 62  assign  video_width  =  change_en[2] ?  s_width : t_width ;      
  4. 63  assign  video_height  =  change_en[2] ?  s_height : t_height ;  
复制代码

        
当显示的数据为放大后的数据时,请求信号和数据由驱动模块输出,反之由显示模块输出。当最后输出的图像为原图输出时,将原图的分辨率输出到显示模块,否则将缩放后的分辨率输出到显示模块。
  1. 83  //显示模块  
  2. 84  lcd_display u_lcd_display(
  3. 85      .lcd_clk   (pixel_clk),                //lcd驱动时钟
  4. 86      .sys_rst_n (sys_rst_n),                //复位信号
  5. 87                 
  6. 88      .pixel_xpos(pixel_xpos),               //像素点横坐标
  7. 89      .pixel_ypos(pixel_ypos),               //像素点纵坐标   
  8. 90      .data_in   (data_in),                  //像素点数据
  9. 91      .h_disp    (h_disp),                   //LCD屏水平分辨率
  10. 92      .v_disp    (v_disp),                   //LCD屏垂直分辨率  
  11. 93      .t_width   (video_width),              //有效显示数据的宽度
  12. 94      .t_height  (video_height),             //有效显示数据的高度
  13. 95                 
  14. 96      .lcd_data  (video_rgb_dis),            //LCD像素点数据
  15. 97      .data_req  (data_req_dis)              //请求像素点颜色数据输入
  16. 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实时显示放大图像


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

如发现本坛存在违规或侵权内容, 请点击这里发送邮件举报 (或致电020-38271790)。请提供侵权说明和联系方式。我们将及时审核依法处理,感谢配合。

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2026-9-2 15:24

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表