Verilog实现VGA通信的驱动
VGA全称是Video Graphics Array,即视频图形阵列,是一个使用模拟信号进行视频传输的标准。分辨率高,显示速度快,虽然如今的笔记本不在支持VGA接口,但是台式机上,VGA仍是制造商支持的最低显示标准。
驱动,顾名思义就是能够使驱使外设正常工作的代码,所以本质上也是代码,为了能够写出驱动,我们必须明确外部设备的接口定义和传输协议,然后针对其接口和传输协议给予相应的信号和数据传输,使其正常的运转起来,这样我们就可以写出一个驱动程序了。
- 传输协议
图一 引脚定义
上图是VGA接口的引脚定义和名称、功能的描述,除去地线,我们一般会用到5个引脚:1(RED)、2(GREEN)、3(BLUE)、13(HSYNC)、14(VSYNC)
1、2、3指的是传输的红、绿、蓝三色的模拟信号,范围为0-0.714V,0代表无色,0.714V代表满色,需要注意的这是模拟信号,因此前面还需要进行一个数模转换。HSYNC(行同步信号)和VSYNC(场同步信号)分别是指传输一行和传输一帧的时序,下面看一下他们的时序图:
图二 行同步时序
图三 场同步时序
可以看出,行同步时序和场同步时序类似,都分别经历4个阶段:拉低、显示后沿、有效数据传输和显示前言。这里需要理解2个地方:一个地方是行同步时序是以像素时钟作为单位的,而场扫描时序是以行同步的周期作为单位的,如何理解呢?就是行同步时序走完一个周期,在场扫描时序看来,只是经过了一个单位而已,而这个单位有可能落在场同步时序的任何一个阶段,因此,有数据传输的是哪个周期呢?就是在场同步时序的有效数据时间内,每个行同步周期的有效数据时间,因此一个场同步周期就完成了一帧图像的传输。比如:一帧图像的分辨率为480*640,那么在一个行同步时序的有效数据时间段就会传输480个像素点,在场同步时序的有效数据时间段完成了640个行同步时序。第二个需要理解的地方就是一个时序的4个阶段到底有多长,这个与图像的分辨率和刷新频率有关,下图列出了针对不同分辨率和刷新频率的显示模式,各个阶段所需要的参数:
表一 不同VGA的时序参数
- 程序设计
在引脚接口和传输协议了解了之后就可以动手设计程序了,在我看来,驱动程序的设计有2个步骤:
- 通过外部的时钟来模拟出外设所工作的时序
- 针对时序不同的阶段进行响应的操作
针对1,我们需要模拟出两个时序,行同步时序和场同步时序,基本的实现方法就是定义两个计数器根据外部时钟和时序参数进行计数,通过计数的数值来控制时序的电平,对于VGA的时序,需要定义两个计数器cnt_h和cnt_v,cnt_h随着像素时钟计数,cnt_v随着cnt_h计数(因为场同步时序是以行同步时序周期为单位的)。
针对2,通过计数器的数值来判断时序的不同阶段,进行响应的操作
思路确定之后,我们还需要具体实现的一些细节:
- 驱动模块需要从其他模块接收数据,因此驱动模块需要提前一个像素周期向数据提供模块传输像素的坐标以获取该坐标的像素数据(至于要传什么数据就可以在另一个模块进行控制了)
- 驱动模块内部需要用一个值来确定是否时序正处在有效数据传输时期
正点原子提供的驱动代码:
1 1 module vga_driver( ( 2 2 input vga_clk, , //VGA驱动时钟 3 3 input sys_rst_n, , //复位信号 4 4 //VGA接口 5 5 output vga_hs, , //行同步信号 6 6 output vga_vs, , //场同步信号 7 7 output [ [15: :0] ] vga_rgb, , //红绿蓝三原色输出 8 8 9 9 input [ [15: :0] ] pixel_data, , //像素点数据 10 10 output [ [ 9: :0] ] pixel_xpos, , //像素点横坐标 11 11 output [ [ 9: :0] ] pixel_ypos //像素点纵坐标 12 12 ); 13 13 14 14 //parameter define 15 15 parameter H_SYNC = = 10'd96; ; //行同步 16 16 parameter H_BACK = = 10'd48; ; //行显示后沿 17 17 parameter H_DISP = = 10'd640; ; //行有效数据 18 18 parameter H_FRONT = = 10'd16; ; //行显示前沿 19 19 parameter H_TOTAL = = 10'd800; ; //行扫描周期 20 20 21 21 parameter V_SYNC = = 10'd2; ; //场同步 22 22 parameter V_BACK = = 10'd33; ; //场显示后沿 23 23 parameter r V_DISP = = 10'd480; ; //场有效数据 24 24 parameter V_FRONT = = 10'd10; ; //场显示前沿 25 25 parameter V_TOTAL = = 10'd525; ; //场扫描周期 26 26 27 27 //reg define 28 28 reg [ [9: :0] ] cnt_h; ; 29 29 reg [ [9: :0] ] cnt_v; ; 30 30 31 31 //wire define 32 32 wire vga_en; ; 33 33 wire data_req; ; 34 34 35 35 //***************************************************** 36 36 //** main code 37 37 //***************************************************** 38 38 //VGA行场同步信号 39 39 assign vga_hs = = ( (cnt_h <= H_SYNC - - 1'b1) ) ? ? 1'b0 : : 1'b1; ; 40 40 assign vga_vs = = ( (cnt_v <= V_SYNC - - 1'b1) ) ? ? 1'b0 : : 1'b1; ; 41 41 42 42 // RGB565数据输出使能信号 43 43 assign vga_en = = (((cnt_h >= H_SYNC+ +H_BACK) ) && ( (cnt_h < < H_SYNC+ +H_BACK+ +H_DISP )) 44 44 &&((cnt_v >= V_SYNC+ +V_BACK) ) && ( (cnt_v < < V_SYNC+ +V_BACK+ +V_DISP ))) 45 45 ? ? 1'b1 : : 1'b0; ; 46 46 47 47 //RGB565数据输出 48 48 assign vga_rgb = = vga_en ? ? pixel_data : : 16'd0; ; 49 49 50 50 //像素点颜色数据输入请求信号 51 51 assign data_req = = (((cnt_h >= H_SYNC+ +H_BACK- -1'b1) ) && ( (cnt_h < < H_SYNC+ +H_BACK+ +H_DISP- -1'b1 )) 52 52 && ((cnt_v >= V_SYNC+ +V_BACK) ) && ( (cnt_v < < V_SYNC+ +V_BACK+ +V_DISP ))) 53 53 ? ? 1'b1 : : 1'b0; ; 54 54 55 55 //像素点坐标 56 56 assign pixel_xpos = = data_req ? ? ( (cnt_h - - ( (H_SYNC + + H_BACK - - 1'b1 )) : : 10'd0; ; 57 57 assign pixel_ypos = = data_req ? ? ( (cnt_v - - ( (V_SYNC + + V_BACK - - 1'b1 )) : : 10'd0; ; 58 58 59 59 //行计数器对像素时钟计数 60 60 always @( posedge vga_clk or negedge sys_rst_n) ) begin 61 61 if (!sys_rst_n) ) 62 62 cnt_h <= 10'd0; ; 63 63 else begin 64 64 if( (cnt_h < < H_TOTAL - - 1'b1) ) 65 65 cnt_h <= cnt_h + + 1'b1; ; 66 66 else 67 67 cnt_h <= 10'd0; ; 68 68 end 69 69 end 70 70 71 71 //场计数器对行计数 72 72 always @( posedge vga_clk or negedge sys_rst_n) ) begin 73 73 if (!sys_rst_n) ) 74 74 cnt_v <= 10'd0; ; 75 75 else if( (cnt_h == H_TOTAL - - 1'b1) ) begin 76 76 if( (cnt_v < < V_TOTAL - - 1'b1) ) 77 77 cnt_v <= cnt_v + + 1'b1; ; 78 78 else 79 79 cnt_v <= 10'd0; ; 80 80 end 81 81 end 82 82 83 83
其中,在51行对颜色请求信号赋值提前了一个周期(注意是在行同步时序里提前的,场同步时序不用提前),是为了在接下来的一个周期里能顺利收到数据并通过VGA传送到显示屏
PS:还有一个数模转换的问题,就是将数字信号转换成VGA接口红、黄、蓝的模拟信号,一些专用的视频转换芯片可以完成,正点原子通过一个电阻网络完成,这涉及到阻抗匹配的问题,具体原理先不讨论了
图四 阻抗匹配网络