硬件平台
正点原子达芬奇Xilinx A7
软件版本
vivado 2018.3
上板效果
程序结构
时钟生成模块clk_wiz
产生74.25MHz和371.25MHz(74.25 x 5) OSERDESE2使用DDR模式,显示分辨率为1280 x 720
需要注意的是使用PLL IP核时,先产生低频的时钟74.25(实际74.219),再设置5倍频的371.25;
先设置高频时钟,导致低频设置的误差变大(实际73.713),最终显示会闪屏(推测是这个原因)。
HDMI数据生成模块
根据之前写的RGB LCD彩条显示修改而来,之前是RGB565的格式,HDMI需要RGB888的格式数据
module lcd_rgb_data #(
parameter H_VALID = 11'd1280 ,
parameter V_VALID = 11'd720
)(
input sys_clk ,
input sys_rst_n ,
input [10:0] lcd_x ,
input [10:0] lcd_y ,
input rgb_data_req,
output reg [23:0] rgb_data
);
localparam RED = 24'hFF0000, //红色
ORANGE = 24'hFF8000, //橙色
YELLOW = 24'hFFFF00, //黄色
GREEN = 24'h00FF00, //绿色
CYAN = 24'h00FFFF, //青色
BLUE = 24'h0000FF, //蓝色
PURPPLE = 24'h8000FF, //紫色
BLACK = 24'h000000, //黑色
WHITE = 24'hFFFFFF, //白色
GRAY = 24'hC0C0C0; //灰色
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
rgb_data <= BLACK;
else if(lcd_x >= 11'd0 && (lcd_x < (H_VALID/10)*1))
rgb_data <= RED;
else if(lcd_x >= ((H_VALID/10)*1) && (lcd_x < (H_VALID/10)*2))
rgb_data <= ORANGE;
else if(lcd_x >= ((H_VALID/10)*2) && (lcd_x < (H_VALID/10)*3))
rgb_data <= YELLOW;
else if(lcd_x >= ((H_VALID/10)*3) && (lcd_x < (H_VALID/10)*4))
rgb_data <= GREEN;
else if(lcd_x >= ((H_VALID/10)*4) && (lcd_x < (H_VALID/10)*5))
rgb_data <= CYAN;
else if(lcd_x >= ((H_VALID/10)*5) && (lcd_x < (H_VALID/10)*6))
rgb_data <= BLUE;
else if(lcd_x >= ((H_VALID/10)*6) && (lcd_x < (H_VALID/10)*7))
rgb_data <= PURPPLE;
else if(lcd_x >= ((H_VALID/10)*7) && (lcd_x < (H_VALID/10)*8))
rgb_data <= BLACK;
else if(lcd_x >= ((H_VALID/10)*8) && (lcd_x < (H_VALID/10)*9))
rgb_data <= WHITE;
else if(lcd_x >= ((H_VALID/10)*9) && (lcd_x < (H_VALID/10)*10))
rgb_data <= GRAY;
else
rgb_data <= BLACK;
endmodule
HDMI信号控制模块
该模块将RGB数据、行同步信号、场同步信号、DE数据有效信号设计时序,然后传输给HDMI驱动模块显示。该部分与RGB LCD显示彩条的原理一样的,同样只是修改了部分的参数得到,其中的lcd_clk和bl(LCD背光控制)没有使用。
module lcd_rgb_ctrl #(
/* 默认显示器参数1280*720 */
parameter H_SYNC = 11'd40 , /* 同步 */
parameter H_BACK = 11'd220 , /* 后沿 */
parameter H_VALID = 11'd1280 , /* 有效数据 */
parameter H_FRONT = 11'd110 , /* 前沿 */
parameter H_TOTAL = 11'd1650 , /* 行周期前四项之和 */
parameter V_SYNC = 11'd5 ,
parameter V_BACK = 11'd20 ,
parameter V_VALID = 11'd720 ,
parameter V_FRONT = 11'd5 ,
parameter V_TOTAL = 11'd750
)(
input sys_clk ,
input sys_rst_n ,
input [23:0] rgb_data ,
output [10:0] lcd_x , /* 回传当前显示位置,获取RGB数据 */
output [10:0] lcd_y ,
output rgb_data_req , /* 数据请求信号 */
output [7:0] rgb_red ,
output [7:0] rgb_green ,
output [7:0] rgb_blue ,
output hsync ,
output vsync ,
output de , /* DE同步信号 */
output lcd_clk ,
output bl /* 背光 */
);
reg [10:0] cnt_h,cnt_v ; /* 行、场计数器 */
wire display_valid ;
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
cnt_h <= 11'd0;
else if(cnt_h == H_TOTAL - 1'd1) /* 达到行周期值 */
cnt_h <= 11'd0;
else
cnt_h <= cnt_h + 1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
cnt_v <= 11'd0;
else if((cnt_v == V_TOTAL - 1'd1) && (cnt_h == H_TOTAL - 1'd1)) /* 达到场周期值,且行周期也达到 */
cnt_v <= 11'd0;
else if(cnt_h == H_TOTAL - 1'd1) /* 行周期结束,场计数加一 */
cnt_v <= cnt_v + 1'b1;
assign hsync = (cnt_h <= H_SYNC - 1)? 1'b0 : 1'b1; /* 同步阶段为0,其余三个阶段为1 */
assign vsync = (cnt_v <= V_SYNC - 1)? 1'b0 : 1'b1;
/* 定义有效显示的范围标志800*480以内 */
assign display_valid = ((cnt_h >= H_SYNC + H_BACK) && (cnt_h < H_SYNC + H_BACK + H_VALID)
&& (cnt_v >= V_SYNC + V_BACK ) && (cnt_v < V_SYNC + V_BACK + V_VALID))
? 1'b1 : 1'b0;
/* 数据请求需要比显示有效区域提前一个周期 */
assign rgb_data_req = ((cnt_h >= H_SYNC + H_BACK - 1) && (cnt_h < H_SYNC + H_BACK + H_VALID - 1)
&& (cnt_v >= V_SYNC + V_BACK - 1) && (cnt_v < V_SYNC + V_BACK + V_VALID - 1))
? 1'b1 : 1'b0;
/* 将传出的坐标信息控制在有效的范围内800*480 */
assign lcd_x = (rgb_data_req == 1'b1)? (cnt_h - (H_SYNC + H_BACK - 1'b1)) : 11'd0;
assign lcd_y = (rgb_data_req == 1'b1)? (cnt_v - (V_SYNC + V_BACK - 1'b1)) : 11'd0;
assign rgb_red = (display_valid == 1'b1)? rgb_data[23:16] : 8'd0;
assign rgb_green = (display_valid == 1'b1)? rgb_data[15:8] : 8'd0;
assign rgb_blue = (display_valid == 1'b1)? rgb_data[7:0] : 8'd0;
assign lcd_clk = sys_clk;
assign de = display_valid;
assign bl = sys_rst_n; /* 复位完成之后点亮LCD背光 */
endmodule
HDMI驱动模块
此模块需要将收到的RGB 24bit的数据与行同步信号、场同步信号划分传输
HDMI数据通道 | 传输数据种类 |
---|---|
D0 | RGB_B、HSYNC、VSYNC |
D1 | RGB_G |
D2 | RGB_R |
在对HDMI差分引脚分配时,需要格外注意,数据是否分配到了对应的HDMI通道上了。
HDMI代码实现步骤:
①TMDS编码RGB、HSYNC、VSYNC信号
②并转串10:1,使用OSERDESE2原语,级联方式
③OBUFDS原语,单端转差分输出
OSERDESE2原语使用
`timescale 1ns / 1ps
/*
并串转换10-->1,调用Xilinx原语
串行时钟的频率需要为并行时钟的5倍,使用的是DDR双数据率处理
*/
module deserializer(
input serial_clk , /* 串行时钟输入 */
input parallel_clk , /* 并行时钟输入 */
input sys_rst ,
input [9:0] parallel_data , /* 并行数据输入 */
output serial_data /* 串行数据输出 */
);
wire shift1,shift2;
// OSERDESE2 : In order to incorporate this function into the design,
// Verilog : the following instance declaration needs to be placed
// instance : in the body of the design code. The instance name
// declaration : (OSERDESE2_inst) and/or the port declarations within the
// code : parenthesis may be changed to properly reference and
// : connect this function to the design. All inputs
// : and outputs must be connected.
// <-----Cut code below this line---->
// OSERDESE2: Output SERial/DESerializer with bitslip
// Artix-7
// Xilinx HDL Language Template, version 2018.3
OSERDESE2 #(
.DATA_RATE_OQ("DDR"), // DDR, SDR
.DATA_RATE_TQ("SDR"), // DDR, BUF, SDR
.DATA_WIDTH(10), // Parallel data width (2-8,10,14)
.INIT_OQ(1'b0), // Initial value of OQ output (1'b0,1'b1)
.INIT_TQ(1'b0), // Initial value of TQ output (1'b0,1'b1)
.SERDES_MODE("MASTER"), // MASTER, SLAVE
.SRVAL_OQ(1'b0), // OQ output value when SR is used (1'b0,1'b1)
.SRVAL_TQ(1'b0), // TQ output value when SR is used (1'b0,1'b1)
.TBYTE_CTL("FALSE"), // Enable tristate byte operation (FALSE, TRUE)
.TBYTE_SRC("FALSE"), // Tristate byte source (FALSE, TRUE)
.TRISTATE_WIDTH(1) // 3-state converter width (1,4)
)
OSERDESE2_Master (
.OFB(), // 1-bit output: Feedback path for data
.OQ(serial_data), // 1-bit output: Data path output
// SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
.SHIFTOUT1(),
.SHIFTOUT2(),
.TBYTEOUT(), // 1-bit output: Byte group tristate
.TFB(), // 1-bit output: 3-state control
.TQ(), // 1-bit output: 3-state control
.CLK(serial_clk), // 1-bit input: High speed clock
.CLKDIV(parallel_clk), // 1-bit input: Divided clock
// D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
.D1(parallel_data[0]),
.D2(parallel_data[1]),
.D3(parallel_data[2]),
.D4(parallel_data[3]),
.D5(parallel_data[4]),
.D6(parallel_data[5]),
.D7(parallel_data[6]),
.D8(parallel_data[7]),
.OCE(1'b1), // 1-bit input: Output data clock enable
.RST(sys_rst), // 1-bit input: Reset
// SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
.SHIFTIN1(shift1), /* 连接Slave的扩展输出 */
.SHIFTIN2(shift2),
// T1 - T4: 1-bit (each) input: Parallel 3-state inputs
.T1(1'b0),
.T2(1'b0),
.T3(1'b0),
.T4(1'b0),
.TBYTEIN(1'b0), // 1-bit input: Byte group tristate
.TCE(1'b0) // 1-bit input: 3-state clock enable
);
OSERDESE2 #(
.DATA_RATE_OQ("DDR"), // DDR, SDR
.DATA_RATE_TQ("SDR"), // DDR, BUF, SDR
.DATA_WIDTH(10), // Parallel data width (2-8,10,14)
.INIT_OQ(1'b0), // Initial value of OQ output (1'b0,1'b1)
.INIT_TQ(1'b0), // Initial value of TQ output (1'b0,1'b1)
.SERDES_MODE("SLAVE"), // MASTER, SLAVE
.SRVAL_OQ(1'b0), // OQ output value when SR is used (1'b0,1'b1)
.SRVAL_TQ(1'b0), // TQ output value when SR is used (1'b0,1'b1)
.TBYTE_CTL("FALSE"), // Enable tristate byte operation (FALSE, TRUE)
.TBYTE_SRC("FALSE"), // Tristate byte source (FALSE, TRUE)
.TRISTATE_WIDTH(1) // 3-state converter width (1,4)
)
OSERDESE2_Slave (
.OFB(), // 1-bit output: Feedback path for data
.OQ(), // 1-bit output: Data path output
// SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
.SHIFTOUT1(shift1),
.SHIFTOUT2(shift2),
.TBYTEOUT(), // 1-bit output: Byte group tristate
.TFB(), // 1-bit output: 3-state control
.TQ(), // 1-bit output: 3-state control
.CLK(serial_clk), // 1-bit input: High speed clock
.CLKDIV(parallel_clk), // 1-bit input: Divided clock
// D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
.D1(1'b0),
.D2(1'b0),
.D3(parallel_data[8]),
.D4(parallel_data[9]),
.D5(1'b0),
.D6(1'b0),
.D7(1'b0),
.D8(1'b0),
.OCE(1'b1), // 1-bit input: Output data clock enable
.RST(sys_rst), // 1-bit input: Reset
// SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
.SHIFTIN1(),
.SHIFTIN2(),
// T1 - T4: 1-bit (each) input: Parallel 3-state inputs
.T1(1'b0),
.T2(1'b0),
.T3(1'b0),
.T4(1'b0),
.TBYTEIN(1'b0), // 1-bit input: Byte group tristate
.TCE(1'b0) // 1-bit input: 3-state clock enable
);
// End of OSERDESE2_inst instantiation
endmodule
OBUFDS原语使用
OBUFDS #(
.IOSTANDARD("TMDS_33"), // Specify the output I/O standard
.SLEW("SLOW") // Specify the output slew rate
) OBUFDS_inst_r (
.O (hdmi_red_p ), // Diff_p output (connect directly to top-level port)
.OB (hdmi_red_n ), // Diff_n output (connect directly to top-level port)
.I (serial_data_r ) // Buffer input
);
工程链接
链接:https://pan.baidu.com/s/1wNH6RC5WTawiDco1omoEjg
提取码:3hpf