TA的每日心情 | 开心 2020-7-25 10:37 |
---|
签到天数: 40 天 连续签到: 1 天 [LV.5]常住居民I
|
本帖最后由 武器哈12 于 2020-7-28 01:47 编辑
高清多媒体接口(High Definition Multimedia Interface,HDMI)是一种全数字化视频和声音发送接口,可以发送未压缩的音频及视频信号。
HDMI 采用和 DVI 相同的传输原理——TMDS(Transition Minimized Differential signal),最小化传输差分信号。 TMDS 传输系统分为分为两个部分:发送端和接收端。TMDS 发送端收到 HDMI 接口传来的示 RGB 信号的 24 位并行数(TMDS 对每个像素的 RGB 三原色分别按 8bit 编码,即 R 信号有 8位,G 信号有 8 位,B 信号有 8 位),然后对这些数据进行编码和并/串转换,再将表示 3 个 RGB信号的数据分别分配到独立的传输通道发送出去。接收端接收来自发送端的串行信号,对其进行解码和串/并转换,然后发送到显示器的控制端。与此同时也接收时钟信号,以实现同步。
HDMI 输出程序设计
实验将实现 HDMI 输出显示,verilog 实现编程驱动 HDMI 输出,在 HDMI 显示器里显示测
试图像彩条。HDMI 输出显示模块分成 3 个模块实现,分别是时钟模块 vidio_pll, 彩条生成模块
color_bar 和 VGA 转 DVI 模块 dvi_encoder。实现的逻辑框图如下:
站在巨人的肩膀上,我们可以把VGA转DVI模块可以当做一个IP来使用,只要我们懂得VGA时序就可以来使用或者完成本次实验。VGA时序:
想更进一步了解VGA时序:https://blog.csdn.net/liuligui5200/article/details/80974319
彩条产生模块 color_bar.v:
color_bar.v 是产生 8 种颜色的 VGA 格式的彩条,彩条分别为白、黄、青、绿、紫、红、蓝和黑。针对 VGA 的时序,行同步和场同步各使用一个计数器,行同步计数器用于产生行同步,行有效像素,场同步计数器用于产生场同步,场有效像素。同时根据计数器的值可以产生水平(X)和垂直(Y)坐标,通过坐标信息,可以实时显示一些图形。
在屏幕中显示汉字:
在 HDMI 显示的基础上增加了一个 osd_display 的模块,“osd_display”模块是用来读取存储在 Rom ip 核里转换后的字符信息,并在指定区域显示。osd_display 模块包timing_gen_xy 模块和 osd_rom 模块。Osd_rom 里存储的字符数据,如果数据为 1,OSD 的区域显示 ROM 中的前景红色,如果数据是 0,OSD 的区域显示数据为背景色(彩条)。程序框图如下图所示:
在“timing_gen_xy”模块是根据 HDMI 时序标准定义了“x_cnt”和“y_cnt”两个计数器并由这两个计数器产生了 HDMI 显示的“x”坐标和“y”坐标。程序中用“vs_edge”和“de_falling”分 别 表 示 场 同 步 开 始 信 号 和 数 据 有 效 结 束 信 号 。
下面利用“FPGA 字模提取”工具生成能够被 Pango FPGA 识别的.dat 文件:
大概知道代码实现方式后,修改下面代码中加粗部分代码,编译后下载到FPGA开发板。
- module osd_display(
- input rst_n,
- input pclk,
- input[23:0] wave_color,
- input adc_clk,
- input adc_buf_wr,
- input[11:0] adc_buf_addr,
- input[7:0] adc_buf_data,
- input i_hs,
- input i_vs,
- input i_de,
- input[23:0] i_data,
- output o_hs,
- output o_vs,
- output o_de,
- output[23:0] o_data
- );
- parameter OSD_WIDTH = 12'd264;
- parameter OSD_HEGIHT = 12'd64;
- parameter OSD_WIDTH_OFFSET = 12'd500;
- parameter OSD_HEGIHT_OFFSET = 12'd320;
- wire[11:0] pos_x;
- wire[11:0] pos_y;
- wire pos_hs;
- wire pos_vs;
- wire pos_de;
- wire[23:0] pos_data;
- reg[23:0] v_data;
- reg[11:0] osd_x;
- reg[11:0] osd_y;
- reg[18:0] osd_ram_addr;
- wire[7:0] q;
- reg region_active;
- reg region_active_d0;
- reg region_active_d1;
- reg region_active_d2;
- reg pos_vs_d0;
- reg pos_vs_d1;
- assign o_data = v_data;
- assign o_hs = pos_hs;
- assign o_vs = pos_vs;
- assign o_de = pos_de;
- //delay 1 clock
- always@(posedge pclk)
- begin
- if(pos_y >= OSD_HEGIHT_OFFSET && pos_y <= OSD_HEGIHT_OFFSET + OSD_HEGIHT - 12'd1 && pos_x >= OSD_WIDTH_OFFSET && pos_x <= OSD_WIDTH_OFFSET + OSD_WIDTH - 12'd1)
- region_active <= 1'b1;
- else
- region_active <= 1'b0;
- end
- always@(posedge pclk)
- begin
- region_active_d0 <= region_active;
- region_active_d1 <= region_active_d0;
- region_active_d2 <= region_active_d1;
- end
- always@(posedge pclk)
- begin
- pos_vs_d0 <= pos_vs;
- pos_vs_d1 <= pos_vs_d0;
- end
- //delay 2 clock
- //region_active_d0
- always@(posedge pclk)
- begin
- if(region_active_d0 == 1'b1)
- osd_x <= osd_x + 12'd1;
- else
- osd_x <= 12'd0;
- end
- always@(posedge pclk)
- begin
- if(pos_vs_d1 == 1'b1 && pos_vs_d0 == 1'b0)
- osd_ram_addr <= 16'd0;
- else if(region_active == 1'b1)
- osd_ram_addr <= osd_ram_addr + 16'd1;
- end
- always@(posedge pclk)
- begin
- if(region_active_d0 == 1'b1)
- if(q[osd_x[2:0]] == 1'b1)//这里很重要!一个字节代表 8 个时钟的像素,所以在从 Rom IP 核中读取 dat 文件的值, //需要判断每一位的值,如果值为 1,显示红色前景色,否则显示背景色
- v_data <= 24'hffff00;
- else
- v_data <= pos_data;
- else
- v_data <= pos_data;
- end
- osd_rom osd_rom_m0 ( //存储字符的RAM
- .addr(osd_ram_addr[16:3]),
- .clk(pclk),
- .rst(1'b0),
- .rd_data(q));
- timing_gen_xy timing_gen_xy_m0(
- .rst_n (rst_n ),
- .clk (pclk ),
- .i_hs (i_hs ),
- .i_vs (i_vs ),
- .i_de (i_de ),
- .i_data (i_data ),
- .o_hs (pos_hs ),
- .o_vs (pos_vs ),
- .o_de (pos_de ),
- .o_data (pos_data ),
- .x (pos_x ),//XY坐标
- .y (pos_y )
- );
- endmodule
实验结果:
|
|