基于FPGA的HDMI彩条显示(2)-实战篇

目录

一、设计目标

二、架构设计

1.有HDMI芯片

2.无HDMI芯片

三、参考代码

1、顶层top

2. clock_and_reset

3. colorbar_gen

4. vga_driver

5. hdmi_iic(参考野火的HDMI驱动)

四、结果分析


一、设计目标

设计目标:使用 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实现,设计参考框图如下

连接好相应器件,下载程序,如下可见,设计实现了预定的功能。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱看小黄鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值