ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

GC4653帧率调试实战:VTS/HTS寄存器计算与联动避坑指南

GC4653帧率调试实战:VTS/HTS寄存器计算与联动避坑指南 做摄像头调试的朋友应该都有经验想让GC4653这颗sensor跑出想要的帧率光在驱动里改个delay是没用的真正控制帧率的地方就藏在两个寄存器里——VTS和HTS。这篇我直接按实际调试的思路把这两个参数怎么算、怎么改、改完要关注哪些连带影响完整走一遍计算步骤和踩过的坑都会写出来你可以直接照着操作。先说结论sensor的帧率不是随便设一个“目标帧率”就完事它由像素时钟PCLK、每行像素数HTS、每帧行数VTS三者共同决定。想从13fps提到20fps、或者从30fps降到25fps本质上都是在调VTS和HTS这两个值只是怎么调法有讲究调完还要防着一堆坑。1. 帧率为什么由VTS和HTS决定1.1 从“逐行读出”理解sensor的时钟流水线CMOS sensor的读出方式和显示器扫描类似一帧图像不是一口气全出来的而是从上到下一行一行读出。你可以把sensor想象成一条流水线每个像素经过模数转换、信号处理之后在PCLK的节拍下打包送出去。每一行需要多少个PCLK周期每一帧包含多少行这两个数一旦定下来整帧的时间也就定死了。这里的行和列不是随便取的。一行里除了有效像素还要包含水平消隐HBlank一帧里除了有效行还要包含垂直消隐VBlank。这就是HTS和VTS的由来HTS也叫line_length_pck单位是“像素时钟周期/行”。它等于一行有效像素加上水平消隐的总长度。VTS也叫frame_length_lines单位是“行/帧”。它等于一帧有效行加上垂直消隐的总行数。PCLK是源头HTS是每一行消耗的节拍数VTS是每一帧包含的行数。三者相乘再取倒数就是帧率。这就像公路上的车队PCLK是总车流量HTS是每辆车的长度VTS是车队里有多少辆车整支车队通过一个路口的时间就是HTS×VTS帧率就是每秒能通过多少支车队。这个模型适用于几乎所有CMOS sensorGC4653也不例外。搞懂它后面控制帧率就是小学数学。1.2 GC4653里的HTS与VTS到底是什么GC4653是格科微的400万像素sensor常用在IPC、USB摄像头、内窥等场景支持MIPI CSI-2输出。分辨率常见的是2688×1520实际画面输出时SoC侧读到的尺寸只是有效区域sensor内部还在同步处理消隐区。HTS就是一条完整行扫描线上的总长度VTS就是一帧完整扫描的总行数。在GC系列sensor上HTS和VTS通常都是16位寄存器分高字节、低字节存放常见映射是0x380c/0x380d对应HTS0x380e/0x380f对应VTS。注意不同批次、不同版本可能有差异在实际操作前建议先查一下手上这颗料对应的datasheet寄存器表不要直接套网上说的地址。寄存器是16位的意味着可配置范围很大HTS可以到65535VTS也可以到65535所以sensor能支持的帧率范围其实非常宽。注意HTS和VTS虽然是两个独立寄存器但sensor内部很多模块都会引用它们比如曝光计算、MIPI打包、内部时序生成。改的时候不要只改一个值要把相关联动一起考虑。2. 帧率计算一条公式走天下2.1 公式拆解与单位关系帧率计算最核心的公式就一条frame_rate PCLK / (HTS × VTS)单位说明PCLK像素时钟单位Hz比如72MHz就是72_000_000 Hz。HTS每行像素时钟数单位“个PCLK/行”。VTS每帧行数单位“行/帧”。frame_rate帧率单位fps。拆开来看就是line_time HTS / PCLK // 一行需要的时间单位秒 frame_time VTS × line_time // 一帧需要的时间单位秒 frame_rate 1 / frame_time // 每秒多少帧这三个等式其实是同一个东西理解line_time对后面调曝光很有帮助。比如PCLK72MHzHTS2304时line_time 2304 / 72_000_000 ≈ 32微秒也就是说这一行数据读出的时间大约32微秒。曝光寄存器里的数值本质上就是“曝光了多少行”曝光时间曝光行数×line_time。所以当你听到别人说“曝光10行”脑海里要立刻算出行时间是多少这是调帧率、调曝光的基本功。2.2 一个真实场景的完整计算示例拿一套我调过的简化参数做演示具体数值以你手上的datasheet和驱动为准但计算过程是通用的。假设当前GC4653配置如下PCLK 72MHzHTS 2304VTS 2400当前帧率frame_rate 72_000_000 / (2304 × 2400) 72_000_000 / 5_529_600 ≈ 13.02 fps也就是说当前实际大概13fps。现在我想把它提到20fps目标20fps时PCLK和HTS不变反推VTSVTS PCLK / (HTS × frame_rate) 72_000_000 / (2304 × 20) 72_000_000 / 46_080 ≈ 1562.5取整为1562。再把1562换算成16位寄存器的两个字节1562的十六进制是0x061A所以高字节写0x06低字节写0x1A。改完验证一下实际帧率frame_rate 72_000_000 / (2304 × 1562) 72_000_000 / 3_598_848 ≈ 20.01 fps目标20fps实际20.01fps误差在千分之一以内完全可以接受。计算过程看起来简单但实际工作中很多人会在这里出错常见的问题是取整方向搞反导致帧率比目标略低或略高对于需要精确同步的场景比如多机同步拍摄最好通过小数计算确认取整后的值落在目标附近。2.3 为什么优先动VTS而不是HTS同样是调帧率有人习惯动HTS有人习惯动VTS。我的经验是优先动VTS尽量不动HTS。原因有三点。第一HTS直接影响MIPI数据率。HTS变小同样PCLK下留给每行有效像素的时钟周期变少MIPI lane的传输速率要求变高一旦超过sensor或者SoC接收端的上限图像就会出现花屏、条纹、丢帧。VTS只影响垂直方向的消隐行数不改变每行内部的数据打包节奏对MIPI带宽几乎没有影响风险小得多。第二HTS影响曝光计算。前面说过line_time HTS / PCLK改HTS等于改了一行的时间所有曝光档位的实际时间都会变化这会导致自动曝光算法重新收敛画面亮度可能出现明显波动。而动VTS不会改变line_time曝光行数对应的实际时间不变对已有曝光策略影响最小。第三HTS通常和sensor内部DDR读写、HDR合成等模块强相关很多sensor的HTS并不是完全自由的它必须满足某个对齐关系随便改小容易出现“有效像素写不进一行”的硬件问题。VTS的自由度就高很多只要保证大于等于有效行数加最小消隐基本都能正常工作。除非你就是为了压MIPI带宽或修改行时间才动HTS否则日常帧率调整把VTS作为第一改的对象是最稳妥的。3. 完整实操从读寄存器到验证帧率3.1 第1步读取当前HTS/VTS和PCLK动手改之前先搞清楚sensor当前处于什么状态。读寄存器最直接的方式是用i2c-tools。先把I2C总线地址搞清楚。GC4653的I2C地址通常是0x297位地址或0x528位地址具体看硬件原理图。读取0x380c和0x380d得到HTS的高字节和低字节读取0x380e和0x380f得到VTS的高字节和低字节。比如i2cget -y 0 0x29 0x380c b i2cget -y 0 0x29 0x380d b i2cget -y 0 0x29 0x380e b i2cget -y 0 0x29 0x380f b假设读回的值是0x380c 0x090x380d 0x00HTS 0x0900 23040x380e 0x090x380f 0x60VTS 0x0960 2400PCLK怎么拿通常有三个来源datasheet里的推荐配置表会列出不同分辨率对应的PCLK。驱动初始化序列里的寄存器组根据MIPI速率和data lane数推算。有些sensor有读回PCLK的寄存器但大多数情况下需要自己算。如果sensor输出MIPI 1-lane、速率720Mbps去掉打包开销后的PCLK大约是72MHz。这个换算关系需要结合MIPI配置判断不同平台差异很大。实在拿不准就用示波器测MCLK和帧同步信号用一个已知帧率的配置做反推也能算出有效PCLK。3.2 第2步在初始化序列或运行时修改拿到当前值后有两种方式修改。方式A临时验证运行时用i2cset直接写寄存器。这种方法适合调试阶段秒级生效不用重新编译内核。# 把VTS设为1562即0x061A i2cset -y 0 0x29 0x380e 0x06 b i2cset -y 0 0x29 0x380f 0x1A b写完后马上测帧率看现象。如果OK再固化到驱动里。这种方式最大的价值是快速排除“寄存器写没写进去”“方向是否搞错”这类低级问题。方式B静态修改驱动初始化数组。调试通过后把最终确定的寄存器值替换到sensor驱动里。要特别注意很多驱动里有preview、capture、video多组初始化序列每一组都可能写HTS和VTS只改一组会导致切换模式后帧率又变回去。用grep搜一下0x380e这类寄存器关键字把出现在所有模式数组里的相关配置全部更新。方式C如果驱动实现了V4L2的blanking控件还可以通过v4l2-ctl动态控制。但GC4653的驱动不一定暴露这个入口看具体驱动实现。能用控件直接调是最好的因为驱动会在底层帮你处理很多联动逻辑。3.3 第3步验证帧率改完寄存器验证帧率是最容易翻车的一步因为很多人只看应用层的“帧率”数字那个数字很可能是缓存或插帧算出来的不是真实帧率。推荐从sensor侧验证。最简单的方法用示波器测量sensor的FSIN或VSYNC引脚数一数一秒内的脉冲个数这就是物理帧率最准确。如果板子上没有引出测试点可以用逻辑分析仪抓I2C通信、或者用SoC侧的时间戳来估算。用v4l2-ctl抓帧并统计时间间隔也能得到大致帧率。v4l2-ctl --device/dev/video0 --stream-mmap --stream-count120 --stream-to/dev/null --stream-poll这个命令会抓120帧配合日志里的时间戳可以算出平均帧率。注意要把应用层的自动暂停、丢帧机制关掉否则统计会有偏差。还有一种更直观的方法用不同帧率连续拍两张带秒表的画面对比实际计时和帧数的关系误差在1%以内都算正常。4. 修改VTS/HTS时的联动参数曝光、消隐、帧长4.1 VTS与最大曝光行数的关系很多人在调高帧率后遇到“画面变暗”的问题以为是sensor坏了其实是VTS减小后曝光行数超过了VTS的限制。sensor是逐行曝光的曝光行数必须小于等于VTS否则一帧还没读完整下一帧的曝光已经开始画面会出现奇怪的明暗条纹甚至全黑。为了避免这种情况sensor驱动在计算曝光时会有一个限制曝光行数 最低消隐需求 ≤ VTS。假设上面例子里当前VTS2400时曝光最大能到2350行左右对应曝光时间约75ms改成VTS1562后最大曝光行数只能到1510行左右对应曝光时间约48ms直接缩水三分之一。如果场景光线不好自动曝光会把曝光值推到上限结果就是画面比之前暗。所以每次改了VTS一定要同步确认当前场景的最大曝光需求。经验做法是最大曝光时间 (VTS - 安全余量行数) × line_time安全余量一般留50~100行防止sensor内部切换、增益计算时出现越界。4.2 改HTS的连带风险有些场景确实需要改HTS比如强制把某档帧率压到某个数值且VTS已经调到很大还不够。但改HTS前必须确认三件事第一HTS不能小于一行有效像素的开销总和。GC4653输出2688×1520时一行有效像素至少需要2688个PCLK周期再加上打包同步、消隐开销HTS通常要比有效像素多几十到几百。如果HTS设得比有效像素还小这一行数据根本装不下图像会直接撕裂。第二HTS减小后MIPI带宽是否会超限。MIPI lane速率有上限HTS越小每行数据必须在更短时间里打出相当于提高了瞬时带宽。如果超了sensor或接收端的能力就会出现周期性丢数、竖条纹。这个可以通过抓raw图看竖条纹或右侧花屏基本就是带宽问题。第三HTS变化带来的行时间变化会影响所有曝光档位。HTS的修改会让line_time成比例变化如果HTS变了曝光时间全部跟着变自动曝光需要重新收敛。调试时如果发现改HTS后亮度跳变不用惊讶属于正常现象但要注意这个跳变是否在可接受范围内。4.3 多模式同步修改的必要性GC4653驱动里通常不只有一组初始化序列。拿Linux V4L2 subdev驱动举例常见的模式数组可能有全分辨率模式裁剪模式高帧率模式HDR模式每一组数组里都有HTS和VTS的配置。如果只是临时用i2cset改了runtime寄存器切换模式后sensor会重新走初始化序列帧率又变回原来。解决方法是把改动同步到每个模式并且检查suspend/resume流程。很多板子在休眠唤醒后会重新下发初始化序列runtime的修改会被覆盖。我踩过最狠的一次坑是preview模式帧率正常一切到capture就回到旧帧率排查了半天最后发现capture模式数组里单独写了个VTS根本没有用到runtime的值。从那以后我每改一个参数都会grep一下整个驱动目录确保没有遗漏的寄存器写入点位。5. 常见问题与排查技巧实录5.1 帧率没变先查“谁覆盖了寄存器”改了VTS/HTS帧率纹丝不动这在调试初期很常见。排查顺序是这样的用i2cget读回寄存器确认写入真的生效。读回值和写入值不一致说明I2C通信有问题或者sensor处于stream off状态寄存器被锁存。读回正常但帧率不变检查驱动里是否有定时器、线程会周期性刷新初始化序列。有些平台有sensor健康检测线程每几秒会重发一遍注册表。检查是否切换了模式。如果你改的是preview数组但当前停在capture模式自然看不到效果。确认PCLK是否和计算时一致。如果输入时钟或PLL配置和预期不同帧率也会偏。这个过程最快的方式是拉I2C总线日志看实际写进总线的数据是什么而不是只盯着驱动源码里写了什么。5.2 画面变暗或亮度跳变看曝光行数上限前面说过VTS减小后最大曝光行数被压缩画面会变暗。如果你确认VTS改完后画面亮度明显下降优先检查自动曝光是否顶到了上限。可以在调试时把曝光锁定在一个中间值看画面亮度是否恢复这样就能判断是曝光问题还是sensor输出有问题。如果画面不是均匀变暗而是上半部分和下半部分亮度不一致多半是曝光行数与VTS的关系没处理好出现了行间亮度干扰。把这个值压到VTS的90%以内通常能解决。5.3 高帧率下出现条纹或花屏查MIPI带宽和HTS高帧率下出现竖条纹或者画面右侧有雪花点大概率是MIPI带宽问题。出现这种情况优先回退HTS或者降低MIPI速率把瞬时带宽降下来。如果必须保持高帧率和高带宽要检查MIPI lane数是否足够PCB走线是否过窄导致信号劣化。横向条纹则可能是光源频闪比如在50Hz工频环境下曝光时间设置为11ms就会和100Hz频闪的负半周产生拍频画面上出现来回滚动的条纹。解决办法是让曝光时间尽量落在光源频闪周期的整数倍上。5.4 常见问题速查表现象可能原因排查方法帧率完全没变寄存器被初始化数组覆盖grep驱动中所有VTS/HTS写入点同步修改帧率比预期低VTS取整方向偏大用目标帧率反推取整后再验算一次画面整体变暗最大曝光行数被VTS压缩检查曝光是否顶到上限增大VTS或降低增益需求竖条纹/右侧花屏HTS过小导致带宽超限回退HTS检查MIPI速率和lane配置亮度上下不一致曝光行数接近VTS上限降低目标曝光行数留出安全余量横向滚条纹曝光时间和光源频闪不匹配调整曝光时间到光源周期的整数倍读回值和写入值不同I2C时序问题或地址错误用示波器对比SDA/SCL时序核对地址字节序最后说一点个人体会帧率调整看起来是个寄存器修改的活但真正考验人的是对“联动关系”的理解。我每次调帧率都会先把目标帧率反推成VTS再用新的VTS计算最大曝光行数确认场景曝光需求是否满足然后才动手改寄存器。顺序反了就容易出现“帧率上去了画质毁了”的尴尬局面。还有个小技巧调试阶段不要一上来就改驱动数组先用i2cset在运行时把目标值打进去确认所有现象正常后再回填到驱动这样每次验证能省下大量编译和烧录时间。
返回列表