正点原子 发表于 2019-7-23 12:32:57

【正点原子FPGA连载】第四十章 SD卡图片显示实验(VGA显示)--摘自【正点原子】开拓者 FPGA 开发指南

本帖最后由 正点原子 于 2020-10-23 16:59 编辑

1)实验平台:正点原子开拓者FPGA开发板
2)平台购买地址:https://item.taobao.com/item.htm?id=579749209820
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-281143-1-1.html
4)本实例源码下载:
5)对正点原子FPGA感兴趣的同学可以加群讨论:712557122点击加入:
6)关注正点原子公众号,获取最新资料更新

第四十章 SD卡图片显示实验(VGA显示)

在“SD卡读写测试实验” 中, 我们成功地在开发板上实现了对SD卡的读写测试。 本章我们将使用FPGA开发板实现从SD卡中读取两张图片, 并通过VGA接口在显示器上循环切换显示两张图片的功能。
本章包括以下几个部分:
40.1 SD卡-VGA图片显示简介
40.2 实验任务
40.3 硬件设计
40.4 程序设计
40.5 下载验证40.1 SD卡-VGA图片显示简介
我们在“SD卡读写测试实验” 中介绍过, SD卡在SD2.0版本协议下, SPI模式的理论最大传输速率为50Mbps, 在SDIO模式下理论传输速率为200Mbps, 加上命令号以及等待SD卡返回响应信号的时间, 实际上的传输速率会比理论传输速率下降不少。 对于采用分辨率为640*480@60Hz
的显示器来说,一幅图像的数据量达到640*480*16bit=4915200bit=4800Kbit(1Kbit=1024bit),每秒钟刷新60次, 那么每秒钟需要传输的数据量达到4800Kbit*60=288000Kbit=281.25Mbit(1Mbit=1024Kbit) 。 由此可以看出, SD卡的读写速度完全跟不上VGA的数据发送速度, 因此
必须先缓存一幅图像, 再通过VGA接口显示。 我们在前面多次提到过, FPGA的片内存储资源较少, 对于缓存如此大量的数据, 只能使用开发板上的SDRAM存储器缓存数据。本次实验使用SDRAM存储器来缓存图片数据, 图片数据来自于SD卡, 那么我们就需要事先向SD卡中导入两张图片, 也就是从电脑中拷贝两张图片放入SD卡。 如果是Micro SD卡(TF卡) ,需要将先将Micro SD卡插入读卡器中, 再将读卡器插入电脑的USB接口。 如果是SD卡, 可以直接将SD卡插入电脑的SD卡插槽内(有些电脑可能没有此接口) , 也可以插入支持SD卡的读卡器中。
VGA的显示格式为16位RGB565格式, 为了使SD卡读出的数据可以直接在VGA上显示, 我们需要将图片通过“IMG2LCD” 上位机软件转成16位的RGB565格式的bin文件, 再将bin文件导入SD卡中。 然后使用WinHex软件查看两个bin文件的扇区地址, 此时查询到的扇区地址就是bin文件
存放的起始扇区地址, 我们只需要按照这个起始扇区地址, 按顺序读出SD卡中的数据即可, 直到读完一张图片中的所有数据。 SD卡中一个扇区存放512个字节, 也就是256个16位数据, 对于分辨率为640*480的图片来说, 共需要读出1200(640*480/256) 个扇区数据。我们在“SD卡读写测试实验”中对SD卡的协议规范作了详细的介绍, 包括SD卡的接口说明、初始化以及读写操作等。 如果大家对这部分内容不是很熟悉的话, 请参考“SD卡读写测试实验”中的SD卡简介部分。
40.2 实验任务
本节实验任务是使用FPGA开发板循环读取SD卡中存储的两张图片(bin格式图片, 分辨率为640*480) , 然后将图片存储在SDRAM中并通过VGA接口在显示器上循环切换显示。
40.3 硬件设计
SD卡接口部分的硬件设计请参考“SD卡读写测试实验” 中的硬件设计部分。
由于SDRAM、SD卡接口和VGA接口的引脚数目较多且在前面相应的章节中已经给出它们的管脚列表, 这里不再列出管脚分配。
40.4 程序设计
图 40.4.1是根据本章实验任务画出的系统框图。PLL时钟模块为其它各模块提供驱动时钟;SD卡读取图片控制模块控制SD卡控制器的读接口; SD卡控制器从SD卡中读出图像数据, 并将读出的数据写入SDRAM控制器; 最后VGA驱动模块通过SDRAM控制器读取SDRAM中存储的图片数据并
通过VGA接口显示在显示器上。


图 40.4.1 SD卡图片显示实验(VGA显示) 系统框图

顶层模块的原理图如下图所示:


图 40.4.2 顶层模块原理图

FPGA顶层模块(top_sd_photo_vga) 例化了以下五个模块: PLL时钟模块(pll_clk) 、 SD卡读取图片控制模块(sd_read_photo) 、 SD卡控制器模块(sd_ctrl_top) 、 SDRAM控制器模块(sdram_top) 和VGA驱动模块(vga_driver) 。
顶层模块(top_sd_photo_vga) : 顶层模块主要完成对其余各模块的例化, 实现各模块之间的数据交互。 需要注意的是, 系统初始化完成是在SD卡以及SDRAM都初始化完成后才开始拉高的, 该信号控制着SD卡读取图片控制模块的复位信号, 因此SD卡读取图片控制模块是在系统
初始化完成后才工作的, 防止因SD卡或者SDRAM初始化未完成导致数据错误。PLL时钟模块(pll_clk) : PLL时钟模块通过调用锁相环(PLL) IP核实现, 总共输出五个时钟, 频率分别为100Mhz、 100Mhz(相位偏移-75度)、 50Mhz、 50Mhz(相位偏移180度)和25Mhz。
两个100Mhz的时钟用于为SDRAM控制器模块提供驱动时钟; 两个50Mhz的时钟用于为SD卡控制器模块提供驱动时钟, 其中50mhz为SD卡读取图片控制模块提供驱动时钟; 25Mhz用于为VGA驱动模块提供驱动时钟。SD卡读取图片控制模块(sd_read_photo) : SD卡读取图片控制模块通过控制SD卡控制器的读接口, 从SD卡中读取图像数据, 并在读完一张图片后延时一段时间, 再去读取另一张图片数据, 实现两张图片的循环切换读取。SD卡控制器模块(sd_ctrl_top) : SD卡控制器模块负责驱动SD卡, 该模块将SD卡的SPI读写操作封装成方便用户使用的接口。有关该模块的详细介绍请大家参考“SD卡读写测试实验”章节。SDRAM读写控制模块(sdram_top) : SDRAM读写控制器模块负责驱动SDRAM片外存储器, 缓存图像传感器输出的图像数据。 该模块将SDRAM复杂的读写操作封装成类似FIFO的用户接口,非常方便用户的使用。
VGA驱动模块(vga_driver) : VGA驱动模块根据VGA时序参数输出行、 场同步信号; 同时它还要输出数据请求信号用于读取SDRAM中的图片数据, 并将图片通过VGA接口显示。
SD卡读取图片控制模块的代码如下:
1 module sd_read_photo(
2 input clk , //时钟信号
3 input rst_n , //复位信号,低电平有效
4 5
input rd_busy , //SD卡读忙信号
6 output reg rd_start_en , //开始写SD卡数据信号
7 output reg rd_sec_addr //读数据扇区地址
8 );
9 1
0 //parameter define
11 parameter PHOTO_SECCTION_ADDR0 = 32'd8256; //第一张图片扇区起始地址
12 parameter PHOTO_SECTION_ADDR1 = 32'd9472 ; //第二张图片扇区起始地址
13 //640*480/256 = 1200
14 parameter RD_SECTION_NUM = 11'd1200 ; //单张图片总共读出的次数
15
16 //reg define
17 reg rd_flow_cnt ; //读数据流程控制计数器
18 reg rd_sec_cnt ; //读扇区次数计数器
19 reg rd_addr_sw ; //读两张图片切换
20 reg delay_cnt ; //延时切换图片计数器
21
22 reg rd_busy_d0 ; //读忙信号打拍, 用来采下降沿
23 reg rd_busy_d1 ;
24
25 //wire define
26 wire neg_rd_busy ; //SD卡读忙信号下降沿
27
28 //*****************************************************
29 //** main code
30 //*****************************************************
31
32 assign neg_rd_busy = rd_busy_d1 & (~rd_busy_d0);
33
34 //对rd_busy信号进行延时打拍,用于采rd_busy信号的下降沿
35 always @(posedge clk or negedge rst_n) begin
36 if(rst_n == 1'b0) begin
37 rd_busy_d0 <= 1'b0;
38 rd_busy_d1 <= 1'b0;
39 end
40 else begin
41 rd_busy_d0 <= rd_busy;
42 rd_busy_d1 <= rd_busy_d0;
43 end
44 end
45
46 //循环读取SD卡中的两张图片(读完之后延时1s再读下一个)
47 always @(posedge clk or negedge rst_n) begin
48 if(!rst_n) begin
49 rd_flow_cnt <= 2'd0;
50 rd_addr_sw <= 1'b0;
51 rd_sec_cnt <= 11'd0;
52 rd_start_en <= 1'b0;
53 rd_sec_addr <= 32'd0;
54 end
55 else begin
56 rd_start_en <= 1'b0;
57 case(rd_flow_cnt)
58 2'd0 : begin
59 //开始读取SD卡数据
60 rd_flow_cnt <= rd_flow_cnt + 2'd1;
61 rd_start_en <= 1'b1;
62 rd_addr_sw <= ~rd_addr_sw; //读数据地址切换
63 if(rd_addr_sw == 1'b0)
64 rd_sec_addr <= PHOTO_SECCTION_ADDR0;
65 else
66 rd_sec_addr <= PHOTO_SECTION_ADDR1;
67 end
68 2'd1 : begin
69 //读忙信号的下降沿代表读完一个扇区,开始读取下一扇区地址数据
70 if(neg_rd_busy) begin
71 rd_sec_cnt <= rd_sec_cnt + 11'd1;
72 rd_sec_addr <= rd_sec_addr + 32'd1;
73 //单张图片读完
74 if(rd_sec_cnt == RD_SECTION_NUM - 11'b1) begin
75 rd_sec_cnt <= 11'd0;
76 rd_flow_cnt <= rd_flow_cnt + 2'd1;
77 end
78 else
79 rd_start_en <= 1'b1;
80 end
81 end
82 2'd2 : begin
83 delay_cnt <= delay_cnt + 26'd1; //读取完成后延时1秒
84 if(delay_cnt == 26'd50_000_000 - 26'd1) begin //50_000_000*20ns = 1s
85 delay_cnt <= 26'd0;
86 rd_flow_cnt <= 2'd0;
87 end
88 end
89 default : ;
90 endcase
91 end
92 end
93
94 endmodule
在代码的第11行至第14行定义了三个参数, PHOTO_SECTION_ADDR0(第一张图片扇区起始地址) 、 PHOTO_SECTION_ADDR1(第二张图片扇区起始地址) 和RD_SECTION_NUM(单张图片总共读出的次数) 。 其中PHOTO_SECTION_ADDR0和PHOTO_SECTION_ADDR1是由WinHex软件查看得到的两张 图片 的 扇区 起始 地址 , 具 体查 看的 方法 我们 在下 载验 证部 分再 详细 讲解 。RD_SECTION_NUM是读取单张图片总共需要读取的次数,即扇区数。单张图片的分辨率为640*480,位宽为16位, 读取一个扇区的字节数为512个字节, 共256个16bit, 所以单张图片需要读取的扇区数为640*480/256=1200。在代码的第47行开始的always语句块中, 实现的功能是根据第一张图片的扇区地址向SD卡控制器模块发送读命令, 读完后延时1秒钟, 并根据第二张图片的扇区地址向SD卡控制器模块发送读命令, 读完后再次延时1秒钟, 并根据第一张图片的扇区地址向SD卡控制器模块再次发送读命令, 就这样循环往复读取SD卡中的两张图片。 这个功能是由代码中定义的读流程控制计数器(rd_flow_cnt) 来实现, 读忙信号的下降沿(neg_rd_busy) 表示当前扇区读取完成,可以进行其它操作。
图 40.4.3为控制SD卡读取图片过程中SignalTap抓取的波形图。 由图可知, 在rd_sec_cnt(读扇区个数计数器)的值为1199(1200-1)时表示当前读取的是图片的最后一个扇区的数据,rd_busy(读忙信号) 拉低后, 单张图片的最后一个扇区地址读完, 此时rd_flow_cnt加1, 延
时计数器(delay_cnt) 开始计数, 等待延时完成后读取下一张图片。


图 40.4.3 控制SD卡读取图片SignalTap波形图

40.5 下载验证
首先我们打开SD 卡图片显示实验(VGA显示) 工程, 在工程所在的路径下打开top_sd_photo_vga/par文件夹, 在里面找到“top_sd_photo_vga.qpf” 并双击打开。 注意工程所在的路径名只能由字母、 数字以及下划线组成, 不能出现中文、 空格以及特殊字符等。 工程
打开后如图 40.5.1所示:


图 40.5.1 SD卡图片显示实验(VGA显示) 工程

在打开下载界面之前, 我们还需要先做一些准备工作, 也就是向SD卡中导入bin格式的图片。 首先来介绍一下如何利用工具“Img2Lcd” 将图片转成bin文件, 该工具位于开发板所随附的资料“6_软件资料/1_软件/Img2Lcd” 目录下找到“Img2Lcd.exe” 并双击打开, 软件打开后
界面如图40.5.2所示。在菜单栏中点击“打开”, 然后在弹出的界面中选择一幅分辨率为640*480的jpg格式图片。图片加载进来之后, 在工具界面左侧设置输出数据类型为“二进制(*.bin)”, 输出灰度为“16位真彩色” , 最大宽度和高度分别为“640” 和“480” , 选中高位在前(MSB First) 。 设置完成后在菜单栏中点击“保存” , 并在弹出的界面中选择bin文件的保存路径并输入文件名。


图 40.5.2 Img2Lcd工具界面

到这里我们已经成功地将第一张图片转成了bin文件, 因为我们实现的功能是两张图片循环切换显示, 因此需要通过上面的方法再生成一个bin文件。 此时, 两张bin格式的图片文件制作完成。SD卡在经过多次存放数据与删除数据之后, 存入的文件有可能不是按照连续的扇区地址存
储的, 为了避免图片显示错误, 我们将bin文件导入SD卡之前, 先把SD卡格式化, 格式化的设置如图 40.5.3所示, 然后点击开始按钮完成格式化。


图 40.5.3 SD卡格式化界面

接下来我们将生成的两个bin文件拷贝到SD卡中, 拷贝完成后如下图所示:


图 40.5.4 SD卡bin文件拷贝完成

文件拷贝完成后, 接下来我们使用WinHex工具软件查看这两个文件的扇区起始地址, 该工具位于开发板所随附的资料中“6_软件资料/1_软件/WinHex” 目录下, 双击“WinHex.exe” 或者“WinHex64.exe” 打开软件。 软件打开后, 在菜单栏中点击“工具” , 然后点击“打开磁盘”,
如下图所示。


、图 40.5.5 WinHex打开界面




、图 40.5.6 WinHex磁盘打开界面

在“物理驱动器” 下, 我们看到箭头2所指的地方有RM2、 SD/MMC(7.5GB, USB) 的字样,可知该物理驱动器对应的是SD卡, 标号为RM2, 我们找到标号RM2在逻辑驱动器中的位置, 即箭头1所指的地方, 选中后点击“确定” 按钮即可查看文件的起始扇区地址, 打开后的界面如下图所示:


图 40.5.7 WinHex查看扇区起始地址界面

由 上 图 可 知 , 两 张 bin 格 式 图 片 的 起 始 扇 区 地 址 分 别 为 8256 和 9472 , 这 和 我 们sd_read_photo 模 块 定 义 的 PHOTO_SECTION_ADDR0 = 32'd8256 , PHOTO_SECTION_ADDR1 =32'd9472是一致的。 如果查看的值不是上图中的值, 需要将代码中定义的这两个参数值改成WinHex查看的扇区地址, 然后重新编译工程。接下来我们将SD卡适配器(用于插入MicroSD卡) 或者SD卡插入开发板的SD卡插槽, 注意带有金属引脚的一面朝上; 然后将VGA连接线一端连接显示器, 另一端与开发板上的VGA接口连接; 接下来将下载器一端连接电脑, 另一端与开发板上对应端口连接, 最后连接电源线并打开电源开关。接下来我们下载程序, 验证SD卡的VGA图片显示功能。 工程打开后通过点击工具栏中的“Programmer” 图 标 打 开 下 载 界 面 , 通 过 “Add File” 按 钮 选 择 top_sd_photo_vga/par/output_files目录下的“top_sd_photo_vga.sof” 文件。 开发板电源打开后, 在程序下载 界 面 点 击 “Hardware Setup” , 在 弹 出 的 对 话 框 中 选 择 当 前 的 硬 件 连 接 为“USB-Blaster” 。然后点击“Start” 将工程编译完成后得到的sof文件下载到开发板中, 如图 40.5.8所示:


图 40.5.8 程序下载完成界面

程序下载完成后, 此时显示器上循环切换显示SD卡中的两张图片, 说明SD卡图片显示实验(VGA显示) 下载验证成功。 如图 40.5.9和图 40.5.10所示:


图 40.5.9 VGA器显示第一张图片




图 40.5.10 VGA器显示第二张图片


页: [1]
查看完整版本: 【正点原子FPGA连载】第四十章 SD卡图片显示实验(VGA显示)--摘自【正点原子】开拓者 FPGA 开发指南