VGA 与 UART 通信的联合使用
基础:
由 PC 通过 UART 发送数据在 VGA 显示。数据可以为字母,数字,汉字,VGA 分为左右两个区域,输入字母或者数字时在VGA 右边显示,输入汉字时在 VGA 左边显示。
发挥:
有删除功能,可通过按键删除显示的数字,字母或者汉字。左边的按键按下字母或者数字删除,右边的按键按下汉字删除。每次仅可删除一个字符。举例:VGA 左侧显示 FPGA 时,按下左边的键,VGA显示 FPG,再按一下,显示 FP。
注:板子为国产安陆板子,时钟周期为24Mhz
Top-Down设计思想
顶层模块负责各个模块信号互连;分频模块通过调用PLL实现倍频;rx模块接收上位机发送的数据并根据按键按下的情况产生标志位;Drive模块通过rx模块产生的标志位控制vga的显示;key用来对按键进行消抖。
顶层模块
module uart_top
(
input wire ext_clk_25m, //输入时钟
input wire ext_rst_n, //复位
input wire rx,
output wire vga_clk, //lcd pixel clock
output wire vga_hs, //lcd horizontal sync
output wire vga_vs, //lcd vertical syn
output wire [7:0] vga_r, //lcd red data
output wire [7:0] vga_g, //lcd green data
output wire [7:0] vga_b, //lcd blue data
input wire key,
input wire key2,
output wire [3:0]led,
output wire [3:0]led1
);
//-------------------------------------
wire clk_25m; //PLL
wire clk_50m; //PLL
wire clk_100m; //PLL
wire sys_rst_n; //
//-------------------------------------
wire flag;
wire [7:0] data;
wire clk_vga;
wire flag_FPGA,flag_geng,key_flag,flag_FPG,key2_flag,flag_zhou,flag_h0;
//sync global clock and reset signal
key_debounce inst
(
.clk(clk_50m),
.rst_n(sys_rst_n),
.key(key),
.key2(key2),
.led(led),
.key_flag(key_flag),
.key2_flag(key2_flag)
);
//输出180Mhz的时钟供vga使用
pll_vga u_pll
(
.refclk (ext_clk_25m ),
.reset (~ext_rst_n ),
.extlock (sys_rst_n1 ),
.clk0_out (clk_vga )
);
//VGA driver timing
Driver u1_Driver
(
.clk (clk_vga ), //180mhz
.clk1(clk_50m),
.rst_n (sys_rst_n1 ),
.lcd_dclk (vga_clk ),
.lcd_hs (vga_hs ),
.lcd_vs (vga_vs ),
.lcd_rgb ({vga_r, vga_g ,vga_b} ),
.flag_FPGA(flag_FPGA),
.flag_geng(flag_geng),
.flag_FPG(flag_FPG),
.flag_FP(flag_FP),
.flag_F(flag_F),
.flag_0(flag_0),
.flag_zhou(flag_zhou),
.flag_h0(flag_h0)
);
uart_rx inst_uart_rx (
.sclk (clk_50m),
.rst_n (sys_rst_n),
.rx (rx),
.po_data (data),
.po_flag (flag),
.flag_FPGA(flag_FPGA),
.flag_geng(flag_geng),
.key_flag(key_flag),
.key2_flag(key2_flag),
.flag_FPG(flag_FPG),
.led(led1),
.flag_FP(flag_FP),
.flag_F(flag_F),
.flag_0(flag_0),
.flag_zhou(flag_zhou),
.flag_h0(flag_h0)
);
//PLL
pll_test u_pll_test
(
.refclk (ext_clk_25m ),
.reset (~ext_rst_n ),
.extlock (sys_rst_n ),
.clk0_out (clk_25m ),
.clk1_out (clk_50m ),
.clk2_out (clk_100m )
);
endmodule
rx模块:
module uart_rx(
input wire sclk,
input wire rst_n,
input wire rx,
input wire key_flag,
input wire key2_flag,
output reg [7:0] po_data,
output reg po_flag,
output reg flag_FPGA,
output reg flag_geng,
output reg flag_FPG,
output reg flag_FP,
output reg flag_F,
output reg flag_0,
output reg flag_zhou,
output reg flag_h0,
output reg [3:0]led
);
parameter CNT_BAUD_MAX = 5207;
parameter CNT_HALF_BAUD_MAX = 2603;
reg rx1,rx2,rx2_reg;
reg rx_flag;
reg [12:0] cnt_baud;
reg bit_flag;
reg [3:0] bit_cnt;
reg [31:0]str;
always @(posedge sclk) begin
{rx2_reg,rx2,rx1}<={rx2,rx1,rx};
end
//接收上位机传来的数据
always @(posedge sclk or negedge rst_n) begin
if (rst_n == 1'b0) begin
rx_flag <= 1'b0;
end
else if (bit_cnt == 4'd8 && bit_flag == 1'b1) begin
rx_flag <= 1'b0;
end
else if (rx2_reg == 1'b1 && rx2 == 1'b0) begin
rx_flag <= 1'b1;
end
end
always @(posedge sclk or negedge rst_n) begin
if (rst_n == 1'b0) begin
cnt_baud <= 'd0;
end
else if (rx_flag == 1'b0) begin
cnt_baud <= 'd0;
end
else if (rx_flag == 1'b1 && cnt_baud == CNT_BAUD_MAX) begin
cnt_baud <= 'd0;
end
else if (rx_flag == 1'b1) begin
cnt_baud <= cnt_baud + 1'b1;
end
end
always @(posedge sclk or negedge rst_n) begin
if (rst_n == 1'b0) begin
bit_flag <= 1'b0;
end
else if (rx_flag == 1'b1 && cnt_baud == CNT_HALF_BAUD_MAX) begin
bit_flag <= 1'b1;
end
else begin
bit_flag <= 1'b0;
end
end
always @(posedge sclk or negedge rst_n) begin
if (rst_n == 1'b0) begin
bit_cnt <= 'd0;
end
else if (bit_flag == 1'b1 && bit_cnt == 'd8) begin
bit_cnt <= 'd0;
end
else if (bit_flag == 1'b1) begin
bit_cnt <= bit_cnt + 1'b1;
end
end
always @(posedge sclk or negedge rst_n) begin
if (rst_n == 1'b0) begin
po_data <='d0;
end
else if (bit_cnt >= 4'd1 && bit_flag == 1'b1) begin
po_data <= {rx2_reg,po_data[7:1]};
end
end
always @(posedge sclk or negedge rst_n) begin
if (rst_n == 1'b0) begin
po_flag <= 1'b0;
end
else if (bit_cnt == 4'd8 && bit_flag == 1'b1) begin
po_flag <= 1'b1;
end
else begin
po_flag <= 1'b0;
end
end
//根据按键控制标志位,key=1代表按键按下
always @(posedge sclk or negedge rst_n) begin
if (rst_n == 1'b0) begin
flag_FPGA <= 1'b0;
end
else if (str=="FPGA") begin
flag_FPGA <= 1'b1;
end
else begin
flag_FPGA <= 1'b0;
end
end
always @(posedge sclk or negedge rst_n) begin
if (rst_n == 1'b0) begin
flag_FPG <= 1'b0;
end
else if (str=="FPGA"&&key_flag==1) begin
flag_FPG <= 1'b1;
end
else
flag_FPG <= 1'b0;
end
always @(posedge sclk or negedge rst_n) begin
if (rst_n == 1'b0) begin
flag_FP <= 1'b0;
end
else if (str=="FPG"&&key_flag==1) begin
flag_FP <= 1'b1;
end
else
flag_FP <= 1'b0;
end
always @(posedge sclk or negedge rst_n) begin
if (rst_n == 1'b0) begin
flag_F<= 1'b0;
end
else if (str=="FP"&&key_flag==1) begin
flag_F <= 1'b1;
end
else
flag_F <= 1'b0;
end
always @(posedge sclk or negedge rst_n) begin
if (rst_n == 1'b0) begin
flag_0<= 1'b0;
end
else if (str=="F"&&key_flag==1) begin
flag_0 <= 1'b1;
end
else
flag_0 <= 1'b0;
end
always @(posedge sclk or negedge rst_n) begin
if (rst_n == 1'b0) begin
flag_geng <= 1'b0;
end
else if (str==32'b10111001101000101101011011011100) //汉字,一个汉字用2个8bit数据拼成
begin
flag_geng <= 1'b1;
end
else begin
flag_geng <= 1'b0;
end
end
always @(posedge sclk or negedge rst_n) begin
if (rst_n == 1'b0) begin
flag_zhou <= 1'b0;
end
else if (str==32'b10111001101000101101011011011100&&key2_flag==1) begin
flag_zhou <= 1'b1;
end
else
flag_zhou <= 1'b0;
end
always @(posedge sclk or negedge rst_n) begin
if (rst_n == 1'b0) begin
flag_h0 <= 1'b0;
end
else if (str==32'b1011100110100010&&key2_flag==1) begin
flag_h0 <= 1'b1;
end
else
flag_h0 <= 1'b0;
end
//根据接收到的字符和按键情况产生字符串
always @(posedge sclk or negedge rst_n)
begin
if (rst_n == 1'b0) begin
str <= 0;
end
else if (po_flag==1) //每接收完8bit数据,存入str低8位中
str <= {str[23:0],po_data};
else if (key_flag==1) begin //此按键控制字符删除,按键按下时str高8位赋0
str <= {8'b0,str[31:8]};
end
else if (key2_flag==1) begin //此按键控制汉字删除,按键按下时str高16位赋0
str <= {16'b0,str[31:16]};
end
end
always @(posedge sclk or negedge rst_n) begin
if (rst_n == 1'b0) begin
led <= 4'b1111;
end
else if (flag_FPG==1)
led[0]<=0;
else led[0]<=1;
end
endmodule
Drive模块:
`timescale 1ns/1ns
/*
************ clk H_SYNC H_BACK H_DISP H_FRONT H_TOTAL V_SYNC V_BACK V_DISP V_FRONT V_TOTAL *
640x480@60Hz 25.2MHz 96 48 640 16 800 2 33 480 10 525 *
800x600@60Hz 40MHz 128 88 800 40 1056 4 23 600 1 628 *
1024x768@60Hz 65MHz 136 160 1024 24 1344 6 29 768 3 806 *
1280x720@60Hz 74.25MHz 40 220 1280 110 1650 5 20 720 5 750 *
1280x1024@60Hz 108MHz 112 248 1280 48 1688 3 38 1024 1 1066 *
1920x1080@60Hz 148.5MHz 44 148 1920 88 2200 5 36 1080 4 1125 *
*/
module Driver
#(
parameter H_SYNC = 112 ,
parameter H_BACK = 248 ,
parameter H_DISP = 1280 ,
parameter H_DISP_half = 640 ,
parameter H_FRONT = 48 ,
parameter H_TOTAL = 1688,
parameter V_SYNC = 3 ,
parameter V_BACK = 38 ,
parameter V_DISP = 1024 ,
parameter V_FRONT = 1 ,
parameter V_TOTAL = 1066
)
(
input wire clk, //VGA clock
input wire rst_n, //sync reset
input wire clk1,
input wire flag_FPGA,
input wire flag_geng,
input wire flag_FPG,
input wire flag_FP,
input wire flag_F,
input wire flag_0,
input wire flag_zhou,
input wire flag_h0,
output wire lcd_dclk, //lcd pixel clock
output wire lcd_hs, //lcd horizontal sync
output wire lcd_vs, //lcd vertical sync
output reg [23:0] lcd_rgb //lcd display data
);
localparam weight =10'd16 ; //显示图像的宽
localparam hight =10'd16 ; //显示图像的高
reg [11:0] hcnt;
reg [11:0] vcnt;
reg [255:0]char[5:0]; //16*16=256个像素
reg [8:0]cnt_z;
reg [8:0]cnt_z2;
reg [8:0]cnt_y;
reg [8:0]cnt_y2;
reg [8:0]cnt_y3;
reg [8:0]cnt_y4;
//wire lcd_request;
/*******************************************
SYNC--BACK--DISP--FRONT
*******************************************/
//根据标志位控制char[4:0]的赋值,需要删除时将对应的char赋值为0,显示时赋值为对应的字模
always @ (posedge clk1 or negedge rst_n)
begin
if (!rst_n)
begin
char[0]<=0;
end
else if(flag_FPG==1||flag_FPGA==1||flag_FP==1||flag_F==1)
begin
char[0]<=256'h0000000000003FFC18021800181018101FF018101800180018007E0000000000; //F
end
else if(flag_0==1)
begin
char[0]<=0;
end
end
always @ (posedge clk1 or negedge rst_n)
begin
if (!rst_n)
begin
char[1]<=0;
end
else if(flag_FPG==1||flag_FPGA==1||flag_FP==1)
begin
char[1]<=256'h0000000000003FF0180C18061806180C1FF018001800180018007E0000000000;//P
end
else if(flag_F==1||flag_0==1)
begin
char[1]<=0;
end
end
always @ (posedge clk1 or negedge rst_n)
begin
if (!rst_n)
begin
char[2]<=0;
end
else if(flag_FPG==1||flag_FPGA==1)
begin
char[2]<=256'h00000000000007F818083008600060006000603C70183018181807E000000000;//G
end
else if(flag_F==1||flag_0==1||flag_FP==1)
begin
char[2]<=0;
end
end
// Flag_FPGA和Flag_FPG会同时为1,所以判断时要把Flag_FPG放在前面,后续有具体解释
always @ (posedge clk1 or negedge rst_n)
begin
if (!rst_n)
begin
char[3]<=0;
end
else if(flag_FPG==1||flag_F==1||flag_0==1||flag_FP==1)
begin
char[3]<=0;
end
else if(flag_FPGA==1)
begin
char[3]<=256'h000000000000018002C002C00460046008200FF0103010182018F83E00000000;//A
end
end
always @ (posedge clk1 or negedge rst_n)
begin
if (!rst_n)
begin
char[4]<=0;
end
else if(flag_geng==1||flag_zhou==1)
begin
char[4]<=256'h0020FF20242024243CA424A424A83D20242024502E50F4504488048805040602;//耿
end
else if(flag_h0==1)
begin
char[4]<=0;
end
end
always @ (posedge clk1 or negedge rst_n)
begin
if (!rst_n)
begin
char[5]<=0;
end
else if(flag_h0==1||flag_zhou==1)
begin
char[5]<=0;
end
else if(flag_geng==1)
begin
char[5]<=256'h00003FF8210821082FE8210821083FF8200827C82448244827C8400840288010; //周
end
end
//在图像显示区域内,每遇到一次时钟上升沿cnt加1,使像素点和字模一一对应
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
cnt_z<= 0;
else if(cnt_z=='d255)
begin
cnt_z<=0;
end
else if((hcnt>=H_SYNC + H_BACK&&hcnt<=H_SYNC + H_BACK+weight-1)&&(vcnt>=V_SYNC + V_BACK&&vcnt<=V_SYNC + V_BACK+hight-1))
begin
cnt_z<=cnt_z+1;
end
end
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
cnt_z2<= 0;
else if(cnt_z2=='d255)
begin
cnt_z2<=0;
end
else if((hcnt>=H_SYNC + H_BACK+weight&&hcnt<=H_SYNC + H_BACK+2*weight-1)&&(vcnt>=V_SYNC + V_BACK&&vcnt<=V_SYNC + V_BACK+hight-1))
begin
cnt_z2<=cnt_z2+1;
end
end
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
cnt_y<= 0;
else if(cnt_y=='d255)
begin
cnt_y<=0;
end
else if((hcnt>=H_SYNC + H_BACK+H_DISP_half+20&&hcnt<=H_SYNC + H_BACK+H_DISP_half+20+weight-1)&&(vcnt>=V_SYNC + V_BACK&&vcnt<=V_SYNC + V_BACK+hight-1))
begin
cnt_y<=cnt_y+1;
end
end
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
cnt_y3<= 0;
else if(cnt_y3=='d255)
begin
cnt_y3<=0;
end
else if((hcnt>=H_SYNC + H_BACK+H_DISP_half+20+2*weight&&hcnt<=H_SYNC + H_BACK+H_DISP_half+20+3*weight-1)&&(vcnt>=V_SYNC + V_BACK&&vcnt<=V_SYNC + V_BACK+hight-1))
begin
cnt_y3<=cnt_y3+1;
end
end
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
cnt_y4<= 0;
else if(cnt_y4=='d255)
begin
cnt_y4<=0;
end
else if((hcnt>=H_SYNC + H_BACK+H_DISP_half+20+3*weight&&hcnt<=H_SYNC + H_BACK+H_DISP_half+20+4*weight-1)&&(vcnt>=V_SYNC + V_BACK&&vcnt<=V_SYNC + V_BACK+hight-1))
begin
cnt_y4<=cnt_y4+1;
end
end
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
cnt_y2<= 0;
else if(cnt_y2=='d255)
begin
cnt_y2<=0;
end
else if((hcnt>=H_SYNC + H_BACK+H_DISP_half+20+weight&&hcnt<=H_SYNC + H_BACK+H_DISP_half+20+2*weight-1)&&(vcnt>=V_SYNC + V_BACK&&vcnt<=V_SYNC + V_BACK+hight-1))
begin
cnt_y2<=cnt_y2+1;
end
end
//行同步信号,单位为像素
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
hcnt <= 12'd0;
else
begin
if(hcnt < H_TOTAL - 1'b1) //line over
hcnt <= hcnt + 1'b1;
else
hcnt <= 12'd0;
end
end
assign lcd_hs = (hcnt <= H_SYNC - 1'b1) ? 1'b0 : 1'b1; // line over flag
//场同步信号,单位为行
always@(posedge clk or negedge rst_n)
begin
if (!rst_n)
vcnt <= 12'b0;
else if(hcnt == H_TOTAL - 1'b1) //line over
begin
if(vcnt == V_TOTAL - 1'b1) //frame over
vcnt <= 12'd0;
else
vcnt <= vcnt + 1'b1;
end
end
assign lcd_vs = (vcnt <= V_SYNC - 1'b1) ? 1'b0 : 1'b1; // frame over flag
// LED clock
assign lcd_dclk = ~clk;
//显示
always@(posedge clk or negedge rst_n)
begin
if (!rst_n)
lcd_rgb <= 24'h0;
else if((hcnt>=H_SYNC + H_BACK+H_DISP_half-5&&hcnt<=H_SYNC + H_BACK+H_DISP_half)&&(vcnt>=V_SYNC + V_BACK&&vcnt<=V_SYNC + V_BACK+V_DISP-1))
lcd_rgb <= 24'h0; //分割线
else if((hcnt>=H_SYNC + H_BACK&&hcnt<=H_SYNC + H_BACK+weight-1)&&(vcnt>=V_SYNC + V_BACK&&vcnt<=V_SYNC + V_BACK+hight-1)&&char[4][255-cnt_z])
lcd_rgb <=24'h0000FF; //左边汉字耿
else if((hcnt>=H_SYNC + H_BACK+weight&&hcnt<=H_SYNC + H_BACK+2*weight-1)&&(vcnt>=V_SYNC + V_BACK&&vcnt<=V_SYNC + V_BACK+hight-1)&&char[5][255-cnt_z2])
lcd_rgb <=24'h0000FF; //左边汉字周
else if((hcnt>=H_SYNC + H_BACK+H_DISP_half+20&&hcnt<=H_SYNC + H_BACK+H_DISP_half+20+weight-1)&&(vcnt>=V_SYNC + V_BACK&&vcnt<=V_SYNC + V_BACK+hight-1)&&char[0][255-cnt_y])
lcd_rgb <=24'h0000FF; //右边F
else if((hcnt>=H_SYNC + H_BACK+H_DISP_half+20+weight&&hcnt<=H_SYNC + H_BACK+H_DISP_half+20+2*weight-1)&&(vcnt>=V_SYNC + V_BACK&&vcnt<=V_SYNC + V_BACK+hight-1)&&char[1][255-cnt_y2])
lcd_rgb <=24'h0000FF; //右边P
else if((hcnt>=H_SYNC + H_BACK+H_DISP_half+20+2*weight&&hcnt<=H_SYNC + H_BACK+H_DISP_half+20+3*weight-1)&&(vcnt>=V_SYNC + V_BACK&&vcnt<=V_SYNC + V_BACK+hight-1)&&(char[2][255-cnt_y3]))
lcd_rgb <=24'h0000FF; //右边G
else if((hcnt>=H_SYNC + H_BACK+H_DISP_half+20+3*weight&&hcnt<=H_SYNC + H_BACK+H_DISP_half+20+4*weight-1)&&(vcnt>=V_SYNC + V_BACK&&vcnt<=V_SYNC + V_BACK+hight-1)&&(char[3][255-cnt_y4]))
lcd_rgb <=24'h0000FF; //右边A
else if((hcnt>=H_SYNC + H_BACK&&hcnt<=H_SYNC + H_BACK+H_DISP-1)&&(vcnt>=V_SYNC + V_BACK&&vcnt<=V_SYNC + V_BACK+V_DISP-1)) //line over
lcd_rgb <=24'hFFFFFF; //背景
else lcd_rgb <= 24'h0;
end
endmodule
注:Flag_FPGA和Flag_FPG有一点时间会同时为1,所以当要删除A时,需要把Flag_FPG的判断放在前面。
按键消抖模块:
当第一次检测到key=0持续5ms时,产生标志位。
module key_debounce(
input wire clk,
input wire rst_n,
input wire key,
input wire key2,
output wire [3:0] led,
output reg key_flag,
output reg key2_flag
);
parameter CNT_END = 249999;
reg [17:0] cnt;
reg cnt_flag;
reg [3:0] shift_led = 4'b0001;
reg [17:0] cnt2;
reg cnt2_flag;
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
cnt <= 'd0;
end
else if (key== 1'b0) begin
cnt <= cnt + 1'b1;
end
else begin
cnt <= 'd0;
end
end
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
cnt_flag <= 1'b0;
end
else if (key == 1'b1) begin
cnt_flag <= 1'b0;
end
else if (cnt == CNT_END) begin
cnt_flag <= 1'b1;
end
end
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
key_flag <= 1'b0;
end
else if (cnt_flag == 1'b0 && cnt == CNT_END) begin
key_flag <= 1'b1;
end
else begin
key_flag <= 1'b0;
end
end
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
cnt2 <= 'd0;
end
else if (key2== 1'b0) begin
cnt2 <= cnt2 + 1'b1;
end
else begin
cnt2 <= 'd0;
end
end
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
cnt2_flag <= 1'b0;
end
else if (key2 == 1'b1) begin
cnt2_flag <= 1'b0;
end
else if (cnt2 == CNT_END) begin
cnt2_flag <= 1'b1;
end
end
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
key2_flag <= 1'b0;
end
else if (cnt2_flag == 1'b0 && cnt2 == CNT_END) begin
key2_flag <= 1'b1;
end
else begin
key2_flag <= 1'b0;
end
end
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
shift_led <= 4'b0001;
end
else if (key_flag == 1'b1) begin
shift_led <= {shift_led[2:0],shift_led[3]};
end
end
assign led = shift_led;
endmodule
下面是上板的实际效果:
删除实际效果:(按键各按了一下)
注:本任务所有控制信号产生部分使用50Mhz时钟,vga显示以及控制字模和像素点一一映射部分(cnt_y等)使用vga时钟。