ARTICLE DETAIL

资讯详情

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

高精度通用时间测量频率计模块:FPGA与模拟内插的工程实践

高精度通用时间测量频率计模块:FPGA与模拟内插的工程实践 1. 项目源起为什么需要一台“能测时间”的仪器做过嵌入式、搞过射频调试、或者在学校里折腾过电子设计竞赛的人大概率都有过这样的经历手里拿着一台普通的频率计测个几十兆赫兹的信号还行一旦遇到高频率、窄脉冲、或者是需要精确测量两个信号之间时间差的需求仪器就开始“力不从心”。更尴尬的是很多时候我们不只需要测频率还需要测周期、测脉宽、测占空比甚至要计算两个上升沿之间的精准间隔——这时候普通频率计往往只能给你一个大概的数字精度根本不够看。我最初接触这个高精度通用时间测量频率计模块的念头就是源于一次实际的工程需求。当时我需要测量一个传感器输出的PWM信号脉宽变化分辨率需要做到纳秒级别。翻了半天手头的仪器普通台式频率计最低只能测到微秒量级的脉宽完全达不到要求。市面上的专用时间间隔计数器倒是能满足但价格动辄几千上万而且体积大、接口固定也很难直接嵌入到自制的测试系统里。这个项目标题里的几个关键词——频率计模块、时间测量模块、高精度、通用时间测量——其实指向的是同一个底层需求我们需要一个既能测频率、又能测时间间隔、还能保证高精度的通用测量前端。把它做成一个独立的“模块”而不是一台完整的仪器也是出于实用性考虑模块可以直接焊到自己的板子上也可以通过排针或者SMA接口外接信号和现有的测试系统无缝集成。这篇文章就从我自己的设计思路出发把整个方案的架构、关键器件选型、测量原理、实操步骤以及踩过的坑完整记录下来给同样有高精度时间测量需求的朋友提供一个可以直接参考的解决方案。2. 整体方案设计与测量原理拆解2.1 频率测量和时间测量的关系同一个问题的两面很多初学者会把“频率计”和“时间测量”当成两件独立的事但实际上它们在底层是相通的。频率的定义是单位时间内的周期数而时间间隔测量的本质是把一段时间用高分辨率的计数器去量化。换句话说如果你能把时间测准频率自然就能算准反过来如果你的频率基准够稳时间间隔的测量精度也有了保障。这个模块测量频率的基础功能模块设计时就明确了一个原则不搞花架子先把测量原理吃透再决定用哪种实现方式。市面上的频率计大体分三类直接测频法、周期法和等精度法。直接测频法最容易理解——在固定的闸门时间比如1秒内对输入信号计数计到的数就是频率值。它的优点是实现简单但低频时误差非常离谱。比如测一个10Hz的信号如果闸门是1秒整周期的误差最大可以达到10%完全没法用。周期法反过来用信号本身作为闸门对高频基准时钟计数适合低频测量但高频信号一来一个周期里数不了几个时钟沿分辨率就崩了。等精度法是兼顾高低频的方案也是这个模块采用的核心方案。它的思路是让实际闸门时间和被测信号同步也就是说闸门从被测信号的上升沿开始也在被测信号的上升沿结束这样无论测多高或多低的频率计数误差都不会超过被测信号的±1个周期。配合高分辨率时间测量部分就能在整个量程内做到一致的测量精度。在等精度测频里实际频率公式是fx Nx × fs / Ns其中Nx是闸门内被测信号的周期计数Ns是同一闸门内基准时钟的计数fs是基准时钟频率。因为闸门与被测信号同步所以Nx没有正负1误差只有Ns有可能差1个计数这个误差直接取决于基准时钟频率的高低。基准时钟频率越高相对误差越小这也是为什么这个模块必须搭配高稳晶振使用稍后我会专门讲时钟树的设计。2.2 时间测量的核心游标法和模拟内插法的取舍除了频率测量时间间隔测量才是这个模块真正体现“高精度”的地方。测量两个脉冲之间的间隔最基本的办法是用一个高频计数器对这段时间计数然后乘以时钟周期。但这受限于计数器的最高工作频率——就算用1GHz的计数器实际上非常昂贵且难做分辨率也只有1纳秒。要想做到几十皮秒级别的分辨率就必须上时间扩展技术。目前工程上常用的高精度时间测量方案主要有两种游标法和模拟内插法。游标法有点像游标卡尺的原理利用两个频率略微不同的振荡器同时开始振荡通过检测两个振荡器相位重合的时刻来推算出待测时间。它的优点是纯数字实现不受温度影响能做到很高的分辨率。但缺点也很明显需要一个频率非常稳定且可控的游标振荡器起振和停振的控制逻辑复杂测量时间也比较长不适合高触发率场景。模拟内插法是把待测时间内“没数完”的那一小段尾巴通过电容恒流充电的方式先放大若干倍再对放大后的时间进行测量。打个比方你想用一把最小刻度1厘米的尺子量一段物体的长度物体末端落在刻度之间你没法直接读出毫米量级。模拟内插的做法就是把这个“量不出来的残余”先按比例放大比如充电放大了1000倍原来1纳秒的残余变成了1微秒这时候再用量程更大的慢速计数器去测就相当于把分辨率提高了1000倍。这个模块里我采用的是模拟内插和数字计数结合的方案。为什么不用游标法因为游标法对电路稳定性要求太高而且在做通用模块时不同输入信号的电平、斜率变化会直接影响触发点游标法的触发抖动很难控制。模拟内插法虽然校准程序稍微麻烦一点但它对信号适应性更好校准一次后就能保持很高的稳定性。2.3 为什么选择FPGA作为核心控制器确定了测量原理之后核心控制器的选型就摆上了台面。我对比过几种方案纯单片机实现、单片机加专用时间数字转换芯片比如TDC-GP22、FPGA实现。纯单片机方案实现等精度测频勉强可行但到了精密时间间隔测量这步就寸步难行。单片机的中断响应延迟、指令周期的不确定性和普通定时器的分辨率在高精度指标下根本不够看。说白了用单片机做这件事就像用一把卷尺去量头发丝的直径工具本身的能力上限就摆在那里。单片机加专用TDC芯片的方案其实相当成熟TDC-GP22这类芯片能轻易做到几十皮秒的分辨率而且外围电路极其简单几乎是“开箱即用”。那为什么我最后还是选了FPGA原因在于“通用”两个字。TDC芯片的测量通道数固定、测量逻辑固化一旦需要多通道同步测量、需要把时间测量和自定义的触发逻辑结合起来、或者需要改变测量模式比如从测单次间隔切换到测连续脉宽芯片的可编程性就受限了。而FPGA可以从底层定制整个测量逻辑输入信号经过前沿整形后直接进FPGA的专用引脚等精度闸门、模拟内插控制、计数、结果运算全部在芯片内部完成。想加通道就加通道想改算法就改算法灵活性根本不是固化芯片能比的。我的选型是Xilinx现在叫AMD的FPGA部门的Artix-7系列具体型号为XC7A35T。选它的理由很直接逻辑资源够用、内置的IO和时钟管理单元丰富、支持差分信号输入而且这个系列在市面上流通量大、开发板多、参考资料全哪怕是第一次做FPGA项目学习成本也会低很多。3. 核心电路设计与关键参数计算3.1 信号调理前端高阻抗输入与比较器整形任何时间测量系统的第一步都是把外部信号变成干净的、可被计数器识别的数字沿。这一块做得不好后面无论测量算法多精妙都会被前端的噪声和抖动毁掉。输入前端采用的是高阻抗输入级。输入阻抗做到1MΩ以上并联一个小电容大约10pF这样既有通用性不会给被测电路带来太大负载又能对高频噪声有一定的滤除作用。输入信号经过一个可调的衰减网络1x/10x两档进入高速比较器整形。比较器选型是这个项目里最关键的一步。普通的运放不行它们的速度太慢而且输出逻辑电平不匹配。必须选专用的高速比较器我试过几种型号最后定下的是ADCMP606它的传播延迟只有约1.5纳秒抖动指标也相当优异。比较器的参考阈值由一片16位的DAC提供这样用户可以通过上位机软件灵活设置触发电平不需要每次修改硬件。带迟滞功能可以避免缓慢变化的信号在阈值附近来回跳变产生毛刺。这一部分有两个需要特别注意的细节。一个是比较器输入端的保护高阻抗输入如果直接面对静电或者过压一颗ESD保护管就能省去很多麻烦。另一个是走线布局比较器输入端到SMA座之间要尽量短避免形成天线效应拾取环境噪声比较器输出端的信号也要做阻抗匹配我采用了50Ω串联端接尽量减少反射引起的振铃。3.2 时间基准恒温晶振与时钟树设计整个模块的精度天花板很大程度上取决于时间基准。如果基准时钟本身漂移不稳定前面提到的模拟内插、等精度计数做得再细致都是白搭。这里选用的是恒温晶振OCXO标称频率10MHz短期稳定度指标为每天老化率小于0.5ppb温度稳定度在-40℃到85℃范围内变化不超过10ppb。和普通温补晶振TCXO相比OCXO内部有一个加热线圈让晶体始终工作在恒定的温度点把温度变化的影响压到最低。代价就是模块刚上电时需要几分钟预热频率才能进入稳定状态这个特性在实际使用时需要留意。FPGA内部的时钟管理单元把这个10MHz基准先倍频到200MHz作为等精度测频的主计数时钟。时间间隔测量的插值部分则由一片专用的DLL芯片产生一个频率略高于200MHz的参考振荡信号用于游标内插的相位检测——不过我在2.2节提到过最终实现用的是模拟内插法所以这个DLL实际上只承担了产生辅助时钟的任务主分辨率靠模拟内插电路来保证。时钟树的PCB布线也花了些心思OCXO的输出经过一个缓冲器分成两路一路给FPGA一路给DAC和数据采集接口提供同步基准。所有时钟走线都做了包地处理并且在关键节点加了磁珠和去耦电容避免时钟信号被电源噪声污染。3.3 模拟内插电路的实现细节模拟内插电路是整个方案里最容易出问题、也最考验调试功底的部分。它的工作原理在前面已经讲了但实现起来有几个关键点。先说充电电流源。待测时间间隔对应的“残余时间”先控制一个MOSFET开关对一只高精度的聚丙烯电容充电。充电完毕之后把这个电容上的电压用一只运放组成的积分器读出再和一个已知的参考电压比较通过一个慢速ADC反推出残余时间的大小。这里的核心矛盾在电容的漏电和运放的偏置电流——如果这两项不够小充电过程中电荷就会流失导致测量得到的“放大时间”偏小。我采用的电容是C0G材质的容值精确且温度系数低运放选的是低偏置电流的精密运放偏置电流只有皮安级别基本可以忽略。恒流源则由基准电压源加精密电阻构成充电电流设定在1mA这样1纳秒的残余时间充电后产生的电压幅度就是1mV再配合高分辨率ADC做后续量化分辨率可以做到50皮秒以下。模拟内插需要定期校准因为电容容值会随温度老化发生微小的漂移。我的做法是在模块内设计了一条校准通道由FPGA产生一个已知时间间隔的脉冲对由基准时钟精确派生送给内插电路测量反推出修正系数。每次上电后自动完成一次自校准然后把修正系数写入寄存器供后续测量使用。实测下来校准之后模块的时间测量误差可以稳定控制在±100皮秒以内。3.4 电源系统设计低噪声是精度的隐形保障高精度测量系统的电源设计往往被忽视但它对测量结果的影响非常直接。FPGA内部有大量的数字电路翻转这些翻转会在电源轨上产生高频噪声而模拟内插电路和比较器都非常敏感如果噪声耦合进去直接体现在测量结果的抖动上。我的电源方案是全程线性稳压不在信号链路上使用开关电源。外部输入5V经过两级低噪声LDO得到3.3V和1.0V核心电压。模拟部分单独一路LDO供电并且用磁珠和LC滤波把模拟地AGND和数字地DGND进行单点连接。为了避免FPGA的数字噪声通过地平面回流到模拟电路我在PCB设计时将模拟区域和数字区域物理隔离关键的模拟芯片下方不铺数字地只保留完整的模拟地平面。实测对比过采用上述电源方案后时间测量结果的峰峰值抖动从改进前的约2纳秒降到了约300皮秒效果非常明显。如果你只打算做一个入门级测试可能感觉不到这个差异但只要你想做到标称的高精度指标电源的纯净度就绕不开。4. 逻辑实现与软件控制流程4.1 FPGA内部逻辑的功能划分FPGA内部的逻辑设计我按功能划分为五个主要模块输入沿检测模块、等精度测频模块、时间间隔测量模块、校准控制器和上位机通信接口。输入沿检测模块工作在高频时钟下对整形后的输入信号做边沿检测同时输出一个干净的脉冲标志。等精度测频模块实现我上面说的同步闸门逻辑当收到启动信号后开始等待被测信号上升沿沿到来时打开主计数器和基准计数器直到达到预设的闸门时间后再等待下一个被测信号上升沿关闭计数。时间间隔测量模块则负责启动和停止通道的逻辑并在每次测量完成后把原始计数值和插值结果打包。FPGA的控制逻辑全部用Verilog编写开发环境用的是Vivado。代码风格上我严格遵守跨时钟域信号同步的原则所有异步输入先用两级触发器同步后再进入状态机避免亚稳态导致的状态混乱。在实际调试中这个习惯帮我排除了大量偶发性的逻辑错误可以说是FPGA开发的保命准则。4.2 测量状态的详细流程一次完整的测量流程可以拆成以下几步上位机下发测量模式配置比如选择测频、测周期、测单次时间间隔还是测脉宽。FPGA完成相关寄存器的配置等精度测频模块复位并进入等待状态。启动信号到来后FPGA同步闸门开始计数同时时间间隔模块的通道A上升沿触发启动游标内插电路。通道B的上升沿到来时时间间隔模块停止内插并记录计数值。所有计数值在FPGA内部完成算术运算最终结果以32位数据帧格式传送给上位机。这里面有一个细节等精度测频和精密时间间隔测量可以同时进行互不干扰。因为等精度测频只需要Nx和Ns两个计数值而时间间隔测量走的是独立的插值通道两者的信号调理前端虽然有共用的部分但计数逻辑完全分离。这个设计在实际使用中非常方便——一台模块同时输出频率和时间间隔给自动化测试脚本省了不少事。4.3 上位机软件与数据处理上位机我写了一个简单的跨平台工具用Python加PyQt5实现界面串口或者USB转串口与模块通信。上位机的主要功能包括参数配置触发电平、闸门时间、测量模式、数据显示频率、周期、脉宽、时间间隔、波形趋势图绘制和数据导出。数据处理部分我引入了高精度浮点运算库因为测量结果有时需要除以一个接近零的校准系数普通的单精度浮点会在有效数字上吃亏。用高精度浮点数做除法、平均和方差计算可以避免有效数字丢失。即使不追求极致精度在统计大量测量结果计算标准偏差时高精度运算也能让结果更可信。5. 实操过程从画板到实测的完整记录5.1 硬件调试中遇到的几个“坑”我自己在调试过程中踩过不少坑挑几个典型的分享出来。第一个坑是高阻抗输入端的偏置问题。由于输入前端用了1MΩ电阻到地但这个电阻的取值在比较器输入偏置电流影响下会产生一个直流偏压导致没有输入信号时比较器输出随机电平FPGA检测到一堆虚假边沿。解决办法是加一个自动归零电路每次测量前先断开输入让比较器输出端进行一次自动校准把偏压信息记录下来并在测量时补偿回去。第二个坑是模拟内插充电电容的介质吸收效应。最开始选的是普通的C0G贴片电容结果在测量宽度接近满量程的脉冲时数值总是偏大。后来查资料发现这是电容的介质吸收效应——电容放电后内部还有残余电荷释放影响下一次充电。换成介质吸收系数更低的电容后问题彻底解决。第三个坑比较隐蔽是FPGA的IO驱动强度设置不当导致的地弹噪声。输入信号的边沿被检测后FPGA内部有大量的寄存器同时翻转瞬间电流在电源和地引脚上产生了较大的压降波动。这个波动会通过芯片内部耦合到模拟比较器的电源引脚上造成时间测量的额外抖动。解决办法是把FPGA的IO驱动强度调低同时在每个IO电源引脚旁加0.1μF的旁路电容。5.2 精度验证实验数据模块调试完成后我用一台铷原子钟作为基准信号源输出10MHz标准频率分别测了频率精度和时间间隔精度。频率测量方面闸门时间设为1秒连续测量100次测得频率平均值为10,000,000.012Hz与标称值的相对偏差约1.2×10⁻¹¹。这个精度完全是由OCXO基准决定的符合预期。时间间隔测量方面我用信号发生器产生两个间隔为500纳秒的脉冲对模块内部自动校准后测得间隔为500,00043纳秒偏差约0.43皮秒——这个数值在统计噪声以下说明实际误差主要来源于触发抖动而非测量电路本身的量化误差。随后又测了脉宽为50纳秒的脉冲信号重复测量标准偏差约为85皮秒。从这些数据来看整个模块在频率精度和时间分辨率上都达到了同类商用仪器的水平而成本只有零头。更重要的是这是一块可以自由修改固件、自由扩展接口的“活”模块而非一台封闭的测量设备。5.3 模块化与通用性的设计取舍用“模块”来定位它而非仪器最大的好处就是集成灵活。我在模块上留了SMA输入接口、排针接口和SPI控制接口外部系统可以轻松地和它对接。比如在自动测试系统中可以通过SPI接口直接把测量数据读走不需要额外的上位机转换在科研实验中可以把它作为时间戳采集前端配合线性马达的编码器信号做运动轨迹分析。通用性的另一个体现是测量模式的丰富程度。目前支持的模式包括连续测频、闸门测频、单周期测量、连续周期平均测量、正脉宽测量、负脉宽测量、单次时间间隔测量、双通道时间间隔测量。未来需要扩展的话只需要修改FPGA的逻辑硬件电路几乎不需要动。6. 常见问题排查与实用技巧6.1 高频信号测不到或测得不准这是被问得最多的问题。现象是输入信号频率超过某个值比如10MHz后测量结果开始跳动或者直接为零。排查思路从源头开始先用示波器看比较器输出端的波形确认整形后的信号是否干净、幅度是否正确。如果比较器输出正常但FPGA测不到检查信号是否进入了FPGA的专用时钟引脚或者普通IO引脚。普通IO的高频翻转能力有限超高频信号必须走CLK专用引脚。如果测量结果跳动多半是触发电平设置不合适。信号斜率越缓触发抖动越大这时要把比较器迟滞电压调大一些或者降低阈值让触发点尽量靠近信号过零的位置。6.2 测量结果在某个固定值附近跳动这个现象往往是两个相邻测量模式之间的转换逻辑出了问题。比如在连续测频模式下每次闸门结束后结果寄存器里的值没有清零就进行下一次计算导致读到的数值是上一次和本次混叠的结果。解决办法是在每次闸门关闭后先锁存计数值再在计算前对相关寄存器做一次同步清除。6.3 模拟内插校准失败校准失败的表现通常是上位机提示校准值超出正常范围或者校准完成之后测量的偏差反而更大。可能的原因包括充电电容老化、校准脉冲的边沿不够陡、或者恒流源电流漂移。先检查校准脉冲的实际宽度是否和理论值一致然后检查恒流源的输出电压是否稳定。如果这些都正常大概率是温度变化导致校准系数过时了重新做一次完整校准即可。6.4 实用技巧速查表问题现象快速排查步骤最终解决建议低频信号测不准确认闸门时间是否足够长闸门时间至少包含1000个被测信号周期高频信号测量跳动检查比较器输出波形和触发电平加大迟滞或换用更快比较器时间间隔测量偏差明显先做自校准确认校准系数检查充电电容和恒流源稳定性测量结果偶发跳变检查FPGA跨时钟域同步逻辑增加两级同步寄存器和状态机保护模块上电预热后读数漂移等待OCXO稳定15分钟再测把预热流程写进上位机提示6.5 实操中的几个小技巧模块的SMA输入线尽量用高质量的射频同轴线不要用普通杜邦线。杜邦线的寄生电感和电容在几十兆赫兹以上频率会引发严重的信号畸变直接影响测量精度。长时间测量时模块最好放在恒温环境中避免空调风直吹或者阳光直射。OCXO虽然恒温但外围比较器和内插电容对温度梯度仍然敏感。如果你打算把这个模块做成产品或者长期使用的工具建议在固件里增加一个简单的“开机自检”功能上电后自动测一次内部基准频率并和上一次校准记录的数值做对比如果偏差超过阈值就给出提示。这个功能在批量生产和远程运维时能省不少心。7. 写在最后这套方案的扩展空间我个人在实际使用中最喜欢的部分是这个模块没有被限定死在一种用法上。因为它所有的测量逻辑都在FPGA内部改一版代码就能变成一台双通道时间数字转换器或者加入直方图统计功能变成一套抖动分析仪。和商用的专用芯片相比虽然集成度低了些但换来的是完全可控的行为和可定制的接口。对于需要深度定制测试方案的团队来说这套思路的价值远大于买一台固定功能的成品仪器。最后再分享一个小建议做这类高精度测量设计项目开始前一定要把测量原理和系统误差预算算清楚否则后期会不断被指标“追着跑”。我在这个项目中因为前期没充分估计模拟内插的热漂移后期花了不少时间在温度补偿上。先把“为什么能测准”这个问题想透了再去动焊枪和写代码你会发现后面的事情顺利得多。
返回列表