前言
基于ROM的VGA显示缺点:需要将图片转化为mif文件,使用的RAM是FPGA内部RAM模拟出来的,占用资源大切换显示图片需要重新转化,对ROM进行写入,使用极不方便,因此这里采用RS232进行VGA显示。
正文
一、基于RS232的VGA显示
1.项目需求
使用目标:设计基于RS232的VGA图片显示驱动模块,完成对100*100的彩色图片进行显示。
2.技术介绍
使用PC机通过RS232(50Mhz)连接FPGA开发板,通过RS232向FPGA发送图片数据,FPGA将图片数据缓存到RAM,使用FPGA的VGA接口驱动屏幕,将传入的数据进行读取,从而完成基于RS232的VGA显示。(640*480*60hz)(25.1Mhz)这里利用RAM进行跨时钟域进行数据处理,并存储数据。
ip核PLL使用的时钟输入为开发板载时钟,当使用锁相环时,系统时钟只能用在锁相环输入,不建议将系统时钟再做其他模块的驱动模块时钟,其他模块时钟可以使用锁相环输出。
UART通信实现与验证(RS232)
3.顶层架构
4.端口描述
clk | 时钟接口(50Mhz) |
rst_n | 复位按键(低电平有效) |
rx | 数据接收接口 |
rgb[7:0] | 颜色数据 |
hs | 场同步信号 |
vs | 行同步信号 |
二、代码验证
vga驱动模块
module vga_ctrl(input clk ,input rst_n ,input[7:0] in_rgb ,output volid_en ,output vga_hs ,output vga_vs ,output[9:0] vga_h ,//坐标output[9:0] vga_v ,//坐标output[7:0] vga_rgb
);reg [9:0] cnt_hs;//列计数器
reg [9:0] cnt_vs;//行计数器
wire hs_en;//列有效显示区域
wire vs_en;//行有效显示区域
//wire volid_en;//有效显示区域parameter hs_sync = 10'd96 ,//同步hs_bask = 10'd40 ,//后沿hs_left = 10'd8 ,//左边hs_vali = 10'd640 ,//有效hs_righ = 10'd8 ,//右边hs_fpon = 10'd8 ,//前沿hs_tota = 10'd800 ;//总共parameter vs_sync = 10'd2 ,//同步 vs_bask = 10'd25 ,//后沿 vs_left = 10'd8 ,//上边 vs_vali = 10'd480 ,//有效 vs_righ = 10'd8 ,//底边 vs_fpon = 10'd2 ,//前沿 vs_tota = 10'd525 ;//总共 always @(posedge clk,negedge rst_n)
beginif(rst_n == 0)cnt_hs <= 10'd0;elseif(cnt_hs < hs_tota - 10'd1)cnt_hs <= cnt_hs + 10'd1;elsecnt_hs <= 10'd0;
end always @(posedge clk,negedge rst_n)
beginif(rst_n == 0)cnt_vs <= 10'd0;elseif(cnt_hs == hs_tota - 10'd1)if(cnt_vs < vs_tota - 10'd1)cnt_vs <= cnt_vs + 10'd1;else cnt_vs <= 10'd0;elsecnt_vs <= cnt_vs;
end assign vga_hs = (cnt_hs < hs_sync)?1'b0:1'b1;//行同步信号赋值assign vga_h = (volid_en == 1'b1)?(cnt_hs - (hs_sync + hs_bask + hs_left)):10'd0;//行坐标(Y)assign vga_vs = (cnt_vs < vs_sync)?1'b0:1'b1;//场同步信号赋值assign vga_v = (volid_en == 1'b1)?(cnt_vs - (vs_sync + vs_bask + vs_left)):10'd0;//列坐标(X)assign hs_en = ((cnt_hs >= hs_sync + hs_bask + hs_left )&&(cnt_hs < hs_sync + hs_bask + hs_left + hs_vali))?1'b1:1'b0;assign vs_en = ((cnt_vs >= vs_sync + vs_bask + vs_left )&&(cnt_vs < vs_sync + vs_bask + vs_left + vs_vali))?1'b1:1'b0;assign volid_en = hs_en & vs_en;//显示区域assign vga_rgb = (volid_en == 1'b1)?in_rgb:8'd0;endmodule
RS232数据接收
module uart_rx(input clk ,input rst_n ,input rx ,output reg[7:0]po_data , //接收到的数据output reg po_flag //数据输出有效);parameter uart_btl ='d9600 ;//串口波特率
parameter clk_shuj ='d50_000_000 ;//时钟频率parameter cnt_max =clk_shuj/uart_btl;reg reg1,reg2,reg3 ;//打排延迟周期,消除亚稳态
reg flag ;//uart工作信号(uart工作时在工作状态切换后产生一个时钟周期高电平)
reg en ;//uart工作使能标志信号(uart工作时在工作状态切换后的下一个时钟周期开始一直拉高,直到检测到停止信号)
reg [15:0] cnt ;//每比特数据持续时钟周期计数器
reg [3 :0] bit_cnt ;//数据个数计数器
reg bit_flag ;//每bit数据接收有效信号
reg [7 :0] rx_data ;//存储输入数据
reg rx_flag ;//输入数据并位结束信号always@(posedge clk,negedge rst_n)//数据打排1
beginif(rst_n == 0)reg1 <= 1'b1;elsereg1 <= rx;
end always@(posedge clk,negedge rst_n)//数据打排2
beginif(rst_n == 0)reg2 <= 1'b1;elsereg2 <= reg1;
end always@(posedge clk,negedge rst_n)//数据打排3
beginif(rst_n == 0)reg3 <= 1'b1;elsereg3 <= reg2;
end always@(posedge clk,negedge rst_n)//uart工作信号赋值
beginif(rst_n == 0)flag <= 1'b0;elseif((reg2 == 1'b0)&&(reg3 == 1'b1)&&(en == 1'b0))flag <= 1'b1;elseflag <= 1'b0;
endalways@(posedge clk,negedge rst_n)//uart工作使能标志信号赋值
beginif(rst_n == 0)en <= 1'b0;elseif(flag == 1'b1)en <= 1'b1;elseif((bit_cnt == 4'd8)&&(bit_flag == 1'b1))en <= 1'b0;elseen <= en;
end always@(posedge clk,negedge rst_n)//每比特数据持续时钟周期计数器驱动逻辑
beginif(rst_n == 0) cnt <= 16'd0;else if((cnt == cnt_max - 1)||(en == 1'b0))cnt <= 16'd0;elsecnt = cnt + 1'b1;
endalways@(posedge clk,negedge rst_n)//每比特数据持续时钟周期计数器驱动逻辑
beginif(rst_n == 0) bit_flag <= 1'b0;elseif(cnt == cnt_max/2 - 1)bit_flag <= 1'b1;elsebit_flag <= 1'b0;
endalways@(posedge clk,negedge rst_n)//数据个数计数器驱动逻辑
beginif(rst_n == 0) bit_cnt <= 4'd0;else if((bit_cnt == 4'd8)&&(bit_flag == 1'b1))bit_cnt <= 4'd0;else if(bit_flag == 1'b1)bit_cnt <= bit_cnt + 1'b1;elsebit_cnt <= bit_cnt;
endalways@(posedge clk,negedge rst_n)//接收数据并位
beginif(rst_n == 0)rx_data <= 8'd0;elseif((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))rx_data <= {reg3,rx_data[7:1]};
endalways@(posedge clk,negedge rst_n)//输入数据并位结束信号
beginif(rst_n == 0)rx_flag <= 1'b0;else if((bit_cnt == 4'd8)&&(bit_flag == 1'b1))rx_flag <= 1'b1;elserx_flag <= 1'b0;
endalways@(posedge clk,negedge rst_n)//输出数据传递
beginif(rst_n == 0)po_data <= 8'd0;else if(rx_flag == 1'b1)po_data <= rx_data;elsepo_data <= po_data;
endalways@(posedge clk,negedge rst_n)//输出使能
beginif(rst_n == 0)po_flag <= 1'b0;else po_flag <= rx_flag;
endendmodule
数据生成
module vga_data(input clk_ram ,input clk ,input rst_n ,input[9:0] pix_x ,input[9:0] pix_y ,input[7:0] po_data , //接收到的数据input po_flag , //数据输出有效output [7:0] vga_rgb_t);parameter show_h = 10'd110,//定位点X坐标show_v = 10'd100;//定位点Y坐标parameter show_chan = 10'd256,//图片长度show_kuan = 10'd32;//图片宽度parameter show_1_rgb = 8'b000_000_00,//背景show_0_rgb = 8'b111_100_00;//图片颜色parameter pot_h = 10'd120,//定位点X坐标pot_v = 10'd100;//定位点Y坐标parameter chan = 10'd100,//图片长度kuan = 10'd100;//图片宽度 reg [255:0] char [31:0];//存储字符数据
wire[9:0] char_x;
wire[9:0] char_y;reg [07:0]vga_rgb;
wire[07:0]rom_data;//存储RAM数据
reg [13:0]addr;//ip核读地址
reg [13:0]wier;//ip核写地址
wire rd_en ;//ip核读使能assign char_x =((pix_x >= show_h)&&(pix_x < (show_h + show_chan)))&&((pix_y >= show_v)&&(pix_y <(show_v + show_kuan)))?(pix_x - show_h):10'h3ff;
assign char_y =((pix_x >= show_h)&&(pix_x < (show_h + show_chan)))&&((pix_y >= show_v)&&(pix_y <(show_v + show_kuan)))?(pix_y - show_v):10'h3ff;always@(posedge clk)
beginchar[0 ] <= 256'h0000000000000000000000000001000000200000000100000020000000010000; char[1 ] <= 256'h0020000000010000003FFFFFFFFF0000003FFFFFFFFF0000003FFFFFFFFF0000;char[2 ] <= 256'h003FFFFFFFFF0000003000180001000000300018000100000030001800010000;char[3 ] <= 256'h0030001800010000003000180000000000300018000000000030001800000000;char[4 ] <= 256'h003000180000000000300018000000000030003C000000000030007E00000000;char[5 ] <= 256'h003803FFE00000000038000000000000003C000000000000003E000000000000;char[6 ] <= 256'h003F8000000000000003C0000000000000006000000000000000000000000000;char[7 ] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;char[8 ] <= 256'h0000000000000000000000000001000000200000000100000020000000010000;char[9 ] <= 256'h0020000000010000003FFFFFFFFF0000003FFFFFFFFF0000003FFFFFFFFF0000;char[10 ] <= 256'h003FFFFFFFFF0000003000060001000000300006000100000030000600010000;char[11 ] <= 256'h0030000600010000003000060000000000300006000000000030000600000000;char[12 ] <= 256'h00300006000000000038000E000000000038000E00000000001C001C00000000;char[13 ] <= 256'h001E003C00000000000F80F800000000000FFFF8000000000007FFF000000000;char[14 ] <= 256'h0001FFC00000000000007F000000000000000000000000000000000000000000;char[15 ] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;char[16 ] <= 256'h00000000000000000000000000000000000000FFE000000000000FFFFE000000;char[17 ] <= 256'h00007FFFFF8000000000FFFFFFE000000003FE001FF800000007E00000FC0000;char[18 ] <= 256'h000F0000003C0000001E0000000E0000001C0000000600000038000000070000;char[19 ] <= 256'h0030000000030000003000000003000000300000000300000030000100030000;char[20 ] <= 256'h00300001000300000018000100060000001C0001800E0000001E0001FFFC0000;char[21 ] <= 256'h001F8001FFF80000001FF001FFF8000000003801FFF800000000000180000000;char[22 ] <= 256'h0000000100000000000000010000000000000001000000000000000000000000;char[23 ] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;char[24 ] <= 256'h00000000000100000000000000010000000000000003000000000000001F0000;char[25 ] <= 256'h0000000001FF0000000000001FE3000000000001FC0100000000001FC0010000;char[26 ] <= 256'h000001FCC000000000001FC0C00000000001FC00C0000000001FC000C0000000;char[27 ] <= 256'h003E0000C0000000007F0000C0000000007FF000C0000000003FFF00C0000000;char[28 ] <= 256'h0001FFF0C000000000001FFFC0000000000001FFFC0100000000001FFFC10000;char[29 ] <= 256'h00000001FFFF0000000000000FFF00000000000000FF000000000000000F0000;char[30 ] <= 256'h0000000000030000000000000001000000000000000100000000000000000000;char[31 ] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
endalways @(posedge clk,negedge rst_n)
beginif(rst_n == 0)vga_rgb <= show_1_rgb;elseif( ((pix_x >= show_h - 1'b1)&&(pix_x < (show_h + show_chan - 1'b1)))&& ((pix_y >= show_v )&&(pix_y <(show_v + show_kuan)))&& (char[char_y][10'd255 - char_x] == 1'b1))vga_rgb <= show_0_rgb;elsevga_rgb <= show_1_rgb;
endassign rd_en = (((pix_x >= pot_h)&&(pix_x < pot_h + chan))&&((pix_y >= pot_v)&&(pix_y <pot_v + kuan)))? 1'b1:1'b0;//ip核图片显示区域always @(posedge clk,negedge rst_n)//读地址
beginif(rst_n == 0)addr <= 14'd0;elseif(addr == 14'd9999)addr <= 14'd0;elseif(rd_en == 1'b1)addr <= addr + 14'd1;elseaddr <= addr;
endalways @(posedge clk_ram,negedge rst_n)//写地址
beginif(rst_n == 0)wier <= 14'd0;elseif((wier == 14'd9999)&&(po_flag == 1'b1))wier <= 14'd0;elseif(po_flag == 1'b1)wier <= wier + 14'd1;elsewier <= wier;
end my_ram my_ram_inst (.data ( po_data ),.inclock ( clk_ram ),.outclock ( clk ),.rdaddress ( addr ),.wraddress ( wier ),.wren ( ~po_flag ),.q ( rom_data ));assign vga_rgb_t = (rd_en)?rom_data:vga_rgb;endmodule
顶层连线
module RS232_vga(input clk ,input rst_n ,input rx ,output hs ,output vs ,output[7:0] rgb );wire clk_50;
wire clk_25;
wire rst_en;wire[7:0]po_data; //接收到的数据
wire po_flag; //数据输出有效wire[9:0]vga_h;//坐标
wire[9:0]vga_v;//坐标wire[7:0]in_rgb;my_pll my_pll_inst (.areset (~rst_n ),.inclk0 ( clk ),.c0 ( clk_50 ),.c1 ( clk_25 ),.locked ( rst_en )
);uart_rx uart_rx(.clk (clk_50 ),.rst_n (rst_en ), .rx (rx ),.po_data (po_data), //接收到的数据.po_flag (po_flag)//数据输出有效);vga_ctrl vga_ctrl(.clk (clk_25 ) ,.rst_n (rst_en ) ,.in_rgb (in_rgb ) ,.volid_en() ,.vga_hs (hs ) ,.vga_vs (vs ) ,.vga_h (vga_h ) ,//坐标.vga_v (vga_v ) ,//坐标.vga_rgb (rgb )
);vga_data vga_data(.clk_ram (clk_50 ) ,.clk (clk_25 ) ,.rst_n (rst_en ) ,.pix_x (vga_h ) ,.pix_y (vga_v ) ,.po_data (po_data ) ,.po_flag (po_flag ) ,.vga_rgb_t (in_rgb ) );endmodule
仿真代码
`timescale 1ns/1ps
module vga_driver_tb();reg clk ;reg rst_n ;reg rx ;wire hs ;wire vs ;wire[7:0] rgb ; RS232_vga vga_driver_instx(.clk (clk ) ,.rst_n (rst_n) ,.rx (rx ) ,.hs (hs ) ,//行脉冲信号,由列计数器控制产生.vs (vs ) ,//场脉冲信号,由行计数器控制产生.rgb (rgb ) );initial clk = 1'b1;
always #10 clk = ~clk;initial beginrst_n = 1'b0;rx = 1'b1;#40rst_n = 1'b1;#200re_byte();#1000000$stop;
end
//赋值函数task rx_bit(input [7:0]data);integer i;for(i = 0;i < 10; i = i + 1)//循环9次begincase(i)0: rx <= 1'b0;1: rx <= data[0];2: rx <= data[1];3: rx <= data[2];4: rx <= data[3];5: rx <= data[4];6: rx <= data[5];7: rx <= data[6];8: rx <= data[7];9: rx <= 1'b1;endcase#(5208*20);//每次延时endendtasktask re_byte();integer j;for(j = 0;j < 8;j = j + 1)rx_bit(j);//传递0——7,8个数据endtaskendmodule
三、仿真验证
运行仿真,观察输出波形。
上图完成了4帧数据的输出,对一帧数据进行放大观察
两条黄色标线区域中为图片数据,接下来追踪该图片数据来源
可以看到在图片区域rgb数据输出的数据来自于IP核rom,实验成功
参考资料
UART通信实现与验证(RS232)