在学习实践过SD卡读写和VGA驱动显示的时序后,在下面4个例程中笔者精心选择了综合性较强的,相信大家静下心把这4个例程都独立地去实现后,FPGA的设计能力又会提高了一大步。
这几个例程更贴近于实战项目可以帮大家丰富简历内容,这里不妨去设想一个很真实的场景,如果您是面试官在看到很多简历尤其是校招中写的都是异步FIFO、UART、VGA等各种培训班或者网课的基本项目,但突然看到一份简历里写的项目内容:SD卡存储图片和音频并显示和播放、OV7725实时采集图像乒乓读写DDR3送HDMI图像边缘检测显示、和上位机端协定报文通过UART Modbus CRC校验,USB2.0 CRC校验以及千兆LAN口UDP协议3种接口通信,实现不同接口程控FPGA端控制并行DAC和运放调理输出任意波形等;再加上掌握图像视频多帧缓存、掌握高速接口串并转换Serdes、掌握数字信号处理FIR和DDS等IP核;掌握示波器、逻辑分析仪和波形发送器等的使用,当然如果是学有余力可以往下深入并展现在简历里,大家也完全可以把前面例程中学到的知识点通过不同描述表现在简历里,显然会在众多雷同相似的简历里脱颖而出。
这里想写一些题外话,也是笔者在写技术博客时和在校大学生朋友沟通后的一些体会吧,关于FPGA应该怎么学习,不同的人会有不同的回答,就好像小时候语文书上“小马过河”的故事,大家不妨可以先通过“FPGA基础知识”专栏入门,再通过“二十个经典例程”进阶,如果能够独立动手完成“二十个经典例程”其实FPGA代码编写和调试的水平已经很不错了,只是可能在一些领域缺少些深度。
笔者未来也会不断地更新实战的工程,预期推出几个实用专栏包括:高速接口设计涵盖SFP和PCIE等;数字信号处理涵盖采样、滤波、上下变频等;视频加速应用涵盖常用视频前后处理方法、多帧缓存技术等;ARM和FPGA项目涵盖报文收发解析、ARM开发相关技术等,最后画龙点睛地呈上FPGA核心内容即时序约束和时序分析,从理论模型到具体例子!
话归正传回到这个例程,我们去实现SD卡存放图片逐一送VGA显示的目的,如下图1所示是整个例程的设计示意图。

图1 SD卡存放图片逐一送VGA显示整体设计示意图
在这里SD卡中事先存储了.bmp格式的图片,FAT32文件格式中规定每一个文件都是从一个扇区第一个字节开始存储,并且文件内容是连续存储的。
因为.bmp格式的图片存在有固定的文件头,所以FPGA端可以通过遍历读取SD卡的扇区方法对该扇区进行判断,如果该扇区不是.bmp格式文件头则继续读取下一个扇区,而如果该扇区是.bmp格式文件头,则从文件头中获取关键性信息并且把.bmp图片的像素数据依次写入DDR3内存颗粒中,FPGA再通过VGA时序逻辑把图片送屏幕上显示,按下按键则触发FPGA从当前的扇区地址继续向后顺序遍历各个扇区,寻找.bmp文件头,当再找到下一幅图片则把新的像素值重新写入到DDR3内存颗粒中,屏幕上即会刷新下一张图片。
大家可以把这个例程看成是对前面所学VGA显示、DDR3读写、SD卡读写等诸多知识点的一个综合性应用项目吧,在后面4个例程中笔者也挑选了实战性较强的,一方面可以去回顾前面16个例程中所学的内容,另一方面也是非常好的过渡,本身非常接近于实战项目,不管是对需要丰富简历内容的朋友,还是承上启下对后期继续学习高速接口设计、数字信号处理、视频加速应用、时序分析约束专栏。
在编码之前,我们首先来理清楚整体思路,不管再复杂的工程,从实际工作角度出发,笔者更推荐大家把功能框图画出来,其次静下心思考数据来源、数据流向、数据缓存和数据处理等细节,再次把模块按照功能进行层层划分想好上下游模块之间的数据交互,最后也把重要的时钟标明清楚,这样做起项目就非常直观明了,做完上面准备编码只是工作量的问题,而不是东一榔头西一棒子,想到哪里代码写到哪里,这样效率非常低下,在后面的4个综合性例程中,笔者会带着大家在编码前画一画功能框图。
如下图2所示是SD卡存放图片逐一送VGA显示的功能框图,整个框图清晰明了,大家在动手写程序之前可以先对照框图再理一理思路,想清楚后就可以很快把代码设计出来了,而且在后续定位问题上对照框图也非常方便去排查。因为在一些中大型公司往往会要求大家在设计之前画出类似的框图方便工程管理,所以在后面4个例程中不妨试着去练习下。

图2 SD卡存放图片逐一送VGA显示功能框图
在““FPGA基础知识”专栏中笔者也多曾次强调了模块划分的设计思想,按照具体项目需求,根据功能层层细化模块,举个例子在这个例程中,大家可以想想看数据来源不就是SD卡扇区中所存储的.bmp图片像素值。
显然我们需要一个SD卡对外的接口模块,以实现读取SD卡各个扇区512字节的数据,但因为SD卡又存在SPI底层驱动和初始化问题,且初始配置系统时钟和读取扇区工作时钟又不一样,所以我们可以模块分层把SD卡分为SPI驱动模块、SD卡初始化模块、SD卡读取扇区模块,并在SD卡顶层模块中把相关信号都例化到一起。
在用户按下按键后,FPGA去顺序遍历SD卡以搜索带. bmp文件头的扇区,其中. bmp文件头带有一些关键性信息,包括图片宽度和高度、分辨率、压缩格式等,如图3所示是.bmp图片的位图文件头(14字节)和位图信息数据头(40字节)的数据格式。


图3 .bmp图片的位图文件头和位图信息数据头格式
大家对照图3可以看到实际上.bmp图片的文件头包含了很多信息,但其实在程序设计中我们只需要关心两个地方就可以了,即第一当前所读取的扇区是否是.bmp图片的文件头,第二如果当前扇区是.bmp图片的文件头,那么所读到的.bmp图片大小是多少。
所以在BMP图片查找模块只需要顺序查找SD卡各个扇区,判断是否是.bmp图片的文件头,并从.bmp图片的文件头中提取出图片大小信息,因为DDR3 MIG IP核读写位宽是128位,所以要先把.bmp的RGB888格式图像数据转换成RGB565格式的,再把每个像素16位的数据信息拼接成128位的数据,连同指示信号送给DDR3控制模块,通过写入FIFO IP和MIG IP把数据顺序写到DDR3内存颗粒中即可。
在例程中前端数据是由SD卡中所产生,并通过MIG IP核写入到DDR3内存颗粒中,DDR3存储了一幅完整的分辨率是640*480图片,那么后端则需要再把图片的数据再按照VGA接口的时序逻辑依次送显即可,所以我们分析到这里,整个例程中各模块的设计思路也就非常清晰了。
下面我们就来动手去一步步模块化完成整个例程的代码设计,这个例程也算是首个比较综合性的小项目,本身具有一定的设计难度,而笔者更多地想借此为大家传递这样的思想:不管再复杂的工程在得到具体需求以后,都可以先静下来把功能框图画好,想好信号流向和上下游模块的设计,把数据来源、数据缓存、数据处理等细节做好,那么代码写漂亮写简洁也就是水到渠成的事了。
当积累了一定的成功设计后,再多去思考和总结一些通用性的设计方法,以及深入底层包括时序分析约束,不同器件的底层逻辑资源等,慢慢从宏观上整个项目的设计架构到微观上器件选型的资源利用把握住,水平也就自然而然地提高了。
首先我们来完成读取SD卡扇区模块的代码设计,大家不妨去回顾下“SD卡任意扇区读写”例程,在那个例程中我们实现了对SD卡任意扇区的读写,并通过串口把从SD卡中读到的数据打印到上位机显示。相似的设计思想,只不过在这个例程中我们需要把“读写SD卡”例程的代码做一些精简,即把写入数据到SD卡扇区的状态机省略,然后把从SD卡中各个扇区读取到的数据信号sd_rd_dout和数据指示信号sd_rd_dout_vld一起送到BMP图片查找模块,如图4所示是SD卡顶层读取模块的代码设计。



图4 SD卡顶层读取模块的代码设计
BMP图片查找模块作为一个中间模块起到承上启下的作用,对上要顺序查找SD卡并在各个扇区中找到.bmp格式的图片,对下要把其像素数据通过FIFO和MIG IP核写入到DDR3内存颗粒中,也是整个例程中设计中关键性模块,如下表1所示是bmp_query模块信号列表。
在这个模块中我们需要实现几个功能:1. 顺序查找SD卡各个扇区,判断是否是.bmp图片的文件头,并从.bmp图片的文件头中提取出图片大小信息;2. 把.bmp的RGB888格式图像数据转换成RGB565格式的,即从把各个像素点的值从24位转换成16位;3.除去54字节的.bmp图片的文件头,并根据提取出的图片大小信息,依次顺序从SD卡的扇区中读取像素值;4. 把每个像素16位的数据信息拼接成128位的数据,连同指示信号送给DDR3控制模块,如图5所示是BMP图片查找模块的代码设计。
| 信号列表 | ||
| 信号名 | I/O | 位宽 |
| clk | I | 1 |
| rst_n | I | 1 |
| sd_rd_done | I | 1 |
| bmp_en | I | 1 |
| sd_rd_dout | I | 8 |
| sd_rd_dout_vld | I | 1 |
| bmp_data | O | 128 |
| bmp_data_vld | O | 1 |
| sdcard_en | O | 1 |
| sec_rd_addr | O | 32 |
表1 bmp_query模块信号列表















图5 BMP图片查找模块的代码设计
然后我们来考虑ddr3_control即DDR3读写控制的核心模块,在这个模块中我们需要去实现大批量像素数据的缓存读写,大家不妨想想看在这个例程中数据来源即为SD卡中读到的.bmp格式图片,数据处理即为640*480分辨率的VGA显示屏。
显然这两个模块的主时钟是不一样的且存在大批量需要跨时钟域的数据,因为在这个例程中DDR3内存颗粒只缓存一张完整的图片,所以大家在按下按键继续向后遍历SD卡时,会出现屏幕同时刷新两张不同图片的现象,但很快第二张图片会把第一张图片完全覆盖,这也就是最基础的FPGA图像处理方法即单帧缓存,实际上在后续OV7725摄像头例程中用到的乒乓处理即为双帧缓存,在后面例程中会展开详细地说明。
如表2所示是ddr3_control模块信号列表,在整个模块的代码设计中也有几个地方值得注意:1.本模块作为上下游跨时钟域数据的缓存,显然需要两个FIFO的支持,即上游写FIFO和下游读FIFO;2.为提高DDR3内存颗粒的读写效率,项目中一般使用连续性读写的方式,在这个例程中我们设计一次性读写64个连续的burst地址空间;3. 当写FIFO高于一定阈值或者读FIFO低于一定阈值时,从写FIFO中读取一定长度数据写入到DDR3或者从 DDR3读取一定长度数据写入到读FIFO中,从原理上分析这个阈值如果是W,那么选择FIFO深度为4W最佳,这样FIFO长期工作在2-3W存储深度之间;4.关于读写DDR3内存颗粒哪个优先,如果写优先,那么当读FIFO为空时有可能读不到数据但数据还在;如果读优先,那么当写FIFO为满时那么要写入的数据就丢失,所以个人认为写优先更靠谱些,如图6所示 DDR3读写控制模块的代码设计。
| 信号列表 | ||
| 信号名 | I/O | 位宽 |
| clk_200m | I | 1 |
| rst_n | I | 1 |
| ddr3_wr_clk | I | 1 |
| ddr3_wr_din | I | 128 |
| ddr3_wr_din_vld | I | 1 |
| ddr3_rd_rdy | I | 1 |
| ddr3_rd_clk | I | 1 |
| ddr3_rd_dout | O | 128 |
| ddr3_rd_dout_vld | O | 1 |
| ddr3_init_done | O | 1 |
表2 ddr3_control模块信号列表

























图6 DDR3读写控制模块的代码设计
我是Rails的新手,所以请原谅简单的问题。我正在为一家公司创建一个网站。那家公司想在网站上展示它的客户。我想让客户自己管理这个。我正在为“客户”生成一个表格,我想要的三列是:公司名称、公司描述和Logo。对于名称,我使用的是name:string但不确定如何在脚本/生成脚手架终端命令中最好地创建描述列(因为我打算将其设置为文本区域)和图片。我怀疑描述(我想成为一个文本区域)应该仍然是描述:字符串,然后以实际形式进行调整。不确定如何处理图片字段。那么……说来话长:我在脚手架命令中输入什么来生成描述和图片列? 最佳答案 对于“文本”数
需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc
我正在尝试找出一种方法来显示来自不在RAILS_ROOT下(在RedHat或Ubuntu环境中)的已安装文件系统的图像。我不想使用符号链接(symboliclink),因为这个应用程序实际上是通过Tomcat部署的,而当我关闭Tomcat时,Tomcat会尝试跟随符号链接(symboliclink)并删除挂载中的所有图像。由于这些文件的数量和大小,将图像放在public/images下也不是一种选择。我查看了send_file,但它只会显示一张图片。我需要在一个格式良好的页面中显示6个请求的图像。由于膨胀,我宁愿不使用Base64编码,但我不知道如何将图像数据与呈现的页面一起传递下去。
提供3种Ubuntu系统安装微信的方法,在Ubuntu20.04上验证都ok。1.WineHQ7.0安装微信:ubuntu20.04安装最新版微信--可以支持微信最新版,但是适配的不是特别好;比如WeChartOCR.exe报错。2.原生微信安装:linux系统下的微信安装(ubuntu20.04)--微信适配的最好,反应最快,但是微信版本只到2.1.1,版本太老,很多功能都没有。3.深度deepin-wine6安装微信:ubuntu20.04+系统deepin-wine6安装新版微信--综合比较好,当前个人使用此种方法1个月,微信版本3.4;没什么大问题,尚可。一、WineHQ7.0安装微信
技术选型1,前端小程序原生MINA框架cssJavaScriptWxml2,管理后台云开发Cms内容管理系统web网页3,数据后台小程序云开发云函数云开发数据库(基于MongoDB)云存储4,人脸识别算法基于百度智能云实现人脸识别一,用户端效果图预览老规矩我们先来看效果图,如果效果图符合你的需求,就继续往下看,如果不符合你的需求,可以跳过。1-1,登录注册页可以看到登录页有注册入口,注册页如下我们的注册,需要管理员审核,审核通过后才可以正常登录使用小程序1-2,个人中心页登录成功以后,我们会进入个人中心页我们在个人中心页可以注册人脸,因为我们做人脸识别签到,需要先注册人脸才可以进行人脸比对,进
我已经安装了最新版本的compass、sass和susy。但我仍然收到此错误:Unabletoactivatesusy-2.1.1,becausesass-3.2.17conflictswithsass(~>3.3.0)有人知道这个Ruby是如何工作的吗?这是我安装的gem的列表:***LOCALGEMS***CFPropertyList(2.2.0)chunky_png(1.3.0)compass(0.12.4)compass-core(1.0.0.alpha.19)compass-import-once(1.0.4)compass-rails(1.1.3)fssm(0.2.10)l
我想建立3步用户注册,在第2步上传头像。所以我遵循RyanBates的指南http://railscasts.com/episodes/217-multistep-forms.我正在使用CarrierWavegem来处理上传。但似乎我无法在用户session中存储上传的文件信息(我收到无法转储文件错误)。我在Controller中使用以下技术ifparams[:user][:img_path]@uploader=FirmImgUploader.new@uploader.store!(params[:user][:img_path])session[:img]=@uploaderpara
我正在尝试从使用RubyonRails的散列创建http参数,我尝试使用URI.encode_www_form(params),但这没有正确生成参数。下面是我的哈希值params['Name'.to_sym]='NiaKun'params['AddressLine1'.to_sym]='AddressOne'params['City'.to_sym]='CityName'这个方法把空格转成+,我要的是把空格转成%20我收到"Name=Nia+Kun&AddressLine1=Address+One&City=City+Name"但我需要将此空格转换为%20
标题说明一切。 最佳答案 我正在使用MiniExiftool,它是Perl的Exiftool的ruby接口(interface)。https://github.com/janfri/mini_exiftoolhttp://www.sno.phy.queensu.ca/~phil/exiftool/用法:exif=MiniExiftool.new(file_path)exif.date_time_original=Time.nowexif["captionextract"]="Thisismynewcaption"exif.sav
FPGA时钟和时钟域时钟树所谓时钟树为FPGA内部资源,分:全局时钟树,区域时钟树,IO时钟树原则上优先使用全局时钟树,在GT接口上使用IO时钟树,一般工具也会对GT时钟加以限制;时钟树使用方式正确的物理连接FPGA会由物理管脚专门用于全局时钟设置,通过查询数据手册可以在PCB设计阶段进行确认,当外部时钟接入此管脚时,工具会自动占有全局时钟树资源,当接入普通信号时不会分配时钟树资源;恰当的代码描述原语的使用,即BUFG的使用,可以将PLL的输出等内部时钟进行全局时钟资源的分配;IO时钟资源需要参考相应接口手册,以ultrascale的GTH为例,其JESD204的时钟方案针对不同的子类会由不同