HDMI系列之一:基于Nios II的HDMI显示图片

来源:互联网 发布:营销活动数据库设计 编辑:程序博客网 时间:2024/04/29 10:00

一休哥将在本文中介绍一个基于Nios II的HDMI显示图片的工程。我将主要分三个部分来介绍这一工程,从而实现工程效果。
1、 Nios II的常规使用套路
2、 自定义HDMI IP核的制作
3、 Nios II显示图片
本文涉及到的全部资料链接:
链接:http://pan.baidu.com/s/1eRNXagy 密码:uv9w

1 Nios II的常规使用套路

虽然使用Nios II可以为我们简化编写Verilog代码的难度。但往往需要额外更多的GUI界面操作。本文为了照顾到Nios II的初学者,将带领大家重新创建一个基于Nios II的FPGA工程。
这里写图片描述
上图是基于Nios II的FPGA工程的简要结构图,其中属于Nios II的部分有两个,一个是Qsys系统,它是Nios II的硬件;一个是软核工程,它是Nios II的软件。
首先我们新建一个空的FPGA工程,然后点击软件界面的Qsys按钮,进入Qsys的建立界面。
这里写图片描述
然后,在界面中,我们双击Clock Source IP核,修改里面的参数,设置时钟频率为100MHz,并给这个IP核重命名为clk。
这里写图片描述
接着,我们添加一个Nios II Processor IP核,在左上角的Library的搜索窗口中搜nios即可。然后重命名为nios_qsys。
这里写图片描述
然后,添加SDRAM Controller IP核,同样搜索之后,我们需要根据自己板子上SDRAM的型号来配置IP核。我使用的是锆石科技的A4开发板,使用的SDRAM芯片为MT48LC16M16A2,所以仅供大家参考。最后同样的重命名为sdram。
这里写图片描述
然后接着添加JTAG UART和System ID Peripheral IP核,无需进行任何配置,仅需要重命名为jtag_uart和sysid_qsys即可。
这里写图片描述
接着,我们添加自定义的HDMI IP核。大家在使用该IP时,需要将我提供的IP核文件放在下图这个路径上。(嘿嘿,由于这个IP核是我仿照锆石科技的VGA IP核而制作的,为了防止被锆石大大给和谐掉,因此也放在同样的IP核路径中。)
这里写图片描述
最后,我们可以在zircon_ip的IP组里找到zircon_hdmi的IP了,直接调用就行了。
这里写图片描述
至此,Qsys系统所需的IP核就调用完了,接下来的操作就是连线,连线主要有clk端口,reset端口,Nios的数据与指令端口和Avalon—MM端口。首先我们把除Clock Source IP核以外的IP核的时钟端口连上。由于我们这个Qsys系统只有一个Clock Source IP核,所以当然是用这个IP提供的时钟端口来连接其他IP核。然后将Clock Source IP核的复位端口连接上其他IP核。接着将Nios II Processor IP核的jtag_debug_module_reset复位端口连接上其他IP核。
这里写图片描述
这里写图片描述
这里写图片描述
接着,我们来连接Nios的数据与指令端口,连接这个的时候有一个规律,就是有存储功能的IP核需要同时连接数据与指令端口,如SDRAM,RAM,ROM,EPCS等,而其他外设只需要连接数据端口。
这里写图片描述
最后,由于我制作的HDMI IP核是基于Avalon-MM协议的,通过这个协议可以实现HDMI IP核与Nios II Processor IP核和SDRAM Controller IP核的数据通讯,所以需要将HDMI IP核的Avalon—MM的主端口与SDRAM Controller IP核来读取存储在SDRAM中的图片数据,HDMI IP核的Avalon—MM的从端口与Nios II Processor IP核的数据端口相连来接收Nios II软件发送的图片地址数据。关于HDMI IP核具体的介绍,将会在后两个部分中详细介绍。
这里写图片描述
完成所有的连线之后,需要双击Nios II Processor IP核,设置其复位向量和异常向量为sdram.s1。接着,设置jtag_uart IP核的中断号,引出sdram IP和HDMI IP的引脚。点击软件界面右上角的System下拉菜单中的Assign Base Address自动分配各个IP的地址。点击软件界面右上角的File下拉菜单中的Save as保存Qsys文件。最后点击软件界面右上角的Generate下拉菜单中的Generate,生成Qsys系统。这样,一个Qsys系统就生成完了。
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
然后,回到Quartus主界面,右键点击Files,添加新生成的Qsys.qip文件。
这里写图片描述
最后,我们的顶层文件是这样的,这样我们的硬件工程就算建立完毕了。

module Qsys_Hdmi_Ip(     //时钟复位端口    CLK_50M,RST_N,    //SDRAM端口    SDRAM_ADDR,SDRAM_BA,SDRAM_CAS_N,SDRAM_CLK,SDRAM_CKE,    SDRAM_CS_N,SDRAM_DQ,SDRAM_DQM,SDRAM_RAS_N,SDRAM_WE_N,    //HDMI端口    HDMI_CLK,HDMI_RST_N,HDMI_HS,HDMI_VS,HDMI_DE,    HDMI_RGB_R,HDMI_RGB_G,HDMI_RGB_B,HDMI_SCLK,HDMI_SDAT);//时钟复位input               CLK_50M;input               RST_N;// SDRAM Interfaceoutput [12:0]   SDRAM_ADDR;output [ 1:0]   SDRAM_BA;output          SDRAM_CAS_N;output          SDRAM_CLK;output          SDRAM_CKE;output          SDRAM_CS_N;inout  [15:0]   SDRAM_DQ;output [ 1:0]   SDRAM_DQM;output          SDRAM_RAS_N;output          SDRAM_WE_N;   //VGAoutput          HDMI_CLK;output          HDMI_RST_N;output          HDMI_HS;                    output          HDMI_VS;    output          HDMI_DE;            output [ 7:0]   HDMI_RGB_R; output [ 7:0]   HDMI_RGB_G; output [ 7:0]   HDMI_RGB_B; output          HDMI_SCLK;                          //iic的时钟信号inout           HDMI_SDAT;                          //iic的数据信号assign   HDMI_RST_N = 1'b1; wire                clk_100m;wire                clk_30m;wire                HDMI_CLK;PLL                                     PLL_Init (    .inclk0                             (CLK_50M        ),    .c0                                 (clk_100m       ),    .c1                                 (SDRAM_CLK      ),    .c2                                 (clk_30m        ));PLL_Hdmi                                PLL_Hdmi_Init (    .inclk0                             (clk_30m        ),    .c0                                 (HDMI_CLK       ));Qsys_system                             Qsys_system_Init(    .clk_clk                            (clk_100m           ), //clk.clk    .reset_reset_n                      (RST_N              ), //reset.reset_n    .sdram_addr                         (SDRAM_ADDR         ), //sdram_conduit.addr    .sdram_ba                           (SDRAM_BA           ), //             .ba    .sdram_cas_n                        (SDRAM_CAS_N        ), //             .cas_n    .sdram_cke                          (SDRAM_CKE          ), //             .cke    .sdram_cs_n                         (SDRAM_CS_N         ), //             .cs_n    .sdram_dq                           (SDRAM_DQ           ), //             .dq    .sdram_dqm                          (SDRAM_DQM          ), //             .dqm    .sdram_ras_n                        (SDRAM_RAS_N        ), //             .ras_n    .sdram_we_n                         (SDRAM_WE_N         ), //             .we_n    .zircon_avalon_hdmi_clk             (HDMI_CLK           ), //  zircon_hdmi.clk    .zircon_avalon_hdmi_hsync           (HDMI_HS            ), //             .hsync    .zircon_avalon_hdmi_vsync           (HDMI_VS            ), //             .vsync    .zircon_avalon_hdmi_de              (HDMI_DE            ), //             .de    .zircon_avalon_hdmi_rgb             (HDMI_RGB           ), //             .rgb    .zircon_avalon_hdmi_sclk            (HDMI_SCLK          ), //             .sclk    .zircon_avalon_hdmi_sdat            (HDMI_SDAT          )  //             .sdat);wire    [31:0]  HDMI_RGB;assign  HDMI_RGB_R = {HDMI_RGB[23:18],2'b11};assign  HDMI_RGB_G = {HDMI_RGB[15:10],2'b11};   assign  HDMI_RGB_B = {HDMI_RGB[ 7: 2],2'b11};   endmodule

2 自定义HDMI IP核的制作

在第一个部分,一休哥详细的介绍了如何新建Nios II的硬件工程。大家可以发现,这个工程十分简洁,两个PLL模块用于产生100M时钟,SDRAM的时钟和HDMI的时钟,一个Qsys系统的顶层模块。这个Qsys系统的顶层模块是我们在Qsys设计界面中生成的。所以,在这个工程中,不需要我们手动编写Verilog逻辑代码吗?嘿嘿,好像真的是这样哦,这就是Nios的简便之处吧,不需要编写复杂代码就能够控制SDRAM。
接下来,一休哥将着重介绍HDMI IP核的制作。首先我们来看下HDMI的硬件原理图。HDMI芯片与FPGA相连的接口可以分为两类,一类是IIC接口,一类是类似于VGA的视频时序接口。驱动HDMI芯片需要两个步骤,先使用IIC接口对HDMI芯片进行配置,然后使用视频时序接口输出视频时序信号。这些操作都是通过HDMI IP核来实现的。
这里写图片描述
接下来,打开HDMI IP核的文件夹。可以看到有5个v文件,这就是HDMI IP核的硬件。后缀为hw的tcl文件是硬件配置文件,这一文件是通过Qsys界面生成的,通过这个文件可以让Qsys系统生成时自动调用这5个v文件融于系统中。
这里写图片描述
其中zircon_avalon_hdmi 是HDMI IP核的硬件的顶层文件,i2c_timing_ctrl是用来完成IIC配置的,zircon_avalon_hdmi_logic是用来模拟视频时序的,并控制读取zircon_avalon_hdmi_fifo的FIFO模块中的数据。zircon_avalon_hdmi_register是HDMI IP核内部的寄存器操作,通过Avalon-MM协议可以实现HDMI IP核与Nios II Processor IP核和SDRAM Controller IP核的数据通讯。关于每个v文件具体的作用,大家可以参考锆石科技推出的《软核演练篇》系列教程中VGA IP核的相关内容,在这里就不一一介绍了。
接下来,我教大家来制作后缀为hw的硬件配置tcl文件。首先我们在路径下将文件夹中的hw文件删除。这时打开Qsys界面,大家可以发现,我们已经找不到HDMI IP核了。
这里写图片描述
这里写图片描述
然后,双击New Component,然后配置IP核的信息。嘿嘿,由于我这个IP核有抄袭锆石科技的嫌疑,在这里允许我把创建人写为zircon吧。(锆石大大,别怪我)
这里写图片描述
然后进入下一个界面,点击添加按钮,将路径中的那5个v文件选中添加,并且将zircon_avalon_hdmi.v文件设置为顶层文件,点击Analysis Synthesis Files文件。
这里写图片描述
这里写图片描述
这里写图片描述
接着,来到最后一个Interfaces界面,针对avalon_slave接口、avalon_master接口和conduit_end接口做如下修改:改名和添加复位信号。最后点击Finish完成IP核的硬件创建。
这里写图片描述
这里写图片描述
这里写图片描述
最后在FPGA工程的文件夹目录中可以找到zircon_avalon_hdmi_hw.tcl,然后打开这个文件,对文件中的部分代码进行修改。然后将这个文件剪切到源HDMI IP核路径中即可。然后重新打开Qsys界面,就可以重新搜索到HDMI IP核了。
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

3 Nios II显示图片

完成了上述两个部分后,接下来我们来介绍Nios 软件工程。首先在FPGA工程的根目录下新建一个software文件夹。
这里写图片描述
然后点击Quartus软件界面中的Nios II Software Build Tools for Eclipse,在弹出来的软件工程路径中选择C:\Users\Administrator\Desktop\Qsys_Hdmi_Ip\software。开始创建软件工程。
这里写图片描述
这里写图片描述
接下来我们进入Nios II-Eclipse软件界面,点击Nios II Application and BSP from Template,开始创建工程。
这里写图片描述
这里写图片描述
创建完工程后,首先我们点击更新BSP
这里写图片描述
这里写图片描述
此时在BSP目录下就可以找到HDMI IP核的c和h文件。问个问题,这些文件是如何自动综合到BSP中的呢?答案就是之前提到的HDMI IP核中的zircon_avalon_hdmi_sw.tcl文件,里面包含了所有c和h文件的详细路径。
这里写图片描述
其中,kai、qiong、qiu1、qiu2、shui和ying的h文件是我使用软件Img2Lcd将图片转换成数组的(该软件的使用方法在上篇博文中具体介绍过。)该软件的具体配置如下,然后点击保存为h文件即可。可以看到,一休哥将640*480的24bit图片转换成了相应的数组。由于一休哥采取的是24位深度转换,所以得到的是一个长度为921600的无符号字符型数组,每相连的3个8bit数据组成一个24bit的像素值。
这里写图片描述
HDMI IP核中,除了包含图片数据外,还包含了IP核所必备的寄存器头文件,功能函数库的c和h文件。
首先,我们从main函数开始看起。首先需要调用HDMI IP核初始化函数zircon_avalon_hdmi_init。这个函数非常简单。第一步,对HDMI IP核的控制寄存器写0,即默认HDMI IP核停止工作。第二步,对HDMI IP核的数据寄存器进行配置,将数组hdmi_buffer的首地址值写进去。第三步,对HDMI IP核的控制寄存器写1,即让DMI IP核开始工作。其中,hdmi_buffer是一个大小为640*480的深度为32bit的数组,这一数组是一个全局变量,即它预先就在SDRAM中占据了一块固定的内存。通过将数组hdmi_buffer的首地址值写入HDMI IP核的数据寄存器。则HDMI IP核就会连续对SDRAM中这块内存进行循环读取操作(起始地址为数组hdmi_buffer的首地址值,范围大小为640*480,深度为32bit)
这里写图片描述
这里写图片描述
这里写图片描述
进行完初始化之后,直接进入一个while的死循环中,在这里就执行着显示图片的操作zircon_avalon_hdmi_DisplayPic1,这里有六个显示图片函数,并且中间会有一段时间的延时。所以本工程的效果就是六张图片来回切换。我们最后来介绍下zircon_avalon_hdmi_DisplayPic1函数,这个函数就是一个连续画点函数,先将原图片数组的3个8bit数据拼成一个24bit数据,然后依次写入数组hdmi_buffer。
这里写图片描述
这里写图片描述
最后我们运行程序,来看一下效果。
这里写图片描述
这里写图片描述
这里写图片描述
总结一下,学习Nios II,我们需要关心Nios II与IP核、IP核与SDRAM之间的数据交换,也就是Avalon-MM协议,这才是我们学习的重点所在。一般我们会用一个FIFO来缓存数据(而不会用ram),解决跨时钟域的数据交换问题。大家可以发现,在本工程的效果图中,图片切换的过程中存在明显的马赛克现象,这是因为SDRAM不支持同时读写的缘故,马赛克的持续时间是由于Nios的读写速度造成的。因此,可以选择将SDRAM换成可支持同时读写的大容量SRAM芯片,不过这个成本很高。最值得尝试的解决办法就是,可以尝试使用DDR芯片,加快读写速度,缩短马赛克现象的存在时间。

原创粉丝点击