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 模块框图:
reg_config_7511_phy.v 模块由 i2c_master_top, i2c 控制状态机,adv7511 初始化数据, 和 uart 串口调试接口这几个部分组成。
状态机代码:
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显示输出模块:
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 芯片中。
display 接口: 根据VESA 标准, fpga 模块输出标准的VESA信号。 在这个模块中,我们需要输出Hsync,Vsync,active de, 以及输出相关的clk时钟。 在de信号产生之前一个时钟周期, 输出hdmi_rd_en信号,通知fifo,输出相关的视频数据
// 水平扫描参数的设定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信号早一个时钟周期,结束也早一个时钟周期。
// 当输入图像数据有效时,输出该图像数据
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 输出取反。





