Video Signals And Timing
警告:不正确地更改 CRTC 或硬件设置可能对视频卡和连接的显示器有害。 配置图形硬件可能是一个值得一做的过程,许多人已经成功实现了他们的目标,甚至篡改了他们远远超出他们的设备原始规格。但是仍然要注意,有“好几个”已知的显示器实例——CRT 和 LCD 等——在输入不良数据后会烧坏。同样,视频卡也可能缺乏防止意外配置错误的类似保护措施。 免责声明:所提供的信息可能不准确,使用这些信息完全由你自己承担风险。在 OSDev.org 的整个历史中没有报告过任何事故,但万一你破坏了某些东西,我们 不负责任。 |
为了使显卡和显示器相互独立,在通信方面有一个标准。 本页描述了该链接的技术部分,并提供了对视频卡进行编程并使其在所附屏幕上正确渲染的必要信息。
显示信号
标准VGA电缆中有15个针脚。 当你的视频卡将其视频数据发送到显示器时,它使用5个数据通道:
- 模拟红色(Analog Red)
- 模拟绿色(Analog Green)
- 模拟蓝色(Analog Blue)
- 水平同步(Horizontal Sync)
- 垂直同步(Vertical Sync)
通过在连接上发送适当的RGB值,逐条扫描线构建屏幕。 然而,只有颜色流不会告诉你流的哪一部分属于屏幕上的左上角像素。 为了解决这个问题,他们又增加了两个信号: 水平同步,当一条水平线完成时发出脉冲; 垂直同步,当所有行都完成时发出脉冲。 因此,每个帧对应于单个垂直同步脉冲。 由于每个帧具有相同的像素量,因此连续脉冲之间的时间是恒定的。 监视器乘以脉冲的频率,然后基于它知道颜色数据应该在什么时候发送到屏幕上的什么位置。
然而,这个系统并没有那么简单。 第一批显示器是CRT,它使用磁场将电子束投射到磷化荧光层上,使它们在短时间内可见。 CRT的磁场具有一定的惯性-无法在一个像素可用的时间内将其从随机位置设置为另一个随机位置。 因此,视频信号必须有一些间隙,以适应监视器需要改变磁场并将电子束指向屏幕另一侧的时间。 在此期间,可能没有颜色信号,或者屏幕上可能有条纹。
自那时以来,crt已经有了很大的改进,现在被高度智能的LCD显示器所取代。 此后,信号标准一直没有改变。 由于最初的标准是为CRT制定的,因此文档的其余部分将隐含地假定为(旧的)CRT。 LCD显示器基本上是对CRT信号进行解码,然后在模拟信令方法下对其进行重组以适合自己的屏幕网格。 类似地,在许多其它参考文献中,你会看到视频卡的显示单元被称为“CRTC”或“CRT控制器”
显示构成
视频信号的所有帧都有特定的布局,视频卡对这些信号有半标准的考虑方式。 基本上,你必须向视频卡提供足够的信息,以便能够导出下图中显示的所有尺寸。 VGA Hardware说明如何将这些尺寸提供给兼容的VGA。 对于其它硬件,你应该检查卡的文档,以了解如何将这些值存储在其中。
在水平和垂直方向上,有6种状态,因此有6种大小。 每种尺寸都以像素为单位。 大部分发射的数据是活动显示器的一部分,即x分辨率 * y分辨率像素的区域。 每一次削减都是在:
- 活动显示进入过扫描
- 过扫描进入消隐的地方
- 消隐使同步线改变的地方
- 其中同步恢复为其原始值
- 消隐回到过扫描的地方
- 过扫描进入活动显示,扫描线或扫描帧完成,下一个扫描开始。
显卡通常为你提供以下可编程寄存器(水平和垂直)
- 分辨率 (有源显示器的像素大小)
- 总数(一次运行中的“像素”总数)
- 消隐开始和结束(或开始和大小)-标记消隐的位置。既不消隐也不活动显示的区域变为过扫描区域。
- 同步开始和结束 (或开始和大小)-标记同步脉冲的位置
频率
当你提高分辨率时,你将需要向显示器发送更多像素。 如果继续以相同的速率发送像素,则传输一帧的时间将增加,因此特定时间间隔内的帧数将减少。 由于CRT显示的像素在耗尽能量之前只会发出短时间的光,因此需要反复刷新。 如果这样做的速度足够快 (大约60Hz,每秒60次),屏幕对人眼来说几乎是恒定的。 当刷新率上升到对人眼无关紧要的程度时,这种情况会进一步改善。 然而,当它下降太多时,屏幕开始闪烁,导致用户头痛。 因此,为了用户的理智,我们需要将频率保持在至少60Hz,并且低于显示器功能所规定的其它速率。 为了使完整的像素帧在六十分之一秒之内,我们必须调整这些像素的传输速度。 这种速度称为像素时钟,或点时钟。 例如,VGA的点时钟为25mhz或28MHz,对应于每秒2500万像素或每秒2800万像素,后者仅足以在60hz下显示720x480的分辨率 (回想一下,活动显示只是帧的一部分)。 因此,大多数较高分辨率的视频可以使用远高于25 MHz的大范围点时钟,当前的范围允许足够的带宽在100赫兹时轻松超过1600x1200。
虽然分辨率受到视频卡的限制,但在大多数非VGA场景中,显示器无法处理信号达到的速度。 监视器具有允许的垂直频率(每秒帧数,通常以Hz为单位)和水平频率(以KHz为单位)。 有些CRT是固定频率的,只允许水平和垂直使用某些频率。 当你向旧的VGA显示器提供与这些速率不完全匹配的信号时,它们会因此烧坏。 虽然现代的CRT基本上不受不良信号的影响,但你必须知道,你可以用这种方式破坏硬件,你需要小心。
通用计时公式
为了应对所有CRT旧设备,VESA产生了一组方程式,可让你在给定所需分辨率的情况下计算所需的各种显示设置。 有三套独立的公式: 一个用于计算分辨率和所需刷新率的所有设置,一个用于计算分辨率和水平频率,还有一个用于计算分辨率和点时钟。
通常在设置模式之前,你会执行以下操作:
- 根据分辨率和频率计算水平刷新率和像素时钟
- 检查显卡是否能提供最接近的像素时钟
- 计算给定分辨率和像素时钟的所有设置。
- 检查水平和垂直频率是否在监视器允许的范围内。
如果频率恰好超出范围,你将不得不调整刷新率和/或分辨率。 因为刷新率可以稍微降低一点而不会出现问题,所以这通常是要修复的第一选择。 因此:
- 将水平频率钳制到极限
- 根据分辨率和新的水平频率计算像素时钟和垂直频率
- 选择显卡具有的适当像素时钟(确保你正在舍入,如果你使用最大水平频率,则向下舍入)
- 在给定新的点时钟的情况下计算水平和垂直频率
- 检查频率是否在范围内。
水平频率现在应该是正确的,你可以决定是否允许新模式,因为垂直速率已降至可接受的速率以下,并且你可以通知用户不支持该模式。
VESA提供的公式使用了不同的常数标度。 Brendan正在努力把尺度换成单位。 目前,尺度空间仍然未知。
GTF公式的通用部分
H_PIXELS_RND = ( ROUND ( H_PIXELS / CELL_GRAN_RND ) ) * CELL_GRAN_RND
if ( INTERLACE_REQUIRED == true )
{
- V_LINES_RND = ROUND ( V_LINES / 2 )
- V_FIELD_RATE_REQUIRED = REFRESH_RATE_REQUIRED * 2
- INTERLACE = 0.5
} else {
- V_LINES_RND = ROUND ( V_LINES) )
- V_FIELD_RATE_REQUIRED = REFRESH_RATE_REQUIRED
- INTERLACE = 0
}
if ( #MARGINS_REQUIRED == true ) {
- TOP_MARGIN_LINES = ROUND ( MARGIN_PERCENT / 100 * V_LINES_RND )
- BOTTOM_MARGIN_LINES = ROUND ( MARGIN_PERCENT / 100 * V_LINES_RND )
- LEFT_MARGIN_PIXELS = ( ROUND ( H_PIXELS_RND * MARGIN_PERCENT / 100 / CELL_GRAN_RND ) ) * CELL_GRAN_RND
- RIGHT_MARGIN_PIXELS = ( ROUND ( H_PIXELS_RND * MARGIN_PERCENT / 100 / CELL_GRAN_RND ) ) * CELL_GRAN_RND
} else {
- TOP_MARGIN_LINES = 0
- BOTTOM_MARGIN_LINES = 0
- LEFT_MARGIN_PIXELS = 0
- RIGHT_MARGIN_PIXELS = 0
}
/* 在此处使用GTF方程之一 */
V_FRONT_PORCH = MIN_PORCH_RND + INTERLACE
H_SYNC = ( ROUND ( H_SYNC_PERCENT / 100 * H_TOTAL / CELL_GRAN_RND ) ) * CELL_GRAN_RND
H_FRONT_PORCH = H_TOTAL / 2 - H_SYNC
H_BACK_PORCH = H_FRONT_PORCH + H_SYNC
使用分辨率和刷新率的GTF
H_PERIOD_ESTIMATE = ( 1 / V_FIELD_RATE_REQUIRED - MIN_V_SYNC_AND_BACK_PORCH / 1000000 )
- / ( V_LINES_RND + 2 * TOP_MARGIN_LINES + MIN_PORCH_RND + INTERLACE ) * 1000000
V_SYNC_AND_BACK_PORCH = ROUND ( MIN_V_SYNC_AND_BACK_PORCH / H_PERIOD_ESTIMATE )
V_BACK_PORCH = V_SYNC_AND_BACK_PORCH - V_SYNC_RND
V_FIELD_RATE_ESTIMATE = 1000000 / H_PERIOD_ESTIMATE / TOTAL_V_LINES
H_PERIOD = H_PERIOD_ESTIMATE * V_FIELD_RATE_ESTIMATE / V_FIELD_RATE_REQUIRED
V_FIELD_RATE = 1000000 / H_PERIOD / TOTAL_V_LINES
if ( INTERLACE_REQUIRED == true ) {
- V_FRAME_RATE = V_FIELD_RATE / 2
} else {
}
TOTAL_ACTIVE_PIXELS = H_PIXELS_RND + LEFT_MARGIN_PIXELS + RIGHT_MARGIN_PIXELS
IDEAL_DUTY_CYCLE = C_PRIME - M_PRIME * H_PERIOD / 1000
H_BLANK_PIXELS = ( ROUND (
- ( TOTAL_ACTIVE_PIXELS * IDEAL_DUTY_CYCLE / ( 100 - IDEAL_DUTY_CYCLE ) / ( 2 * CELL_GRAN_RND ) )
- ) ) * 2 * CELL_GRAN_RND
H_TOTAL = TOTAL_ACTIVE_PIXELS + H_BLANK_PIXELS
PIXEL_FREQ = H_TOTAL / H_PERIOD * 1000000
GTF Using resolution and pixel clock
PIXEL_FREQ = PIXEL_FREQ_REQUIRED
TOTAL_ACTIVE_PIXELS = H_PIXELS_RND + RIGHT_MARGIN_PIXELS + LEFT_MARGIN_PIXELS
IDEAL_H_PERIOD = ( ( C_PRIME - 100 ) + ( SQRT ( ( ( 100 - C_PRIME ) ^ 2 ) +
- ( 0.4 * M_PRIME * ( TOTAL_ACTIVE_PIXELS + RIGHT_MARGIN_PIXELS + LEFT_MARGIN_PIXELS ) / PIXEL_FREQ / 1000000 ) ) )
- ) ) / 2 / M_PRIME * 1000
IDEAL_DUTY_CYCLE = C_PRIME - ( #M_PRIME * IDEAL_H_PERIOD / 1000 )
H_BLANK_PIXELS = ( ROUND ( TOTAL_ACTIVE_PIXELS * IDEAL_DUTY_CYCLE / ( 100 - IDEAL_DUTY_CYCLE ) / ( 2 * CELL_GRAN_RND ) ) ) * 2 * CELL_GRAN_RND
H_TOTAL = TOTAL_ACTIVE_PIXELS + H_BLANK_PIXELS
H_FREQ = PIXEL_FREQ / H_TOTAL
H_PERIOD = 1 / H_FREQ
V_SYNC_AND_BACK_PORCH = ROUND ( MIN_V_SYNC_AND_BACK_PORCH * H_FREQ / 1000000 )
V_BACK_PORCH = V_SYNC_AND_BACK_PORCH - V_SYNC_RND
TOTAL_V_LINES = V_LINES_RND + TOP_MARGIN_LINES + BOTTOM_MARGIN_LINES + INTERLACE + V_SYNC_AND_BACK_PORCH + MIN_PORCH_RND
V_FIELD_RATE = H_FREQ / TOTAL_V_LINES
if ( INTERLACE_REQUIRED == true) {
- V_FRAME_RATE = V_FIELD_RATE / 2
} else {
}
使用分辨率和水平频率的GTF
H_FREQ = H_FREQ_REQUIRED
V_SYNC_AND_BACK_PORCH = ROUND ( MIN_V_SYNC_AND_BACK_PORCH * H_FREQ / 1000000 )
V_BACK_PORCH = V_SYNC_AND_BACK_PORCH - V_SYNC_RND
TOTAL_V_LINES = V_LINES_RND + TOP_MARGIN_LINES + BOTTOM_MARGIN_LINES + INTERLACE + V_SYNC_AND_BACK_PORCH + MIN_PORCH_RND
V_FIELD_RATE = H_FREQ / TOTAL_V_LINES
if ( INTERLACE_REQUIRED == true)
{
- V_FRAME_RATE = V_FIELD_RATE / 2
} else {
}
TOTAL_ACTIVE_PIXELS = H_PIXELS_RND + RIGHT_MARGIN_PIXELS + LEFT_MARGIN_PIXELS
IDEAL_DUTY_CYCLE = C_PRIME - ( M_PRIME / H_FREQ )
H_BLANK_PIXELS = ( ROUND ( TOTAL_ACTIVE_PIXELS * IDEAL_DUTY_CYCLE / ( 100 - IDEAL_DUTY_CYCLE ) / ( 2 * CELL_GRAN_RND ) ) ) * 2 * CELL_GRAN_RND
H_TOTAL = TOTAL_ACTIVE_PIXELS + H_BLANK_PIXELS
H_PERIOD = 1 / H_FREQ
PIXEL_FREQ = H_TOTAL * H_FREQ
变量引用
以下是上述方程式中使用的变量:
硬件属性
对于给定的监视器,这些是恒定的。 它们描述了CRT的功能和局限性。
C_PRIME
- 默认为40,可以根据EDID数据计算出实际值。
M_PRIME
- 默认为600,可以从EDID数据计算实际值。
CELL_GRAN_RND
- 单元格粒度-计时应与之对齐的像素量。 例如,VGA以字符时钟的倍数使用水平计时,每个字符的宽度为8(图形和某些文本模式)或9(大多数文本模式)像素。 对于VGA图形模式,你需要在此处提供8。 根据你的特定硬件,常见的值为8或1。
MIN_PORCH_RND
- 通常为1。可能用于在同步周期周围强制至少一个消隐单位,以免混淆无法处理同步脉冲周围没有消隐的图形硬件。
MIN_V_SYNC_AND_BACK_PORCH
- 监视器需要检测垂直同步信号并随后返回屏幕左上角的时间(以微秒为单位)。默认值为550。
V_SYNC_RND
- 垂直同步脉冲有效的扫描线数量。 Todo 似乎取决于VESA没有告诉你的内容。 等于3,尽管在传统VGA模式12/13中使用了两条扫描线。
H_SYNC_PERCENT
- 应使同步脉冲处于活动状态的扫描线百分比。等于8%
视频模式输入
这些构成了用户/程序员的请求。
MARGINS_REQUIRED
- 页边距用于为显示的图片添加可见的黑色环境。 我假设小边距是为了确保当图片不在屏幕上完全居中时,整个图片仍然可见。
:理论上,可以使用边距在大屏幕上模拟小屏幕,或者完美地模拟不同的纵横比。 例如,如果要在16:10的显示屏上设置640*480视频模式,则可以将左右页边距分别设置为64像素(没有上边距或下边距),使其看起来与4:3显示屏上的完全相同,而不是拉伸以适应。 相反的情况也是可能的 (例如,在带有 “信箱” 的4:3显示器上显示16:10视频数据)。 不幸的是,GTF计算实际上不支持不同大小的左/右和上/下边距。
MARGIN_PERCENT
- (修复:百分比!)你希望在屏幕上显示的页边距大小。 VESA建议默认1.8,以允许屏幕不完全适合图像的中心。
INTERLACE_REQUIRED
- 有两种视频模式,隔行模式和渐进模式。 逐行模式一次显示完整的帧,而隔行扫描模式在每次运行中显示彼此的扫描线,采用两帧来渲染完整的画面。 通常,你不想要这个,许多视频卡并不支持它,所以你通常想在这里提供 “false”。
V_LINES
- 所需显示器的垂直分辨率。对于640x480分辨率,请提供480。
H_PIXELS
- Horizontal resolution of the display you want. 如果分辨率为640x480,请提供640。 请注意,由于 单元格粒度,视频卡通常希望这是八的倍数。
PIXEL_FREQ_REQUIRED
- 点时钟(或像素时钟)的速度,以像素每秒为单位。 这通常与点时钟的频率相同(以赫兹为单位),但在某些情况下存在2倍的差异 (时钟加倍时的频率为2倍,每隔一个周期发出像素时的频率为一半) 点时钟通常受到视频卡的限制,因为更高的帧率和分辨率需要更高的时钟。
REFRESH_RATE_REQUIRED
- 每秒field数值,单位为赫兹。 这也是非隔行模式每秒的帧数,或者隔行模式每秒的帧数的两倍。
H_FREQ_REQUIRED
- 所需的水平频率,单位为Hz。 在最常见的用法中,你会希望这是显示器的最大水平频率,以找到不违反显示器限制的最佳刷新率。
输出
GTF公式的最终结果。 这些参数与VGA CRTC型号的参数相对应。
PIXEL_FREQ
- 所需的点时钟(检查单位?)
H_TOTAL
- 扫描线中的像素量
LEFT_MARGIN_PIXELS
- 左边的边距量
RIGHT_MARGIN_PIXELS
- 右边的保证金金额
H_TOTAL
- 以像素为单位的水平同步持续时间
H_BACK_PORCH
- 水平同步脉冲与下一条扫描线开始之间的像素量
H_BACK_PORCH
- 活动显示结束和水平同步脉冲开始之间的像素量
TOTAL_V_LINES
- 帧中的扫描线数量
TOP_MARGIN_LINES
- 顶部的边距量。
BOTTOM_MARGIN_LINES
- 底部的页边距量
V_BACK_PORCH
- 垂直同步脉冲和下一个field开始之间的扫描线数量
V_FRONT_PORCH
- field结束和垂直回程开始之间的扫描线数量
生成完整信号所需的其余三个组件是输入。 作为参考,这些是: H_PIXELS V_LINES 构成分辨率,以及 V_SYNC_RND,这是给定监视器的常数。
Intermediates
其它结果是副产品,但可能对错误和兼容性检查有用。
V_LINES_RND
- 每帧活动显示扫描线的数量。 等于[#V_LINES|Y分辨率]],除非使用隔行模式。
H_PIXELS_RND
- 使用的实际水平分辨率。基本上包含X分辨率舍入到最接近的允许边界。
V_FIELD_RATE_REQUIRED
- 每秒发送到监视器的完整图像的速率。除非使用隔行模式,否则等于刷新率。
H_FREQ
- 以赫兹为单位的水平频率
H_PERIOD
- 扫描线上花费的时间(秒)
H_PERIOD
- 在扫描线上花费的时间 (以秒为单位) 的初始估计
INTERLACE
- 隔行扫描模式通过跳过半条扫描线来指示,以便水平同步移动半个周期。 等于0或0.5,具体取决于是否包含隔行扫描信号。
V_SYNC_AND_BACK_PORCH
- 用于VSync和底部消隐的扫描线数量
V_FIELD_RATE_ESTIMATE
- 每秒字段的初始估计值(检查单位?)
V_FIELD_RATE
- 每秒的field数(检查单位?)
V_FRAME_RATE
- 每秒的帧数 (检查单位?)
TOTAL_ACTIVE_PIXELS
- 水平可见像素的数量,等于活动显示加上过扫描
IDEAL_DUTY_CYCLE
- 百分比。计算水平冲裁量的步骤之一。
IDEAL_H_PERIOD
- 在扫描线上花费的最佳时间。 这通常无法实现,因为扫描线是整数像素数。
H_BLANK_PIXELS
- 水平方向的空白像素总数(包括水平同步)
Brendan的旁注
引述:
我认为部分问题是VESA的原始GTF公式是从他们的电子表格中复制的,因此 (举一个愚蠢的例子) 而不是做类似的事情:
frequency = 10000000 period = 1 / frequency seconds
他们会:
frequency_in_MHz = 10 period_in_ms = 1 / (frequency_in_MHz * 1000000) * 1000
然后将其简化为:
frequency_in_MHz = 10 period_in_ms = 1 / frequency_in_MHz / 1000
然后,他们将删除他们所做的任何指示:
frequency = 10 period = 1 / frequency / 1000
最后,来自VESA的公式变成了一团混乱,你永远不会太确定是否有隐藏的比例因子。 另一个简单的例子(不是虚构的)是“MARGINS_PERCENT”,它是一个范围从0到50的值,通常除以100;它不是一个从0到0.5的值,不需要缩放。
我计划再次通过公式,并确保所有变量都是像素,扫描线,秒或赫兹 (没有隐含/隐藏毫秒,微秒,千赫兹,兆赫或 “百分比” 比例因子)。
我还注意到,每个公式中的一些计算是相同的。 与拥有3个独立的/更大的代码片段不同,它可以拆分成3个较小的代码片段,这些代码片段都使用相同的子例程来处理常见的事情。 例如,你可以用 “call do_precalculations” 替换每个公式中的大约10行,以避免代码重复 (重复三遍?)。
此外,我只发布了“基本公式”。 还有一个“二级公式”,其中第一组计算的结果被用来寻找其它参数;还有另一个计算(称为“空白公式”),用于从一些称为C、M、J和K的变量中找出“C_Prime”和“M_Prime”的值,该变量用于“二级计时”。 我想对这些公式做同样的事情,就像我对主要计算所做的那样。 然后,我需要检查所有这些参数,找出哪些参数真正有用(既可以使用VBE设置视频模式,也可以在“裸机”视频驱动程序中设置视频模式),然后反向删除冗余计算(即仅用于创建不需要的信息的计算)。 完成此操作后,我可以将其与“公共子例程(common subroutines)”思想相结合,这样整个“二级公式”最终将成为“do_postculations()”例程的一部分,并且整个空白公式将成为“do_preculations()”例程的一部分。
然后我想找到所有计算中使用的所有变量的有效范围。 这主要是取决于我计划如何实现它。 例如,如果我知道“H_FREQ”的范围是50000到1000000,那么我就可以使用这个变量的“20:12”定点来提高从32位整数得到的精度。
最后,如果你假设正在使用 “默认GTF值”,则所有这些计算都可以大大简化。 我想提供公式的简化版本,因为大多数时候你可以使用默认的GTF值,并且可以跳过很多工作。
另见
论坛
- Getting a list of usable video modes - with emphasis on usable
外部链接
- http://www.cs.unc.edu/Research/stc/FAQs/Video/GTF_V1R1.xls is one of the GTF spreadsheets you can find on the web.
- VGA connector on wikipedia
- EDID on wikipedia