• 【小梅哥FPGA进阶教程】第十四章 TFT屏显示图片


    十四、TFT屏显示图片

    本文由杭电网友曾凯峰贡献,特此感谢

    学习了小梅哥的TFT显示屏驱动设计后,想着在此基础上通过TFT屏显示一张图片,有了这个想法就开始动工了。首先想到是利用FPGA内部ROM存储图片数据,然后通过控制读取数据地址将图片数据传给TFT驱动模块,从而将每个图片数据显示在对应的像素点上。整个设计的框图如下:

    图片1

    主要是在小梅哥TFT驱动设计基础上增加了图片数据发送控制模块Imgdata_send,该模块包括存储图片数据的rom,和一些简单的逻辑控制。具体的rom IP核的建立我这里就不说了,不懂的可以参看小梅哥的相关内容的视频,我这里主要讲如何将图片转换成mif文件。这里有两种方法,可以作为参考,主要用到如下软件:

    图片2

    步骤1:利用Img2Lcd将图片转化为BMP格式的(当然图片本身为BMP格式就不需要转了,直接进入步骤2);

    步骤2:利用BMP2Mif可将图片转化为mif文件。

    具体实现如下:

    步骤1:先打开Img2Lcd打开一张图片,选择输出格式为BMP格式,输出灰度选择24位真彩色(由于BMP2Mif软件只能载入24位或8位的,所以这里就没有直接选16位的真彩色),最大宽度和高度根据图片实际的大小进行选择的,由于内部rom能存储的数据量有限,我选择了一张像素为160*120的图片如果想显示大的图片,如480*272图片,用这种方法就不能实现(看到这里有人想,如果想显示大点的图片那应该怎么解决,后面我会有其他方法来实现TFT屏保显示)。

    图片3

    步骤2:打开BMP2Mif软件,加载刚转换输出的24位BMP格式图片,选择输出图像格式和文件类型,点击一键转换便将图片转换为了mif文件了,可以将mif文件名更改下以区别不同图片mif文件。

    图片4

    可以用Notepad++将转化的mif文件打开看看,截取部分图如下:

    图片5

    rom IP核设置完成后就是数据发送控制模块Imgdata_send中控制逻辑的编写,主要是让图片显示在屏幕指定的地方,这就需要根据TFT_CTRL模块的TFT行和场扫描计数器输出信号来控制rom的数据地址,从而控制TFT_CTRL的待显示数据data_in。具体代码如下:

    1   module Imgdata_send(
    2       clk50M,
    3       rst_n,
    4       tft_de,
    5       hcount,
    6       vcount,
    7       data_in
    8   );
    9 
    10      input clk50M;           //系统时钟,默认50M
    11      input rst_n;            //复位信号,低电平有效
    12      input tft_de;           //TFT数据使能
    13      input [9:0]hcount;     //TFT行扫描计数器
    14      input [9:0]vcount;     //TFT场扫描计数器
    15      output [15:0]data_in; //待显示图片数据
    16      
    17      wire img_ack;              //图片数据使能
    18      
    19      localparam IMG_H = 160,  //图片行像素点个数
    20                   IMG_V = 120;  //图片场像素点个数
    21                    
    22      localparam TFT_H = 480,  //TFT屏行像素点个数
    23                   TFT_V = 272;  //TFT屏场像素点个数
    24                    
    25      localparam IMG_HM = TFT_H - IMG_H,  //图片行方向可移动像素点个数
    26                 IMG_VM = TFT_V - IMG_V;    //图片场方向可移动像素点个数
    27                    
    28      reg [9:0]img_hbegin = 0;   //图片左上角第一个像素点在TFT屏的行向坐标
    29      reg [9:0]img_vbegin = 0;   //图片左上角第一个像素点在TFT屏的场向坐标
    30      
    31      reg [14:0]addr;             //读图片数据rom地址                  
    32      wire [15:0]img_data;       //读出图片数据
    33      
    34      rom u4_rom(
    35          .address(addr),
    36          .clock(clk50M),
    37          .q(img_data)
    38      );
    39      
    40      assign img_ack = tft_de && (hcount >= img_hbegin && hcount < img_hbegin + IMG_H) &&
    41                            (vcount >= img_vbegin && vcount < img_vbegin + IMG_V)?1'b1:1'b0;
    42      
    43      always@(posedge clk50M or negedge rst_n)
    44      begin
    45          if(!rst_n)
    46              addr <= 15'd0;
    47          else if(img_ack)
    48              addr <= (hcount - img_hbegin) + (vcount - img_vbegin)*IMG_H;
    49          else
    50              addr <= 15'd0;  
    51      end
    52      
    53      assign data_in = img_ack ? img_data : 16'h0;
    54      
    55  endmodule 

    接下来就是仿真验证,利用已有的TFT_CTRL模块的hcount、vcount、tft_de作为Imgdata_send模块的输出进行仿真验证,代码如下:

    1   `timescale 1ns/1ns
    2   `define PERIOD_CLK 20
    3 
    4   module Imgdata_send_tb;
    5 
    6       reg clk50M;
    7       reg rst_n;
    8       wire tft_de;
    9       wire [9:0]hcount;
    10      wire [9:0]vcount;
    11      wire [15:0]data_in;
    12      
    13      wire clk9M;
    14
    15      Imgdata_send u0_Imgdata_send(
    16          .clk50M(clk50M),
    17          .rst_n(rst_n),
    18          .tft_de(tft_de),
    19          .hcount(hcount),
    20          .vcount(vcount),
    21          .data_in(data_in)
    22      );
    23      
    24      pll u1_pll(
    25          .areset(!rst_n),
    26          .inclk0(clk50M),
    27          .c0(clk9M)
    28      );
    29      
    30      TFT_CTRL u2_TFT_CTRL(
    31          .clk9M(clk9M),
    32          .rst_n(rst_n),
    33          .data_in(),
    34          .hcount(hcount),
    35          .vcount(vcount),
    36          .tft_rgb(),
    37          .tft_hs(),
    38          .tft_vs(),
    39          .tft_clk(),
    40          .tft_de(tft_de),
    41          .tft_pwm()
    42      );
    43      
    44      initial clk50M = 1'b1;
    45      always #(`PERIOD_CLK/2) clk50M = ~clk50M;
    46      
    47      initial 
    48      begin
    49          rst_n = 1'b0;
    50          #(`PERIOD_CLK*200+1)
    51          rst_n = 1'b1;   
    52      end
    53
    54  endmodule 

    仿真验证的波形图如下:

    图片8图片9

    从仿真结果可以看出,图片数据是在我们指定的区域输出的。该模块仿真验证正确后,进行顶层电路文件的设计。顶层文件编写如下:

    1   module rom_tft_img(
    2 
    3       clk50M,
    4       rst_n,  
    5       
    6       tft_rgb,
    7       tft_hs,
    8       tft_vs,
    9       tft_clk,
    10      tft_de,
    11      tft_pwm
    12  );
    13
    14      input clk50M;
    15      input rst_n;    
    16      
    17      output [15:0]tft_rgb;
    18      output tft_hs;
    19      output tft_vs;
    20      output tft_clk;
    21      output tft_de;
    22      output tft_pwm;
    23      
    24      wire [15:0]data_in;
    25      wire [9:0]hcount;
    26      wire [9:0]vcount;
    27      wire clk9M;
    28      
    29      Imgdata_send u0_Imgdata_send(
    30          .clk50M(clk50M),
    31          .rst_n(rst_n),
    32          .tft_de(tft_de),
    33          .hcount(hcount),
    34          .vcount(vcount),
    35          .data_in(data_in)
    36      );
    37      
    38      pll u1_pll(
    39          .areset(!rst_n),
    40          .inclk0(clk50M),
    41          .c0(clk9M)
    42      );
    43      
    44      TFT_CTRL u2_TFT_CTRL(
    45          .clk9M(clk9M),
    46          .rst_n(rst_n),
    47          .data_in(data_in),
    48          .hcount(hcount),
    49          .vcount(vcount),
    50          .tft_rgb(tft_rgb),
    51          .tft_hs(tft_hs),
    52          .tft_vs(tft_vs),
    53          .tft_clk(tft_clk),
    54          .tft_de(tft_de),
    55          .tft_pwm(tft_pwm)
    56      );
    57
    58  endmodule 

    生成的顶层电路图如下:

    图片11

    接下来就是仿真验证,仿真验证程序如下:

    1   `timescale 1ns/1ps
    2   `define PERIOD_CLK 20
    3 
    4   module rom_tft_img_tb;
    5       //模块输入端口
    6       reg clk50M;
    7       reg rst_n;
    8       
    9       //模块输出端口
    10      wire [15:0]tft_rgb;
    11      wire tft_hs;
    12      wire tft_vs;
    13      wire tft_clk;
    14      wire tft_de;
    15      wire tft_pwm;
    16      
    17      reg [7:0]v_cnt = 0; //扫描帧数统计计数器
    18
    19      rom_tft_img rom_tft_img( 
    20
    21          .clk50M(clk50M),
    22          .rst_n(rst_n),  
    23          
    24          .tft_rgb(tft_rgb),
    25          .tft_hs(tft_hs),
    26          .tft_vs(tft_vs),
    27          .tft_clk(tft_clk),
    28          .tft_de(tft_de),
    29          .tft_pwm(tft_pwm)
    30      );
    31      
    32      initial clk50M = 1'b1;
    33      always #(`PERIOD_CLK/2) clk50M = ~clk50M;
    34      
    35      initial
    36      begin
    37          rst_n = 1'b0;
    38          #(`PERIOD_CLK*200 + 1);
    39          rst_n = 1'b1;
    40      end
    41      
    42      initial 
    43      begin
    44          wait(v_cnt == 3);  //等待扫描2帧后结束仿真
    45          $stop;
    46      end
    47      
    48      always@(posedge tft_vs)   //统计总扫描帧数
    49          v_cnt = v_cnt + 1'b1;
    50
    51  endmodule 

    仿真波形如下:

    图片13图片14

    从波形可以看出,图片数据在指定区域显示。接下来就是板级验证,引脚分配参照芯航线FPGA学习套件引脚分配表进行分配,然后布局布线,板级程序下载最后实现的效果图如下:

    图片15

    我们设置的是显示在屏幕的左上角,与预期效果是一致的,想要改变图片的位置,可以更改Imgdata_send模块的28、29行代码:

    28      reg [9:0]img_hbegin = 0;   //图片左上角第一个像素点在TFT屏的行向坐标
    29      reg [9:0]img_vbegin = 0;   //图片左上角第一个像素点在TFT屏的场向坐标

    将上面的(0、0)更改为其他的数,图片位置就会改变,如果想让图片在屏幕上自动的移动,可以自己设置一种路径让img_hbegin、img_vbegin的值按你的路径变化就可实现图片的自动移动,读者可以自己改进学习。

    如有更多问题,欢迎加入芯航线 FPGA 技术支持群交流学习:472607506

    小梅哥

    芯航线电子工作室

    关于学习资料,小梅哥系列所有能够开放的资料和更新(包括视频教程,程序代码,教程文档,工具软件,开发板资料)都会发布在我的云分享。(记得订阅)链接:http://yun.baidu.com/share/home?uk=402885837&view=share#category/type=0

    赠送芯航线AC6102型开发板配套资料预览版下载链接:链接:http://pan.baidu.com/s/1slW2Ojj 密码:9fn3

    赠送SOPC公开课链接和FPGA进阶视频教程。链接:http://pan.baidu.com/s/1bEzaFW 密码:rsyh

  • 相关阅读:
    qt调用simsimi api实现小黄鸡
    [机器学习系列] k-近邻算法(K–nearest neighbors)
    Ubuntu上安装flashplayer
    关于ubuntu下qt编译显示Cannot connect creator comm socket /tmp/qt_temp.xxx/stub-socket的解决办法
    Linux下添加源的几种方法
    Ubuntu字符界面输入密码始终提示错误 login incorrect 解决办法
    boost::algorithm(字符串算法库)
    boost::assign(标准容器填充库)
    boost::format(字符串格式化库)
    C/C++内存对齐 ZZ
  • 原文地址:https://www.cnblogs.com/xiaomeige/p/6441061.html
Copyright © 2020-2023  润新知