十四、TFT屏显示图片

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

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

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

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

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

具体实现如下:

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

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

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

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

   module Imgdata_send(
clk50M,
rst_n,
tft_de,
hcount,
vcount,
data_in
); input clk50M; //系统时钟,默认50M
input rst_n; //复位信号,低电平有效
input tft_de; //TFT数据使能
input [:]hcount; //TFT行扫描计数器
input [:]vcount; //TFT场扫描计数器
output [:]data_in; //待显示图片数据 wire img_ack; //图片数据使能 localparam IMG_H = , //图片行像素点个数
IMG_V = ; //图片场像素点个数 localparam TFT_H = , //TFT屏行像素点个数
TFT_V = ; //TFT屏场像素点个数 localparam IMG_HM = TFT_H - IMG_H, //图片行方向可移动像素点个数
IMG_VM = TFT_V - IMG_V; //图片场方向可移动像素点个数 reg [:]img_hbegin = ; //图片左上角第一个像素点在TFT屏的行向坐标
reg [:]img_vbegin = ; //图片左上角第一个像素点在TFT屏的场向坐标 reg [:]addr; //读图片数据rom地址
wire [:]img_data; //读出图片数据 rom u4_rom(
.address(addr),
.clock(clk50M),
.q(img_data)
); assign img_ack = tft_de && (hcount >= img_hbegin && hcount < img_hbegin + IMG_H) &&
(vcount >= img_vbegin && vcount < img_vbegin + IMG_V)?'b1:1'b0; always@(posedge clk50M or negedge rst_n)
begin
if(!rst_n)
addr <= 'd0;
else if(img_ack)
addr <= (hcount - img_hbegin) + (vcount - img_vbegin)*IMG_H;
else
addr <= 'd0;
end assign data_in = img_ack ? img_data : 'h0; endmodule

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

   `timescale 1ns/1ns
`define PERIOD_CLK module Imgdata_send_tb; reg clk50M;
reg rst_n;
wire tft_de;
wire [:]hcount;
wire [:]vcount;
wire [:]data_in; wire clk9M; Imgdata_send u0_Imgdata_send(
.clk50M(clk50M),
.rst_n(rst_n),
.tft_de(tft_de),
.hcount(hcount),
.vcount(vcount),
.data_in(data_in)
); pll u1_pll(
.areset(!rst_n),
.inclk0(clk50M),
.c0(clk9M)
); TFT_CTRL u2_TFT_CTRL(
.clk9M(clk9M),
.rst_n(rst_n),
.data_in(),
.hcount(hcount),
.vcount(vcount),
.tft_rgb(),
.tft_hs(),
.tft_vs(),
.tft_clk(),
.tft_de(tft_de),
.tft_pwm()
); initial clk50M = 'b1;
always #(`PERIOD_CLK/) clk50M = ~clk50M; initial
begin
rst_n = 'b0;
#(`PERIOD_CLK*+)
rst_n = 'b1;
end endmodule

仿真验证的波形图如下:

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

   module rom_tft_img(

       clk50M,
rst_n, tft_rgb,
tft_hs,
tft_vs,
tft_clk,
tft_de,
tft_pwm
); input clk50M;
input rst_n; output [:]tft_rgb;
output tft_hs;
output tft_vs;
output tft_clk;
output tft_de;
output tft_pwm; wire [:]data_in;
wire [:]hcount;
wire [:]vcount;
wire clk9M; Imgdata_send u0_Imgdata_send(
.clk50M(clk50M),
.rst_n(rst_n),
.tft_de(tft_de),
.hcount(hcount),
.vcount(vcount),
.data_in(data_in)
); pll u1_pll(
.areset(!rst_n),
.inclk0(clk50M),
.c0(clk9M)
); TFT_CTRL u2_TFT_CTRL(
.clk9M(clk9M),
.rst_n(rst_n),
.data_in(data_in),
.hcount(hcount),
.vcount(vcount),
.tft_rgb(tft_rgb),
.tft_hs(tft_hs),
.tft_vs(tft_vs),
.tft_clk(tft_clk),
.tft_de(tft_de),
.tft_pwm(tft_pwm)
); endmodule

生成的顶层电路图如下:

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

   `timescale 1ns/1ps
`define PERIOD_CLK module rom_tft_img_tb;
//模块输入端口
reg clk50M;
reg rst_n; //模块输出端口
wire [:]tft_rgb;
wire tft_hs;
wire tft_vs;
wire tft_clk;
wire tft_de;
wire tft_pwm; reg [:]v_cnt = ; //扫描帧数统计计数器 rom_tft_img rom_tft_img( .clk50M(clk50M),
.rst_n(rst_n), .tft_rgb(tft_rgb),
.tft_hs(tft_hs),
.tft_vs(tft_vs),
.tft_clk(tft_clk),
.tft_de(tft_de),
.tft_pwm(tft_pwm)
); initial clk50M = 'b1;
always #(`PERIOD_CLK/) clk50M = ~clk50M; initial
begin
rst_n = 'b0;
#(`PERIOD_CLK* + );
rst_n = 'b1;
end initial
begin
wait(v_cnt == ); //等待扫描2帧后结束仿真
$stop;
end always@(posedge tft_vs) //统计总扫描帧数
v_cnt = v_cnt + 'b1; endmodule

仿真波形如下:

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

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

      reg [:]img_hbegin = ;   //图片左上角第一个像素点在TFT屏的行向坐标
reg [:]img_vbegin = ; //图片左上角第一个像素点在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

【小梅哥FPGA进阶教程】第十四章 TFT屏显示图片的更多相关文章

  1. 【小梅哥FPGA进阶教程】第十三章 四通道数字电压表

    十三.四通道数字电压表 本文由山东大学研友袁卓贡献,特此感谢 实验目的 设计一个四通道的数字电压表 实验平台 芯航线FPGA核心板.AD/DA模块 实验现象 实现一个四通道的数字电压表,其中可以用按键 ...

  2. 【小梅哥FPGA进阶教程】第十一章 四通道幅频相可调DDS信号发生器

    十一.四通道幅频相可调DDS信号发生器 本文由山东大学研友袁卓贡献,特此感谢 实验目标 实现多通道可调信号发生器 实验平台 芯航线FPGA核心板.ADDA模块 实验现象 实现基于FPGA的多通道可调信 ...

  3. 【小梅哥FPGA进阶教程】第十二章 数字密码锁设计

    十二.数字密码锁设计 本文由山东大学研友袁卓贡献,特此感谢 实验目的 实现数字密码锁设计,要求矩阵按键输出且数码管显示输入密码,密码输入正确与否均会有相应标志信号产生. 实验平台 芯航线FPGA核心板 ...

  4. 【小梅哥FPGA进阶教程】串口发送图片数据到SRAM在TFT屏上显示

    十五.串口发送图片数据到SRAM在TFT屏上显示 之前分享过rom存储图片数据在TFT屏上显示,该方法只能显示小点的图片,如果想显示TFT屏幕大小的图片上述方法rom内存大小不够.小梅哥给了个方案,利 ...

  5. 【小梅哥FPGA进阶教程】第九章 基于串口猎人软件的串口示波器

    九.基于串口猎人软件的串口示波器 1.实验介绍 本实验,为芯航线开发板的综合实验,该实验利用芯航线开发板上的ADC.独立按键.UART等外设,搭建了一个具备丰富功能的数据采集卡,芯航线开发板负责进行数 ...

  6. 【小梅哥FPGA进阶教程】MC8051软核在FPGA上的使用

    十.MC8051软核在FPGA上的使用 本教程内容力求以详细的步骤和讲解让读者以最快的方式学会 MC8051 IP core 的应用以及相关设计软件的使用,并激起读者对 SOPC 技术的兴趣.本实验重 ...

  7. SpringBoot进阶教程(六十四)注解大全

    在Spring1.x时代,还没出现注解,需要大量xml配置文件并在内部编写大量bean标签.Java5推出新特性annotation,为spring的更新奠定了基础.从Spring 2.X开始spri ...

  8. python 教程 第十四章、 地址薄作业

    第十四章. 地址薄作业 #A Byte of Python #!/usr/bin/env python import cPickle import os #define the contacts fi ...

  9. SpringBoot进阶教程(七十四)整合ELK

    在上一篇文章<SpringBoot进阶教程(七十三)整合elasticsearch >,已经详细介绍了关于elasticsearch的安装与使用,现在主要来看看关于ELK的定义.安装及使用 ...

随机推荐

  1. 启动tomcat服务器,配置CATALINA_HOME和JAVA_HOME

    遇到很多次运行startup.bat后,一个窗口一闪而过的问题,但是从来没去纠正怎样修改配置才是正确的,现在从网上查阅的资料整理如下:tomcat在启动时,会读取环境变量的信息,需要一个CATALIN ...

  2. 「小程序JAVA实战」小程序模块页面引用(18)

    转自:https://idig8.com/2018/08/09/xiaochengxu-chuji-18/ 上一节,讲了模板的概念,其实小程序还提供了模块的概念.源码:https://github.c ...

  3. ATL接口返回类型&&ATL接口返回字符串BSTR*

    感觉在ATL中做COM组件,添加方法的时候,其返回值只能是HRESULT,我想返回其他数据类型,可以吗? 非也非也 HRESULT指示返回的状态,即正确与否, 返回值是这样的 HRESULT MyMe ...

  4. 如何有效地学习《空中英语教室》&《彭蒙惠英语》

    读者定位: <大家说英语>是学习美式口语入门书,内容全部是情境会话,定位为“初级美式生活会话”. <空中英语教室>以浅显英语提供从新闻.旅游到时尚等流行话题,丰富会话材料,定位 ...

  5. 如何在ArcMap中监听键盘鼠标事件

    昨天有个朋友想要实现一个功能,就是在ArcMap中编辑数据的时候,能够通过快捷键自动设置预定义的属性,比如,选中若干要素,按A键,就自动填充属性,按B键,则又自动填充另外的属性字段. 单就这个功能而言 ...

  6. 前端开发之JavaScript HTML DOM实战篇

    实战案例一: “灯泡发光” <!DOCTYPE html> <html lang="en"> <head> <meta charset=& ...

  7. Linux实战教学笔记32:企业级Memcached服务应用实践

    一, Memcached介绍 1.1 Memcached与常见同类软件对比 (1)Memcached是什么? Memcached是一个开源的,支持高性能,高并发的分布式内存缓存系统,由C语言编写,总共 ...

  8. Excel VBA 入门(零)

    本教程所用系统环境: Windows 10 Excel 2013 1. 添加开发工具 打开Excel,依然找到"文件"->"选项"->"自 ...

  9. 实现SwipeRefreshLayout首次进入自动刷新

    看到了Android版知乎实现了这种效果,就自己也实现了一下. 先来一张效果图 实现方式: 方法一. ①在onWindowFocusChanged()方法中,设置为刷新状态为true @Overrid ...

  10. POST 与 GET请求区别