#1
高清多媒体接口(High Definition Multimedia Interface,HDMI )是一种全数字化视频和声音发送接口,可以发送未压缩的音频及视频信号。HDMI可用于机顶盒、DVD播放机、个人计算机、电视、游戏主机、综合扩大机、数字音响与电视机等设备。HDMI可以同时发送音频和视频信号,由于音频和视频信号采用同一条线材,大大简化系统线路的安装难度。
HDMI是日常生活中常用的视频输入输出接口协议,可以同时传输视频和音频,且速率高,有四种接口,但是比较常用的是Type-A接口,长下面这个样子。
然后是时序标准,可以看到,采用的是1920 x 1080的分辨率,60Hz的刷新率
可以看到其中行的像素点总共是2200个,其中有效显示图像的像素点是1920个,行同步信号是44个像素点,后沿是148个像素点,前沿是88个像素点,这样在HDMI里面一行就有1920+44+148+88=2200个像素点.
HDMI编码接口可以用开发工具中现成的IP,我们只需要把RGB的数据输出到IP中就可以了,先简单的显示一下彩条,最基础的图像测试。
宽在这里面叫做场,可以看到一共有1125个像素点,其中有1080个像素点是有效显示图像的,场同步信号是5个像素点,后沿是36个,前沿是4个,加起来就是1125+36+4+5=1125.
因为左右和上下边框都是0,这里就可以不用管他们。
这样我们就可以定义参数:
parameter H_SYNC = 12'd44 ,
H_BACK = 12'd148 ,
H_VALD = 12'd1920 ,
H_FRONT = 12'd88 ,
H_TOTAL = 12'd2200 ;
parameter V_SYNC = 12'd5 ,
V_BACK = 12'd36 ,
V_VALD = 12'd1080 ,
V_FRONT = 12'd4 ,
V_TOTAL = 12'd1125 ;
首先显示一个彩条,这是最简单的测试了,如果想实现竖的彩条,那就在行上面做文章,我们可以分成三等分,每640个像素点换个颜色,比如三原色,红蓝绿,这个很简单,用一个always块便可以实现。
always @(posedge pclk)
if((pix_x >= 192) && (pix_x <=832))
pix_data<=RED;
else if((pix_x > 832) && (pix_x <=1472))
pix_data<=GREEN;
else if((pix_x > 1472) && (pix_x <=2112))
pix_data<=BLUE;
else
pix_data<=24'hff;
然后显示图像原图,我们需要准备一张喜欢的图片,但是要注意这个图片的大小,因为板子的资源是有限的,要注意好板子可以存多大的图片,这里用的是200*200的大小,我们用MATLAB把图片生成一个coe文件,这样就可以把图片放到我们的rom中。
这里用的一个简单的单端口rom,因为我们只需要读出数据就可以了。
输出位宽24位,因为RGB888,红蓝绿每个占8位.
然后这里我们选中本地生成的coe文件。
然后是行扫描和场扫描的计数器,每个时钟来临让像素点动一次,扫描完一行换下一行。
always @(posedge pclk)
if(!rst_n)
cnt_h <= 'd0;
else if(cnt_h == H_TOTAL - 1)
cnt_h <= 'd0;
else
cnt_h <= cnt_h + 1;
always @(posedge pclk)
if(!rst_n)
cnt_v <= 'd0;
else if(cnt_v == V_TOTAL - 1 && cnt_h == H_TOTAL - 1)
cnt_v <= 'd0;
else if(cnt_h == H_TOTAL - 1)
cnt_v <= cnt_v + 1;
然后涉及到一个显示区域的问题,因为显示器的标格式有前沿后沿,左边框右边框等,所以我们可以单独设置一个DE信号,只有当显示器需要显示内容时才显示,其他时候给一个默认值即可,这个操作可以用一个三目运算符完成。
assign de = (cnt_h >= H_SYNC + H_BACK && cnt_h < H_SYNC + H_BACK + H_VALD
&& cnt_v >= V_SYNC + V_BACK && cnt_v < V_SYNC + V_BACK + V_VALD) ? 1: 0;
assign rgb = (de)? rgb_data : 24'hffffff;
然后是图片模块
这里可以直接调用官方的IP核,找到romIP和进行配置,因为我们只需要读数据就可以了
这里的数据位宽选择24位,因为是RGB888,所以一个像素点有24个数据,然后深度选择65536,因为我的图片格式是256x256=65536
这里的rom数据源我们就选择转换出来的coe文件,记得要对上深度和位宽,不然会报错的。
然后就可以简单的写一个模块,输入坐标数据,然后按顺序读出数据就好,没有什么问题。
module vga_pic(
input wire pclk ,
input wire rst_n ,
input wire [11:0] pic_x ,
input wire [11:0] pic_y ,
output wire [23:0] rgb_data
);
assign rgb_data = data_out ;
blk_mem_gen_0 your_instance_name (
.clka(pclk),
.addra({pic_y[7:0] ,pic_x[7:0]}),
.douta(data_out)
这里的时钟是用的官方的IP核,因为视频显示的时钟是很快的,开发板默认的50M时钟是肯定来不及的,所以我们需要更快的时钟才能完成数据的输入输出。
这里下面的时钟源输入50MHz,然后输出两个148.5的时钟还有742.5的时钟,这个148.5的时钟
这里的148.5的时钟是因为这样的,我们可以计算一下
这就是为什么时钟需要用148.5MHz的,然后还有一个742.5MHz的时钟,因为HMDI有一个TMDS模块,会进行一个8转10bit的操作,所以我们需要在一个工作时钟周期内串行传10个数据。
你也可能会说那为什么时钟是742.5的,不应该是1485MHz吗,为什么只有一半,是因为他是双沿输出,一个时钟周期可以输出两个数据,所以时钟只需要一般就可以了,所以是742.5。
因为我们是要进行三种图片处理的展示,一种原图,一种灰度,一种原图,所以可以单独写一个控制模块,这个模块的主要作用是将显示的区域隔开,我们可以界定一个区域显示一种效果。
`timescale 1ns / 1ps
module pic_ctrl(
input pclk ,
input rst_n ,
input wire [11:0] pic_x ,
input wire [11:0] pic_y ,
input [23:0] rgb_data_src ,//原图数据
input [23:0] rgb_data_gray ,//灰度数据
input [23:0] rgb_data_binary ,//二值化数据
input rgb_de_src ,//使能信号
input rgb_de_gray ,//使能信号
input rgb_de_binary ,//使能信号
output reg [23:0] rgb_data_aim //最终输出视频信号
);
parameter block1=256;
parameter block2=512;
parameter block3=768;
parameter SIZE=256;
always @(posedge pclk)
if(!rst_n)
rgb_data_aim<=0;
else if(pic_x > 0 && pic_x <=256 && pic_y >= 0 && pic_y < 256)//使能信号
rgb_data_aim<=rgb_data_src;//原图数据
else if(pic_x > 256 && pic_x <=512 && pic_y >= 0 && pic_y < 256)//使能信号
rgb_data_aim<=rgb_data_gray;//灰度数据
else if(pic_x > 512 && pic_x <=768 && pic_y >= 0 && pic_y < 256)//使能信号
rgb_data_aim<=rgb_data_binary;//二值化数据
else
rgb_data_aim<=24'h00ffff;
//assign rgb_data = (pic_x > image_x && pic_x <=image_x + SIZE && pic_y >= image_y && pic_y < image_y + SIZE) ? data_out : 24'h0;
endmodule
这里的原图数据已经接入了,然后还差灰度和二值化的数据,所以接下来就是数据处理的模块。
灰度处理
这里是直接用的算法,网上有很多的算法,可以自己挑选一个喜欢的。
先进行一个取值,取出我们的RGB数据
wire [7:0] red;
wire [7:0] blue;
wire [7:0] green;
assign blue =rgb_data[7:0];
assign green =rgb_data[15:8];
assign red=rgb_data[23:16];
然后进行一个乘法处理,然后相加,最后移位来代替一个除法
always @(posedge pclk) begin
if(!rst_n) begin
red_r <= 0;
green_r <= 0;
blue_r <= 0;
end else begin
red_r <= red * 8'd77;
green_r <= green * 8'd150;
blue_r <= blue * 8'd29;
end
end
// 第二级加法寄存器
reg [15:0] gray_r1;
always @(posedge pclk) begin
if(!rst_n)
gray_r1 <= 0;
else
gray_r1 <= red_r + green_r + blue_r;
end
// 第三级移位寄存器
reg [7:0] gray_r2;
always @(posedge pclk) begin
if(!rst_n)
gray_r2 <= 0;
else
gray_r2 <= gray_r1>>8;
end
最后将这个gray_r2复制三遍,拼在一起组成一个24为的数据即可。
assign rgb_gray={gray_r2,gray_r2,gray_r2};//gray_r2,gray_r2,
灰度到这里就处理结束了
二值化处理
二值化这个就太简单了,刚刚不是完成了图片的灰度化处理吗,直接判断这个数值的大小,我们可以界定一个界限,当这个值超过一定数时就给黑色,当小于等于时就给白色。
assign rgb_data = (rgb_gary>127)?24'hffffff:24'h0;
然后是顶层模块
module TOP(
input wire sys_clk ,
input wire sys_rst_n ,
output wire hdmi_clk_p ,
output wire hdmi_clk_n ,
output wire hdmi_red_p ,
output wire hdmi_red_n ,
output wire hdmi_green_p,
output wire hdmi_green_n,
output wire hdmi_blue_p ,
output wire hdmi_blue_n ,
output wire HDMI_EN
);
assign HDMI_EN = 1;
wire [11:0] pic_x ;
wire [11:0] pic_y ;
wire de;
wire vga_clk;
wire locked;
wire rst;
wire [23:0] rgb_data;
wire [23:0] rgb_gray;
wire [23:0] rgb_binary;
wire [23:0] rgb;
wire [23:0] rgb_data_aim;
wire vsync;
wire hsync;
wire vga_clk_5x;
wire fps_flag;
assign rst = locked & sys_rst_n;
hdmi_ctrl hdmi_ctrl_inst(
.sys_clk (vga_clk ),
.sys_clk_5x (vga_clk_5x ),
.sys_rst_n (rst ),
.de (de ),
.hsync (hsync ),
.vsync (vsync ),
.rgb_blue (rgb[7:0]), //输入的8bitrgb数据
.rgb_green (rgb[15:8]), //输入的8bitrgb数据
.rgb_red (rgb[23:16]), //输入的8bitrgb数据
.hdmi_clk_p (hdmi_clk_p ),
.hdmi_clk_n (hdmi_clk_n ),
.hdmi_red_p (hdmi_red_p ),
.hdmi_red_n (hdmi_red_n ),
.hdmi_green_p(hdmi_green_p),
.hdmi_green_n(hdmi_green_n),
.hdmi_blue_p (hdmi_blue_p ),
.hdmi_blue_n (hdmi_blue_n )
);
vga_ctrl vga_ctrl_u1(
.pclk (vga_clk ),
.rst_n (rst ),
.rgb_data(rgb_data_aim),//rgb_data,最终输入视频信号
.pic_x (pic_x ),
.pic_y (pic_y ),
.hsync (hsync ),
.vsync (vsync ),
.de (de ),
.fps_flag(fps_flag),
.rgb (rgb )//输出rgb数据
);
总图输出
pic_ctrl pic_ctrl(
. pclk ( vga_clk ) ,
. rst_n ( rst ) ,
. pic_x ( pic_x ) ,
. pic_y ( pic_y ) ,
. rgb_data_src ( rgb_data ) ,//原图数据
. rgb_data_gray ( rgb_gray ) ,//灰度数据
. rgb_data_binary( rgb_binary ) ,//二值化数据
. rgb_de_src ( rgb_de_src ) ,//使能信号,悬空未使用
. rgb_de_gray ( rgb_de_gray ) ,//使能信号,悬空未使用
. rgb_de_binary ( rgb_de_binary ) ,//使能信号,悬空未使用
. rgb_data_aim ( rgb_data_aim ) //最终输出视频信号
);
///二值化处理 ///
binaryimage binary(
. pclk (vga_clk ) ,//pclk时钟
. rst_n (rst ) , //复位
. rgb_gray (rgb_gray ) ,//输入灰度化数据
. rgb_binary (rgb_binary )//输出二值化
);
///灰度化处理 ///
rgbgray rgb1(
. pclk ( vga_clk ) ,//pclk时钟
. rst_n ( rst ) , //复位
. iValid ( de ) ,//输入有效信号,这里悬空,没使用
. rgb_data ( rgb_data ) ,//ram输入的rgb数据
. oValid ( oValid ) ,//输出有效信号,这里悬空,没使用
. rgb_gray ( rgb_gray ) ///输入的24位灰度数据
);
//原图数据输出 /
vga_pic vga_pic_u1(
.pclk (vga_clk ),//pclk时钟
.rst_n (rst ), //复位
.pic_x (pic_x ), //像素x
.pic_y (pic_y ), //像素y
.fps_flag(fps_flag), //帧结束信号
.rgb_data(rgb_data) //原图数据 256*256
);
clk_wiz_0 clk_wiz_0_inst
(
// Clock out ports
.clk_out1(vga_clk), // output clk_out1
// Status and control signals
.clk_out2(vga_clk_5x),
.resetn(sys_rst_n), // input resetn
.locked(locked), // output locked
// Clock in ports
.clk_in1(sys_clk));
endmodule
结束。