当前位置: 首页 > news >正文

verilog实现的VGA显示自反弹移动小方块

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

      本文描述如何实现一个以60px / sec初始方向斜向下45°移动的方块(碰到边界后根据反射原理反向移动)。方块颜色由8个开关控制,方块大小为32px * 24px

   1 基本步骤:

编写主体代码:

1编写扫描整个屏幕的模块;

2编写根据vsync信号和坐标边界判断当前矩阵有效范围坐标,移动方向的模块;

3 编写根据当前所扫描坐标和矩阵坐标范围确定对应颜色的模块。

查看综合电路图,确定是否符合设计逻辑。

编写接口约束。将输入输出变量指定到开发板的端口。

编写测试代码,进行模拟测试.模拟测试可以观察波形图来判断设计是否正确。

将程序写入开发板运行,检查运行结果。

  2 基本原理:

1)VGA引脚图

一般常用引脚就是行同步信号,列同步信号,和8RGB引脚。实验中也只用到了VGA的这几个引脚。RGB引脚控制颜色信号是0 ~0.7v 0v就是全黑。0.7V就是全亮,代码中二进制1为高电平,0为低电平。

HS为水平有效信号,VS为垂直有效信号。在建立阶段垂直信号有2个时钟,水平信号有96个时钟,这个时段对应有效信号要设为0。之后要设为1



102506_7Zjb_2348884.png


2)VGA时序逻辑:

  可以看出水平扫描和垂直扫描周期中各个阶段要花费的时钟周期。

102506_hCBB_2348884.png


 

可以得知水平信号是和25MHz时钟同步计数的,垂直信号是用水平信号的周期计数的。

   之后发现这张表有不对的地方,就是Ts开始的地方应该是在下表所示的位置。

102506_6s5Z_2348884.png


3) VGA counter 的电路逻辑:

102506_Pl3r_2348884.png


3 源代码

.v

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date:    21:08:50 12/07/2014 
// Design Name: 
// Module Name:    VGA 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//
module VGA(
input clk, rst, 
input [7:0] rect_color,
output reg hsync,vsync,
output reg [7:0] color
    );

reg [9:0] hgrid = 0; // 800 TS , x 
reg [9:0] vgrid = 0; // 521 Ts , y
reg clk_4fp = 0;
reg [1:0] count = 0;
//分频 25M Hz
always @ (posedge clk or posedge rst) begin
  if(rst) 
      count <= 0; 
  else 
 count <= count + 1; 
end

always @ (posedge clk or posedge rst) begin
  if(rst)
  clk_4fp <= 0;  
  else begin 
  
    if (count[1] == 1)
	    clk_4fp <= 1;
	 else
	    clk_4fp <= 0;
end 
    
end

 // 扫描整个屏幕,包括非显示区,确定什么时候
always @ (posedge clk_4fp or posedge rst) begin
  if(rst) begin 
    hgrid <= 0;
	 vgrid <= 0;
 
  end
  else begin
  //根据basic VGA controller的电路图,在水平方向扫描一次后,使得垂直方向开始扫描
  // 因为水平方向是时钟计数的,垂直方向是根据水平方向的脉冲计数的
    if(hgrid >= 800) begin 
	    hgrid <= 0;
		 vgrid <= (vgrid >= 521? 0 : vgrid + 1'b1);
	 end 
    else
	    hgrid <= hgrid + 1'b1;
  end
end

//设置行选,列选信号有效。 由于有建立的Tpw时间,所以要把Tpw(脉冲宽度)时间段内的坐标视为无效
always @(posedge clk_4fp or posedge rst) begin
if(rst) begin 
  hsync <= 0;
  vsync <= 0;
end
else begin
  if(hgrid < 752 &&hgrid  >= 656) // 脉冲内为0 (800 - Tbp -Tpw) ~ (800 - Tbp)
     hsync <= 0;
  else 
     hsync <= 1;
	  
  if(vgrid < 492 && vgrid >= 490 ) // 脉冲内为0 (521 - Tbp -Tpw) ~ (521 - Tbp)
     vsync <= 0;
  else 
     vsync <= 1;
end

end



// 显示移动矩形

parameter  WIDTH = 32, //矩形长
           HEIGHT = 24,  //矩形宽
			  // 显示区域的边界
			  DISV_TOP = 10'd480,  // display top bound
			  DISV_DOWN =10'd0,  // display down bound
			  DISH_LEFT = 10'd0, // display left bound
			  DISH_RIGHT = 10'd640; // display right bound
			  
//初始矩形的位置,在显示区的左下角			   
reg [9:0] topbound = DISV_DOWN + HEIGHT;
reg [9:0] downbound = DISV_DOWN ;
reg [9:0] leftbound = DISH_LEFT ;
reg [9:0] rightbound = DISH_LEFT + WIDTH ;
//初始方向为东南方向
reg [1:0] movexy = 2'b11;
/*
根据时间选择不同范围坐标的像素显示颜色,使其成为一个移动的矩形。
由于是60/s, vsync的Ts恰好是移动1px所花的时间,所以用vsync信号的上升沿判断
*/

//确立每一个像素时钟里矩形的坐标范围

always @ (posedge vsync or posedge rst) begin
if(rst) begin 
   topbound = DISV_DOWN + HEIGHT ;
	downbound = DISV_DOWN;
	leftbound = DISH_LEFT;
	rightbound = DISH_LEFT + WIDTH ;
	movexy = 2'b11;
 
end

else begin
     //碰到边界,改变方向
	 case(movexy[1:0])
	 2'b11: begin // 东南
	         if (topbound == DISV_TOP && rightbound < DISH_RIGHT )
				    movexy = 2'b10;
				else if (topbound < DISV_TOP && rightbound == DISH_RIGHT )
				    movexy = 2'b01;
			   else if (topbound == DISV_TOP && rightbound == DISH_RIGHT )
				    movexy = 2'b00;
	        end
	 2'b10: begin // 东北
	         if (downbound  == DISV_DOWN&& rightbound < DISH_RIGHT )
				    movexy = 2'b11;
				else if (downbound > DISV_DOWN && rightbound == DISH_RIGHT )
				    movexy = 2'b00;
			   else if (downbound == DISV_DOWN && rightbound == DISH_RIGHT )
				    movexy = 2'b01;
	        end
	 2'b00: begin // 西北
	         if (downbound == DISV_DOWN && leftbound > DISH_LEFT )
				    movexy = 2'b01;
				else if (downbound > DISV_DOWN  && leftbound == DISH_LEFT )
				    movexy = 2'b10;
			   else if (downbound == DISV_DOWN && leftbound == DISH_LEFT )
				    movexy = 2'b11;
	        end
	 2'b01:  begin // 西南
	         if (topbound == DISV_TOP && leftbound > DISH_LEFT )
				    movexy = 2'b00;
				else if (topbound < DISV_TOP && leftbound == DISH_LEFT )
				    movexy = 2'b11;
			   else if (topbound == DISV_TOP && leftbound == DISH_LEFT )
				    movexy = 2'b10;
	        end
	 default: movexy = 2'b11;
	 endcase
	 
	  topbound <= topbound + ( movexy[0]? 1 : -1 );
	  downbound <= downbound + ( movexy[0]? 1 : -1 );
	  leftbound <= leftbound + ( movexy[1]? 1 : -1 );
     rightbound <= rightbound + ( movexy[1]? 1 : -1 );	

end

end

// 确定扫描到哪一个像素该显示什么颜色
always @(posedge clk_4fp or posedge rst) begin
if(rst)
     color <= 8'b0000_0000; 
	  
else begin
if (hgrid >= DISH_LEFT  && hgrid <= DISH_RIGHT  && vgrid >= DISV_DOWN && vgrid <= DISV_TOP) begin
    if(hgrid >= leftbound && hgrid <= rightbound && vgrid >= downbound && vgrid <= topbound)
	   color <= rect_color;  
	 else 		
	   color <= 8'b0000_0011; //黑色
end
else begin
   color <= 8'b0000_0000;
end

end
 

end

endmodule


 .ucf

NET "clk" LOC = "V10";
NET "rst" CLOCK_DEDICATED_ROUTE = FALSE; 

NET "color[7]" LOC = "U7";
NET "color[6]" LOC = "V7";
NET "color[5]" LOC = "N7";
NET "color[4]" LOC = "P8";
NET "color[3]" LOC = "T6";
NET "color[2]" LOC = "V6";
NET "color[1]" LOC = "R7";
NET "color[0]" LOC = "T7";

NET "rect_color[7]" LOC = "T5";
NET "rect_color[6]" LOC = "V8";
NET "rect_color[5]" LOC = "U8";
NET "rect_color[4]" LOC = "N8";
NET "rect_color[3]" LOC = "M8";
NET "rect_color[2]" LOC = "V9";
NET "rect_color[1]" LOC = "T9";
NET "rect_color[0]" LOC = "T10";


NET "hsync" LOC = "N6";
NET "vsync" LOC = "P7";


4 后记

在调试过程中出现的显示区域边界与显示屏边界不能重合的问题,这些主要就是这四个参数怎么计算的问题:

         DISV_TOP = 10'd480,  // display top bound

                              DISV_DOWN =10'd0,  // display down bound

                              DISH_LEFT = 10'd0, // display left bound

                              DISH_RIGHT = 10'd640; // display right bound

这四个参数最开始是依据下面的图和参数来算的,可是发现不对。按照这样算的话有效显示的计数坐标范围是(144-784 31-511),之后发现这样计算显示就会有问题。方块可以正常移动反弹,就是边界显示有问题,于是就不断去调整显示区的边界坐标,发现是和pdf说明文档中另一幅图吻合的。真是个坑。

 

 103401_60JP_2348884.png

 

正确的TS应该是以下这幅图中“Total  Horizon time”这一段,也就是说有效显示区的计数范围是(0-640 0-480)的。然后hsyncvsync0的阶段也是Tpw阶段,之前根据上面那幅图Tpw阶段是(0-96,0-2),运行的时候会出现方块颜色为渐变色的bug,不过根据下面这幅图计算,计数坐标的范围就是(656-752 490-492)为Tpw阶段,这样就正常了。

103425_VPWA_2348884.png

 



转载于:https://my.oschina.net/u/2348884/blog/406689

相关文章:

  • UNIX和Linux Shell正则表达式语法介绍
  • iOS.FBTweak
  • 数据结构-图
  • Linux 下完整安装ffmpeg(包括各种解码器)
  • poj2031
  • logistic regression
  • Linux中tty、pty、pts的概念区别
  • CISCO交换机QOS设置文档
  • QCon全球软件开发大会(北京站)2015精彩回顾和总结
  • MBA = married but available
  • (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境
  • Android中的Button自定义点击效果
  • 取得前一次MySQL操作所影响的记录行数
  • 计算圆的包含(两两圆不相交)
  • cacti安装文档
  • [js高手之路]搞清楚面向对象,必须要理解对象在创建过程中的内存表示
  • 4. 路由到控制器 - Laravel从零开始教程
  • DataBase in Android
  • extract-text-webpack-plugin用法
  • GitUp, 你不可错过的秀外慧中的git工具
  • Hexo+码云+git快速搭建免费的静态Blog
  • maven工程打包jar以及java jar命令的classpath使用
  • passportjs 源码分析
  • Quartz初级教程
  • 百度小程序遇到的问题
  • 聚簇索引和非聚簇索引
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 聊聊redis的数据结构的应用
  • 悄悄地说一个bug
  • 腾讯视频格式如何转换成mp4 将下载的qlv文件转换成mp4的方法
  • 学习HTTP相关知识笔记
  • 赢得Docker挑战最佳实践
  • 曜石科技宣布获得千万级天使轮投资,全方面布局电竞产业链 ...
  • ​​快速排序(四)——挖坑法,前后指针法与非递归
  • ​LeetCode解法汇总1276. 不浪费原料的汉堡制作方案
  • ​RecSys 2022 | 面向人岗匹配的双向选择偏好建模
  • #Js篇:单线程模式同步任务异步任务任务队列事件循环setTimeout() setInterval()
  • $emit传递多个参数_PPC和MIPS指令集下二进制代码中函数参数个数的识别方法
  • (007)XHTML文档之标题——h1~h6
  • (07)Hive——窗口函数详解
  • (1)虚拟机的安装与使用,linux系统安装
  • (31)对象的克隆
  • (Java数据结构)ArrayList
  • (阿里云万网)-域名注册购买实名流程
  • (非本人原创)我们工作到底是为了什么?​——HP大中华区总裁孙振耀退休感言(r4笔记第60天)...
  • (亲测成功)在centos7.5上安装kvm,通过VNC远程连接并创建多台ubuntu虚拟机(ubuntu server版本)...
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (算法)前K大的和
  • (转载)微软数据挖掘算法:Microsoft 时序算法(5)
  • .bat批处理(四):路径相关%cd%和%~dp0的区别
  • .NET Framework Client Profile - a Subset of the .NET Framework Redistribution
  • .Net mvc总结
  • .NET 反射 Reflect
  • .Net 垃圾回收机制原理(二)
  • .NET/ASP.NETMVC 深入剖析 Model元数据、HtmlHelper、自定义模板、模板的装饰者模式(二)...