尧图精选

正余弦编码器ADC采样时序陷阱:8 LSB台阶的根因与修复

🕒 发布时间:2026/10/1 1:37:02 📁 来源:尧图网络
1. 现象正余弦编码器角度轨迹上的8 LSB台阶1.1 现场描述前阵子调一套高精度运动控制板前端用的是正余弦编码器输出Sin、Cos两路差分信号经调理电路后送到两片14位SAR型ADC做同步采样FPGA负责时序控制和角度解算。基本原理不复杂把两路AD采样值做atan2运算就能得到机械角度再差分得到速度。整套链路里ADC看起来是最“标准”的一环数据手册指标也都正常14位分辨率、无失码、INL在几个LSB以内。但实际跑起来角度数据和理论预期对不上。现象非常典型编码器匀速转动时角度值本应线性递增但细看波形每隔一段会出现一个固定的小台阶台阶高度换算成ADC码值不多不少正好8个LSB。用上位机把裸采样数据拉出来也验证了这一点——两路ADC的输出并不是平滑递进而是走8个码、停一拍、再走8个码像是有人在中间隔了一拍才让数据通过。这个台阶肉眼扫一眼可能觉得“就是量化噪声嘛”但在闭环系统里后果很直接速度环反馈出现周期性扰动电机低速运行时能听到明显的“咯噔咯噔”声。这个案例值得写出来是因为问题根源不在ADC本身而在采样时序的理解上。尤其对刚接触FPGAADC配合的工程师很容易踩进去看着数据手册里的BUSY、DRDY、SCK时序图觉得都懂一到Verilog里写锁存逻辑就差那么一拍系统性能就打了折扣。1.2 把8 LSB换算成看得懂的误差先算一下8 LSB到底是个什么量级。以常用的2.5V参考电压、14位ADC为例1 LSB 2.5V / 2^14 2.5V / 16384 ≈ 152.6µV 8 LSB ≈ 1.22mV如果换算成角度假设正余弦编码器信号经过调理后满摆幅正好对应ADC的满量程一圈360度对应16384个码那么8 LSB对应的角度就是360° / 16384 × 8 ≈ 0.176°单看0.176度似乎不大但放在高精度运动控制里这相当于把一个14位编码器系统的有效分辨率直接降到了11位左右因为按8步长跳跃等效只用了2048个台阶。再叠加机械减速比、负载惯量反映到末端执行机构上可能就是一个不小的位置偏差。更要命的是这个台阶是周期性的、确定性的而不是随机噪声控制器学不会“忽略”它只能跟着抖。所以“8 LSB台阶”不是精度完美主义者小题大做是实打实的系统级缺陷。排查的时候我第一反应是查电源纹波、参考电压、模拟前端带宽折腾了一圈最后发现真凶藏在数字时序里这一步排查思路值得展开讲讲。2. 根因14位ADC采样时序中的“半拍”陷阱2.1 从数据手册读出的关键参数我用的ADC是常见的一类并行/SPI接口SAR型芯片。这类芯片有一个共同的标志性信号转换完成指示有的叫BUSY有的叫DRDY国产芯片上还可能叫EOC。它们的工作流程都差不多外部给一个启动转换脉冲比如CONVST或CNV引脚拉低一下芯片内部开始逐次逼近此时BUSY拉高转换完成后BUSY拉低同时数据寄存器里的结果准备好可以对外读出了。卡点就在“BUSY拉低”和“数据真正稳定”之间。数据手册上有一组参数专门描述这段时间比如tOD、tDO、tVALID意思是输出使能之后到数据线上出现有效逻辑电平的延迟。很多SAR ADC的这个延迟在10ns到40ns之间看起来很短但对于系统时钟动辄50MHz、100MHz的FPGA来说这就是一到两个时钟周期的事情。我当时的错误很典型状态机里检测到BUSY下降沿下一个时钟周期就直接去锁存数据总线。严格来说这等于在数据的建立时间窗口内拍了一下锁存到的值可能还是上一拍的旧数据或者正处在翻转中间的不稳定值。2.2 为什么多看的这一拍如此关键这里的“多看一拍”指的是在检测到BUSY下降沿之后不立即锁存数据而是先在代码里插入一个或半个等待周期让数据线真正稳定下来再去采样。为什么这么小的延迟会造成周期性的8 LSB台阶关键在于旧值和新值的差值。编码器匀速旋转时两路正弦信号以固定频率振荡ADC每个采样周期里信号变化量基本恒定。比如某个工作条件下一个采样周期内Sin通道的信号自然增量正好对应8 LSB那么如果FPGA有一小半几率读的是旧值系统就会看到“当前值和上一拍相同下一拍又比当前值大16 LSB”的跳变。换个角度看就是从原始16 LSB的均匀步进变成了8 LSB步进、0步进、8 LSB步进交替数据序列上就形成了等宽的台阶。这类“半拍”问题还有另一种表现不是读旧值而是读到亚稳态。如果恰好锁存在数据线翻转的中间点D触发器输出可能在一个时钟周期内是不确定状态下一拍才稳定下来。这会让台阶高度变成随机的而不是固定的8 LSB。我这次遇到的是固定8 LSB说明数据线翻转很快、延迟非常一致状态机每次读到的时机都稳定地“早了半个有效窗口”所以台阶才那么整齐。2.3 为什么偏偏是8 LSB而不是随机跳变这大概是整个排查过程里最让人困惑的一点。如果纯粹是亚稳态台阶应该是跳跃的、不重复的如果纯粹是外部干扰台阶应该是抖动而非固定步长。但8 LSB台阶在多次上电、多次匀速跑动实验中都稳定复现说明它是一个确定性的系统误差和信号斜率、采样周期有固定的数学关系。验证方法很简单把电机速度翻倍再观察台阶高度。如果信号斜率翻倍而“少看那一拍”的时间不变那么旧值和新值的差值也会翻倍台阶应该从8 LSB变成16 LSB。实测确实是这样的——台阶高度从8 LSB变成了16 LSB左右这基本就锁定了问题方向读取时刻相对数据有效时刻的延迟是固定的而信号变化率是可变的。再做一个反向验证把ADC采样率提高保持电机速度不变每个采样周期内信号增量会变小8 LSB台阶也会等比缩小。当采样率高到一定程度台阶缩小到1-2个LSB以内看起来就像普通量化噪声了这时候问题就被“掩盖”了。但掩盖不等于解决所以最终还是得回到时序上动刀。3. 修复采样逻辑多看一拍的正确姿势3.1 修改锁存时序的两种做法确定了根因之后改动很小但要改得规范。我在FPGA里用了两种处理结合起来效果最好。第一种是“数据延迟锁存”原本检测到BUSY下降沿后立刻读数据改成检测到BUSY下降沿后延时一个系统时钟周期再读。系统时钟50MHz一个周期20ns覆盖了ADC约30ns的数据有效延迟。代码很短reg busy_d, busy_dd; wire busy_fall busy_d ~busy_dd; always (posedge clk) begin busy_d busy; busy_dd busy_d; end // 检测到下降沿后晚一个时钟再锁存 reg [13:0] adc_data_reg; always (posedge clk) begin if (busy_fall) begin adc_data_reg adc_data_reg; // 不动作留给下一拍 end else if (busy_go) begin adc_data_reg adc_data; // 这一拍数据已稳定 end end实际工程里我不会真的写得这么绕直接用一个延迟计数器reg [1:0] delay_cnt; always (posedge clk) begin if (busy_fall) delay_cnt 0; else if (delay_cnt 2) delay_cnt delay_cnt 1; else adc_data_reg adc_data; end第二种是“打两拍同步”把BUSY信号本身先打两拍消除跨时钟域带来的亚稳态同时用打拍后的边沿作为锁存使能。这个方法在FPGA工程里是基本功但很多人只在对外部异步信号处理时用没想过对ADC完成信号也要这么干。3.2 参数计算延迟多少纳秒才够延迟不是越久越好。虽然“多看一拍”能解决建立时间不足但如果看得太多拍数据虽然稳定却已经是“过期数据”了。对正余弦采样来说Sin和Cos两路必须保持同步如果一路多延迟了几拍、另一路没有atan2算出来的角度就会带上相位误差表现为低速时的角度非线性。所以需要按数据手册算出一个安全窗口。以我用的那款ADC为例BUSY下降沿到数据输出有效最大30ns FPGA系统时钟周期20ns50MHz 建议锁存时刻BUSY下降沿后 30ns ~ 80ns 之间30ns大约是1.5个时钟周期80ns是4个时钟周期。我最后选的是BUSY下降沿之后数到2个时钟周期再锁存也就是40ns处锁存。这个位置既保证了数据已经完全建立又不会因为延迟过大引入明显的相位滞后。如果时钟换成100MHz周期10ns同样的ADC建议数到3拍也就是30ns处锁存留一点余量。这里有个通用原则锁存时刻不要卡在参数边界上。比如ADC的tOD最大30ns、典型20ns你按典型值20ns算作1个时钟去锁存就可能在最坏情况芯片上踩坑。按最大值再加20%余量来算颗粒度粗一点、稳定一点比省那一拍划算得多。3.3 验证台阶消失后的实测数据对比改完时序后重新跑同样的匀速实验。裸数据曲线从“走8步、停一步”变成了均匀的、接近理论斜率的递增序列。由于ADC本身还存在INL/DNL相邻点差值不会完全等长但不再出现周期性的大台阶。角度轨迹上的锯齿消失速度环反馈波形从周期性脉动变成了接近白噪声的小幅抖动电机的“咯噔”声也听不到了。更直观的验证是把同一段数据拿去做直方图统计。修复前采样点集中落在若干个间隔8 LSB的窄带上修复后采样点在满量程范围内的分布明显更均匀量化步长回到了真实的1 LSB水平。这个统计方法以后排查ADC类问题时也可以复用比眼睛看波形要可靠得多。4. 复盘还有哪些“多看一拍”的坑4.1 通道切换需要多等建议时间如果是多通道ADC比如一片ADC内部带模拟多路开关专门用来轮询采样Sin和Cos那“多看一拍”还有一个更重要的应用场景通道切换后的模拟建立时间。SAR ADC内部的采样电容在切换到新通道时会带着上一个通道的电荷必须经过足够的时间把电容充到新通道的电压否则读出的值就是新旧通道的加权混合。这种情况下芯片手册会给出一个“通道切换稳定时间”常见的是几百纳秒到几微秒。如果软件里切换通道后立刻启动转换数据就会偏高或偏低而且误差大小随信号频率变化。处理办法就是切换通道之后多读一次并丢弃或者插入固定延时再启动转换。原理和“多看一拍”完全一样——给系统足够的时间去看清真实世界的电压而不是急着抓一把就走。4.2 SPI接口的DRDY/DOUT时序很多14位ADC用的是SPI接口比如AD7946这一类的PulSAR结构芯片。SPI模式下同样存在“多看一拍”的时序陷阱而且更隐蔽。芯片的DRDY信号拉低表示转换完成但此时SDO引脚上的第一个数据位还没有真正输出必须由主控端给SCK时钟数据才会逐个移出来。如果状态机在DRDY下降沿后立刻读DOUT读到的大概率是上一帧的残留位或者中间态。常见解法是在DRDY下降沿后先给一个空SCK脉冲或者用“半拍延迟”的方式让第一个SCK的有效采样点落在SDO稳定之后。我在SPI模式下调过一款芯片手册要求DRDY下降沿后至少等待50ns才能开始发SCK换算成25MHz的SPI时钟就是还要多等1个多周期。工程上最稳妥的做法是状态机里加一个微周期计数器而不是用固定拍数——这样换芯片或改时钟频率时不用重写整个状态机。4.3 哪些情况不该盲目加一拍“多看一拍”不是万能药加多了也会引入新问题。比如在校验角度闭环时如果Sin和Cos两路ADC都各自加了一拍延迟理论上相位保持一致但实际因为走线长度、片间差异两路延迟可能不完全相等反而引入新的Sin/Cos相位失配。这种情况表现为角度轨迹出现正弦形状的误差频率是编码器信号频率的2倍和原来的台阶特征完全不同。还有一种情况如果ADC带内部数字滤波/均值功能很多多通道ADC有过采样平均那么转换完成信号本身就不是实时结果而是若干次平均后的值。这时候“多看一拍”只会让数据更旧不会改善噪声。遇到这类芯片正确的做法是反过来仔细读数据手册确认BUSY信号对应的是哪一次转换的结果再决定锁存时机。我见过有同事把过采样模式当成直出模式调了两天最后发现每隔一段读出来的值都偏小其实就是数据管道延迟没对齐。5. 调试技巧与工具心得5.1 逻辑分析仪VIO抓时序排查这次问题最大的功臣是FPGA内部的逻辑分析仪IP比如Xilinx的ILA和VIO在线调试工具。我把BUSY信号、系统时钟、数据锁存使能、以及最终的锁存值都拉进波形里触发条件设成“BUSY下降沿”然后抓一段编码器匀速转动的数据。这样能直接看到锁存使能相对于BUSY下降沿的相对位置和ADC数据手册上的时序要求一对比立刻就能发现锁存早了还是晚了。用逻辑分析仪抓数据总线的时候有个经验要把时钟信号一起抓进去观察数据线和时钟沿的相对关系。很多工程师只抓数据线和BUSY一看波形觉得“BUSY都拉低了数据也没问题”实际上数据在哪个时钟沿被采进寄存器才是关键。把“锁存使能”这一个内部信号拉出来看比盯外部引脚有用得多。5.2 8 LSB台阶的快速判别方法以后再遇到类似“固定LSB台阶”问题我建议按这个顺序快速排查第一步确认台阶高度是否随信号斜率线性变化。改变转速或信号频率如果台阶高度跟着变说明和采样时序相关如果不变优先怀疑基准、电源或ADC本身。第二步确认台阶是“周期性”还是“随机性”。周期性台阶大概率是数字时序问题随机性台阶先考虑抖动和亚稳态。第三步用示波器同时抓模拟输入和BUSY下降沿看看模拟信号在BUSY拉低前后是否已经完全建立。有些问题其实是模拟前端带宽不够导致信号没建立完看起来和时序问题一模一样。第四步对照数据手册确认“数据有效”的精确时刻。不要只看时序图的大概位置要看具体的参数符号和数值。这四步走下来大部分ADC台阶问题都能定位。我自己这次就是栽在第二步和第四步之间太早相信了“ADC哪有什么时序问题”这个先入为主的判断。5.3 数据后处理插值和中值滤波的联合使用在彻底修改时序之前我还试过一个“软补丁”用中值滤波把偶发的台阶滤掉然后在角度解算前做线性插值。中值滤波对孤立跳点确实有效但面对“固定8 LSB台阶”这种连续出现的模式效果就很一般——它会把正常数据和台阶数据各滤掉一半角度曲线变平滑了但等效分辨率并没有真正恢复。后来我意识到后处理只能掩盖症状不能解决根因。ADC采样链条里的时序问题修复成本可能只是几行代码但如果不修后处理模块再复杂也是白搭。这也是这次调试给我印象最深的地方很多“玄学”一样的台阶、毛刺、偶发跳变落到时序图上一看都是很朴素的原因。芯片手册不是拿来查参数用的是拿来解读信号行为用的尤其是那些带BUSY、DRDY、VALID之类“完成标志”的接口读懂它们的建立时间工程问题至少能少一半。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →