目录
一、设计目标
设计目标:使用 FPGA 开发板驱动 HDMI 显示器显示五色等宽彩条(从左到右,依次为白色、黑色、红色、绿色、蓝色), HDMI 显示模式为 1024*768@60。
二、架构设计
模块设计分为俩种情况,一为FPGA 开发板有HDMI芯片的情况,需要配置HDMI芯片;二为FPGA 开发板无HDMI芯片的情况,需要写一个HDMI编码模块。
1.有HDMI芯片
2.无HDMI芯片
三、参考代码
以有HDMI芯片为例(无HDMI芯片的架构需要把 8bit 数据通过最小转换编码为 10bit 数据,并转换为差分信号),以下为参考代码:
1、顶层top
module top(
input wire sys_clk , //输入工作时钟,频率50MHz
//配置 HDMI芯片
output wire ddc_scl ,
inout wire ddc_sda ,
output wire hdmi_out_clk ,
output wire hdmi_out_rst_n ,
//vga
output wire hdmi_out_hsync , //输出行同步信号
output wire hdmi_out_vsync , //输出场同步信号
output wire [23:0] hdmi_out_rgb , //输出像素信息
output wire hdmi_out_de
);
wire hdmi_clk;
wire hdmi_clkx5;
wire hdmi_reset;
wire o_vga_hsync;
wire o_vga_vsync;
wire o_vga_de;
wire [23:0] o_vga_rgb888;
wire o_vga_frame_req;
wire o_vga_line_req;
wire [15:0] o_vga_rgb565;
assign hdmi_out_rst_n = ~hdmi_reset;
assign hdmi_out_clk = hdmi_clk;
clock_and_reset clock_and_reset
(
.i_clk_50m (sys_clk),
.o_hdmi_clk (hdmi_clk), //65Mhz
.o_hdmi_clkx5 (hdmi_clkx5),//用于无HDMI芯片时
.o_hdmi_reset (hdmi_reset)
);
colorbar_gen #(
.VIDEO_ACTIVE_WIDTH(1024),
.VIDEO_ACTIVE_HIGH (768)
) colorbar_gen (
.vga_clk (hdmi_clk),
.reset (hdmi_reset),
.i_vga_frame_req (o_vga_frame_req),
.i_vga_line_req (o_vga_line_req) ,
.o_vga_rgb565 (o_vga_rgb565)
);
vga_driver vga_driver
(
.vga_clk (hdmi_clk) ,
.reset (hdmi_reset) ,
.o_vga_frame_req (o_vga_frame_req),
.o_vga_line_req (o_vga_line_req) ,
.i_vga_rgb565 (o_vga_rgb565) ,
.o_vga_hsync (hdmi_out_hsync) ,
.o_vga_vsync (hdmi_out_vsync) ,
.o_vga_de (hdmi_out_de) ,
.o_vga_rgb888 (hdmi_out_rgb)
);
hdmi_i2c hdmi_i2c(
.sys_clk (hdmi_clk),
.sys_rst_n (~hdmi_reset),
.cfg_done (),
.sccb_scl (ddc_scl),
.sccb_sda (ddc_sda)
);
endmodule
2. clock_and_reset
`timescale 1ns / 1ps
module clock_and_reset(
input i_clk_50m ,
output o_hdmi_clk ,
output o_hdmi_clkx5,
output reg o_hdmi_reset
);
wire locked;
reg [11:0] cnt;
always @(posedge o_hdmi_clk or negedge locked) begin
if (~locked) begin
cnt <= 12'd0;
o_hdmi_reset <= 1'b1;
end
else if (cnt <= 12'd600) begin
cnt <= cnt + 1'b1;
o_hdmi_reset <= 1'b1;
end
else begin
cnt <= cnt;
o_hdmi_reset <= 1'b0;
end
end
//调用时钟ip核
clk_wiz_0 clk_wiz_0
(
.clk_out1(o_hdmi_clk), // 65M
.clk_out2(o_hdmi_clkx5), // 325M
.reset (0),
.locked (locked),
.clk_in1 (i_clk_50m)
); // input clk_in1
endmodule
3. colorbar_gen
module colorbar_gen #(
parameter VIDEO_ACTIVE_WIDTH = 1024,
parameter VIDEO_ACTIVE_HIGH = 768
)(
input vga_clk,
input reset,
input i_vga_frame_req,
input i_vga_line_req ,
output reg [15:0] o_vga_rgb565 //rgb565
);
localparam WHITE = 16'b11111_111111_11111; //RGB565 白色
localparam BLACK = 16'b00000_000000_00000; //RGB565 黑色
localparam RED = 16'b11111_000011_00000; //RGB565 红色
localparam GREEN = 16'b00000_111111_00000; //RGB565 绿色
localparam BLUE = 16'b00000_000000_11111; //RGB565 蓝色
reg [15:0] pixel_cnt;
reg [15:0] line_cnt;
always @(posedge vga_clk) begin
if (reset)
pixel_cnt <= 0;
else if (~i_vga_frame_req)
pixel_cnt <= 0;
else if (pixel_cnt == VIDEO_ACTIVE_WIDTH - 1 && i_vga_line_req)
pixel_cnt <= 0;
else if (i_vga_line_req)
pixel_cnt <= pixel_cnt + 1;
else
pixel_cnt <= pixel_cnt;
end
always @(posedge vga_clk ) begin
if (reset)
line_cnt <= 0;
else if (~i_vga_frame_req)
line_cnt <= 0;
else if (pixel_cnt == VIDEO_ACTIVE_WIDTH - 1 && i_vga_line_req && line_cnt == VIDEO_ACTIVE_HIGH - 1)
line_cnt <= 0;
else if (pixel_cnt == VIDEO_ACTIVE_WIDTH - 1 && i_vga_line_req)
line_cnt <= line_cnt + 1;
else
line_cnt <= line_cnt;
end
always @(posedge vga_clk ) begin
if (reset)
o_vga_rgb565 <= 0;
else if ( pixel_cnt >= 0 && pixel_cnt < (VIDEO_ACTIVE_WIDTH/5)*1 )
o_vga_rgb565 <= WHITE;
else if ( pixel_cnt >= (VIDEO_ACTIVE_WIDTH/5)*1 && pixel_cnt < (VIDEO_ACTIVE_WIDTH/5)*2 )
o_vga_rgb565 <= BLACK;
else if ( pixel_cnt >= (VIDEO_ACTIVE_WIDTH/5)*2 && pixel_cnt < (VIDEO_ACTIVE_WIDTH/5)*3 )
o_vga_rgb565 <= RED;
else if ( pixel_cnt >= (VIDEO_ACTIVE_WIDTH/5)*3 && pixel_cnt < (VIDEO_ACTIVE_WIDTH/5)*4 )
o_vga_rgb565 <= GREEN;
else if ( pixel_cnt >= (VIDEO_ACTIVE_WIDTH/5)*4 && pixel_cnt < (VIDEO_ACTIVE_WIDTH/5)*5 )
o_vga_rgb565 <= BLUE;
else
o_vga_rgb565 <= o_vga_rgb565;
end
endmodule
4. vga_driver
`timescale 1ns / 1ps
module vga_driver(
input vga_clk,
input reset,
/*--------------vga驱动端口--------------------*/
output o_vga_hsync, //行同步信号
output o_vga_vsync, //场同步信号
output o_vga_de, //数据使能
output [23:0] o_vga_rgb888, //RGB888颜色数据
/*--------------用户端口--------------------*/
output o_vga_frame_req,
output o_vga_line_req,
input [15:0] i_vga_rgb565
);
/*------------------------------------------*\
1024x768@60hz,clk = 65m
\*------------------------------------------*/
localparam H_SYNC = 11'd136; //行同步
localparam H_BACK = 11'd160; //行显示后沿
localparam H_ACTIVE = 11'd1024; //行有效数据
localparam H_FRONT = 11'd24; //行显示前沿
localparam H_TOTAL = 11'd1344; //行扫描周期
localparam V_SYNC = 11'd6; //场同步
localparam V_BACK = 11'd29; //场显示后沿
localparam V_ACTIVE = 11'd768; //场有效数据
localparam V_FRONT = 11'd3; //场显示前沿
localparam V_TOTAL = 11'd806; //场扫描周期
/*------------------------------------------*\
复位信号/计数器定义
\*------------------------------------------*/
reg reset_sync_d0;
reg reset_sync_d1;
reg reset_sync;
reg [15:0] pixel_cnt;
reg [15:0] line_cnt;
/*------------------------------------------*\
assign
\*------------------------------------------*/
assign o_vga_hsync = pixel_cnt <= H_SYNC -1 ? 1'b1 : 0;
assign o_vga_vsync = line_cnt <= V_SYNC -1 ? 1'b1 : 0;
assign o_vga_de = (pixel_cnt >= H_SYNC + H_BACK) & (pixel_cnt <= H_SYNC + H_BACK + H_ACTIVE-1) & (line_cnt >= V_SYNC + V_BACK) & (line_cnt <= V_SYNC + V_BACK + V_ACTIVE-1);
assign o_vga_rgb888 = o_vga_de ? {i_vga_rgb565[15:11], 3'h0 , i_vga_rgb565[10:5] , 2'h0 , i_vga_rgb565[4:0] , 3'h0 } : 0;
assign o_vga_frame_req = ~o_vga_vsync;
assign o_vga_line_req = (pixel_cnt >= H_SYNC + H_BACK - 1) & (pixel_cnt <= H_SYNC + H_BACK + H_ACTIVE - 2) & (line_cnt >= V_SYNC + V_BACK) & (line_cnt <= V_SYNC + V_BACK + V_ACTIVE-1);
/*------------------------------------------*\
同步复位信号
\*------------------------------------------*/
always @(posedge vga_clk) begin
reset_sync_d0 <= reset;
reset_sync_d1 <= reset_sync_d0;
reset_sync <= reset_sync_d1;
end
/*------------------------------------------*\
计数器设计
\*------------------------------------------*/
always @(posedge vga_clk) begin
if (reset_sync)
pixel_cnt <= 0;
else if (pixel_cnt == H_TOTAL-1)
pixel_cnt <= 0;
else
pixel_cnt <= pixel_cnt + 1;
end
always @(posedge vga_clk) begin
if (reset_sync)
line_cnt <= 0;
else if (line_cnt == V_TOTAL-1 && pixel_cnt == H_TOTAL-1)
line_cnt <= 0;
else if (pixel_cnt == H_TOTAL-1)
line_cnt <= line_cnt + 1;
else
line_cnt <= line_cnt;
end
endmodule
5. hdmi_iic(参考野火的HDMI驱动)
hdmi_i2c
`timescale 1ns / 1ps
module hdmi_i2c(
input wire sys_clk , //系统时钟
input wire sys_rst_n , //复位信号
output wire cfg_done , //寄存器配置完成
output wire sccb_scl , //SCL
inout wire sccb_sda //SDA
);
//parameter define
parameter BIT_CTRL = 1'b0 ; // 字地址位控制参数(16b/8b)
parameter CLK_FREQ = 26'd25_000_000; // i2c_dri模块的驱动时钟频率(CLK_FREQ)
parameter I2C_FREQ = 18'd250_000 ; // I2C的SCL时钟频率
//wire define
wire cfg_end ;
wire cfg_start ;
wire [31:0] cfg_data ;
wire cfg_clk ;
i2c_ctrl
#(
.SYS_CLK_FREQ (CLK_FREQ ), //i2c_ctrl模块系统时钟频率
.SCL_FREQ (I2C_FREQ ) //i2c的SCL时钟频率
)
i2c_ctrl_inst
(
.sys_clk (sys_clk ), //输入系统时钟,50MHz
.sys_rst_n (sys_rst_n ), //输入复位信号,低电平有效
.wr_en (1'b1 ), //输入写使能信号
.rd_en ( ), //输入读使能信号
.i2c_start (cfg_start ), //输入i2c触发信号
.addr_num (BIT_CTRL ), //输入i2c字节地址字节数
.device_addr (cfg_data[31:24]),
.byte_addr (cfg_data[23:8]), //输入i2c字节地址
.wr_data (cfg_data[7:0] ), //输入i2c设备数据
.rd_data ( ), //输出i2c设备读取数据
.i2c_end (cfg_end ), //i2c一次读/写操作完成
.i2c_clk (cfg_clk ), //i2c驱动时钟
.i2c_scl (sccb_scl ), //输出至i2c设备的串行时钟信号scl
.i2c_sda (sccb_sda ) //输出至i2c设备的串行数据信号sda
);
//------------- hdmi_cfg_inst -------------
hdmi_cfg hdmi_cfg_inst(
.sys_clk (cfg_clk ), //系统时钟,由iic模块传入
.sys_rst_n (sys_rst_n ), //系统复位,低有效
.cfg_end (cfg_end ), //单个寄存器配置完成
.cfg_start (cfg_start ), //单个寄存器配置触发信号
.cfg_data (cfg_data ), //ID,REG_ADDR,REG_VAL
.cfg_done (cfg_done ) //寄存器配置完成
);
endmodule
i2c_ctrl
`timescale 1ns/1ns
module i2c_ctrl
#(
parameter SYS_CLK_FREQ = 26'd50_000_000 , //输入系统时钟频率
parameter SCL_FREQ = 18'd250_000 //i2c设备scl时钟频率
)
(
input wire sys_clk , //输入系统时钟,50MHz
input wire sys_rst_n , //输入复位信号,低电平有效
input wire wr_en , //输入写使能信号
input wire rd_en , //输入读使能信号
input wire i2c_start , //输入i2c触发信号
input wire addr_num , //输入i2c字节地址字节数
input wire [7:0] device_addr ,
input wire [15:0] byte_addr , //输入i2c字节地址
input wire [7:0] wr_data , //输入i2c设备数据
output reg i2c_clk , //i2c驱动时钟
output reg i2c_end , //i2c一次读/写操作完成
output reg [7:0] rd_data , //输出i2c设备读取数据
output reg i2c_scl , //输出至i2c设备的串行时钟信号scl
inout wire i2c_sda //输出至i2c设备的串行数据信号sda
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
// parameter define
parameter CNT_CLK_MAX = (SYS_CLK_FREQ/SCL_FREQ) >> 2'd3 ; //cnt_clk计数器计数最大值
parameter CNT_START_MAX = 8'd100; //cnt_start计数器计数最大值
parameter IDLE = 4'd00, //初始状态
START_1 = 4'd01, //开始状态1
SEND_D_ADDR = 4'd02, //设备地址写入状态 + 控制写
ACK_1 = 4'd03, //应答状态1
SEND_B_ADDR_H = 4'd04, //字节地址高八位写入状态
ACK_2 = 4'd05, //应答状态2
SEND_B_ADDR_L = 4'd06, //字节地址低八位写入状态
ACK_3 = 4'd07, //应答状态3
WR_DATA = 4'd08, //写数据状态
ACK_4 = 4'd09, //应答状态4
START_2 = 4'd10, //开始状态2
SEND_RD_ADDR = 4'd11, //设备地址写入状态 + 控制读
ACK_5 = 4'd12, //应答状态5
RD_DATA = 4'd13, //读数据状态
N_ACK = 4'd14, //非应答状态
STOP = 4'd15; //结束状态
// wire define
wire sda_in ; //sda输入数据寄存
wire sda_en ; //sda数据写入使能信号
wire [6:0] device_addr_i;
// reg define
reg [7:0] cnt_clk ; //系统时钟计数器,控制生成clk_i2c时钟信号
reg [3:0] state ; //状态机状态
reg cnt_i2c_clk_en ; //cnt_i2c_clk计数器使能信号
reg [1:0] cnt_i2c_clk ; //clk_i2c时钟计数器,控制生成cnt_bit信号
reg [2:0] cnt_bit ; //sda比特计数器
reg ack ; //应答信号
reg i2c_sda_reg ; //sda数据缓存
reg [7:0] rd_data_reg ; //自i2c设备读出数据
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
assign device_addr_i=device_addr[7:1];
// cnt_clk:系统时钟计数器,控制生成clk_i2c时钟信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_clk <= 8'd0;
else if(cnt_clk == CNT_CLK_MAX - 1'b1)
cnt_clk <= 8'd0;
else
cnt_clk <= cnt_clk + 1'b1;
// i2c_clk:i2c驱动时钟
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
i2c_clk <= 1'b1;
else if(cnt_clk == CNT_CLK_MAX - 1'b1)
i2c_clk <= ~i2c_clk;
// cnt_i2c_clk_en:cnt_i2c_clk计数器使能信号
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_i2c_clk_en <= 1'b0;
else if((state == STOP) && (cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
cnt_i2c_clk_en <= 1'b0;
else if(i2c_start == 1'b1)
cnt_i2c_clk_en <= 1'b1;
// cnt_i2c_clk:i2c_clk时钟计数器,控制生成cnt_bit信号
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_i2c_clk <= 2'd0;
else if(cnt_i2c_clk_en == 1'b1)
cnt_i2c_clk <= cnt_i2c_clk + 1'b1;
// cnt_bit:sda比特计数器
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_bit <= 3'd0;
else if((state == IDLE) || (state == START_1) || (state == START_2)
|| (state == ACK_1) || (state == ACK_2) || (state == ACK_3)
|| (state == ACK_4) || (state == ACK_5) || (state == N_ACK))
cnt_bit <= 3'd0;
else if((cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
cnt_bit <= 3'd0;
else if((cnt_i2c_clk == 2'd3) && (state != IDLE))
cnt_bit <= cnt_bit + 1'b1;
// state:状态机状态跳转
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
state <= IDLE;
else case(state)
IDLE:
if(i2c_start == 1'b1)
state <= START_1;
else
state <= state;
START_1:
if(cnt_i2c_clk == 3)
state <= SEND_D_ADDR;
else
state <= state;
SEND_D_ADDR:
if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
state <= ACK_1;
else
state <= state;
ACK_1:
if((cnt_i2c_clk == 3) && (ack == 1'b0))
begin
if(addr_num == 1'b1)
state <= SEND_B_ADDR_H;
else
state <= SEND_B_ADDR_L;
end
else
state <= state;
SEND_B_ADDR_H:
if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
state <= ACK_2;
else
state <= state;
ACK_2:
if((cnt_i2c_clk == 3) && (ack == 1'b0))
state <= SEND_B_ADDR_L;
else
state <= state;
SEND_B_ADDR_L:
if((cnt_bit == 3'd7) && (cnt_i2c_clk == 3))
state <= ACK_3;
else
state <= state;
ACK_3:
if((cnt_i2c_clk == 3) && (ack == 1'b0))
begin
if(wr_en == 1'b1)
state <= WR_DATA;
else if(rd_en == 1'b1)
state <= START_2;
else
state <= state;
end
else
state <= state;
WR_DATA:
if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
state <= ACK_4;
else
state <= state;
ACK_4:
if((cnt_i2c_clk == 3) && (ack == 1'b0))
state <= STOP;
else
state <= state;
START_2:
if(cnt_i2c_clk == 3)
state <= SEND_RD_ADDR;
else
state <= state;
SEND_RD_ADDR:
if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
state <= ACK_5;
else
state <= state;
ACK_5:
if((cnt_i2c_clk == 3) && (ack == 1'b0))
state <= RD_DATA;
else
state <= state;
RD_DATA:
if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
state <= N_ACK;
else
state <= state;
N_ACK:
if(cnt_i2c_clk == 3)
state <= STOP;
else
state <= state;
STOP:
if((cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
state <= IDLE;
else
state <= state;
default: state <= IDLE;
endcase
// ack:应答信号
always@(*)
case (state)
IDLE,START_1,SEND_D_ADDR,SEND_B_ADDR_H,SEND_B_ADDR_L,
WR_DATA,START_2,SEND_RD_ADDR,RD_DATA,N_ACK:
ack <= 1'b1;
ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
if(cnt_i2c_clk == 2'd0)
ack <= sda_in;//1'b0;//
else
ack <= ack;
default: ack <= 1'b1;
endcase
// i2c_scl:输出至i2c设备的串行时钟信号scl
always@(*)
case (state)
IDLE:
i2c_scl <= 1'b1;
START_1:
if(cnt_i2c_clk == 2'd3)
i2c_scl <= 1'b0;
else
i2c_scl <= 1'b1;
SEND_D_ADDR,ACK_1,SEND_B_ADDR_H,ACK_2,SEND_B_ADDR_L,
ACK_3,WR_DATA,ACK_4,START_2,SEND_RD_ADDR,ACK_5,RD_DATA,N_ACK:
if((cnt_i2c_clk == 2'd1) || (cnt_i2c_clk == 2'd2))
i2c_scl <= 1'b1;
else
i2c_scl <= 1'b0;
STOP:
if((cnt_bit == 3'd0) &&(cnt_i2c_clk == 2'd0))
i2c_scl <= 1'b0;
else
i2c_scl <= 1'b1;
default: i2c_scl <= 1'b1;
endcase
// i2c_sda_reg:sda数据缓存
always@(*)
case (state)
IDLE:
begin
i2c_sda_reg <= 1'b1;
rd_data_reg <= 8'd0;
end
START_1:
if(cnt_i2c_clk <= 2'd0)
i2c_sda_reg <= 1'b1;
else
i2c_sda_reg <= 1'b0;
SEND_D_ADDR:
if(cnt_bit <= 3'd6)
i2c_sda_reg <= device_addr_i[6 - cnt_bit];
else
i2c_sda_reg <= 1'b0;
ACK_1:
i2c_sda_reg <= 1'b1;
SEND_B_ADDR_H:
i2c_sda_reg <= byte_addr[15 - cnt_bit];
ACK_2:
i2c_sda_reg <= 1'b1;
SEND_B_ADDR_L:
i2c_sda_reg <= byte_addr[7 - cnt_bit];
ACK_3:
i2c_sda_reg <= 1'b1;
WR_DATA:
i2c_sda_reg <= wr_data[7 - cnt_bit];
ACK_4:
i2c_sda_reg <= 1'b1;
START_2:
if(cnt_i2c_clk <= 2'd1)
i2c_sda_reg <= 1'b1;
else
i2c_sda_reg <= 1'b0;
SEND_RD_ADDR:
if(cnt_bit <= 3'd6)
i2c_sda_reg <= device_addr_i[6 - cnt_bit];
else
i2c_sda_reg <= 1'b1;
ACK_5:
i2c_sda_reg <= 1'b1;
RD_DATA:
if(cnt_i2c_clk == 2'd2)
rd_data_reg[7 - cnt_bit] <= sda_in;
else
rd_data_reg <= rd_data_reg;
N_ACK:
i2c_sda_reg <= 1'b1;
STOP:
if((cnt_bit == 3'd0) && (cnt_i2c_clk < 2'd3))
i2c_sda_reg <= 1'b0;
else
i2c_sda_reg <= 1'b1;
default:
begin
i2c_sda_reg <= 1'b1;
rd_data_reg <= rd_data_reg;
end
endcase
// rd_data:自i2c设备读出数据
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_data <= 8'd0;
else if((state == RD_DATA) && (cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
rd_data <= rd_data_reg;
// i2c_end:一次读/写结束信号
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
i2c_end <= 1'b0;
else if((state == STOP) && (cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
i2c_end <= 1'b1;
else
i2c_end <= 1'b0;
// sda_in:sda输入数据寄存
assign sda_in = i2c_sda;
// sda_en:sda数据写入使能信号
assign sda_en = ((state == RD_DATA) || (state == ACK_1) || (state == ACK_2)
|| (state == ACK_3) || (state == ACK_4) || (state == ACK_5))
? 1'b0 : 1'b1;
// i2c_sda:输出至i2c设备的串行数据信号sda
assign i2c_sda = (sda_en == 1'b1) ? i2c_sda_reg : 1'bz;
endmodule
hdmi_cfg
`timescale 1ns/1ns
module hdmi_cfg
(
input wire sys_clk , //系统时钟,由iic模块传入
input wire sys_rst_n , //系统复位,低有效
input wire cfg_end , //单个寄存器配置完成
output reg cfg_start , //单个寄存器配置触发信号
output wire [31:0] cfg_data , //ID,REG_ADDR,REG_VAL
output reg cfg_done //寄存器配置完成
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter REG_NUM = 7'd6 ; //总共需要配置的寄存器个数
parameter CNT_WAIT_MAX = 10'd1023; //寄存器配置等待计数最大值
//wire define
wire [31:0] cfg_data_reg[REG_NUM-1:0] ; //寄存器配置数据暂存
//reg define
reg [9:0] cnt_wait ; //寄存器配置等待计数器
reg [6:0] reg_num ; //配置寄存器个数
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//cnt_wait:寄存器配置等待计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_wait <= 15'd0;
else if(cnt_wait < CNT_WAIT_MAX)
cnt_wait <= cnt_wait + 1'b1;
//reg_num:配置寄存器个数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
reg_num <= 7'd0;
else if(cfg_end == 1'b1)
reg_num <= reg_num + 1'b1;
//cfg_start:单个寄存器配置触发信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cfg_start <= 1'b0;
else if(cnt_wait == (CNT_WAIT_MAX - 1'b1))
cfg_start <= 1'b1;
else if((cfg_end == 1'b1) && (reg_num < REG_NUM))
cfg_start <= 1'b1;
else
cfg_start <= 1'b0;
//cfg_done:寄存器配置完成
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cfg_done <= 1'b0;
else if((reg_num == REG_NUM) && (cfg_end == 1'b1))
cfg_done <= 1'b1;
//cfg_data:ID,REG_ADDR,REG_VAL
assign cfg_data = (cfg_done == 1'b1) ? 32'h0000 : cfg_data_reg[reg_num];
//----------------------------------------------------
//cfg_data_reg:寄存器配置数据暂存 ID REG_ADDR REG_VAL
//assign cfg_data_reg[00] = {8'h12, 8'h80};
assign cfg_data_reg[0] ={8'h72,16'h08,8'h35};
assign cfg_data_reg[1] ={8'h7a,16'h2f,8'h00};
assign cfg_data_reg[2] ={8'h60,16'h05,8'h10};
assign cfg_data_reg[3] ={8'h60,16'h08,8'h05};
assign cfg_data_reg[4] ={8'h60,16'h09,8'h01};
assign cfg_data_reg[5] ={8'h60,16'h05,8'h04};
//-------------------------------------------------------
endmodule
四、结果分析
采用vivado实现,设计参考框图如下
连接好相应器件,下载程序,如下可见,设计实现了预定的功能。