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

FPGA通信之VGA

文章目录

    • 基本概念:
      • 水平扫描:
      • 垂直扫描:
    • 时序如下:
    • 端口设计
    • 疑问
      • 为什么需要输出那么多端口
      • 不输出时钟怎么保证电子枪移动速度符合时序
      • VGA转HDMI
    • 仿真电路图
    • 代码
    • 总结:野火电子yyds

为了做图像处理, 现在我们开始研究VGA

基本概念:

水平扫描:

  • 有效数据段:显示图像的部分。
  • 右边界区域(Right Border):扫描完有效数据后的空白区域。
  • 水平前沿(Front Porch):扫描完有效数据但未收到同步信号前的空白时间段
  • 行同步信号脉冲(Sync Pulse):通知电子枪回到左侧。
  • 水平后沿(Back Porch):电子枪回到左侧的时间段。
  • 左边界区域(LEFT Border):显示图像前的空白区域。

垂直扫描:

  • 有效行段:显示图像的行。
  • 下边界区域(Bottom Border):扫描完所有行后的空白区域。
  • 垂直前沿(Front Porch):扫描完所有行但未收到同步信号前的空白时间段
  • 场同步信号脉冲(Sync Pulse):通知电子枪回到顶部。
  • 垂直后沿(Back Porch):电子枪回到顶部的时间段。
  • 上边界区域(Top Border):显示图像前的空白区域。

时序如下:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
基于上述对VGA时序的详细分析,设计一个VGA控制器主要也就包括行计数器、场计数器,以及行、场同步信号在合适的时刻产生低电平脉冲,以及在显示有效区域将图像数据输出

  • 时间节点分析:
    行同步有效: 96个clk --> 0,1,2----95 —>95的下一个时钟加入下一步
    回扫时间: 40个clk --> 96, 97—135 -->135的下一个时钟加入下一步
    左缓冲边界: 8个clk --> 136,137—143 -->143的下一个时钟加入下一步
    数据段: 640个clk–> 144, 145–783 -->783的下一个时钟加入下一步
    右缓冲边界: 8个clk --> 784, 785–791 -->791的下一个时钟加入下一步
    等待回扫: 8个clk --> 792, 793–799 -->799的下一个时钟加入下一步
  • 现在对时钟的感知有以下进步:
    1. 三个点确定两个线段, 也就是两个时间段
    2. 但是现在应该有一个模型在脑子里, 也就是每一个时间点 右方 都有一个自己的时间段
    3. 假如从某个时间节点x开始要维持y个时间单元
    4. 根据第二点结论, 除原点x外我们需要y-1个时间点, 那么我们可以计算出最后一个时间节点的值为x+y-1, 例如从零时刻开始维持8个时钟周期, 那么最后一个时刻对应的节点为7

端口设计

在这里插入图片描述
or
在这里插入图片描述

疑问

为什么需要输出那么多端口

因为这个模块不是顶层模块, 所以需要增加很多输出, 作为内部变量方便顶层访问, 比如输出的计数器, 顶层模块访问后可以知道当前显示屏光标的位置,就可以在特定区域实现特定控制
如下为常用的顶层
module VGA (
input i_clk,
input i_arstn,
input [23:0] i_data,
output o_h_sync,
output o_v_sync,
output [23:0] o_data
// 可选隐藏:o_VGA_clk, o_VGA_BLK, o_VGA_ho_cnt, o_VGA_ve_cnt
);
而且, FPGA在连接显示屏前还需要一个DAC芯片(数字转模拟)

如图:顶层模块只需要三个干净的输出,野火电子教程
在这里插入图片描述

  1. 系统上电后,板卡传入系统时钟(sys_clk)和复位信号(sys_rst_n)到顶层模块;
  2. 系统时钟由顶层模块传入时钟生成模块(clk_gen),分频产生VGA工作时钟(vga_clk),作为图像数据生成模块(vga_pic)和VGA时序控制模块(vga_ctrl)的工作时钟;
  3. 图像数据生成模块以VGA时序控制模块传入的像素点坐标(pix_x,pix_y)为约束条件,生成待显示彩条图像的色彩信息(pix_data);
  4. 图像数据生成模块生成的彩条图像色彩信息传入VGA时序控制模块,在模块内部使用使能信号滤除掉非图像显示有效区域的图像数据,产生RGB色彩信息(rgb),在行、场同步信号(hsync、vsync)的同步作用下,将RGB色彩信息扫描显示到VGA显示器,显示出彩条图像

不输出时钟怎么保证电子枪移动速度符合时序

电子枪移动速度的同步原理
(1) 模拟信号的“自时钟”特性
CRT显示器内部的行扫描电路本质上是一个模拟振荡器,其振荡频率由以下因素锁定:
HSYNC信号的频率:显示器会强制将自身的行扫描频率与输入的HSYNC信号同步(类似锁相环PLL的原理)。
例如:在640x480@60Hz模式下,HSYNC频率为31.47kHz,显示器会自动调整电子枪的水平扫描速度以匹配这一频率。
(2) 电子枪的物理运动控制
行扫描电路:CRT内部的行偏转线圈(由锯齿波电流驱动)控制电子枪的水平移动速度。
锯齿波的斜率(即电子枪移动速度)由显示器电路根据HSYNC频率自动调整。
FPGA的RGB数据速率必须与电子枪的物理扫描速度匹配,但这通过以下方式保证:
FPGA以固定的像素时钟(如25.175MHz)生成RGB数据。
由于HSYNC频率是像素时钟的衍生信号*(如每800个像素时钟触发一次HSYNC),因此电子枪的移动速度自然与FPGA数据同步。

VGA转HDMI

https://blog.csdn.net/gslscyx/article/details/137930810

仿真电路图

调用锁相环IP生成25Mhz信号
在这里插入图片描述

代码

`timescale 1ns / 1ps
//****************************************VSCODE PLUG-IN**********************************// 
//---------------------------------------------------------------------------------------- 
// IDE :                   VSCODE      
// VSCODE plug-in version: Verilog-Hdl-Format-2.4.20240526
// VSCODE plug-in author : Jiang Percy 
//---------------------------------------------------------------------------------------- 
//****************************************Copyright (c)***********************************// 
// Copyright(C)            COMPANY_NAME
// All rights reserved      
// File name:               
// Last modified Date:     2025/05/17 22:22:41 
// Last Version:           V1.0 
// Descriptions:            
//---------------------------------------------------------------------------------------- 
// Created by:             USER_NAME
// Created date:           2025/05/17 22:22:41 
// Version:                V1.0 
// TEXT NAME:              VGA_top.v 
// PATH:                   D:\vivado\VGA\VGA_top.v 
// Descriptions:            
//                          
//---------------------------------------------------------------------------------------- 
//****************************************************************************************// module VGA_top(input                               sys_clk                    ,input                               sys_rst_n                  ,output                              o_horizon_syn              ,output                              o_vertical_syn             ,output             [  23: 0]        o_VGA_data                  
);//---------------------------------------------------------------------------------------// PLL锁相环IP调用, 将时钟频率定义为25MHZ                                                                                    //---------------------------------------------------------------------------------------wire                                PPL_clk                     ;//锁相环wire                                locked                      ;clk_wiz_0 instance_name(// Clock out ports.clk_out1                           (                          ),// output clk_out1.clk_out2                           (                          ),// output clk_out2.clk_out3                           (                          ),// output clk_out3.clk_out4                           (PPL_clk                   ),// output clk_out4// Status and control signals.reset                              (~sys_rst_n                     ),// input reset.locked                             (locked                    ),// output locked// Clock in ports.clk_in1                            (sys_clk)                  );//---------------------------------------------------------------------------------------//                                                                                     //---------------------------------------------------------------------------------------wire               [  23: 0]        w_VGA_data                  ;wire                                w_VGA_clk                   ;wire                                w_VGA_BLK                   ;wire               [   9: 0]        w_cnt_horizon               ;wire               [   9: 0]        w_cnt_vertical              ;
VGA_crl u_VGA(.i_clk                              (PPL_clk                   ),.i_arstn                            (sys_rst_n   && locked     ),.i_data                             (w_VGA_data                ),.o_h_sync                           (o_horizon_syn             ),.o_v_sync                           (o_vertical_syn            ),.o_data                             (o_VGA_data                ),.o_VGA_clk                          (w_VGA_clk                 ),// 输出一个同步时钟, 基于工程实践, 设置为输入时钟的反时钟更佳.o_VGA_BLK                          (w_VGA_BLK                 ),// 设置一个消隐信号, 似乎用不到.o_VGA_ho_cnt                       (w_cnt_horizon             ),// 方便顶层读取当前扫描位置.o_VGA_ve_cnt                       (w_cnt_vertical            ) // 方便顶层读取现在扫描位置
);VGA_pic u_VGA_pic(.clk                                (PPL_clk                   ),.rst_n                              (sys_rst_n    && locked    ),.i_cnt_horizon                      (w_cnt_horizon             ),// 从visible像素开始计算.i_cnt_vertical                     (w_cnt_vertical            ),// 从visible像素开始计算.o_data                             (w_VGA_data                ) 
);endmodule
// verilog 不支持链式条件, 必须分开
`timescale 1ns / 1ps
module VGA_pic(input                               clk                        ,input                               rst_n                      ,input              [   9: 0]        i_cnt_horizon              ,//从visible像素开始计算input              [   9: 0]        i_cnt_vertical             ,//从visible像素开始计算output             [  23: 0]        o_data                      
);//---------------------------------------------------------------------------------------// 时序参数定义:parameter                           VGA_HS_end                 = 10'd96,//水平同步器维持时间VGA_H_bach_Porch=10'd40,VGA_left_border=10'd8,VGA_H_DATA_time=10'd640,VGA_right_border=10'd8,VGA_H_front_Porch=10'd8;parameter                           period_horizon             = VGA_HS_end+VGA_H_bach_Porch+VGA_left_border+VGA_H_DATA_time+VGA_right_border+VGA_H_front_Porch;parameter                           VGA_h_visible_start        = VGA_HS_end+VGA_H_bach_Porch+VGA_left_border-1;parameter                           VGA_h_visible_end          = VGA_h_visible_start+VGA_H_DATA_time;parameter                           VGA_VS_end                 = 10'd2 ,//垂直同步器维持时间VGA_V_bach_Porch=10'd25,VGA_top_border=10'd8,VGA_V_DATA_time=10'd480,VGA_bottom_border=10'd8,VGA_V_front_Porch=10'd2;parameter                           period_vertical            = VGA_VS_end+VGA_V_bach_Porch+VGA_top_border +VGA_V_DATA_time+VGA_bottom_border+VGA_V_front_Porch;parameter                           VGA_v_visible_start        = VGA_VS_end+VGA_V_bach_Porch+VGA_top_border-1;parameter                           VGA_v_visible_end          = VGA_v_visible_start+VGA_V_DATA_time;//---------------------------------------------------------------------------------------//---------------------------------------------------------------------------------------//定义颜色编码localparamBLACK = 24'h000000,                                         //黑色BLUE = 24'h0000FF,                                          //蓝色RED = 24'hFF0000,                                           //红色PURPPLE =24'hFF00FF,                                        //紫色GREEN = 24'h00FF00,                                         //绿色CYAN = 24'h00FFFF,                                          //青色YELLOW = 24'hFFFF00,                                        //黄色WHITE = 24'hFFFFFF;                                         //白色//定义每个像素块的默认显示颜色值localparamR0_C0 = BLACK,                                              //第0行0列像素块R0_C1 = BLUE,                                               //第0行1列像素块R1_C0 = RED,                                                //第1行0列像素块R1_C1 = PURPPLE,                                            //第1行1列像素块R2_C0 = GREEN,                                              //第2行0列像素块R2_C1 = CYAN,                                               //第2行1列像素块R3_C0 = YELLOW,                                             //第3行0列像素块R3_C1 = WHITE;                                              //第3行1列像素块                                                                                    //---------------------------------------------------------------------------------------//---------------------------------------------------------------------------------------// 列检测wire                                C0_act                      ;assign                              C0_act                      = (0 <= i_cnt_horizon && i_cnt_horizon<320);wire                                C1_act                      ;assign                              C1_act                      = (320 <= i_cnt_horizon && i_cnt_horizon<640);// 行检测wire                                R1_act,R2_act,R3_act,R0_act  ;assign                              R0_act                      = (0<=i_cnt_vertical&& i_cnt_vertical<120);assign                              R1_act                      = (120<=i_cnt_vertical&&i_cnt_vertical<240);assign                              R2_act                      = (240<=i_cnt_vertical&& i_cnt_vertical<360);assign                              R3_act                      = (360<=i_cnt_vertical&& i_cnt_vertical<480);//---------------------------------------------------------------------------------------      reg                [  23: 0]        r_VGA_data                  ;always@(*)beginif (R3_act&C1_act) beginr_VGA_data = R3_C1;endelse if (R3_act&C0_act) beginr_VGA_data = R3_C0;endelse if (R2_act&C1_act) beginr_VGA_data = R2_C1;endelse if (R2_act&C0_act) beginr_VGA_data = R2_C0;endelse if (R1_act&C1_act) beginr_VGA_data = R1_C1;endelse if (R1_act&C0_act) beginr_VGA_data = R1_C0;endelse if (R0_act&C1_act) beginr_VGA_data = R0_C1;endelse if (R0_act&C0_act) beginr_VGA_data = R0_C0;endelse r_VGA_data =24'd0;endassign                              o_data                      = r_VGA_data;
endmodule
module VGA_crl (input                               i_clk                      ,input                               i_arstn                    ,input              [  23: 0]        i_data                     ,output                              o_h_sync                   ,output                              o_v_sync                   ,output             [  23: 0]        o_data                     ,output                              o_VGA_clk                  ,//输出一个同步时钟, 基于工程实践, 设置为输入时钟的反时钟更佳output                              o_VGA_BLK                  ,//设置一个消隐信号, 似乎用不到output             [   9: 0]        o_VGA_ho_cnt               ,//方便顶层读取当前扫描位置output             [   9: 0]        o_VGA_ve_cnt                //方便顶层读取现在扫描位置             
);//---------------------------------------------------------------------------------------// 时序参数定义:parameter                           VGA_HS_end                 = 10'd96,//水平同步器维持时间VGA_H_bach_Porch=10'd40,VGA_left_border=10'd8,VGA_H_DATA_time=10'd640,VGA_right_border=10'd8,VGA_H_front_Porch=10'd8;parameter                           period_horizon             = VGA_HS_end+VGA_H_bach_Porch+VGA_left_border+VGA_H_DATA_time+VGA_right_border+VGA_H_front_Porch;parameter                           VGA_h_visible_start        = VGA_HS_end+VGA_H_bach_Porch+VGA_left_border-1;parameter                           VGA_h_visible_end          = VGA_h_visible_start+VGA_H_DATA_time;parameter                           VGA_VS_end                 = 10'd2 ,//垂直同步器维持时间VGA_V_bach_Porch=10'd25,VGA_top_border=10'd8,VGA_V_DATA_time=10'd480,VGA_bottom_border=10'd8,VGA_V_front_Porch=10'd2;parameter                           period_vertical            = VGA_VS_end+VGA_V_bach_Porch+VGA_top_border +VGA_V_DATA_time+VGA_bottom_border+VGA_V_front_Porch;parameter                           VGA_v_visible_start        = VGA_VS_end+VGA_V_bach_Porch+VGA_top_border-1;parameter                           VGA_v_visible_end          = VGA_v_visible_start+VGA_V_DATA_time;//---------------------------------------------------------------------------------------reg                [   9: 0]        r_cnt_h                     ;// horizon counterreg                [   9: 0]        r_cnt_v                     ;// vertical counteralways @(posedge i_clk or negedge i_arstn) beginif (!i_arstn) beginr_cnt_h <= 0;endelse beginif (r_cnt_h == period_horizon-1) beginr_cnt_h <= 0;endelse beginr_cnt_h <= r_cnt_h + 1;endendendalways @(posedge i_clk or negedge i_arstn) beginif (!i_arstn) beginr_cnt_v <= 0;endelse beginif (r_cnt_h == period_horizon-1) beginif (r_cnt_v == period_vertical-1) beginr_cnt_v <= 0;endelse beginr_cnt_v <= r_cnt_v + 1;endendelse r_cnt_v <= r_cnt_v;endendwire                                w_VGA_HS                    ;// 行同步信号wire                                w_VGA_VS                    ;// 场同步信号assign                              w_VGA_HS                    = (r_cnt_h > VGA_HS_end-1);assign                              w_VGA_VS                    = (r_cnt_v > VGA_VS_end-1);
/*时间节点分析:行同步有效: 96个clk --> 0,1,2----95 --->95的下一个时钟加入下一步回扫时间:   40个clk --> 96, 97---135 -->135的下一个时钟加入下一步左缓冲边界: 8个clk  --> 136,137---143 -->143的下一个时钟加入下一步数据段:     640个clk--> 144, 145--783 -->783的下一个时钟加入下一步右缓冲边界: 8个clk  --> 784, 785--791 -->791的下一个时钟加入下一步等待回扫:   8个clk  --> 792, 793--799  -->799的下一个时钟加入下一步*/wire                                w_dat_act                   ;// 数据使能信号, 我们只希望在中间输出数据, 因此在这个阶段使能, 注意不输出数据不代表直接关闭电子枪assign                              w_dat_act                   = (VGA_h_visible_start < r_cnt_h && r_cnt_h <=VGA_h_visible_end) && (VGA_v_visible_start < r_cnt_v && r_cnt_v <= VGA_v_visible_end);//RGB控制wire               [  23: 0]        w_VGA_GRB                   ;assign                              w_VGA_GRB                   = (w_dat_act)? i_data:24'd0;assign                              o_data                      = w_VGA_GRB;//同步时钟控制assign                              o_VGA_clk                   = ~i_clk;//行场同步信号assign                              o_h_sync                    = w_VGA_HS;assign                              o_v_sync                    = w_VGA_VS;//消隐信号, BLK等同于data_act,   高电平代表着输出数据(visible)的时间锻, 可能是低电平有效, 这里和原理不太一样,原理上消隐信号应该只在等待回扫和回扫时间有效assign                              o_VGA_BLK                   = w_dat_act;//行场的计数器输出, 方便顶层读取当前位置assign                              o_VGA_ho_cnt                = (w_dat_act)? (r_cnt_h-VGA_h_visible_start):10'd0;assign                              o_VGA_ve_cnt                = (w_dat_act)? (r_cnt_v-VGA_v_visible_start):10'd0;
endmodule

总结:野火电子yyds

将VGA控制器分成三个子模块:

  1. PLL生成固定时钟
  2. VGA_CRL: 实现VGA协议的基本时序控制, 输出VGA数据以及可视区计数器
  3. VGA_PIC: 通过读取CRL的可视区计数器, 知道当前可视区像素位置, 将要输出的图像写在这里, 并把图像背后的像素数据发送给CRL, CRL会通过内部指针决定什么时候传递出顶层端口
    在这里插入图片描述

相关文章:

  • 【结构体宏定义】C语言结构体与宏定义:传感器配置的巧妙结合
  • transformer网络
  • 全栈开发中主流 AI 编程辅助工具的实践与对比分析20250522
  • thinkpad x220降频到0.7Ghz解决办法
  • 小白的进阶之路系列之三----人工智能从初步到精通pytorch计算机视觉详解下
  • Python 训练 day31
  • Python打卡训练营day32
  • 改写文章打造原创内容,ai智能改写工具在线高效完成!
  • 点云技术原理概要
  • Oracle 的V$ACTIVE_SESSION_HISTORY 视图
  • 大语言模型 18 - MCP Model Context Protocol 基本项目 测试案例
  • 技术篇-2.3.Golang应用场景及开发工具安装
  • 数巅智能亮相中国石油石化企业信息技术交流大会 以大模型能力驱动能源行业数智化升级
  • AIGC消除软件概览:优化内容创作的新工具
  • 嵌入式STM32学习——串口USART 2.3(串口发送数据控制LED灯)
  • [初阶--使用milvus向量数据库实现简单RAG]
  • 云曦25年春季期中考核复现
  • 滚珠导轨:重构精密仪器传动架构,开启微纳世界
  • MacBookPro上macOS安装第三方应用报错解决方案:遇到:“无法打开“XXX”,因为Apple无法检查其是否包含恶意软件 问题如何解决
  • python学习打卡day33
  • 电信宽带办理/索引擎优化 seo
  • 网站建设方案书 下载/东莞关键词优化推广
  • 车床加工东莞网站建设/做引流的公司是正规的吗
  • 免费源码html网站/短视频seo
  • wordpress hosts/百度seo推广方案
  • 企业所得税和增值税的区别/seo整站优化哪家好