5. 基于sdram的hdmi图像显示¶
上一章节,我们使用RS232发送图片数据、SDRAM作数据缓存,成功实现了大幅图片的VGA显示,加深了读者对RS232、VGA、SDRAM相关知识的理解与掌握。那么在本章节,我们进一步实现基于SDRAM的HDMI图像显示,带领读者回顾一下HDMI的相关知识。
5.1. 理论学习¶
有关本章节涉及串口RS232、SDRAM读写控制器和HDMI的相关理论知识,我们在前面章节已经做过详细讲解。读者若有忘记请回顾查阅,在此不再做过多讲解。
5.2. 实战演练¶
5.2.1. 实验目标¶
在PC机上使用串口助手助手发送一幅分辨率为640*480的图片数据给FPGA,FPGA以外接SDRAM做缓存,将接收到的图片数据通过HDMI显示器显示出来。
5.2.2. 硬件资源¶
详见“HDMI显示器驱动设计与验证”、“串口RS232”、“SDRAM读写控制器的设计与验证”章节的“硬件资源”介绍。
5.3. 程序设计¶
5.3.1. 整体框图¶
图 55‑1 SDRAM –HDMI图像显示整体框图
由图可知,本实验工程共调用6个模块(sdram_top视为一个模块),顶层模块为uart_sdram_vga_pic模块,内部实例化5个功能子模块,连接各子模块对应信号,外部接收数据读写请求、读写地址和读写数据,输出SDRAM控制信号、地址、数据,以及VGA行场同步信号和图像数据;顶层模块内部实例化 的4个功能子模块分别为 clk_gen、uart_rx、sdram_top、vga_ctrl和hdmi_ctrl。各部分功能描述,具体见表格 55‑1。
表格 55‑1 各模块功能描述
模块名称 |
功能描述 |
---|---|
uart_sdram_hdmi_pic |
SDRAM –VGA图像显示顶层模块 |
clk_gen |
时钟生成模块 |
uart_rx |
串口数据接收模块 |
sdram_top |
SDRAM读写控制器 |
vga_ctrl |
VGA驱动控制 |
hdmi_ctrl |
HDMI驱动控制 |
本实验工程使用的串口模块、SDRAM控制器、VGA驱动模块和HDMI驱动模块均是在前面章节设计模块的基础上稍加改动得到的,读者若有遗忘可回顾各章节相关内容。
5.3.1.1. 顶层模块¶
模块框图
经过前面章节的学习,读者已经知道,顶层模块是使用工程必不可少的一部分,内部实例化个子功能模块,外部进行工程输入输出信号的连接,作用巨大。但顶层模块代码写起来较为简单,无需波形图绘制,只需连接对应端口即可。顶层模块模块框图,具体见图 55‑2;输入输出信号功能描述具体见表格 55‑2。
图 55‑2 顶层模块框图
表格 55‑2 输入输出端口功能描述
信号 |
位宽 |
类型 |
功能描述 |
---|---|---|---|
sys_clk |
1Bit |
Input |
系统时钟,频率50MHz |
sys_rst_n |
1Bit |
Input |
复位信号,低有效 |
rx |
1Bit |
Input |
串口数据接收端口 |
ddc_scl |
1Bit |
Output |
DDC时钟 |
dda_sda |
1Bit |
Output |
DDC数据 |
tmds_clk_p |
1Bit |
Output |
时钟差分信号 |
tmds_clk_n |
1Bit |
Output |
时钟差分信号 |
tmds_data_p[2] |
3Bit |
Output |
图像信息差分信号 |
tmds_data_n[2] |
3Bit |
Output |
图像信息差分信号 |
sdram_clk |
1Bit |
Output |
SDRAM写时钟 |
sdram_cke |
1Bit |
Output |
SDRAM时钟有效信号 |
sdram_cs_n |
1Bit |
Output |
SDRAM片选信号 |
sdram_ras_n |
1Bit |
Output |
SDRAM行选通信号 |
sdram_cas_n |
1Bit |
Output |
SDRAM列选通信号 |
sdram_we_n |
1Bit |
Output |
SDRAM写使能信号 |
sdram_ba |
2Bit |
Output |
SDRAM逻辑Bank地址 |
sdram_addr |
13Bit |
Output |
SDRAM地址总线 |
sdram_dqm |
2Bit |
Output |
SDRAM数据掩码 |
sdram_dq |
16Bit |
Output |
SDRAM数据总线 |
代码编写
顶层模块参考代码具体见代码清单 55‑1。
代码清单 55‑1 顶层模块参考代码(uart_sdram_hdmi_pic.v)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | module uart_sdram_hdmi_pic
(
input wire sys_clk , //系统时钟,50MHz
input wire sys_rst_n , //复位信号
input wire rx , //串口接收
output wire ddc_scl ,
output wire ddc_sda ,
output wire tmds_clk_p ,
output wire tmds_clk_n , //HDMI时钟差分信号
output wire [2:0] tmds_data_p ,
output wire [2:0] tmds_data_n , //HDMI图像差分信号
output wire sdram_clk , //SDRAM时钟
output wire sdram_cke , //SDRAM时钟使能
output wire sdram_cs_n , //SDRAM片选
output wire sdram_cas_n , //SDRAM列选通
output wire sdram_ras_n , //SDRAM行选通
output wire sdram_we_n , //SDRAM写使能
output wire [1:0] sdram_ba , //SDRAM Bank地址
output wire [12:0] sdram_addr , //SDRAM地址总线
output wire [1:0] sdram_dqm , //SDRAM数据掩码
inout wire [15:0] sdram_dq //SDRAM数据总线
);
////
//\* Parameter and Internal Signal \//
////
//parameter define
parameter H_PIXEL = 24'd640 ; //水平方向像素个数,用于设置SDRAM缓存大小
parameter V_PIXEL = 24'd480 ; //垂直方向像素个数,用于设置SDRAM缓存大小
parameter UART_BPS = 20'd115200 , //比特率
CLK_FREQ = 26'd50_000_000 ; //时钟频率
// wire define
//uart_rx
wire [7:0] rx_data ; //拼接后的8位图像数据
wire rx_flag ; //数据标志信号
//vga_ctrl
wire data_req ; //TFT数据请求信号
wire [15:0] data_in ; //TFT图像数据
wire [15:0] rgb_vga ; //VGA显示数据
wire rgb_valid ; //VGA有效显示区域
wire hsync ; //VGA行同步信号
wire vsync ; //VGA场同步信号
//clk_gen
wire clk_25m ;
wire clk_50m ;
wire clk_125m ;
wire clk_125m_shift ; //pll产生时钟
wire locked ; //pll锁定信号
wire rst_n ; //复位信号
////
//\* Main Code \//
////
//rst_n:复位信号
assign rst_n = sys_rst_n & locked;
assign ddc_scl = 1'b1;
assign ddc_sda = 1'b1;
//------------- clk_gen_inst -------------
clk_gen clk_gen_inst (
.inclk0 (sys_clk ),
.areset (~sys_rst_n ),
.c0 (clk_25m ),
.c1 (clk_50m ),
.c2 (clk_125m ),
.c3 (clk_125m_shift ),
.locked (locked )
);
//------------- uart_rx_inst -------------
uart_rx
#(
.UART_BPS (UART_BPS ), //串口波特率
.CLK_FREQ (CLK_FREQ ) //时钟频率
)
uart_rx_inst
(
.sys_clk (clk_50m ), //input sys_clk
.sys_rst_n (rst_n ), //input sys_rst_n
.rx (rx ), //input rx
.po_data (rx_data ), //output [7:0] rx_data
.po_flag (rx_flag ) //output rx_flag
);
//------------- vga_ctrl_inst -------------
vga_ctrl vga_ctrl_inst
(
.vga_clk (clk_25m ), //输入工作时钟,频率25MHz
.sys_rst_n (rst_n ), //输入复位信号,低电平有效
.pix_data ({data_in[7:5],2'b0,data_in[4:2],3'b0,data_in[1:0],3'b0}),
.rgb_valid (rgb_valid ), //VGA有效显示区域
.pix_data_req(data_req ),
.hsync (hsync ), //输出行同步信号
.vsync (vsync ), //输出场同步信号
.rgb (rgb_vga ) //输出像素信息
);
//------------- hdmi_ctrl_inst -------------
hdmi_ctrl hdmi_ctrl_inst
(
.clk_1x (clk_25m ), //输入系统时钟
.clk_5x (clk_125m ), //输入5倍系统时钟
.sys_rst_n (rst_n ), //复位信号,低有效
.rgb_blue ({rgb_vga[4:0],3'b0} ), //蓝色分量
.rgb_green ({rgb_vga[10:5],2'b0} ), //绿色分量
.rgb_red ({rgb_vga[15:11],3'b0} ), //红色分量
.hsync (hsync ), //行同步信号
.vsync (vsync ), //场同步信号
.de (rgb_valid ), //使能信号
.hdmi_clk_p (tmds_clk_p ),
.hdmi_clk_n (tmds_clk_n ), //时钟差分信号
.hdmi_r_p (tmds_data_p[2] ),
.hdmi_r_n (tmds_data_n[2] ), //红色分量差分信号
.hdmi_g_p (tmds_data_p[1] ),
.hdmi_g_n (tmds_data_n[1] ), //绿色分量差分信号
.hdmi_b_p (tmds_data_p[0] ),
.hdmi_b_n (tmds_data_n[0] ) //蓝色分量差分信号
);
//------------- sdram_top_inst -------------
sdram_top sdram_top_inst
(
.sys_clk (clk_125m ), //sdram 控制器参考时钟
.clk_out (clk_125m_shift ), //用于输出的相位偏移时钟
.sys_rst_n (rst_n ), //系统复位
//用户写端口
.wr_fifo_wr_clk (clk_50m ), //写端口FIFO: 写时钟
.wr_fifo_wr_req (rx_flag ), //写端口FIFO: 写使能
.wr_fifo_wr_data ({8'b0,rx_data} ), //写端口FIFO: 写数据
.sdram_wr_b_addr (24'd0 ), //写SDRAM的起始地址
.sdram_wr_e_addr (H_PIXEL*V_PIXEL), //写SDRAM的结束地址
.wr_burst_len (10'd512 ), //写SDRAM时的数据突发长度
.wr_rst (~rst_n ), //写复位
//用户读端口
.rd_fifo_rd_clk (clk_25m ), //读端口FIFO: 读时钟
.rd_fifo_rd_req (data_req ), //读端口FIFO: 读使能
.rd_fifo_rd_data (data_in ), //读端口FIFO: 读数据
.sdram_rd_b_addr (24'd0 ), //读SDRAM的起始地址
.sdram_rd_e_addr (H_PIXEL*V_PIXEL), //读SDRAM的结束地址
.rd_burst_len (10'd512 ), //从SDRAM中读数据时的突发长度
.rd_rst ( ), //读复位
.rd_fifo_num ( ), //读fifo中的数据量
//用户控制端口
.read_valid (1'b1 ), //SDRAM 读使能
.init_end ( ), //SDRAM 初始化完成标志
//SDRAM 芯片接口
.sdram_clk (sdram_clk ), //SDRAM 芯片时钟
.sdram_cke (sdram_cke ), //SDRAM 时钟有效
.sdram_cs_n (sdram_cs_n ), //SDRAM 片选
.sdram_ras_n (sdram_ras_n ), //SDRAM 行有效
.sdram_cas_n (sdram_cas_n ), //SDRAM 列有效
.sdram_we_n (sdram_we_n ), //SDRAM 写有效
.sdram_ba (sdram_ba ), //SDRAM Bank地址
.sdram_addr (sdram_addr ), //SDRAM 行/列地址
.sdram_dq (sdram_dq ), //SDRAM 数据
.sdram_dqm (sdram_dqm ) //SDRAM 数据掩码
);
endmodule
|
5.3.1.2. RTL视图¶
至此实验工程基本完成,在Quartus中对代码进行编译,编译若有错误,请读者根据错误提示信息作出更改,直至编译通过,编译通过后查看RTL视图,与顶层模块框图对比,两者一致,各信号连接正确。RTL视图,具体见图 55‑3。
图 55‑3 RTL视图
5.4. 上板调试¶
5.4.1. 引脚约束¶
仿真验证通过后,准备上板验证,上板验证之前先要进行引脚约束。工程中各输入输出信号与开发板引脚对应关系如表格 55‑3所示。
表格 55‑3 引脚分配表
信号名 |
信号类型 |
对应引脚 |
备注 |
---|---|---|---|
sys_clk |
Input |
E1 |
时钟 |
sys_rst_n |
Input |
M15 |
复位 |
rx |
Input |
N6 |
串口接受数据 |
sdram_clk |
Output |
R4 |
SDRAM芯片时钟 |
sdram_cke |
Output |
R9 |
SDRAM时钟有效信号 |
sdram_cs_n |
Output |
R12 |
SDRAM片选信号 |
sdram_cas_n |
Output |
R10 |
SDRAM列地址选通脉冲 |
sdram_ras_n |
Output |
R11 |
SDRAM行地址选通脉冲 |
sdram_we_n |
Output |
L9 |
SDRAM写允许位 |
sdram_ba[0] |
Output |
R13 |
SDRAM的L-Bank地址线 |
sdram_ba[1] |
Output |
R14 |
SDRAM的L-Bank地址线 |
sdram_addr[0] |
Output |
P11 |
SDRAM地址总线 |
sdram_addr[1] |
Output |
P14 |
SDRAM地址总线 |
sdram_addr[2] |
Output |
N9 |
SDRAM地址总线 |
sdram_addr[3] |
Output |
N11 |
SDRAM地址总线 |
sdram_addr[4] |
Output |
T14 |
SDRAM地址总线 |
sdram_addr[5] |
Output |
T13 |
SDRAM地址总线 |
sdram_addr[6] |
Output |
T12 |
SDRAM地址总线 |
sdram_addr[7] |
Output |
T11 |
SDRAM地址总线 |
sdram_addr[8] |
Output |
T10 |
SDRAM地址总线 |
sdram_addr[9] |
Output |
P9 |
SDRAM地址总线 |
sdram_addr[10] |
Output |
T15 |
SDRAM地址总线 |
sdram_addr[11] |
Output |
N12 |
SDRAM地址总线 |
sdram_addr[12] |
Output |
M11 |
SDRAM地址总线 |
sdram_dqm[0] |
Output |
M10 |
SDRAM数据掩码 |
sdram_dqm[1] |
Output |
M9 |
SDRAM数据掩码 |
sdram_dq[0] |
Output |
R3 |
SDRAM数据总线 |
sdram_dq[1] |
Output |
T9 |
SDRAM数据总线 |
sdram_dq[2] |
Output |
R5 |
SDRAM数据总线 |
sdram_dq[3] |
Output |
R6 |
SDRAM数据总线 |
sdram_dq[4] |
Output |
R7 |
SDRAM数据总线 |
sdram_dq[5] |
Output |
M8 |
SDRAM数据总线 |
sdram_dq[6] |
Output |
R8 |
SDRAM数据总线 |
sdram_dq[7] |
Output |
N8 |
SDRAM数据总线 |
sdram_dq[8] |
Output |
P8 |
SDRAM数据总线 |
sdram_dq[9] |
Output |
T8 |
SDRAM数据总线 |
sdram_dq[10] |
Output |
T7 |
SDRAM数据总线 |
sdram_dq[11] |
Output |
T6 |
SDRAM数据总线 |
sdram_dq[12] |
Output |
T5 |
SDRAM数据总线 |
sdram_dq[13] |
Output |
T4 |
SDRAM数据总线 |
sdram_dq[14] |
Output |
T3 |
SDRAM数据总线 |
sdram_dq[15] |
Output |
T2 |
SDRAM数据总线 |
ddc_scl |
Output |
P15 |
DDC时钟 |
dda_sda |
Output |
N14 |
DDC数据 |
tmds_clk_p |
Output |
R16 |
时钟差分信号 |
tmds_clk_n |
Output |
P16 |
时钟差分信号 |
tmds_data_p[2] |
Output |
K15 |
红色分量差分信号 |
tmds_data_n[2] |
Output |
K16 |
红色分量差分信号 |
tmds_data_p[1] |
Output |
L15 |
绿色分量差分信号 |
tmds_data_n[1] |
Output |
L15 |
绿色分量差分信号 |
tmds_data_p[0] |
Output |
N15 |
蓝色分量差分信号 |
tmds_data_n[0] |
Output |
N16 |
蓝色分量差分信号 |
下面进行管脚分配,管脚的分配方法在前面章节已有所讲解,在此就不再过多叙述,管脚的分配如下图 55‑4、图 55‑5所示。
图 55‑4 管脚分配
图 55‑5 管脚分配
5.4.1.1. 结果验证¶
如图 55‑6所示,开发板连接12V直流电源、USB-Blaster下载器JTAG端口、USB数据线以及HDMI显示器。线路正确连接后,打开开关为板卡上电。
图 55‑6 程序下载连线图
如图 55‑7所示,使用“Programmer”为开发板下载程序。
图 55‑7 程序下载图
程序下载完成后,HDMI屏幕显示随机彩条,如所示图 55‑8;使用串口助手向开发板发送图片数据,HDMI显示屏上逐行扫描出待显示图像,如图 55‑9、图 55‑10所示;扫描完毕后,HDMI显示器显示完整图像,如图 55‑11所示。
图 55‑8 随机彩条
图 55‑9 串口发送图片数据
图 55‑10 HDMI扫描显示图片
图 55‑11 HDMI显示完整图片
5.5. 章末总结¶
本章节使用SDRAM做缓存,实现较大图片的HDMI显示 ,对于SDRAM的相关知识,读者要做到切实掌握,灵活运用。