1)实验平台:正点原子达芬奇FPGA开发板
2)购买链接:https://detail.tmall.com/item.htm?id=624335496505
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_dafenqi.html
4) 正点原子官方B站:https://space.bilibili.com/394620890
5)对正点原子FPGA感兴趣的同学可以加群讨论:876744900 点击加入:
第十二章HDMI彩条显示实验
在上一章中我们介绍了使用MicroBlaze软核处理器在LCD液晶屏上显示彩条的方法,本章将介绍如何使用MicroBlaze处理器在HDMI显示屏上显示出彩条。本章包括以下几个部分:
1212.1 简介
12.2 实验任务
12.3 硬件设计
12.4 软件设计
12.5 下载验证
12.1简介
本章实验将在第十一章的基础上进行设计,由于使用HDMI屏幕而非LCD屏幕显示彩条,所以驱动模块要将LCD_RGB_TOP替换为HDMI_COLORBAR_TOP。本实验不涉及传输参数lcd_id进行突发次数的判断,所以AXI4_DDR_READ IP核要去除lcd_id信号和相关逻辑并重新封装。因为去除lcd_id而且没有其他参数传递,因此可以删除参数传递模块AXI4_Lite_COMM。
由于本次实验将使用第十章封装的HDMI_COLORBAR_TOP IP核来驱动HDMI屏显示彩条,所以会同时验证HDMI_COLORBAR_TOP IP核是否封装正确可用。
12.2实验任务
本章的实验任务是使用Microblaze处理器向DDR3中写彩条,然后通过自定义的具有AXI接口的IP核将彩条数据从DDR3中读出来,并在与开发板上两个HDMI接口相连的分辨率为640*480@60Hz的HDMI显示器上显示出彩条。
12.3硬件设计
根据实验任务我们可以画出本次实验的系统框图,如下图所示:
图 12.3.1 系统架构图
由上图可知,本章实验由MicroBlaze软核处理器将彩条数据写入外部DDR3中,然后数据被具有AXI4协议只读功能的模块读出并缓存在FIFO中,最后驱动模块驱动数据在HDMI显示屏显示彩条。与第十一章相比,系统没有从外部传入参数,因此没有调用参数传递模块AXI4_Lite_COMM。
自定义AXI4_DDR_READ IP核从DDR3中读取彩条显示数据并写入缓存FIFO。由于本实验分辨率指定为640*480,所以AXI4_DDR_READ IP核的图像的突发次数需要指定为640*480*16/32/256 = 600。
HDMI_COLORBAR_TOP IP核用于从缓存FIFO中获取16位的RGB565格式的彩条数据,并将其转化成24位的RGB888格式的数据后传递给HDMI显示屏。该IP核可依据实际需要灵活设置分辨率相关的参数,本实验指定分辨率为640*480@60Hz。
接下来在第十一章的基础上进行硬件设计。
由于本实验需要驱动HDMI显示屏,分辨率指定为640*480@60Hz,所以AXI4协议内的突发次数需要指定为固定值,因此在进行硬件系统设计之前,我们先对第十一章封装好的AXI4_DDR_READ IP核进行简单的修改并重新封装。首先打开“RGB LCD彩条显示实验”的Vivado工程,打开后依次点击菜单栏的“File->Project->Save As...”,将工程名改为“hdmi_colorbar_top”,如下图所示:
图 12.3.2 工程命名
新建工程后,依次点击“Tools->Setting…->IP->Repository”,由于此工程由其他工程通过“另存为”的方式创建的,该工程路径下并没有下图中的几个IP核,所以我们需要将它们的路径删除。选中IP核并点击红框内的“-”,如下图所示:
图 12.3.3 删除原先IP核路径
在工程路径下新建ip_repo文件夹,拷贝第十一章封装的axi4_ddr_read IP核到此文件夹下,拷贝后如下图所示:
图 12.3.4 拷贝IP核
紧接着回到IP核加载界面,并添加该IP核至工程,如下图所示:
图 12.3.5 添加IP核路径
接下来点击工程左侧的“Open Block Design”,在Diaream窗口中右键axi4_ddr_read_0模块,选择“Edit IP Package”,如下图所示:
图 12.3.6 编辑IP核
在弹出的界面点击“OK”,工程名字和路径自动填充,继续点击“OK”,如下图所示:
图 12.3.7 工程名和路径
稍等片刻,软件自动打开封装IP工程,点击下图红框关闭当前(代码修改前)封装界面,如下图所示:
图 12.3.8 关闭封装界面
使用Notepad++打开上图中两个模块文件,路径如下图所示:
图 12.3.9 模块文件路径
由于本次实验指定HDMI分辨率为640*480@60Hz,所以无需由LCD屏幕的lcd_id来计算突发次数,将突发次数设为固定值即可。修改前计算突发次数的代码如下图所示:
图 12.3.10 突发次数修改前
修改后指定分辨率640*480的突发次数代码如下图所示:
图 12.3.11 分辨率640*480的突发次数
修改固定分辨率的突发次数代码后,接着再删除两个模块的lcd_id相关的端口代码,然后保存。(注意,为避免大家在修改时遗漏或者错误导致后续封装的IP核不能正常使用,所以建议使用例程中的代码文件封装IP核或使用例程中封装好的IP核进行设计)
回到IP封装工程,选中顶层模块,然后右键,点击“Refresh Hierarchy”进行刷新,接着点击左侧“Edit IP Package”重新打开封装界面(代码修改后的封装界面),如下图所示:
图 12.3.12 刷新并重新打开封装界面
重新打开封装IP核后界面如下图所示:
图 12.3.13 封装IP新界面
由于是在原先IP核上进行修改,故可以跳至“Customization Parameters”界面刷新变量,如下图所示:
图 12.3.14 刷新变量
接下来在“Ports and interface”界面中可以发现lcd_id端口被移除,如下图所示:
图 12.3.15 IP核端口与接口
最后,在“Review and Package”界面点击“Package IP”重新封装IP,如下图所示:
图 12.3.16 重新封装IP核
接着刷新IP核路径,在IP核加载界面依次点击“Refresh All->Apply->OK”,如下图所示:
图 12.3.17 刷新IP核路径
刷新路径之后,点击“Report IP Status”,如下图所示:
图 12.3.18 Report IP Status
接着在下方IP Status下可以看到系统内被修改的IP核axi_ddr_read_0,两个找不到定义的IP核axi_lite_comm_0和lcd_rgb_top_0(因为前面将路径删除了),如下图所示:
图 12.3.19 IP Status
在此,我们对修改的IP核进行升级,其他两个IP核在后面会进行处理。取消勾选“Others(2)”,保留选中“Revision Change”,点击“Upgrade Seleted”对修改的IP核进行升级,如下图所示:
图 12.3.20 IP升级
弹出“IP Upgrade Compeleted”即表明IP核升级完成,点击“OK”,如下图所示:
图 12.3.21 升级完成提示
继续点击“Skip”,暂时跳过IP核编译步骤。因为升级IP核之后,还要继续进行硬件设计,所以暂时可以不用对升级的IP核进行编译,等到设计完成后再进行编译也不迟。如下图所示:
图 12.3.22 跳过IP编译
回到Diagram窗口,观察IP核axi_ddr_read_0可以发现,红标处原先存在的lcd_id信号接口已经消失了,说明升级后的新IP核已经生效。保持当前连线不做修改即可,如下图所示:
图 12.3.23 新IP核生效
双击axi4_ddr_read_0,参数C_M_AXI_TARGET_SLAVE_BASE_ADDR为DDR3读彩条起始地址,我们在此将值设置为0x85000000(在后续软的件设计中,MicroBlaze写彩条数据的起始地址需要设置同样值),点击“OK”,如下图所示:
图 12.3.24 写数据地址
由于本实验指定HDMI分辨率为640*480@60Hz,所以需要修改FIFO读时钟和像素时钟。在该分辨率下需要设置的时钟值参考下图中的第一行:
图 12.3.25 各分辨率下时钟和参数
双击clk_wize_1,勾选clk_out4(5倍像素时钟),并将clk_out3、clk_out4分别设置为25.175MHz和125.875MHz,然后点击“OK”,如下图所示:
图 12.3.26 时钟设置
接着点击选中FIFO的读时钟“rd_clk”,并右键选择“Disconnect Pin”断开FIFO读时钟连线,然后将其连接至clk_out3。连接后如下图所示:
图 12.3.27 FIFO读时钟
由于本实验通过HDMI显示彩条,所以无需传递参数lcd_id,选中axi4_lite_comm_0,右键并选择“Delete”删除该IP核,如下图所示:
图 12.3.28 删除axi4_lite_comm IP核
接下来,使用第十章封装完成的用于驱动HDMI屏幕显示彩条的HDMI_COLORBAR_TOP IP核来代替lcd_rgb_top模块。从第十章拷贝该IP核至本实验ip_repo文件夹下,拷贝后IP核位置如下图所示:
图 12.3.29 拷贝IP核
将其添加至工程中,如下图所示:
图 12.3.30 添加IP核至工程
选中系统中的lcd_rgb_top模块和它引出的管脚,右键并选择“Delete”,如下图所示:
图 12.3.31 删除原模块
在弹出的提示界面中,删除项目中包含了该模块和它的接口与端口,点“OK”,如下图所示:
图 12.3.32 删除提示
在Diagram窗口搜索HDMI_COLORBAR_TOP IP核,然后并双击添加至硬件系统,如下图所示:
图 12.3.33 添加HDMI_COLORBAR_TOP IP核
接着添加连线和引出信号:复位信号sys_rst_n连接时钟模块的locked信号,DDR3初始化完成信号ddr_init_done接MIG核的init_calib_complete信号,时钟pixel_clk、pixel_clk_5x分别连接时钟模块的clk_out3和clk_out4,data_req和data_in与FIFO读接口连接,分别引出TMDS、TMDS1、tmds_oen、tmds_oen1并改名为hdmi_out0、hdmi_out1、tmds_oen0、tmds_oen1。连线和引出信号后如下图所示:
图 12.3.34 HDMI_COLORBAR_TOP连接图
验证无误后,保存设计。最后搭建的硬件系统如下图所示:
图 12.3.35 最终布局图
在Source窗口中右键点击Block Design设计文件“system.bd”,然后执行“Generate Output Products”,编译IP时选择“Global”,编译完成后执行“Create HDL Wrapper”。
紧接着添加约束文件:打开system_wrapper.xdc文件删除之前的约束文件,添加如下管脚约束:
- create_clock -period 20.000 -name sys_clk [get_ports sys_clk]
- set_property -dict {PACKAGE_PIN R4 IOSTANDARD LVCMOS33} [get_ports sys_clk]
- set_property -dict {PACKAGE_PIN U2 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]
- #UART
- set_property -dict {PACKAGE_PIN U5 IOSTANDARD LVCMOS33} [get_ports UART_rxd]
- set_property -dict {PACKAGE_PIN T6 IOSTANDARD LVCMOS33} [get_ports UART_txd]
- #--------------------------------HDMI_A-----------------------------------------
- set_property PACKAGE_PIN B20 [get_ports {hdmi_out0_tmds_data_p[2]}]
- set_property PACKAGE_PIN B21 [get_ports {hdmi_out0_tmds_data_p[1]}]
- set_property PACKAGE_PIN C22 [get_ports {hdmi_out0_tmds_data_p[0]}]
- set_property IOSTANDARD TMDS_33 [get_ports {hdmi_out0_tmds_data_p[2]}]
- set_property IOSTANDARD TMDS_33 [get_ports {hdmi_out0_tmds_data_p[1]}]
- set_property IOSTANDARD TMDS_33 [get_ports {hdmi_out0_tmds_data_p[0]}]
- set_property PACKAGE_PIN C18 [get_ports hdmi_out0_tmds_clk_p]
- set_property IOSTANDARD TMDS_33 [get_ports hdmi_out0_tmds_clk_p]
- set_property -dict {PACKAGE_PIN E21 IOSTANDARD LVCMOS33} [get_ports tmds_oen0]
- set_property IOSTANDARD LVCMOS33 [get_ports tmds_oen0]
- #--------------------------------HDMI_B----------------------------------------
- set_property PACKAGE_PIN A13 [get_ports {hdmi_out1_tmds_data_p[2]}]
- set_property PACKAGE_PIN A15 [get_ports {hdmi_out1_tmds_data_p[1]}]
- set_property PACKAGE_PIN A18 [get_ports {hdmi_out1_tmds_data_p[0]}]
- set_property IOSTANDARD TMDS_33 [get_ports {hdmi_out1_tmds_data_p[2]}]
- set_property IOSTANDARD TMDS_33 [get_ports {hdmi_out1_tmds_data_p[1]}]
- set_property IOSTANDARD TMDS_33 [get_ports {hdmi_out1_tmds_data_p[0]}]
- set_property PACKAGE_PIN B17 [get_ports hdmi_out1_tmds_clk_p]
- set_property IOSTANDARD TMDS_33 [get_ports hdmi_out1_tmds_clk_p]
- set_property -dict {PACKAGE_PIN C20 IOSTANDARD LVCMOS33} [get_ports tmds_oen1]
- set_property IOSTANDARD LVCMOS33 [get_ports tmds_oen1]
- #SPI
- set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
- set_property CONFIG_MODE SPIx4 [current_design]
- set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
- set_property CFGBVS VCCO [current_design]
- set_property CONFIG_VOLTAGE 3.3 [current_design]
- set_property BITSTREAM.CONFIG.UNUSEDPIN PULLUP [current_design]
复制代码
管脚分配完成后按快捷键“Ctrl+S”保存管脚约束。点击“Generate Bitstream”,对设计进行综合、实现、并生成Bit文件。
至此,本实验的硬件设计已经全部完成。Bit文件生成后导出至工程的vitis文件夹,并启动Vitis。
12.4软件设计
打开Vitis开发环境后,创建应用工程的步骤都是一样的,这里不再赘述,新创建的空白应用工程命名为“hdmi_colorbar”。然后为应用工程新建一个源文件“main.c”,我们在新建的main.c文件中输入本次实验的代码,代码如下所示:
- 48 #include <stdio.h>
- 49 #include "platform.h"
- 50 #include "xil_printf.h"
- 51 #include "xparameters.h"
- 52 #include "xil_cache.h"
- 53
- 54 //宏定义
- 55 #define BYTES_PIXEL 2
- 56
- 57 //函数声明
- 58 void colorbar(u8 *frame, u32 width, u32 height, u32 stride);
- 59
- 60 unsigned int const frame_buffer_addr = (XPAR_MIG_7SERIES_0_BASEADDR);
- 61
- 62 int main()
- 63 {
- 64 //分辨率640*480的width height参数设置
- 65 u16 hor_res = 0x280,ver_res = 0x1E0;
- 66
- 67 init_platform();
- 68
- 69 //写彩条
- 70 colorbar((u8*)frame_buffer_addr, hor_res, ver_res, hor_res*BYTES_PIXEL);
- 71
- 72 cleanup_platform();
- 73 return 0;
- 74 }
- 75
- 76 //写彩条函数
- 77void colorbar(u8 *frame, u32 width, u32 height, u32 stride)
- 78{
- 79 u32 color_edge, colorbar_2, colorbar_3, colorbar_4, colorbar_5;
- 80 u32 x_pos, y_pos;
- 81 u32 y_stride = 0;
- 82 u16 rgb = 0;
- 83
- 84 color_edge = width * BYTES_PIXEL / 5;
- 85 colorbar_2 = color_edge * 2;
- 86 colorbar_3 = color_edge * 3;
- 87 colorbar_4 = color_edge * 4;
- 88 colorbar_5 = color_edge * 5;
- 89
- 90 //开始写彩条
- 91 xil_printf("write color star \r\n");
- 92 for (y_pos = 0; y_pos < height; y_pos++) {
- 93 for (x_pos = 0; x_pos < width * BYTES_PIXEL; x_pos += BYTES_PIXEL) {
- 94 if (x_pos < color_edge) { //白色
- 95 rgb = 0xffff;
- 96 } else if ((x_pos >= color_edge) && (x_pos < colorbar_2)) { //黑色
- 97 rgb = 0x0;
- 98 } else if ((x_pos >= colorbar_2) && (x_pos < colorbar_3)) { //红色
- 99 rgb = 0xf800;
- 100 } else if ((x_pos >= colorbar_3) && (x_pos < colorbar_4)) { //绿色
- 101 rgb = 0x07e0;
- 102 } else if ((x_pos >= colorbar_4) && (x_pos < colorbar_5)) { //蓝色
- 103 rgb = 0x001f;
- 104 }
- 105 frame[x_pos + y_stride + 0] = rgb;
- 106 frame[x_pos + y_stride + 1] = rgb>>8;
- 107 }
- 108 y_stride += stride;
- 109 }
- 110 Xil_DCacheFlush(); //刷新Cache,数据更新至DDR3中
- 111 xil_printf("show color bar\r\n");
- 112}
复制代码
与第十一章代码不同的是,本试验代码不需要获取lcd_id以及计算参数width、height,直接将width、height设为固定值即可,其余代码与第十一章的基本一致。
第60行获取写彩条的基地址,按“Ctrl”键点击宏“XPAR_MIG_7SERIES_0_BASEADDR”进入该值设置文件xparameters.h,我们将其设置为0x85000000(必须与硬件系统中axi4_ddr_read_1设置的读数据起始地址相同),点击“保存”。地址设置如下图所示:
图 12.4.1 写彩条基地址设置
主函数中,代码第65行对分辨率640*480的width height参数进行了设置。
其余代码不再赘述,请参考第十一章代码的解释。
保存,编译整个工程,生成ELF文件。
12.5下载验证
首先我们将下载器与达芬奇开发板上的JTAG接口连接,下载器另外一端与电脑连接。然后使用USB连接线将USB_UART接口与电脑连接,用于串口通信。将HDMI线一端连接在开发板上两个HDMI接口中的任意一个,另一端连接至HDMI屏幕。最后连接开发板的电源,并打开电源开关。
打开Vitis的Terminal并连接,开始下载程序。随着程序的执行,当串口打印出“show color bar”时,证明程序执行完毕,如下图所示:
图 12.5.1 程序执行完毕
在HDMI屏幕上即可看到彩条图像,如下图所示:
图 12.5.2 HDMI屏幕彩条显示
至此,HDMI彩条显示实验完成,同时验证了第十章封装的HDMI_COLORBAR_TOP IP核正确可用。
|