Menu Close

图像采集与传输系统(HDMI显示输出)

 

ADV7511 显示输出部分, 由adv7511 配置模块和图像数据显示输出模块组成。

  • adv7511 配置模块(reg_config_7511_phy.v)文件主要是用来配置adv7511芯片寄存器,主要是通过i2c 总线对adv7511 芯片进行配置,在配置完成后, 也可以使用uart方式,继续配置adv7511 或者用来debug adv 7511 芯片。
  • 图像显示输出模块:主要是根据VESA 标准,将图像数据输出到adv7511 芯片上, 这个模块,同时也产生符合VESA 标准的H,V , de ,clk 等信号,这些信号也是输出到adv7511芯片上的。

参考目录:图像采集与传输系统的设计

ADV7511 模块框图:

%title插图%num

reg_config_7511_phy.v 模块由 i2c_master_top, i2c 控制状态机,adv7511 初始化数据, 和 uart 串口调试接口这几个部分组成。

%title插图%num

 

状态机代码:

assign hdmi_reg_done = reg_conf_done_reg ; 
//*********5640 configuration register ****************** 
reg [1:0] i2c_st = 0;
always@(posedge clk_50m ) 
if(~rst_n_50m) 
begin
    i2c_st <= 0 ;
    reg_wr_i2c <= 0 ;
    reg_index <= 0 ;
    reg_conf_done_reg <= 0 ;
    cnt_time_delay <= 0 ; 
end
else 
begin
    if(reg_conf_done_reg == 1'b0)
    begin 
        if( reg_index <= 19 ) 
        begin
            case(i2c_st)
            0: 
            begin
                cnt_time_delay <= 0 ; 
                i2c_data <= {adv_7511_id, 1'b0, reg_data} ; 
                reg_wr_i2c <= 1 ;
                i2c_st <= 1 ;
            end
            1: 
            begin
                if(wr_done_i2c) 
                begin 
                    reg_wr_i2c <= 0 ;
                    i2c_st <= 2 ;
                end
                else ;
            end
            2: 
            begin
                reg_index <= reg_index + 1'b1;
                i2c_st <= 3;
            end
            3: 
            begin 
                reg_index <= reg_index ; 
                cnt_time_delay <= cnt_time_delay + 1 ; 
                if ( cnt_time_delay >= 20'd 250000)
                    i2c_st <= 0 ;
                else i2c_st <= 3 ;
            end 
            endcase
        end
        else 
            reg_conf_done_reg <= 1'b1;
    end
    else 
    begin 
        i2c_st <= 0 ;
        reg_wr_i2c <= 0 ;
        reg_index <= 0 ;
        reg_conf_done_reg <= 1 ; 
    end 
end

 

 

初始化状态机 reset 信号需要同步,这是一个很好的习惯, 虽然不同步,也可以使用,但会给整个系统带来不必要的时序问题。

在状态机初始化时: reg_conf_done_reg信号为零, 表示adv7511没有初始化,i2c_st 状态机,接下来就要对adv7511进行相关的初始化操作。

状态0:读取adv7511寄存器列表中的数据,送给i2c_master 模块。

状态1:等待i2c_master操作完成

状态2:读取adv7511寄存器的列表加一

状态3:等待一段时间, 这个不是i2c_master所必须的, 但是adv7511芯片所需要的,(并不是每个寄存器都需要,但每个寄存器加上delay也是没有问题的)

当19个寄存器全部设置完毕,reg_conf_done_reg = 1, 表示 adv7511 芯片配置过程结束, 这个信号可以用来提示其他模块,adv7511 已经配置完成。

 

ADV7511寄存器列表:

always@(reg_index) 
begin
    case(reg_index)
    0 :reg_data <= 24'h004150 ; 
    1 :reg_data <= 24'h004110 ; 
    2 :reg_data <= 24'h009803 ; 
    3 :reg_data <= 24'h009a03 ; 
    4 :reg_data <= 24'h009c30 ; 
    5 :reg_data <= 24'h009d01 ; 
    6 :reg_data <= 24'h00a2a4 ; 
    7 :reg_data <= 24'h00a3a4 ; 
    8 :reg_data <= 24'h00e0d0 ; 
    9 :reg_data <= 24'h00f900 ; 
    10:reg_data <= 24'h001520 ;
    11:reg_data <= 24'h001630 ; 
    12:reg_data <= 24'h00af02 ; 
    13:reg_data <= 24'h000100 ; 
    14:reg_data <= 24'h000218 ; 
    15:reg_data <= 24'h000300 ; 
    16:reg_data <= 24'h000a03 ; 
    17:reg_data <= 24'h000b6e ; 
    18:reg_data <= 24'h000cbd ; 
    19:reg_data <= 24'h00d6c0 ;

    default:reg_data <= 24'h000000;
    endcase 
end

 

adv7511寄存器列表: 为保持和camera使用相同的i2c_master IP,寄存器使用16bit,实际上adv7511芯片只需要8bit 寄存器地址就可以了。 当前的数据结构时 16bit 寄存器地址, 8bit 数据。 数据的具体内容, 可以查看相关的adv7511 寄存器手册。

 

UART 调试接口:

reg [2:0] uart_st = 0;
always @ (posedge clk_50m)
if(reg_conf_done_reg == 0)
begin
    uart_wr_i2c <= 0 ;
    uart_rd_i2c <= 0 ;
    uart_st <= 0 ;
    uart_sel <= 0 ;
end
else 
begin 
    case ( uart_st )
    0:
    begin 
        txd_start <= 0;
        uart_wr_i2c <= 0;
        uart_rd_i2c <= 0;
        if(rxd_done & (rxd_data == 02))
            uart_st <= 1 ; 
        else 
            uart_st <= 0 ;
    end 
    1:
    begin
        uart_wr_i2c <= 0;
        uart_rd_i2c <= 0;

        if(rxd_done)
        begin
            uart_sel <=1 ;
            cmd_dir <= rxd_data ;
            uart_st <= 2 ;
        end
        else ;
    end
    2:
    begin
        if(rxd_done)
        begin
            i2c_data_r[23:16] <= rxd_data ;
            uart_st <= 3 ;
        end
        else ;
    end
    3:
    begin
        if( rxd_done )
        begin
            i2c_data_r[15:08] <= rxd_data ;
            if(cmd_dir[0]) 
                uart_st <= 6 ;
            else 
                uart_st <= 4 ;
        end
        else ;
    end
    4: // write
    begin
        if(rxd_done)
        begin
            i2c_data_r[07:00] <= rxd_data;
            uart_wr_i2c <= 1;
            uart_st <= 5;
        end
        else ;
    end
    5:
    begin
        if( wr_done_i2c ) 
        begin
            uart_wr_i2c <= 0;
            uart_sel <= 0;
            uart_st <= 0; 
        end
        else ; 
    end
    6: //read
    begin
        uart_rd_i2c <= 1;
        uart_st <= 7;
    end
    7:
    begin
        if( rd_done_i2c )
        begin
            uart_rd_i2c <= 0;
            txd_data <= i2c_read_data ;
            txd_start <= 1 ;
            uart_sel <= 0 ;
            uart_st <= 0 ;
        end
    end
    endcase
end

 

adv7511 的 uart 调试接口: 这个接口的逻辑和camera 的调试接口非常类似,只是在状态0时,接受的数据选择 02, 代表从计算机发来的调试 是指向 adv7511 芯片的(不是ov5640)。其他的状态机与camera 调试接口相同。

uart接口在工程中会经常使用,实现这个接口所需逻辑不多,但给调试带来极大的方便,在工程中会经常使用(经验)。灵活使用,将大大提高工作效率,同时在解决突发bug时,也有好处。

 

display显示输出模块:

%title插图%num

display 模块得到reg_config_7511_phy.v 发送过来的hdmi_reg_done 信号后,模块就可以正常工作了。这个模块会产生VESA 标准, 当前的例子中使用的是640×480@60Hz 的VESA标准, 根据VESA 标准, 一方面通知pic_storage.v 模块,得到相应的图像数据, 另外一个方面,产生h,v 信号, 并将从pic_storage.v 里得到的图像数据,按照VESA 标准输出到adv 7511 芯片中。

%title插图%num

display 接口: 根据VESA 标准, fpga 模块输出标准的VESA信号。 在这个模块中,我们需要输出Hsync,Vsync,active de, 以及输出相关的clk时钟。 在de信号产生之前一个时钟周期, 输出hdmi_rd_en信号,通知fifo,输出相关的视频数据

%title插图%num

// 水平扫描参数的设定640*480 
parameter LinePeriod = 800 ; // 行周期数
parameter H_SyncPulse = 96 ; // 行同步脉冲(Sync a)
parameter H_BackPorch = 48 ; // 显示后沿 (Back porch b)
parameter H_ActivePix = 640 ; // 显示时序段(Display interval c)
parameter H_FrontPorch = 16 ; // 显示前沿 (Front porch d) 
parameter Hde_start = H_SyncPulse + H_BackPorch;
parameter Hde_end = Hde_start + H_ActivePix;

// 垂直扫描参数的设定640*480 VGA 60FPS 25.175

parameter FramePeriod = 525 ; // 列周期数
parameter V_SyncPulse = 2 ; // 列同步脉冲(Sync o)
parameter V_BackPorch = 33 ; // 显示后沿(Back porch p)
parameter V_ActivePix = 480 ; // 显示时序段(Display interval q)
parameter V_FrontPorch = 10 ; // 显示前沿(Front porch r)
parameter Vde_start = V_SyncPulse + V_BackPorch ;
parameter Vde_end = Vde_start + V_ActivePix ; //

x_cnt 计数器代码:

//----------------------------------------------------------------
////////// 水平扫描计数
//----------------------------------------------------------------
always @ (posedge vga_clk)
begin
    if( (vga_rst_n == 1'b0 ) || (~hdmi_reg_done) )
    begin 
        x_cnt <= 10'd1;
    end 
    else 
    begin 
        if(x_cnt == LinePeriod) 
            x_cnt <= 1;
        else 
            x_cnt <= x_cnt + 1;
    end
end

x_cnt: 产生一行所需要的pixel, 也就是说一行需要多少个时钟周期。这个信号也用来作为标尺, 产生h,v,de 等信号。

//----------------------------------------------------------------
////////// 水平扫描信号hsync,hsync_de产生
//----------------------------------------------------------------

// 产生 hsync 信号
always @ (posedge vga_clk)
begin
    if( (vga_rst_n == 1'b0 ) || (~hdmi_reg_done) ) 
        hsync_r <= 1'b0;
    else if(x_cnt == LinePeriod)
        hsync_r <= 1'b1; 
    else 
        if(x_cnt == H_SyncPulse)
            hsync_r <= 1'b0 ;
        else ;
end

// 产生 hsync_de 信号 
always @ (posedge vga_clk)
begin 
    if( (vga_rst_n == 1'b0 ) || (~hdmi_reg_done) )
        hsync_de <= 1'b0;
    else 
    begin
        if(x_cnt == Hde_start)
            hsync_de <= 1'b1; 
        else 
        begin
            if(x_cnt == Hde_end) 
                hsync_de <= 1'b0; 
            else ; 
        end
    end
end

 

产生h 信号: 注意,当前产生的h信号 是正电平的,而VESA 需要的是负电平的。 一般情况下在fpga 内部都是使用正电平,只有到端口输出时:选择负电平。

产生h方向的 de 信号: h方向的信号,和之后v方向的de信号组合, 最终会输出VESA 所需要的video de 信号。 de 信号,就是VESA 标准中的 active video 信号,在很多芯片的定义中,使用de,或者是vde。 de 信号 一般来说都是高电平的,很少有低电平有效的信号。

//----------------------------------------------------------------
////////// 垂直扫描计数
//----------------------------------------------------------------
always @ (posedge vga_clk)
begin
    if( (vga_rst_n == 1'b0 ) || (~hdmi_reg_done) )
        y_cnt <= 10'd1;
    else 
    begin 
        if (x_cnt == LinePeriod)
        begin 
            if ( y_cnt == FramePeriod)
                y_cnt <= 1; 
            else 
                y_cnt <= y_cnt + 1;
        end 
        else 
            y_cnt <= y_cnt;
    end 
end
//----------------------------------------------------------------
////////// 垂直扫描信号vsync, vsync_de产生
//----------------------------------------------------------------

//产生vsync信号
always @ (posedge vga_clk)
begin
    if( (vga_rst_n == 1'b0 ) || (~hdmi_reg_done) ) 
        vsync_r <= 1'b0;
    else 
    begin
        if( (y_cnt == FramePeriod) & (x_cnt == LinePeriod) )
            vsync_r <= 1'b1; 
        else 
        begin
            if( (y_cnt == V_SyncPulse) & (x_cnt == LinePeriod) ) 
                vsync_r <= 1'b0;
            else ;
        end
    end
end

 

场计数器: 当每行的pixel 寄存器结束后, 场计数器加一 场计数器用于产生vde, v 信号 。

v 信号: 当每行的pixel 寄存器结束后, 同时场计数器开始为1,y_cnt == v_syncpulse 后 v信号为0.

//产生vsync_de信号
always @ (posedge vga_clk)
begin
    if( (vga_rst_n == 1'b0 ) || (~hdmi_reg_done) ) 
        vsync_de <= 1'b0; 
    else 
    begin
        if( (y_cnt == Vde_start) & (x_cnt == LinePeriod) ) 
            vsync_de <= 1'b1; 
        else 
        begin
            if( (y_cnt == Vde_end) & (x_cnt == LinePeriod) ) 
                vsync_de <= 1'b0; 
            else ;
        end
    end
end
// HDMI 接口图像显示有效RGB888的图像数据的标识信号 
assign data_en = hsync_de & vsync_de;

 

// HDMI 接口图像显示有效RGB888的图像数据的标识信号

assign data_en = hsync_de & vsync_de;

场方向de 信号: 当每行的pixel 寄存器结束后, 根据VESA标准,产生相应的 场方向de信号。 h方向的de 和 v方向的de 组合,最终输出VESA 标准de

// 产生从 fifo_128_to_16读取图像数据的读使能
always @ (posedge vga_clk)
begin 
    if ( (vga_rst_n == 1'b0 ) || (~hdmi_reg_done) ) 
        hdmi_rd_en <= 0 ; 
    else 
    begin
        if ( vsync_de && (x_cnt == Hde_start - 1) ) 
            hdmi_rd_en <= 1 ;
        else if (vsync_de && (x_cnt == Hde_end - 1) )
            hdmi_rd_en <= 0 ; 
        else ;
    end 
end

 

产生读取fifo的信号: fifo的读取信号比de信号早一个时钟周期,结束也早一个时钟周期。

%title插图%num

// 当输入图像数据有效时,输出该图像数据

assign vga_r = data_valid ? {fifo_hdmi_dout [15:11],3'b000 } : 8'h00 ;
assign vga_g = data_valid ? {fifo_hdmi_dout [10:5], 2'b00 } : 8'h00 ;
assign vga_b = data_valid ? {fifo_hdmi_dout [4:0], 3'b000 } : 8'b00 ;


// HDMI 接口图像显示有效RGB888的图像数据的标识信号 
assign data_en = hsync_de & vsync_de;

assign vga_hs = ~hsync_r; // negative
assign vga_vs = ~vsync_r; // negative

assign hdmi_end = vsync_r;

 

最终输出的所有信号: fifo输出的图像为RGB565, hdmi接口为RGB888, 所以rgb 信号需要补齐到8bit de 为hde和vde 的组合 由于vesa 需要h,v 为低电平,所以最终fpga 输出取反。

 

Posted in FPGA, FPGA, Verilog, Verilog, 教材与教案, 文章

发表评论

相关链接