您现在的位置是:首页 >技术交流 >基于 FPGA 的彩色图像灰度化的设计实现网站首页技术交流

基于 FPGA 的彩色图像灰度化的设计实现

C.V-Pupil 2024-06-17 10:32:01
简介基于 FPGA 的彩色图像灰度化的设计实现


前言

本次实验主要是在上一节的基础上,加入图像处理模块,实现在 PC 端通过上位机下发尺寸为 400*480 大小的彩色图像数据到 FPGA 的串口,FPGA 通过串口接收的彩色图像数据并进行实时彩色图像灰度化处理,然后将原始彩色图像和处理后的图像拼接在一起并缓存在DDR3 中,最终在 TFT 屏上同时显示处理前的彩色图像和处理后的灰度图像。

提示:以下是本篇文章正文内容,下面案例可供参考

一、系统整体设计

系统整体设计框图如下。大体结构与“基于 DDR3 的串口传图帧缓存系统设计实现”基本一致,增加了彩色图像灰度化处理模块 rgb2gray 模块和图像合并模块 image_stitche_x 模块。
在这里插入图片描述

二、各模块的功能

(1)uart_byte_rx 模块:负责串口图像数据的接收,该模块的设计前面章节已经有讲。数据(图像数据是 16bit 的 RGB565 的数据,电脑是通过串口将一个像素点数据分两次发送到 FPGA,FPGA 需将串口接收数据重组成 16bit 的图像数据),实现过程相对比较简单(可参考串口接收)。
(2)bit8_trans_bit16 模块:将串口接收的每两个 8bit 数据转换成一个 16bit(可参考8_trans_16)。
(3)rgb2gray 模块:彩色图像灰度化处理,对串口接收的彩色图像数据实时进行灰度化处理(参考第三章rgb2gray 模块)。
(4)image_stitche_x 模块:将串口接收的尺寸为 400480 大小的彩色图像与灰度化处理后的 400480 大小的图像数据以左右形式合并成一张 800*480 的图像(image_stitche_x 模块)。
(5)disp_driver 模块:tft 屏显示驱动控制,对缓存在 DDR3 中的图像数据进行显示(可参考VGA成像原理)。
(6)wr_ddr3_fifo 模块:使用的 FIFO IP ,主要用于写入 DDR3 数据的缓存、解决数据跨时钟域以及数据位宽的转换(参考串口传图IP模块)。
(7)rd_ddr3_fifo 模块:使用的 FIFO IP ,主要用于读出 DDR3 数据的缓存、解决数据跨时钟域以及数据位宽的转换(参考串口传图IP模块)。
(8)fifo2mig_axi 模块:主要是用于接口的转换,将 MIG IP 的 AXI 接口换成与 FIFO 对接的接口(可参考fifo2mig_axi)。
(9)mig_7series_0 模块: DDR3 控制器,使用的 Memory Interface Generator(MIG 7 Series)IP(可参考DDR3 控制器 MIG IP 详解完整版)。
(10)pll 模块:上述各个模块所需时钟的产生,使用 PLL IP(参考串口传图IP模块)。

本系统就是在上一系统基础上添加图像处理模块搭建系统。除去使用 IP 和前面章节讲过的模块外,还需要设计的模块包括 rgb2gray 模块和 image_stitche_x 模块。

三、彩色图像灰度化处理模块的设计

1.基本原理

将彩色图像转化为灰度图像的过程称为图像灰度化处理。常见的 24 位深度彩色图像RGB888 中的每个像素的颜色由 R、G、B 三个分量决定,并且三个分量各占 1 个字节,每个分量可以取值 0~255,这样一个像素点可以有 1600 多万(255255255)的颜色的变化范围。而灰度图像是 R、G、B 三个分量相同的一种特殊的彩色图像,其一个像素点的变化范围为 0~255。对于一幅彩色图来说,其对应的灰度图则是只有 8 位的图像深度,这也说明了用灰度图做图像处理所需的计算量确实要少。不过需要注意的是,虽然丢失了一些颜色等级,但是从整幅图像的整体和局部的色彩以及亮度等级分布特征来看,灰度图描述与彩色图的描述是一致的。一般有分量法、最大值法、平均值法、加权平均法四种方法对彩色图像进行灰度化。

2.彩色图像灰度化处理方法介绍

方法 1:分量法

将彩色图像中的三分量的亮度作为三个灰度图像的灰度值,可根据应用需要选取一种灰度图像。具体表达式如下。

                                         gray1(?,?) = ?(?,?)
                                         gray2(?,?) = ?(?,?)
                                         gray3(?,?) = ?(?,?)

其中,gray1(?,?), gray2(?,?), gray3(?,?)为转换后的灰度图像在(i,j)处的灰度值,R(i,j),G(i,j),B(i,j)分别为转换前的彩色图像在(i,j)处 R、G、B 三个分量的值。

方法 2:最大值法

将彩色图像中的三分量亮度 R,G,B 的最大值作为灰度图的灰度值。具体表达式如下。
gray(i, j) = max[?(?,?),?(?,?),?(?,?)]

方法 3:平均值法

将彩色图像中的三分量亮度求平均得到一个灰度值。如下:
gray(i, j) =(?(?,?) + ?(?,?) + ?(?,?))/3
上式中有除法,考虑到在 FPGA 中实现除法比较的消耗资源,这里在实现前可以先做如下的近似处理。可以将上面公式乘以 3/256,这样就需要同时乘以 256/3 保证公式的正确性。公式处理过程如下:
在这里插入图片描述
对 256/3 做近似取整处理,将 256/3 替换成 85,则公式变为如下。
在这里插入图片描述这样式子中除以 256 就可以采用移位方法来处理,式子变为如下:
在这里插入图片描述
上面处理过程中使用是对 256/3 的近似处理,当然这里可以采用其他数据,比如512/3、1024/3、2048/3 等等,基本的原则是将平均公式法中分母的 3 替换成 2 的幂次的数,这样除法就可以使用移位的方式实现,减小 FPGA 中由于存在除法带来的资源消耗。

平均值法的实现

该方法实现起来并不复杂,通过上面的计算公式可以知道,计算公式里只有加法、乘法和移位计算,这里的乘法通过移位相加的方式进行计算,计算具体实现见下面代码。

//求平均法 GRAY = (R+B+G)/3=(R+B+G)*85)>>8
 wire [9:0]sum;
 reg [15:0]gray_r;
 
 assign sum = red_8b_i + green_8b_i + blue_8b_i;
 
 always@(posedge clk or posedge reset_p)
 begin
 if(reset_p)
 gray_r <= 16'd0;
 else if(rgb_valid)
 gray_r <= (sum << 6)+(sum << 4)+(sum << 2)+ sum;
 else
 gray_r <= 16'd0;
 end
assign gray_8b_o = gray_r[15:8];
 always@(posedge clk)
 begin
 gray_valid <= rgb_valid;
 gray_hs <= rgb_hs;
 gray_vs <= rgb_vs;
 end

对该模块的仿真也相对比较简单,只需要在 testbech 中给时钟复位激励以及 RGB 三通道的图像数据即可,具体代码如下,仿真中分别给 R、G、B 通道不同的是起始数据值,然后通过递增加 1 的形式改变 R、G、B 通道数据,验证设计的正确性。

 initial begin
 reset_p = 1;
 rgb_valid = 0;
 red_8b_i = 0;
 green_8b_i = 0;
 blue_8b_i = 0; 
 #(`CLK_PERIOD*200+1);
 reset_p = 0;
 red_8b_i = 56;
 green_8b_i = 124;
 blue_8b_i = 203;
 #2000;
 
 rgb_valid = 1;
 repeat(256)begin
 #(`CLK_PERIOD)
 red_8b_i = red_8b_i + 1;
 green_8b_i = green_8b_i + 1;
 blue_8b_i = blue_8b_i + 1;
 end
 rgb_valid = 0;
 #2000;
 $stop; 
 end

为了能验证采用平均值法实现彩色图像灰度化计算的正确性,在仿真代码中加入了如下代码。直接通过平均法的原始公式产生一组对比数据。

always@(posedge clk)
 begin
 if(rgb_valid == 1'b1)
 comp1_gray <= (red_8b_i + green_8b_i + blue_8b_i)/3;
 else
 comp1_gray <= 0;
 end

这样可以比较容易的通过对比 comp1_gray 和 gray_8b_o 的值来验证设计模块的正确性。其仿真波形图如下:

在这里插入图片描述
在这里插入图片描述
在后面有个地方可以看到从一个地方开始 comp1_gray 和 gray_8b_o 的值相差 1,可以分析下出现这个问题的原因,其实 gray_8b_o 和 comp1_gray 的计算公式并非完全一样,gray_8b_o 的计算公式是为了避免除法做了一定的近似,而在仿真文件中 comp1_gray 是直接求的平均。可以通过 red、green、blue 这 3 个数据分别对 gray_8b_o 和 comp1_gray 进行计算,计算结果与仿真波形结果是一致的。这也说明了,避免除法做近似处理计算的 gray 和直接通过除法求的平均值 comp1_gray 是稍存在偏差的。这个是可以接收的误差范围。

方法 4 加权平均法

根据重要性及其它指标,将三个分量以不同的权值进行加权平均。有一个很著名的心理学公式:
在这里插入图片描述
这里 0.299+0.587+0.114=1,刚好是满偏,这是通过不同的敏感度以及经验总结出来的公式,一般可以直接用这个。在实际应用时,为了能避免低速的浮点运算以及除法运算,可以先将式子缩放 1024 倍来实现运算算法,如下:

在这里插入图片描述
通过近似取整处理后得到近似公式如下。

在这里插入图片描述

式子中除以 1024(这里是 2 的 n 次方就可以,n 不同,结果会略微有差别)可以采用移位方法来处理,式子变为如下:

在这里插入图片描述
也可以压缩到 8 位以内,式子变为如下。具体压缩到多少位可以根据实际需求。

在这里插入图片描述

加权平均法的实现

对于加权平均法可通过两种方式实现,公式直接计算法和查找表法。
 公式直接计算法
公式直接计算法与方法 3 实现类似,通过转换公式直接进行计算,只是具体计算数值发生了变化,同样乘法采用移位相加的方式实现。具体代码如下:

//典型灰度转换公式 Gray = R*0.299+G*0.587+B*0.114=(R*77 + G*150 + B*29) >>8
 wire [15:0]red_x77;
 wire [15:0]green_x150;
 wire [15:0]blue_x29;
 reg [15:0]sum;
 //乘法转换成移位相加方式
 assign red_x77 = (red_8b_i << 6) + (red_8b_i << 3) + (red_8b_i <<
2) + red_8b_i;
 assign green_x150 = (green_8b_i<< 7) + (green_8b_i<< 4) + (green_8b_i<<
2) + (green_8b_i<<1);
 assign blue_x29 = (blue_8b_i << 4) + (blue_8b_i << 3) + (blue_8b_i <<
2) + blue_8b_i;
 always@(posedge clk or posedge reset_p)
 begin
 if(reset_p)
 sum <= 16'd0;
 else if(rgb_valid)
 sum <= red_x77 + green_x150 + blue_x29;
 else
 sum <= 16'd0;
 end
 
 assign gray_8b_o = sum[15:8];
 always@(posedge clk)
 begin
 gray_valid <= rgb_valid;
 gray_hs <= rgb_hs;
 gray_vs <= rgb_vs;
 end

 公查找表法
通过观察计算公式发现,R、G、B 数据值均乘以了一个定值,然后对乘法之后的结果相加,最后右移 8 位,上面采用直接计算法实现是对常数乘法采用的移位相加方法计算,对于这类固定范围内的数值,同时可取数据不多情况下(这里 R、G、B 数值范围在 0~255,可取的数据有限)乘以一个常数,可以采用查找表方法实现,该方法主要的优势是直接通过访问 ROM 内的数据,相对使用移位相加实现乘法使用的 LUT 资源会少点,但占用的存储器会更多。因为需要将 R、G、B 乘以系数之后的数值存储在 ROM 中,然后通过读取 ROM方式来得到计算之后的数值。这里使用 Vivado 添加 3 个 ROM IP 核,分别通过 R75、G147、B36(0≤R≤255,0≤G≤255,0≤B≤255)的计算值建立 3 个初始化 coe 文件,然后在ROM IP 核中分别添加 coe 文件进行初始化。具体代码如下:代码中 rom_red_x77、rom_green_x150、rom_blue_x29 分别存储着 R 75、G147、B36(0≤R≤255,0≤G≤255,0≤B≤255)256 个数值。

//查找表方式,可以省去公式法中乘法运算 Gray =(R*77 + G*150 + B*29) >>8,将 3 个分量
乘以系数后的数值存储在 ROM 中
 wire [14:0]red_x77;
 wire [15:0]green_x150;
 wire [13:0]blue_x29; 
 reg [15:0]sum;
 reg rgb_valid_dly1;
 reg rgb_hs_dly1;
 reg rgb_vs_dly1;
 
 rom_red_x77 rom_red_x77(
 .clka (clk ), // input wire clka
 .ena (rgb_valid ), // input wire ena
 .addra (red_8b_i ), // input wire [7 : 0] addra
 .douta (red_x77 ) // output wire [14 : 0] douta
 );
 
 rom_green_x150 rom_green_x150(
 .clka (clk ), // input wire clka
 .ena (rgb_valid ), // input wire ena
 .addra (green_8b_i ), // input wire [7 : 0] addra
 .douta (green_x150 ) // output wire [15 : 0] douta
 );
 
 rom_blue_x29 rom_blue_x29(
 .clka (clk ), // input wire clka
 .ena (rgb_valid ), // input wire ena
 .addra (blue_8b_i ), // input wire [7 : 0] addra
 .douta (blue_x29 ) // output wire [13 : 0] douta
 );
 always@(posedge clk)
 begin
 rgb_valid_dly1 <= rgb_valid;
 rgb_hs_dly1 <= rgb_hs;
 rgb_vs_dly1 <= rgb_vs;
 end
 always@(posedge clk or posedge reset_p)
 begin
 if(reset_p)
 sum <= 16'd0;
 else if(rgb_valid_dly1)
 sum <= red_x77 + green_x150 + blue_x29;
 else
 sum <= 16'd0;
 end
 assign gray_8b_o = sum[15:8];
 always@(posedge clk)
 begin
 gray_valid <= rgb_valid_dly1;
 gray_hs <= rgb_hs_dly1;
 gray_vs <= rgb_vs_dly1;
 end

仿真过程与方法 3 类似,这里就不重复描述。对于方法 4 的两种不同实现方式,可以对比下综合后消耗的资源情况。下图 1 和下图 2 分别为使用公式直接计算法和查找表法实现使用资源情况。
在这里插入图片描述比较两种方法使用资源情况,可以很明显的看出查找表方式消耗的 LUT 相对较少,但消耗了 1.5 个 Block RAM 资源。

对于同一功能多种不同实现方法的模块代码如何整合到一起呢?当然每种方法作为一个单独的模块使用一个.v 文件保存肯定是没有问题的,这个就不太便于后期的维护和使用。如果能将多种实现方法整合到一个模块保存在一个.v 文件,使用起来就更加的方便。方法肯定是有的,而且还不只一种。下面提供两种方式,宏定义法,和使用 generate -if 方法。宏定义法相对比较好理解,通过不同的宏定义条件编译方式进行选择某种实现方式,实现的部分代码如下。该种方法可通过修改模块代码的宏定义选择不同的方法,还算是比较方便的。(此部分可参考语法使用)

rgb2gray 模块

/
// Module Name   : rgb2gray
// Description   : 图像处理之彩色图像灰度化,提供三种方式

//

module rgb2gray
#(
  parameter PROC_METHOD = "FORMULA" //"AVERAGE"     :求平均法
                                    //or "FORMULA"  :直接公式法
                                    //or "LUT"      :查找表法
)
(
  input           clk,         //时钟
  input           reset_p,     //复位
  input           rgb_valid,   //rgb输入有效标识
  input           rgb_hs,      //rgb输入行信号
  input           rgb_vs,      //rgb输入场信号
  input     [7:0] red_8b_i,    //R输入
  input     [7:0] green_8b_i,  //G输入
  input     [7:0] blue_8b_i,   //B输入

  output    [7:0] gray_8b_o,   //GRAY输出
  output reg      gray_valid,  //gray输出有效标识
  output reg      gray_hs,     //gray输出行信号
  output reg      gray_vs      //gray输出场信号
);

generate
  if (PROC_METHOD == "AVERAGE") begin: PROC_AVERAGE
//---------------------------------------------
//求平均法GRAY = (R+B+G)/3=(R+B+G)*85)>>8
    wire [9:0]sum;
    reg [15:0]gray_r;
    
    assign sum = red_8b_i + green_8b_i + blue_8b_i;
    
    always@(posedge clk or posedge reset_p)
    begin
      if(reset_p)
        gray_r <= 16'd0;
      else if(rgb_valid)
        gray_r <= (sum << 6)+(sum << 4)+(sum << 2)+ sum;
      else
        gray_r <= 16'd0;
    end
  
    assign gray_8b_o = gray_r[15:8];

    always@(posedge clk)
    begin
      gray_valid <= rgb_valid;
      gray_hs    <= rgb_hs;
      gray_vs    <= rgb_vs;
    end
//---------------------------------------------
  end
  else if (PROC_METHOD == "FORMULA") begin: PROC_FORMULA
//---------------------------------------------
//典型灰度转换公式Gray = R*0.299+G*0.587+B*0.114=(R*77 + G*150 + B*29) >>8
    wire [15:0]red_x77;
    wire [15:0]green_x150;
    wire [15:0]blue_x29;
    reg  [15:0]sum;

    //乘法转换成移位相加方式
    assign red_x77    = (red_8b_i  << 6) + (red_8b_i  << 3) + (red_8b_i  << 2) + red_8b_i;
    assign green_x150 = (green_8b_i<< 7) + (green_8b_i<< 4) + (green_8b_i<< 2) + (green_8b_i<<1);
    assign blue_x29   = (blue_8b_i << 4) + (blue_8b_i << 3) + (blue_8b_i << 2) + blue_8b_i;

    always@(posedge clk or posedge reset_p)
    begin
      if(reset_p)
        sum <= 16'd0;
      else if(rgb_valid)
        sum <= red_x77 + green_x150 + blue_x29;
      else
        sum <= 16'd0;
    end
    
    assign gray_8b_o = sum[15:8];

    always@(posedge clk)
    begin
      gray_valid <= rgb_valid;
      gray_hs    <= rgb_hs;
      gray_vs    <= rgb_vs;
    end
//---------------------------------------------
  end
  else if(PROC_METHOD == "LUT") begin: PROC_LUT
//---------------------------------------------
//查找表方式,可以省去公式法中乘法运算Gray =(R*77 + G*150 + B*29) >>8,将3个分量乘以系数后的数值存储在ROM中
    wire [14:0]red_x77;
    wire [15:0]green_x150;
    wire [13:0]blue_x29;    
    reg  [15:0]sum;
    reg        rgb_valid_dly1;
    reg        rgb_hs_dly1;
    reg        rgb_vs_dly1;
  
    rom_red_x77 rom_red_x77(
      .clka  (clk       ), // input wire clka
      .ena   (rgb_valid ), // input wire ena
      .addra (red_8b_i  ), // input wire [7 : 0] addra
      .douta (red_x77   )  // output wire [14 : 0] douta
    );
    
    rom_green_x150 rom_green_x150(
      .clka  (clk        ), // input wire clka
      .ena   (rgb_valid  ), // input wire ena
      .addra (green_8b_i ), // input wire [7 : 0] addra
      .douta (green_x150 )  // output wire [15 : 0] douta
    );
  
    rom_blue_x29 rom_blue_x29(
      .clka  (clk        ), // input wire clka
      .ena   (rgb_valid  ), // input wire ena
      .addra (blue_8b_i  ), // input wire [7 : 0] addra
      .douta (blue_x29   )  // output wire [13 : 0] douta
    );

    always@(posedge clk)
    begin
      rgb_valid_dly1 <= rgb_valid;
      rgb_hs_dly1    <= rgb_hs;
      rgb_vs_dly1    <= rgb_vs;
    end

    always@(posedge clk or posedge reset_p)
    begin
      if(reset_p)
        sum <= 16'd0;
      else if(rgb_valid_dly1)
        sum <= red_x77 + green_x150 + blue_x29;
      else
        sum <= 16'd0;
    end

    assign gray_8b_o = sum[15:8];

    always@(posedge clk)
    begin
      gray_valid <= rgb_valid_dly1;
      gray_hs    <= rgb_hs_dly1;
      gray_vs    <= rgb_vs_dly1;
    end
//---------------------------------------------
  end
  else begin: PROC_NONE
//---------------------------------------------
    assign gray_8b_o = 8'h00;

    always@(posedge clk)
    begin
      gray_valid <= rgb_valid;
      gray_hs    <= rgb_hs;
      gray_vs    <= rgb_vs;
    end
//---------------------------------------------
  end
endgenerate

endmodule 

rgb2gray TB文件

/

// Module Name   : rgb2gray_tb
// Description   : 图像处理之彩色图像灰度化仿真

/

`timescale 1ns/1ns
`define CLK_PERIOD 20

module rgb2gray_tb();
  reg       clk;
  reg       rst_n;
  reg       rgb_valid;
  reg       rgb_hs;
  reg       rgb_vs;
  reg [7:0] red_8b_i;
  reg [7:0] green_8b_i;
  reg [7:0] blue_8b_i;
  reg [7:0] comp1_gray;
  reg [7:0] comp2_gray;

  initial clk = 1'b1;
  always #(`CLK_PERIOD/2)clk = ~clk;

  initial begin
    reset_p    = 1;
    rgb_valid  = 0;
    rgb_hs     = 0;
    rgb_vs     = 0;
    red_8b_i   = 0;
    green_8b_i = 0;
    blue_8b_i  = 0;
    #(`CLK_PERIOD*200+1);
    reset_p    = 0;
    red_8b_i   = 56;
    green_8b_i = 124;
    blue_8b_i  = 203;
    #2000;

    rgb_hs    = 1;
    rgb_vs    = 1;
    rgb_valid = 1;
    repeat(256)begin
      #(`CLK_PERIOD)
      red_8b_i   = red_8b_i   + 1;
      green_8b_i = green_8b_i + 1;
      blue_8b_i  = blue_8b_i  + 1;
    end
    rgb_valid = 0;
    rgb_hs    = 0;
    rgb_vs    = 0;

    #2000;
    $stop;  
  end

  always@(posedge clk)
  begin
    if(rgb_valid == 1'b1)
      comp1_gray <= (red_8b_i + green_8b_i + blue_8b_i)/3;  
    else
      comp1_gray <= 0;
  end

  always@(posedge clk)
  begin
    if(rgb_valid == 1'b1)
      comp2_gray <= red_8b_i*0.299 + green_8b_i*0.587 + blue_8b_i*0.114;  
    else
      comp2_gray <= 0;
  end

  rgb2gray
  #(
    .PROC_METHOD("AVERAGE")  //"AVERAGE"     :求平均法
                             //or "FORMULA"  :直接公式法
                             //or "LUT"      :查找表法
  )rgb2gray_average
  (
    .clk       (clk       ),
    .reset_p   (reset_p   ),
    .rgb_valid (rgb_valid ),
    .rgb_hs    (rgb_hs    ),
    .rgb_vs    (rgb_vs    ),
    .red_8b_i  (red_8b_i  ),
    .green_8b_i(green_8b_i),
    .blue_8b_i (blue_8b_i ),
    .gray_8b_o (          ),
    .gray_valid(          ),
    .gray_hs   (          ),
    .gray_vs   (          ) 
  );

  rgb2gray
  #(
    .PROC_METHOD("FORMULA")  //"AVERAGE"     :求平均法
                             //or "FORMULA"  :直接公式法
                             //or "LUT"      :查找表法
  )rgb2gray_formula
  (
    .clk       (clk       ),
    .reset_p   (reset_p   ),
    .rgb_valid (rgb_valid ),
    .rgb_hs    (rgb_hs    ),
    .rgb_vs    (rgb_vs    ),
    .red_8b_i  (red_8b_i  ),
    .green_8b_i(green_8b_i),
    .blue_8b_i (blue_8b_i ),
    .gray_8b_o (          ),
    .gray_valid(          ),
    .gray_hs   (          ),
    .gray_vs   (          ) 
  );

  rgb2gray
  #(
    .PROC_METHOD(  "LUT"  )  //"AVERAGE"     :求平均法
                             //or "FORMULA"  :直接公式法
                             //or "LUT"      :查找表法
  )rgb2gray_lut
  (
    .clk       (clk       ),
    .reset_p   (reset_p   ),
    .rgb_valid (rgb_valid ),
    .rgb_hs    (rgb_hs    ),
    .rgb_vs    (rgb_vs    ),
    .red_8b_i  (red_8b_i  ),
    .green_8b_i(green_8b_i),
    .blue_8b_i (blue_8b_i ),
    .gray_8b_o (          ),
    .gray_valid(          ),
    .gray_hs   (          ),
    .gray_vs   (          ) 
  );

endmodule 

四、uart_ddr3_tft_rgb2gray模块

该模块主要作用是串联其他模块,完成传图过程。其中各模块对应的介绍均在第二节后附有连接,以供参考。

/
// Module Name   : uart_ddr3_tft_rgb2gray
// Description   : 基于串口传图DDR3缓存TFT屏显示彩色图像灰度化处理
// Name          :C.V-Pupil

/

module uart_ddr3_tft_rgb2gray(
  //System clock reset
  input           clk50m        , //系统时钟输入,50MHz
  input           reset_n       , //复位信号输入
  //LED
  output [3:0]    led           ,
  //Uart interface              
  input           uart_rx       , //串口输入信号
  //TFT Interface               
  output [15:0]   TFT_rgb       , //TFT数据输出
  output          TFT_hs        , //TFT行同步信号
  output          TFT_vs        , //TFT场同步信号
  output          TFT_clk       , //TFT像素时钟
  output          TFT_de        , //TFT数据使能
  output          TFT_pwm       , //TFT背光控制
  //DDR3 Interface
  // Inouts
  inout  [15:0]   ddr3_dq       ,
  inout  [1:0]    ddr3_dqs_n    ,
  inout  [1:0]    ddr3_dqs_p    ,
  // Outputs      
  output [13:0]   ddr3_addr     ,
  output [2:0]    ddr3_ba       ,
  output          ddr3_ras_n    ,
  output          ddr3_cas_n    ,
  output          ddr3_we_n     ,
  output          ddr3_reset_n  ,
  output [0:0]    ddr3_ck_p     ,
  output [0:0]    ddr3_ck_n     ,
  output [0:0]    ddr3_cke      ,
  output [0:0]    ddr3_cs_n     ,
  output [1:0]    ddr3_dm       ,
  output [0:0]    ddr3_odt      
);
//*********************************
//Internal connect
//*********************************
  //clock
  wire          pll_locked;
  wire          loc_clk50m;
  wire          loc_clk200m;
  wire          loc_clk33m;
  wire          loc_clk9m;
  //reset
  wire          g_rst_p;
  //uart Interface
  wire [7:0]    uart_byte;
  wire          uart_byte_vaild;
  //wr_fifo Interface
  wire [15:0]   wrfifo_din;
  wire          wrfifo_wren;
  wire          wrfifo_rden;
  wire [127:0]  wrfifo_dout;
  wire [5 : 0]  wrfifo_rd_cnt;
  wire          wrfifo_empty;
  wire          wrfifo_wr_rst_busy;
  wire          wrfifo_rd_rst_busy;
  //rd_fifo Interface
  wire          rdfifo_wren;
  wire [127:0]  rdfifo_din;
  wire          rdfifo_rden;
  wire [15 :0]  rdfifo_dout;
  wire [5 : 0]  rdfifo_wr_cnt;
  wire          rdfifo_full;
  wire          rdfifo_wr_rst_busy;
  wire          rdfifo_rd_rst_busy;
  //mig Interface 
  wire          mig_reset_n;
  wire          aresetn;
  wire          mmcm_locked;
  wire          init_calib_complete;
  wire          ui_clk;
  wire          ui_clk_sync_rst;

  wire[3:0]     s_axi_awid;
  wire[27:0]    s_axi_awaddr;
  wire[7:0]     s_axi_awlen;
  wire[2:0]     s_axi_awsize;
  wire[1:0]     s_axi_awburst;
  wire[0:0]     s_axi_awlock;
  wire[3:0]     s_axi_awcache;
  wire[2:0]     s_axi_awprot;
  wire[3:0]     s_axi_awqos;
  wire          s_axi_awvalid;
  wire          s_axi_awready;

  wire[127:0]   s_axi_wdata;
  wire[15:0]    s_axi_wstrb;
  wire          s_axi_wlast;
  wire          s_axi_wvalid;
  wire          s_axi_wready;

  wire [3:0]    s_axi_bid;
  wire [1:0]    s_axi_bresp;
  wire          s_axi_bvalid;
  wire          s_axi_bready;

  wire[3:0]     s_axi_arid;
  wire[27:0]    s_axi_araddr;
  wire[7:0]     s_axi_arlen;
  wire[2:0]     s_axi_arsize;
  wire[1:0]     s_axi_arburst;
  wire[0:0]     s_axi_arlock;
  wire[3:0]     s_axi_arcache;
  wire[2:0]     s_axi_arprot;
  wire[3:0]     s_axi_arqos;
  wire          s_axi_arvalid;
  wire          s_axi_arready;

  wire [3:0]    s_axi_rid;
  wire [127:0]  s_axi_rdata;
  wire [1:0]    s_axi_rresp;
  wire          s_axi_rlast;
  wire          s_axi_rvalid;
  wire          s_axi_rready;
  //tft
  wire          clk_disp;
  wire          frame_begin;

  wire          rdfifo_clr;  
  reg           rdfifo_clr_sync_ui_clk;
  reg           rd_addr_clr;  

  wire          wrfifo_clr;
  reg           wrfifo_clr_sync_ui_clk;
  reg           wr_addr_clr;

  reg           frame_rx_done_flip;

//兼容小梅哥TFT5.0寸和TFT4.3寸显示屏,可根据实际进行配置选择
/*
  parameter DISP_WIDTH  = 480;
  parameter DISP_HEIGHT = 272;
  
  assign clk_disp = loc_clk9m;
*/
  parameter DISP_WIDTH  = 800;
  parameter DISP_HEIGHT = 480;

  assign clk_disp = loc_clk33m;

  assign mig_reset_n = pll_locked;
  assign aresetn     = pll_locked;
  assign g_rst_p     = ui_clk_sync_rst;

  assign led = {frame_rx_done_flip,init_calib_complete,mmcm_locked,pll_locked};

  pll pll
  (
    // Clock out ports
    .clk_out1 (loc_clk50m   ), // output clk_out1
    .clk_out2 (loc_clk200m  ), // output clk_out2
    .clk_out3 (loc_clk33m   ), // output clk_out3
    .clk_out4 (loc_clk9m    ), // output clk_out4
    // Status and control signals
    .resetn   (reset_n      ), // input reset
    .locked   (pll_locked   ), // output locked
    // Clock in ports
    .clk_in1  (clk50m       )  // input clk_in1
  );

  uart_byte_rx#(
    .CLK_FRQ(50000000)
  )
  uart_byte_rx(
    .clk      (loc_clk50m      ),
    .reset_p  (g_rst_p         ),

    .baud_set (3'd5            ), //1562500bps
    .uart_rx  (uart_rx         ),

    .data_byte(uart_byte       ),
    .rx_done  (uart_byte_vaild )
  );

//---------------------------------------------
//仅仿真用,正常功能时,将197~226行代码屏蔽
//仿真时,取消屏蔽197~226行代码,将179~191代码屏蔽
//---------------------------------------------
//  reg [15:0]col_data_cnt;
//  reg [15:0]row_data_cnt;
//
//  always@(posedge loc_clk50m or posedge g_rst_p)
//  begin
//    if(g_rst_p)
//      col_data_cnt <= 16'd0;
//    else if(!init_calib_complete)
//      col_data_cnt <= 16'd0;
//    else if(col_data_cnt == DISP_WIDTH/2)
//      col_data_cnt <= 16'd0;
//    else
//      col_data_cnt <= col_data_cnt + 1'b1;
//  end
//
//  always@(posedge loc_clk50m or posedge g_rst_p)
//  begin
//    if(g_rst_p)
//      row_data_cnt <= 16'd0;
//    else if(col_data_cnt == DISP_WIDTH/2)
//      if(row_data_cnt >= DISP_HEIGHT-1)
//        row_data_cnt <= 16'hffff;
//      else
//        row_data_cnt <= row_data_cnt + 1'b1;
//    else
//      row_data_cnt <= row_data_cnt;
//  end
//
//  assign uart_byte = row_data_cnt[7:0];
//  assign uart_byte_vaild = (col_data_cnt > 1'b0) && (col_data_cnt <= DISP_WIDTH/2) && (row_data_cnt <= DISP_HEIGHT-1);
//-------------------------------------------

  wire [15:0]image_data;
  wire       image_data_valid;
  reg [15:0] image_data_hcnt;
  reg [15:0] image_data_vcnt;
  reg        image_data_hs;
  reg        image_data_vs;

  bit8_trans_bit16 bit8_trans_bit16
  (
    .clk             (loc_clk50m      ),
    .reset_p         (g_rst_p         ),

    .bit8_in         (uart_byte       ),
    .bit8_in_valid   (uart_byte_vaild ),

    .bit16_out       (image_data      ),
    .bit16_out_valid (image_data_valid)
  );

  //generate image data hs or vs
  always@(posedge loc_clk50m or posedge g_rst_p)
    if(g_rst_p)
      image_data_hcnt <= 'd0;
    else if(image_data_valid) begin
      if(image_data_hcnt == (DISP_WIDTH/2 - 1'b1))
        image_data_hcnt <= 'd0;
      else
        image_data_hcnt <= image_data_hcnt + 1'b1;
    end

  always@(posedge loc_clk50m or posedge g_rst_p)
    if(g_rst_p)
      image_data_vcnt <= 'd0;
    else if(image_data_valid) begin
      if(image_data_hcnt == (DISP_WIDTH/2 - 1'b1)) begin
        if(image_data_vcnt == (DISP_HEIGHT - 1'b1))
          image_data_vcnt <= 'd0;
        else
          image_data_vcnt <= image_data_vcnt + 1'b1;
      end
    end
  //hs
  always@(posedge loc_clk50m or posedge g_rst_p)
    if(g_rst_p)
      image_data_hs <= 1'b0;
    else if(image_data_valid && image_data_hcnt == (DISP_WIDTH/2 - 1'b1))
      image_data_hs <= 1'b0;
    else
      image_data_hs <= 1'b1;
  //vs
  always@(posedge loc_clk50m or posedge g_rst_p)
    if(g_rst_p)
      image_data_vs <= 1'b0;
    else if(image_data_valid && image_data_hcnt == (DISP_WIDTH/2 - 1'b1) &&
            image_data_vcnt == (DISP_HEIGHT - 1'b1))
      image_data_vs <= 1'b0;
    else
      image_data_vs <= 1'b1;

  always@(posedge loc_clk50m or posedge g_rst_p)
    if(g_rst_p)
      frame_rx_done_flip <= 1'b0;
    else if(image_data_valid && image_data_hcnt == (DISP_WIDTH/2 - 1'b1) &&
            image_data_vcnt == (DISP_HEIGHT - 1'b1))
      frame_rx_done_flip <= ~frame_rx_done_flip;

//---------------------------------------------
//image processing
//---------------------------------------------
  wire [7:0] image_gray_data;
  wire       image_gray_data_valid;
  wire [15:0]image_stitche_data;
  wire       image_stitche_data_valid;

  rgb2gray
  #(
    .PROC_METHOD ( "FORMULA" )//"AVERAGE"     :求平均法
                              //or "FORMULA"  :直接公式法
                              //or "LUT"      :查找表法
  )rgb2gray
  (
    .clk        (loc_clk50m                ),//input
    .reset_p    (g_rst_p                   ),//input
    .rgb_valid  (image_data_valid          ),//input
    .rgb_hs     (image_data_hs             ),//input
    .rgb_vs     (image_data_vs             ),//input
    .red_8b_i   ({image_data[15:11],3'b000}),//input     [7:0]
    .green_8b_i ({image_data[10:5],2'b00}  ),//input     [7:0]
    .blue_8b_i  ({image_data[4:0],3'b000}  ),//input     [7:0]

    .gray_8b_o  (image_gray_data           ),//output    [7:0]
    .gray_valid (image_gray_data_valid     ),//output reg
    .gray_hs    (                          ),//output reg
    .gray_vs    (                          ) //output reg
  );

image_stitche_x
#(
    .DATA_WIDTH        ( 16          ),  //16 or 24
    //image1_in: 400*480
    .IMAGE1_WIDTH_IN   ( DISP_WIDTH/2),
    .IMAGE1_HEIGHT_IN  ( DISP_HEIGHT ),
    //image2_in: 400*480
    .IMAGE2_WIDTH_IN   ( DISP_WIDTH/2),
    .IMAGE2_HEIGHT_IN  ( DISP_HEIGHT ),
    //image_out: 800*480
    .IMAGE_WIDTH_OUT   ( DISP_WIDTH  ),
    .IMAGE_HEIGHT_OUT  ( DISP_HEIGHT )
)image_stitche_x(
  .clk_image1_in      (loc_clk50m           ),
  .clk_image2_in      (loc_clk50m           ),
  .clk_image_out      (loc_clk50m           ),
  .reset_p            (g_rst_p              ),

  .rst_busy_o         (                     ),
  .image_in_ready_o   (                     ),

  .image1_data_pixel_i(image_data           ),
  .image1_data_valid_i(image_data_valid     ),

  .image2_data_pixel_i({image_gray_data[7:3],image_gray_data[7:2],image_gray_data[7:3]}),
  .image2_data_valid_i(image_gray_data_valid),

  .data_out_ready_i   (1'b0                 ),
  .data_pixel_o       (image_stitche_data   ),
  .data_valid_o       (image_stitche_data_valid)
);

//---------------------------------------------
  assign wrfifo_din  = image_stitche_data;
  assign wrfifo_wren = image_stitche_data_valid;

  disp_driver disp_driver
  (
    .ClkDisp     (clk_disp       ),
    .Rst_p       (g_rst_p        ),

    .Data        (rdfifo_dout    ),
    .DataReq     (rdfifo_rden    ),

    .H_Addr      (               ),
    .V_Addr      (               ),

    .Disp_HS     (TFT_hs         ),
    .Disp_VS     (TFT_vs         ),
    .Disp_Red    (TFT_rgb[15:11] ),
    .Disp_Green  (TFT_rgb[10:5]  ),
    .Disp_Blue   (TFT_rgb[4:0]   ),
    .Frame_Begin (frame_begin    ),
    .Disp_DE     (TFT_de         ),
    .Disp_PCLK   (TFT_clk        )
  );

  assign TFT_pwm = 1'b1;

  assign wrfifo_clr = ui_clk_sync_rst;
  assign rdfifo_clr = frame_begin || ui_clk_sync_rst;

  wr_ddr3_fifo wr_ddr3_fifo
  (
    .rst           (wrfifo_clr         ), // input  wire rst
    .wr_clk        (loc_clk50m         ), // input  wire wr_clk
    .rd_clk        (ui_clk             ), // input  wire rd_clk
    .din           (wrfifo_din         ), // input  wire [15 : 0] din
    .wr_en         (wrfifo_wren        ), // input  wire wr_en
    .rd_en         (wrfifo_rden        ), // input  wire rd_en
    .dout          (wrfifo_dout        ), // output wire [127 : 0] dout
    .full          (                   ), // output wire full
    .empty         (wrfifo_empty       ), // output wire empty
    .rd_data_count (wrfifo_rd_cnt      ), // output wire [5 : 0] rd_data_count
    .wr_data_count (                   ), // output wire [8 : 0] wr_data_count
    .wr_rst_busy   (wrfifo_wr_rst_busy ), // output wire wr_rst_busy
    .rd_rst_busy   (wrfifo_rd_rst_busy )  // output wire rd_rst_busy
  );

  rd_ddr3_fifo rd_ddr3_fifo
  (
    .rst           (rdfifo_clr         ), // input wire rst
    .wr_clk        (ui_clk             ), // input wire wr_clk
    .rd_clk        (clk_disp           ), // input wire rd_clk
    .din           (rdfifo_din         ), // input wire [127 : 0] din
    .wr_en         (rdfifo_wren        ), // input wire wr_en
    .rd_en         (rdfifo_rden        ), // input wire rd_en
    .dout          (rdfifo_dout        ), // output wire [15 : 0] dout
    .full          (rdfifo_full        ), // output wire full
    .empty         (                   ), // output wire empty
    .rd_data_count (                   ), // output wire [8 : 0] rd_data_count
    .wr_data_count (rdfifo_wr_cnt      ), // output wire [5 : 0] wr_data_count
    .wr_rst_busy   (rdfifo_wr_rst_busy ), // output wire wr_rst_busy
    .rd_rst_busy   (rdfifo_rd_rst_busy )  // output wire rd_rst_busy
  );

  always@(posedge ui_clk)
  begin
    wrfifo_clr_sync_ui_clk <= wrfifo_clr;
    wr_addr_clr <= wrfifo_clr_sync_ui_clk;
  end

  always@(posedge ui_clk)
  begin
    rdfifo_clr_sync_ui_clk <= rdfifo_clr;
    rd_addr_clr <= rdfifo_clr_sync_ui_clk;
  end

  fifo2mig_axi
  #(
    .WR_DDR_ADDR_BEGIN (0         ),
    .WR_DDR_ADDR_END   (DISP_WIDTH*DISP_HEIGHT*2 ),
    .RD_DDR_ADDR_BEGIN (0         ),
    .RD_DDR_ADDR_END   (DISP_WIDTH*DISP_HEIGHT*2 ),

    .AXI_ID            (4'b0000   ),
    .AXI_LEN           (8'd31     )  //axi burst length = 32
  )fifo2mig_axi
  (
    //FIFO Interface ports
    .wr_addr_clr         (wr_addr_clr         ), //1:clear sync ui_clk
    .wr_fifo_rdreq       (wrfifo_rden         ),
    .wr_fifo_rddata      (wrfifo_dout         ),
    .wr_fifo_empty       (wrfifo_empty        ),
    .wr_fifo_rd_cnt      (wrfifo_rd_cnt       ),
    .wr_fifo_rst_busy    (wrfifo_wr_rst_busy | wrfifo_rd_rst_busy),

    .rd_addr_clr         (rd_addr_clr         ), //1:clear sync ui_clk
    .rd_fifo_wrreq       (rdfifo_wren         ),
    .rd_fifo_wrdata      (rdfifo_din          ),
    .rd_fifo_alfull      (rdfifo_full         ),
    .rd_fifo_wr_cnt      (rdfifo_wr_cnt       ),
    .rd_fifo_rst_busy    (rdfifo_wr_rst_busy | rdfifo_rd_rst_busy),
    // Application interface ports
    .ui_clk              (ui_clk              ),
    .ui_clk_sync_rst     (ui_clk_sync_rst     ),
    .mmcm_locked         (mmcm_locked         ),
    .init_calib_complete (init_calib_complete ),
    // Slave Interface Write Address Ports
    .m_axi_awid          (s_axi_awid          ),
    .m_axi_awaddr        (s_axi_awaddr        ),
    .m_axi_awlen         (s_axi_awlen         ),
    .m_axi_awsize        (s_axi_awsize        ),
    .m_axi_awburst       (s_axi_awburst       ),
    .m_axi_awlock        (s_axi_awlock        ),
    .m_axi_awcache       (s_axi_awcache       ),
    .m_axi_awprot        (s_axi_awprot        ),
    .m_axi_awqos         (s_axi_awqos         ),
    .m_axi_awvalid       (s_axi_awvalid       ),
    .m_axi_awready       (s_axi_awready       ),
    // Slave Interface Write Data Ports
    .m_axi_wdata         (s_axi_wdata         ),
    .m_axi_wstrb         (s_axi_wstrb         ),
    .m_axi_wlast         (s_axi_wlast         ),
    .m_axi_wvalid        (s_axi_wvalid        ),
    .m_axi_wready        (s_axi_wready        ),
    // Slave Interface Write Response Ports
    .m_axi_bid           (s_axi_bid           ),
    .m_axi_bresp         (s_axi_bresp         ),
    .m_axi_bvalid        (s_axi_bvalid        ),
    .m_axi_bready        (s_axi_bready        ),
    // Slave Interface Read Address Ports
    .m_axi_arid          (s_axi_arid          ),
    .m_axi_araddr        (s_axi_araddr        ),
    .m_axi_arlen         (s_axi_arlen         ),
    .m_axi_arsize        (s_axi_arsize        ),
    .m_axi_arburst       (s_axi_arburst       ),
    .m_axi_arlock        (s_axi_arlock        ),
    .m_axi_arcache       (s_axi_arcache       ),
    .m_axi_arprot        (s_axi_arprot        ),
    .m_axi_arqos         (s_axi_arqos         ),
    .m_axi_arvalid       (s_axi_arvalid       ),
    .m_axi_arready       (s_axi_arready       ),
    // Slave Interface Read Data Ports
    .m_axi_rid           (s_axi_rid           ),
    .m_axi_rdata         (s_axi_rdata         ),
    .m_axi_rresp         (s_axi_rresp         ),
    .m_axi_rlast         (s_axi_rlast         ),
    .m_axi_rvalid        (s_axi_rvalid        ),
    .m_axi_rready        (s_axi_rready        )
  );

  mig_7series_0 u_mig_7series_0 (
    // Memory interface ports
    .ddr3_addr            (ddr3_addr           ),  // output [13:0]   ddr3_addr
    .ddr3_ba              (ddr3_ba             ),  // output [2:0]    ddr3_ba
    .ddr3_cas_n           (ddr3_cas_n          ),  // output          ddr3_cas_n
    .ddr3_ck_n            (ddr3_ck_n           ),  // output [0:0]    ddr3_ck_n
    .ddr3_ck_p            (ddr3_ck_p           ),  // output [0:0]    ddr3_ck_p
    .ddr3_cke             (ddr3_cke            ),  // output [0:0]    ddr3_cke
    .ddr3_ras_n           (ddr3_ras_n          ),  // output          ddr3_ras_n
    .ddr3_reset_n         (ddr3_reset_n        ),  // output          ddr3_reset_n
    .ddr3_we_n            (ddr3_we_n           ),  // output          ddr3_we_n
    .ddr3_dq              (ddr3_dq             ),  // inout [15:0]    ddr3_dq
    .ddr3_dqs_n           (ddr3_dqs_n          ),  // inout [1:0]     ddr3_dqs_n
    .ddr3_dqs_p           (ddr3_dqs_p          ),  // inout [1:0]     ddr3_dqs_p
    .init_calib_complete  (init_calib_complete ),  // output          init_calib_complete
    .ddr3_cs_n            (ddr3_cs_n           ),  // output [0:0]    ddr3_cs_n
    .ddr3_dm              (ddr3_dm             ),  // output [1:0]    ddr3_dm
    .ddr3_odt             (ddr3_odt            ),  // output [0:0]    ddr3_odt
    // Application interface ports
    .ui_clk               (ui_clk              ),  // output          ui_clk
    .ui_clk_sync_rst      (ui_clk_sync_rst     ),  // output          ui_clk_sync_rst
    .mmcm_locked          (mmcm_locked         ),  // output          mmcm_locked
    .aresetn              (aresetn             ),  // input           aresetn
    .app_sr_req           (1'b0                ),  // input           app_sr_req
    .app_ref_req          (1'b0                ),  // input           app_ref_req
    .app_zq_req           (1'b0                ),  // input           app_zq_req
    .app_sr_active        (                    ),  // output          app_sr_active
    .app_ref_ack          (                    ),  // output          app_ref_ack
    .app_zq_ack           (                    ),  // output          app_zq_ack
    // Slave Interface Write Address Ports
    .s_axi_awid           (s_axi_awid          ),  // input [3:0]     s_axi_awid
    .s_axi_awaddr         (s_axi_awaddr        ),  // input [27:0]    s_axi_awaddr
    .s_axi_awlen          (s_axi_awlen         ),  // input [7:0]     s_axi_awlen
    .s_axi_awsize         (s_axi_awsize        ),  // input [2:0]     s_axi_awsize
    .s_axi_awburst        (s_axi_awburst       ),  // input [1:0]     s_axi_awburst
    .s_axi_awlock         (s_axi_awlock        ),  // input [0:0]     s_axi_awlock
    .s_axi_awcache        (s_axi_awcache       ),  // input [3:0]     s_axi_awcache
    .s_axi_awprot         (s_axi_awprot        ),  // input [2:0]     s_axi_awprot
    .s_axi_awqos          (s_axi_awqos         ),  // input [3:0]     s_axi_awqos
    .s_axi_awvalid        (s_axi_awvalid       ),  // input           s_axi_awvalid
    .s_axi_awready        (s_axi_awready       ),  // output          s_axi_awready
    // Slave Interface Write Data Ports
    .s_axi_wdata          (s_axi_wdata         ),  // input [127:0]   s_axi_wdata
    .s_axi_wstrb          (s_axi_wstrb         ),  // input [15:0]    s_axi_wstrb
    .s_axi_wlast          (s_axi_wlast         ),  // input           s_axi_wlast
    .s_axi_wvalid         (s_axi_wvalid        ),  // input           s_axi_wvalid
    .s_axi_wready         (s_axi_wready        ),  // output          s_axi_wready
    // Slave Interface Write Response Ports
    .s_axi_bid            (s_axi_bid           ),  // output [3:0]    s_axi_bid
    .s_axi_bresp          (s_axi_bresp         ),  // output [1:0]    s_axi_bresp
    .s_axi_bvalid         (s_axi_bvalid        ),  // output          s_axi_bvalid
    .s_axi_bready         (s_axi_bready        ),  // input           s_axi_bready
    // Slave Interface Read Address Ports
    .s_axi_arid           (s_axi_arid          ),  // input [3:0]     s_axi_arid
    .s_axi_araddr         (s_axi_araddr        ),  // input [27:0]    s_axi_araddr
    .s_axi_arlen          (s_axi_arlen         ),  // input [7:0]     s_axi_arlen
    .s_axi_arsize         (s_axi_arsize        ),  // input [2:0]     s_axi_arsize
    .s_axi_arburst        (s_axi_arburst       ),  // input [1:0]     s_axi_arburst
    .s_axi_arlock         (s_axi_arlock        ),  // input [0:0]     s_axi_arlock
    .s_axi_arcache        (s_axi_arcache       ),  // input [3:0]     s_axi_arcache
    .s_axi_arprot         (s_axi_arprot        ),  // input [2:0]     s_axi_arprot
    .s_axi_arqos          (s_axi_arqos         ),  // input [3:0]     s_axi_arqos
    .s_axi_arvalid        (s_axi_arvalid       ),  // input           s_axi_arvalid
    .s_axi_arready        (s_axi_arready       ),  // output          s_axi_arready
    // Slave Interface Read Data Ports
    .s_axi_rid            (s_axi_rid           ),  // output [3:0]    s_axi_rid
    .s_axi_rdata          (s_axi_rdata         ),  // output [127:0]  s_axi_rdata
    .s_axi_rresp          (s_axi_rresp         ),  // output [1:0]    s_axi_rresp
    .s_axi_rlast          (s_axi_rlast         ),  // output          s_axi_rlast
    .s_axi_rvalid         (s_axi_rvalid        ),  // output          s_axi_rvalid
    .s_axi_rready         (s_axi_rready        ),  // input           s_axi_rready
    // System Clock Ports
    .sys_clk_i            (loc_clk200m         ),
    .sys_rst              (mig_reset_n         )   // input sys_rst
  );

endmodule

五、传图显示

顶层的仿真与“基于 DDR3 的串口传图帧缓存系统”类似,这里就不做详细讲解,读者自己建立仿真文件完成顶层的仿真。
在顶层设计分析综合没有错误并且顶层仿真确认设计功能没有问题后,进行上板验证。对工程的管脚和时钟进行约束后,生成 Bit 文件。上板调试硬件平台基于 ACX720 开发板,使用一根数据线,一端接入 ACX720 开发板的的 USB2TTL 接口,另一端接入 PC 机的 USB口,显示屏使用的是 TFT5.0 寸屏幕,开发板连接示意图与上一章节一样。硬件连接好并上电后然后下载生成的 Bit 文件。下载完成后,开发板上 LED0~LED2 会亮,TFT5.0 寸屏上显示花屏状态。如下图所示。
在这里插入图片描述
出现这种花屏是因为这个时候,DDR3 中并未写入数据,显示的数据是不可知的一些数据。LED0 和 LED1 分别表示的是DDR 控制器内时钟锁相环的 locked 信号和 DDR 初始化校准完成信号的状态,亮表示这个两个信号均变为高电平,说明 DDR 已经正常完成初始化和校准操作。接下来通过串口传图工具向 FPGA 传输图片数据。
双击打开串口传图工具,通过点击“打开图片”按钮设置图片存放路径;图片宽度设置成与显示屏分辨率宽度一半,高度设置成与显示屏分辨率高度一致(TFT5.0 寸屏是 800480,上位机上宽度设置为 400,高度设置为 480;TFT4.3 寸屏是 480272,上位机上宽度设置为240,高度设置为 272)。
:这里提供的上位机要求图片为位深度为 24 的 bmp 格式图片,图片的宽度和高度需要为 400480(插 5 寸屏情况下)或 240272(插 4.3 寸屏情况下)。
传图过程中,可以看到 TFT 屏上开始显示发送的图片。图片传送完成后,TFT 屏显示效果如下。
在这里插入图片描述
通过对比可以看出,TFT 屏上左右两边分别显示的是原始的彩色图像和灰度化之后的灰度图像。可通过改变在顶层例化彩色图像灰度化模块使用的方式观察不同的实现方法下的实现效果。

【附件:】链接:https://pan.baidu.com/s/1kk3gUrrznMLlZoH3Tz0GGQ?pwd=r1ds
提取码:r1ds

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。