SD卡读取BMP图片显示例程

在前面的实验中我们练习了SD卡读写,VGA视频显示等例程,本实验将SD卡里的BMP图片读出,写入到外部存储器,再通过VGA、LCD等显示。在前面的实验中我们在VGA上显示的是彩条,是FPGA内部产生的数据,本实验将彩条替换为SD内的BMP图片数据,但是SD卡读取速度远远不能满足显示速度的要求,只能先写入外部高速RAM,再读出后给视频时序模块显示。

 

我想对于初学者来说看了一个这么复杂的网络,最开始一定是处于比较懵逼的状态。但是身为一个FPGA工程师,我们一定要具备像工藤新一一样抽丝剥茧的能力,这么一想是不是很酷,每解决一个例程,是不是都会有成就感!

其实仔细琢磨一下,RTL视图出现的每个模块我们都似曾相识,下面我们来一一解析一下(这里应该来一个背景音乐,真実はいつもひとつ!),接下来我们扮演的角色就是沉睡的毛利小五郎了。先来分析最简单的PLL,RTL中出现了两个PLL,一个是video_pll,另一个是sys_pll,我们来追根溯源,看看它到底是提供给哪个模块的时钟?

 通过连线我们可以清晰的发现video_pll中分频/倍频出的时钟c0是向video_timing_data这个模块提供clk(video_clk),同时在frame_read_write这个模块中的read_clk也找到了这个c0的影子。身为名侦探的我们当然不能放过这条线索,接下来我们就来揭开它神秘的面纱:

SD卡→SDRAM 由于是两个不同的时钟,这里需要做一个跨时域处理。

SDRAM→VGA显示 又要做一个跨时域处理。

由于是多位信号,所以我们不能通过边沿检测,或者多级锁存处理,这里要用到FIFO,在之后我专门做一次跨时域处理的课程。言归正传,所以我们设计了frame_read_write这个模块,来进行跨时域的数据交互。

frame_read_write这个熟悉的朋友,我们又一次探索它,其实在前几次的交手中,我们已经很了解它了,这个模块就是一个进行跨时域的数据交互。通过连线可以看到read_clk是连线到afifo_16_256这个FIFO中的rdclk,我们可以通过两个FIFO时钟的连线(从中我们需要分析sys_pll的路径,与video_pll路径分析方法一致,就不多加赘述了),确定进行数据交互的对象是哪些?afifo_16_256(read_buf)完成了由sdram_core到video_timing_data的数据交互,afifo_16_256(write_buf)完成了sd_card_bmp到sdram_core的数据交互。看我们根据了read_clk这一条线索就看出了frame_read_write这个模块的功能,但是对于frame_read_write这个模块最重要的几个信号是wrreq以及rdreq,这是能完成数据交互的关键,我们将在接下来的verilog中仔细分析,以及讲述设计缘由。

废话不多说,我们下面来讲述这个网络对我们来说的新设计,也就是我们之前没有接触过的。

 

sd_card_bmp,主要完成了对SD卡中BMP格式的文件进行检索,以及进行数据处理,输出RGB565(一个像素点)的数据。

关于网络中出现sdram_core这里就不用多说了吧,它主要完成了SDRAM控制器的功能。以及seg_scan和seg_decorder完成的数码管显示局部网络,video_timing_data主要负责将FIFO中的data按照VGA的时序输出,这些在前文中都有讲过,所以不多加赘述。

下面我们来着重讲述一下sd_card_bmp模块的逻辑设计,在这个项目中我们是通过按键去播放BMP格式的图片。所以按键就是一个触发,对检索文件的触发。

 

 

 

由于我们需要通过按键触发,所以在这里我们例化了ax_debounce.v,前文已讲过。

由于我么需要对SD卡中进行数据读取,所以我们这里例化了sd_card_top,前文已讲过。

既然需要显示BMP格式的图片,我们就需要设计一个检索该格式文件的模块。所以我们在这里例化了bmp_read,从模块名也能了解它大概的功能,下面我们就仔细讲一下设计流程,你就会发现它与之前的WAV文件检索如出一辙。

由于我们要完成通过按键触发到文件检索,再到读取相关数据,这一连串的顺序操作,我们需要通过状态机完成,所以在这里我们设计了一个状态机。

 

SD卡初始化完成后,进入到IDLE状态。 

 

按键按下后,即find信号置1,系统进入检索文件状态,我们依然向之前那个项目中检索WAV文件一样,每4K空间寻找一次头文件,如果找到了,进入SD卡读取等待状态,也就是将sd_sec_read信号置1,同时将write_req置1,这里有人会好奇write_req到底是一个什么信号?我之前在前文中有讲过write_req和write_req_ack是一组反馈信号,write_req是frame_fifo_write模块中状态机的启动信号,有人估计又会问”我记得你讲过frame_fifo_write这个模块完成fifo到SDRAM数据的写入啊,那为什么会在SD卡的检索模块有一个控制信号给它呢?”我们要想我们整个系统就是一个并行工作的机器,我们所写的任何模块都是在并行工作的,当有按键按下,就是为我们这个系统发出了播放图片的命令,当系统中找到了命令中让它播放的图片格式时,它就要开始做好准备了,让它其中frame_fifo_write这个零部件去实时监测fifo中的数据够不够一次写入SDRAM的。我们在做任何设计的时候都要让自己成为一个顾大局的人,做好完全的准备,不能等麻烦(data)来了,手忙脚乱的

当frame_fifo_write这个模块做好准备后,系统进入到S_READ(也就是SD卡读取数据状态),开始读取数据,每次读完一个扇区的数据,sd_sec_read_addr(扇区地址+1),开始读下一个扇区的数据,直到读完整个文件的数据(bmp_len_cnt>=file_len),系统进入S_END,否则继续读。

分析完整个状态机,大家应该会对具体的状态流程心里有数了,接下来我们对状态机里出现的信号继续分析。

found信号S_FIND这个状态中出现过,它是检索文件成功(找到头文件)的一个标志信号,下面我们来看它又是怎么被定义的呢。

我们默认头文件就是在扇区的头部,BMP文件的头文件的前两个字节就是’B’,’M’,在第3到6个字节是该文件的文件长度,在第19到22个字节是该文件的宽度。

这里我们根据文件头,以及文件宽度来判断是否为我们要找的文件。

rd_cnt是我们在检索文件过程中设计的计数器,它起到了对SD卡读取的数据排序作用。

这里应该都可以看懂吧,没什么说的。

bmp_len_cnt是我们进入S_READ状态中,对读取的数据计数,起到一个记录文件大小的作用。

 

bmp_data_valid是文件有效数据的标志信号,我们知道一个的组成是由头文件数据和有效数据组成的。这里就会同学产生疑问了:“既然已经进入了读取数据状态,不是就在读有效数据了吗?”答案可想而知,你太天真了,我们仔细看,在系统检索到文件后,进入读取状态的过程中,sd_sec_read_addr(扇区地址)并没有改变,在进入读状态后,系统依然在检索到头文件的扇区地址读取数据,所以读到的数据依然存在头文件数据。

bmp_len_cnt_tmp,其实通过注释我们就可已看出它的功能,这个注释言简意赅,RGB counter

就是RGB计数器,前文中我们有讲过一个像素点就是由RGB三原色组成的。这里我们通过bmp_len_cnt_tmp这个计数器,来记录读出来的数据到底是R还是G还是B,也就是对其标记。有人估计又会产生疑问了:“那么你又是如何确定每个原色的数据刚好是8位呢?”那么你该仔细看一下设计文档了,我清楚的写到本实验使用不压缩,24(16M色,真彩色)的BMP图片。那么刚好R :8位,G: 8位,B:8位。

 

这段语句又是描述了一个怎样的功能?我之前有说过bmp_len_cnt_tmp起到了一个区分原色的功能,那么根据该计数器的标志信号,将对应的数据存放到bmp_data这个寄存器中相应的位置。在读取完一个像素点的所有数据后,将bmp_data_wr_en(也是连接在fifo的写使能上)置1。这里估计有同学又会产生疑问:“为什么不再一开始就将写使能置1,把数据直接写入到FIFO中?”我们在上一个实验中确实直接将有效数据写入到了FIFO中,但这次不一样,我们需要在写入前对数据进行整合,因为我们用的是RGB565,也就是16位数据描述一个像素点,而我们读取的是24位的数据,所以我们需要稍加处理,再送进FIFO中,真所谓好饭不怕晚!所以有了下面这段设计:

 

而write_data正是送入到FIFO中的数据。

 

 

  • 0
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值