以640*480@60Hz分辨率显示(意思最大可以显示640*480大小的图片),首先通过fpga的VGA显示图像包括四个部分:分频模块,vga驱动模块,显示模块,顶层模块。
1.分频模块,用的是xilinx spartan_6 的板子,输出晶振50M;640*480@60Hz的时钟是25MHz(800*525*60)。就简单计数翻转分频,效果如图:
2.VGA驱动模块
显示顺序:从屏幕上到下,从左到右。
时序图:行时序分为5个进程(a.行地址复位b.行地址复位完成 准备录入数据 c.数据显示d.准备复位行地址)
场时序分为四个进程:(a.场地址复位b.场地址复位完成 准备录入数据 c.数据显示d.准备复位场地址)
!!!!对于驱动部分检查,首先看时序图:一个场信号的翻转包含多个行信号(一场中扫描多行)
`timescale 1ns / 1ps
`define H_FRONT 11'd16
`define H_SYNC 11'd96
`define H_BACK 11'd48
`define H_DIP 11'd640
`define H_TOTAL 11'd800
`define V_FRONT 11'd10
`define V_SYNC 11'd2
`define V_BACK 11'd33
`define V_DIP 11'd480
`define V_TOTAL 11'd525
//////////////////////////////////////////////////////////////////////////////////
//640*480@60hz
//
//////////////////////////////////////////////////////////////////////////////////
module driver(
clk,
reset,
lcd_hs,
lcd_vs,
lcd_en,
lcd_xpos,
lcd_ypos
);
input clk,reset;//VGA_clk
output lcd_hs,lcd_vs,lcd_en;
output[10:0] lcd_xpos,lcd_ypos;
wire lcd_request;
reg[11:0] h_cnt;//行扫描单位计数
reg[11:0] v_cnt;//列扫描单位计数
////////////////////////////////
//行扫描单位计数
always@(posedge clk or negedge reset)
begin
if(!reset)begin h_cnt<=11'd0; end
else
begin
if(h_cnt<`H_TOTAL-1'b1) begin h_cnt<=h_cnt+1'b1;end
else h_cnt<=11'd0;
end
end
assign lcd_hs=(h_cnt<=`H_SYNC-1'b1)?1'b0:1'b1; //行同步信号
////////////////////////////////
//列扫描单位计数
always@(posedge clk or negedge reset)
begin
if(!reset)begin v_cnt<=11'd0; end
else if(h_cnt==`H_TOTAL-1'b1)//横扫完
begin
if(v_cnt<`V_TOTAL-1'b1)begin v_cnt<=v_cnt+1'b1;end
else v_cnt<=11'd0;
end
end
assign lcd_vs=(v_cnt<=`V_SYNC-1'b1)?1'b0:1'b1; //场同步信号
////////////////////////////////
//数据使能信号
assign lcd_en=(h_cnt >= `H_SYNC + `H_BACK && h_cnt < `H_SYNC + `H_BACK + `H_DIP) &&
(v_cnt >= `V_SYNC + `V_BACK && v_cnt < `V_SYNC + `V_BACK + `V_DIP)
? 1'b1 : 1'b0;
////////////////////////////////
//外部数据请求
assign lcd_request = (h_cnt >= `H_SYNC + `H_BACK - 1'b1 && h_cnt < `H_SYNC + `H_BACK + `H_DIP - 1'b1) &&
(v_cnt >= `V_SYNC + `V_BACK && v_cnt < `V_SYNC + `V_BACK + `V_DIP)
? 1'b1 : 1'b0;
//lcd xpos & ypos
assign lcd_xpos = lcd_request ? (h_cnt - (`H_SYNC + `H_BACK - 1'b1)) : 11'd0;////////????
assign lcd_ypos = lcd_request ? (v_cnt - (`V_SYNC + `V_BACK)) : 11'd0;///////????/???????
endmodule
3.显示部分(该板子VGA565)
1>对于静态图片的显示,将一张图片通过img2lcd软件转成一个个8bit数据,存入ip rom/ram中,通过输入地址调用其中数据。
2>对于动态数据的接收,可以通过串口输入,存入一个ram/rom再调用。(写数据的clk用串口输入数据完成标志的上升沿来驱动,读数据的clk直接用VGA扫描时钟)
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
module displayimg(
input clk,
input clkin,//ram写入时钟
input start_flag,
input reset,
input lcd_en,
input[10:0] lcd_xpos,
input[10:0] lcd_ypos,
output reg[15:0] lcd_data,
input[7:0] data_out
);
reg[4:0] rgb_r;
reg[5:0] rgb_g;
reg[4:0] rgb_b;
reg[15:0] counter_in;
reg[15:0] counter_out;
wire[7:0] data_get;
wire[14:0] displyarea;
reg start_enter_flag;
vga_uart_ram u8(
.clka(clkin),
.wea(1),
.addra(counter_in),
.dina(data_out),
.clkb(clk),
.addrb(counter_out),
.doutb(data_get)
);
always@(posedge clkin or negedge reset)//接收数据完成进来
begin
if(!reset) begin counter_in <= 'd0;end
else if(counter_in < 'd30000)//200*150的图片
begin
counter_in <= counter_in + 1'b1;
end
else counter_in <= 'd0;//
end
//区域
assign displyarea=lcd_xpos>=11'd10&&lcd_xpos<11'd210&&lcd_ypos>=11'd10&&lcd_ypos<11'd160;
//rgb
always@(posedge clk or negedge reset)
begin
if(!reset) begin
rgb_r <= 'd0;
rgb_g <= 'd0;
rgb_b <= 'd0;
end
else begin
rgb_r[4:0] <= data_get[7:3];
rgb_g[5:0] <= data_get[7:2];
rgb_b[4:0] <= data_get[7:3];
end
end
always@(posedge clk or negedge reset)
begin
if(!reset)
counter_out <= 'd0;
else if(lcd_ypos > 'd160)
counter_out <= 'd0;
else if(displyarea == 1'b1)
counter_out <= counter_out + 1'b1;
end
always@(posedge clk or negedge reset)
begin
if(!reset)
lcd_data <= 'd0;
else if(displyarea == 1'b1)
lcd_data <= {rgb_r,rgb_g,rgb_b};
else lcd_data<='d0;
end
endmodule
4.top