☰
正余弦编码器采样台阶问题:多通道ADC采样保持残留电荷与多看一拍解法
2026/10/1 16:35:17 网站建设 项目流程

写这篇工程手记之前,先交代一下背景:我在做一套正余弦编码器的角度解算电路,MCU 自带 14 位 ADC,前端经过运放调理,正余弦信号双双送进 ADC 的两个输入通道。调试了几天之后,我盯着串口打印出来的码值曲线足足愣了半分钟——那条曲线不是平滑的楼梯,而是一段一段跳着走的台阶,每级台阶恰好是 8 个 LSB。更邪门的是,这种台阶只在正余弦信号过零附近出现,其他区段反而挺正常。

最开始我怀疑是自己滤波函数写错了,后来确认算法没问题,又怀疑是参考电压纹波,结果换来换去一整天,问题依然健在。直到我做了“同一相位连续采样两次”的实验,才把真正的凶手揪出来:采样保持电容里欠着上一笔的“债”,正余弦通道切换后没有多等一拍,导致第一次转换读数带着残留电压。这篇文章就把完整的定位过程和“多看一拍”的解决方案记录下来,给同样在 14 位 ADC 上做正余弦采样的人当个避坑参考。

1. 从一张“难看”的码值曲线说起

1.1 正余弦过零段的异常平台

先描述一下现场。电路本身不复杂:旋转编码器输出差分正余弦信号,经过仪表放大器转单端,然后分别接到 MCU 的采样通道。我让信号源输出 0.5 Hz 的低频正弦波,同时给余弦通道一个 90° 相位偏移,这样两个通道都能看到完整的过零过程。

理想情况下,14 位 ADC 的结果应该是一个码一个码往上爬,就算有量化误差,最多也就是 ±1 LSB 的抖动。但我看到的实际序列是下面这个样子:

输入电压(折算到 ADC 输入)理想码值实际读取码值
-1.60 mV-8-8
-1.40 mV-7-8
-1.20 mV-6-8
-1.00 mV-5-8
-0.80 mV-40
-0.60 mV-30
-0.40 mV-20
-0.20 mV-10
0.00 mV00
0.20 mV10
0.40 mV20
0.60 mV30
0.80 mV48
1.00 mV58
1.20 mV68
1.40 mV78
1.60 mV88

可以看出,码值不是逐 1 LSB 变化,而是在一段区间内完全不变,然后瞬间跳 8 个 LSB。平台期持续大约 0.4 mV 的输入范围,也就是 8 LSB 的“台阶”。这种表现和普通量化噪声完全不同,更像是有个东西把 ADC 的输入“粘住”了,等那里的电压累积到足够大才猛然释放。

1.2 14 位 ADC 的量化底子与我的预期

14 位 ADC 的量化分辨力其实已经很细了,以 3.3V 参考电压为例:

1 LSB = 3.3V / 2^14 ≈ 201µV

也就是说,理想情况下电路里任何小于 201µV 的电压变化都不可能反映到输出码值上。8 个 LSB 对应的电压大约是 1.61 mV,这在精密信号链路里不算小数目。对于正余弦编码器来说,如果信号幅度是 1.65V 峰值,那么 1.61 mV 的误差在过零附近折算成角度大约是:

θ_error ≈ arcsin(1.61mV / 1650mV) ≈ 0.056°

看起来不大,但如果是用在精密转台或者伺服反馈上面,0.056° 的台阶足以让电流环和速度环抖出明显噪声。更让人难受的是,这个台阶不是固定的偏移,而是和信号变化方向、通道切换顺序都有关系,想用常规校准把它消除都不容易。

1.3 最先排除的三个常规嫌疑

我一开始按照常规排查顺序,依次怀疑了三个东西。

第一个是电源纹波。我用示波器看 ADC 参考电压引脚,直流纹波只有不到 0.5 mVpp,而且明显不到 8 LSB 的等效电压。把参考电压退耦电容从 1µF 加到 10µF 后,台阶纹丝不动——嫌疑排除。

第二个是基准源动态响应。如果基准电压芯片在 ADC 内部 DAC 抽电流时建立不住,也会出现进位误差。我换了更低输出阻抗的基准芯片,结果用高分辨率示波器观察基准引脚在转换期间的波动,确认建立时间足够快,而且台阶位置和输入电压强相关,与转换时钟关系不大——嫌疑变轻。

第三个是我自己写的软件滤波。我用串口打印原始码值,关掉了所有均值和中值滤波,直接看单次转换结果——台阶依然存在。这说明问题在模拟链路或者 ADC 自己的采样机制里,不在数据处理环节。

连续排除三个嫌疑后,我把注意力放到了 ADC 内部的采样保持电路上。

2. 8 LSB 台阶背后的两个物理机制

2.1 量化台阶与“假台阶”的区别

ADC 有量化误差是天然的。逐次逼近型 ADC 每次比较器的分辨力有限,加上 DAC 电容阵列的失配,个别码位偶尔偏几个 LSB 也很常见。但是那种误差一般是随机分布的,或者表现为 INL/DNL 曲线上的个别峰谷,而不会出现“一段区间完全锁死,然后突跳 8 个 LSB”的规律性台阶。

一个最直观的验证方法:把输入信号改成缓慢三角波,同时降低转换速率,观察码值序列。如果是真的量化误差,码值会随机地在理想值附近抖动,因为信号是单调上升的,输出码值虽然可能忽大忽小,但总体趋势不会出现长时间停留。而我们看到的台阶是在一个输入区间内输出始终是同一个值,然后到某个临界点后直接跨过好几个码值,这更像“迟滞”或者“电荷残留”。

2.2 采样保持电容的残留电压:前一次采样欠下的“债”

SAR ADC 的内部结构里,采样阶段要闭合采样开关,让外部信号给内部电容阵列充电。转换阶段里,电容阵列和比较器共同完成逐位逼近。当一次转换结束后,电容阵列内部仍然保留着刚刚那组开关状态对应的电荷。下一次采样开始时,外部信号需要把电容阵列重新充到新的输入电压。

问题就出在这个重新充电的速度上。如果外部信号源的内阻太大,或者走线滤波电容太大,外部源在采样窗口内来不及把内部电容阵列“纠正”到新电压,那么 ADC 采到的实际上是“旧电压残余 + 新电压变化量的一部分”。

用时间常数的思路来算一笔账:假设 ADC 的内部采样电容大约是 20 pF,外部串联电阻加开关电阻合计 10 kΩ,那么时间常数 τ = 200 ns。14 位 ADC 需要约 10 个时间常数才能建立到 1/2^14 的精度,也就是大约 2µs。如果采样时间只有 800ns,则建立误差大约是:

V_error ≈ ΔV * e^(-t/τ)

其中 ΔV 是上一次和当前输入之间的电压差。如果 ΔV 有 1.65V 那么夸张,t/τ=4 时误差已经是输入差值的 1.8%,约 30 mV,远超 8 LSB。而在正余弦过零附近,信号本身变化不快,但两个通道的信号差值可能很大——比如上一拍采的是数值接近 +1.65V 的 SIN,这一拍要采的是接近 0V 的 COS,差值近 1.65V,残留误差很容易超过 8 LSB。

这就是“债务”的根源:ADC 总是带着上一次采样的残留电荷进入下一次采样,如果外部电路没有足够能力把它冲掉,台阶就会一直存在。

2.3 通道切换时电荷注入的叠加效应

很多 ADC 采样系统会在信号源和 ADC 之间放置一个多路复用器,或者利用 MCU 内部的多通道扫描。通道切换本身就是一次“台阶信号”:MUX 输出从上一通道的电平瞬间跳变到当前通道的电平,这会向后续滤波电容注入一个电流脉冲。

如果 MUX 后跟随了一个较大的滤波电容,比如 1nF,那么通道切换后,电容上的电压需要从上一通道的电平逐渐建立到当前通道的电平。此时如果立即启动 ADC 采样,采样保持电容和这个滤波电容之间会发生电荷分享。由于滤波电容上的电压还没完全稳定,ADC 采到的就是两个电容电荷再分配后的中间值。

一个典型的表现是:如果上一通道电压比当前通道高,当前通道的转换结果会偏高;如果上一通道电压比当前通道低,转换结果会偏低。这种误差与两个通道的电压差值成比例,会让正余弦信号在交替采样时出现“你欠我、我欠你”的台阶。我这次遇到的过零台阶,恰好就是 COS 通道切换在 SIN 通道之后时出现的——因为 SIN 在过零附近数值较大,会给 COS 留下明显的残余。

所以“8 LSB 台阶”不是单纯的量化问题,而是电荷残留 + 通道串扰 + 建立时间不足三者叠加的结果。

3. 模拟前端验证:双采样实验定位残留电荷

3.1 用慢速三角波做迟滞对比

为了确认残留电荷方向与信号变化方向的关系,我做了这样一个实验:用极慢的三角波(0.05 Hz)作为 VIN1 输入,VIN2 接地,ADC 在两个通道之间循环采样。然后分别记录三角波上升段和下降段中 VIN1 通道的码值。

结果非常明显:上升段时,VIN1 通道的码值总体偏低;下降段时,VIN1 通道的码值总体偏高。换句话说,VIN1 的读数总是被 VIN2 的上一拍电压“拉”着走。上升时 VIN2 是 0V,把 VIN1 的读数往下拉;下降时 VIN2 还是 0V,但 VIN1 的自身残留又让它偏高。

这个现象完美解释了正余弦过零台阶的方向性。正余弦信号同时变化,SIN 大时 COS 小,SIN 小时 COS 大,两个通道互相拖拽,导致每个通道都在接近过零时被另一个通道“按住”,直到差值积累到超过残留电荷的保持能力,才猛地跳出去。

3.2 同一相位连续采样两次,第二次才正常

接下来做了整个排查中最重要的实验:让 ADC 保持同一个输入通道不变,但在极短时间间隔内连续触发两次转换,把两次码值都打出来。如果残留电荷理论成立,那么第一次转换应该带着上一周期的残留,第二次转换应该更接近真实值,因为第一次转换后的“债务”已经被当前输入电平冲抵了一部分。

结果没有让我失望。以 COS 通道过零前的某一次采样为例:

  • 第一次转换:码值 0;
  • 第二次转换:码值 -6;
  • 理想码值:约 -6。

第二次转换的码值基本就等于正确值,第一次转换却躲在 0 的平台上。这是因为第一次转换前,ADC 内部电容阵列还记着 SIN 通道留下的高电压,采样窗口里虽然开始向 COS 的 0V 靠近,但没来得及走完,结果就被判成了 0。等第一次转换做完,电容阵列又被强制重新充满过一次,这次才真正贴近 COS 的当前电压。

我把这个实验重复了 200 组,每组都丢第一个转换结果、只保留第二个转换结果。码值曲线立刻恢复了清晰的楼梯形态,台阶消失。这个结果基本确立了“多看一拍”的解决方向。

3.3 示波器探头引出的岔路:输入节点电压为什么反而升高

在排查过程中我还踩过一个探头的坑,这里顺带提一句,免得有人走弯路。我用普通 10× 探头直接点到 ADC 输入引脚上,发现引脚电压比信号发生器设定值偏高了几十毫伏。起初以为电路板漏电,后来才意识到是探头本身的输入电容(大约 15 pF)和 ADC 采样保持电容在采样瞬间发生了电荷再分配。

当 ADC 采样开关闭合时,内部电容阵列会从外部抽取电荷,如果外部源阻抗较高,采样瞬间引脚电压会下降,而普通示波器探头会引入额外电容,改变这个压降的幅度和恢复时间。换句话说,示波器一接上去,观察到的是被探头发改之后的“系统扰动”,而不是真实工作状态下的波形。所以后面所有临界时序的验证,我都改用高阻差分探头或者干脆用 ADC 自身的读数来反推,不再用普通探头单独判断 ADC 输入引脚上的瞬态电压。

4. “多看一拍”怎么落地

4.1 数字侧时序调整:通道切换后强制插入 dummy 转换

定位到残留电荷问题后,我做的第一项修正就是在多通道采样序列中插入“空转换”。这套思路非常简单:

  • 第 1 拍:启动转换,转换结果是包含上一通道残留的“坏值”,丢弃;
  • 第 2 拍:启动转换,这时候电容阵列已经被“纠正”到当前通道电平附近,转换结果是可用的“好值”;
  • 采样完成后,才切换通道。

这里的“多看一拍”,本质上就是给 ADC 一次额外的重采样机会,让内部电容阵列先从上一通道的欠债里恢复过来。如果你只用 MCU 的扫描模式,没法在两个通道之间插入 dummy 采样,那就改用手动触发模式,或者把 N 个通道的扫描序列扩展为 2N 个槽位,偶数位放真正需要的通道,奇数位强制接到同样的输入通道或者干脆短接到地。

FPGA 里实现更简单:维护一个采样状态机,状态切换顺序为 IDLE -> SETTLE -> CONVERT -> HANDLE。SETTLE 状态只有一个目的——等待 MUX 切换完成、外部滤波电容建立、内部保持电容进行足够的预充电。这个状态下不做比较,只是把采样开关闭合一段时间。等 SETTLE 完成后,才开始真正的逐次逼近转换。

实用参数参考:若外部滤波电阻为 1 kΩ,滤波电容为 470 pF,那么 SETTLE 时间至少需要 10 × 1kΩ × 470pF = 4.7 µs。如果 ADC 的采样时间配置不够,就用 dummy 转换来补足。我系统里最终选择的是在 MUX 切换后先插入一个 10µs 的 SETTLE,再启动一次 dummy 转换,最后再启动有效转换,整体采样周期从原来的 20µs 拉长到 40µs,完全够用。

4.2 增加采样建立时间的寄存器配置

除了在逻辑上多插一拍,硬件寄存器里也要把采样时间配长一些。很多 MCU 的 ADC 都支持可编程采样时间,比如 1.5 个 ADCCLK 到 239.5 个 ADCCLK。如果你原来用的是 1.5 cycles,现在建议至少翻到 7.5 cycles 或更高。

采样时间的关键作用是直接控制采样开关闭合的持续时间。采样开关闭合时间越长,外部电压就越有机会把内部电容阵列充到正确值。需要注意的是,采样时间也不是越长越好:太长的采样时间会增加整个系统的延迟,对高频正余弦信号会造成采样时刻偏移。一般建议根据信号频率计算允许的最大相位误差,再倒推采样时间。对于 1 kHz 以内的正余弦信号,采样时间在 1µs 到 10µs 之间通常不会引入明显相位滞后。

我最终的配置是:ADCCLK 设为 18 MHz,采样时间为 14.5 cycles,算下来大约是 0.8µs;同时在外围增加软件 dummy 转换。这样既保证了建立精度,又没有把采样周期拖得太离谱。

4.3 算法侧补刀:阶梯抑制与相位同步判断

硬件改为“多看一拍”之后,8 LSB 的大台阶已经基本消失。但为了保险起见,我在算法里再加了一重保护,专门对付漏网的小台阶和边缘毛刺。思路就是利用正余弦信号本身的光滑性:角度解算不可能瞬间突跳 8 个以上码值,除非真的发生了极高速的运动。

做法很简单:维护一个历史码值缓冲区,每次读入新采样值时,先与缓冲区最后一个有效值比较。如果差值绝对值在 2 个 LSB 以内,正常更新;如果差值在 3 到 8 个 LSB 之间,就按照当前信号斜率方向做线性插值,而不是直接采信这个跳变;如果差值超过 8 个 LSB,就认为可能是真实的高速变化或者丢码,用多次中值滤波确认后再更新。

这套保护只会在异常瞬间起作用,对真实信号的影响极小,因为正常采样下正余弦信号相邻两拍的变化量远小于 8 LSB。它作为最后一道防线,让我在后续调高运动速度时也不至于因为偶发残留电荷导致角度解算出现毛刺。

5. 正余弦采样还有哪些隐藏坑

5.1 采样点与激励源相位未同步造成的“游动台阶”

解决掉通道切换问题之后,我又碰到了一个新的台阶,位置还会随着电机转速变化而变化。后来分析发现,那是采样时刻与正余弦编码器的激励信号相位没对齐导致的。

许多正余弦编码器需要外部激励信号,典型频率在 1 kHz 到 20 kHz 之间。ADC 每次采样的时刻如果在激励周期里不是固定的,那么相邻两次采集的 sin/cos 瞬时值就可能差出好几个 LSB。比如激励频率 10 kHz 时,信号斜率每秒变化 2π×10k×A ≈ 62831×A V/s。如果采样时刻抖动 1µs,瞬时电压偏差可达 0.063 V(A 取 1V 时),远超 8 LSB。

所以正余弦采样一定要尽量做到和激励源同源触发:用激励信号的过零检测或者 PWM 同步信号作为 ADC 触发源,保证每次采样点都落在激励周期的同一相位上。我最后改成了由 PWM 中心对齐事件触发 ADC 采样,台阶问题才彻底消失。

5.2 差分增益/失调不对称让 sin 和 cos 台阶高度不同

如果你用的是单端 ADC 采样差分正余弦信号,那么前端运放的增益误差和共模抑制能力也会被“放大”成不同高度的台阶。我的板子上 SIN 通道和 COS 通道各用了一个仪表放大器,两片芯片的增益匹配误差大约 0.3%,这在高速旋转时会转化为幅度误差。更麻烦的是,两片芯片的失调电压不同,会让两个通道的零点错开,于是同一个机械角度在 sin 和 cos 通道上的码值比例发生变化,atan2 解算出来的角度会出现周期性非线性。

建议做一个出厂自校准:在无激励或者激励固定幅值时,短接运算放大器输入,记录两个通道的零位偏移量;然后用一个实测幅度值标定两通道增益比例。软件里采用一点校正和两点校正结合的方式,把这个误差从系统里抠掉。我做完之后,在空载低速下测量到的 LSB 台阶基本只剩下 ±1 的随机量化抖动。

5.3 我目前的工程清单与一段完整可用的采集伪代码

最后把我这次调试沉淀下来的工程清单整理如下:

检查项推荐做法
通道切换切换后至少等待 10 个外部 RC 时间常数,再启动采样
采样时间不低于 10 个内部采样 RC 时间常数,宁长勿短
dummy 转换每个真实采样之前,插入一次结果丢弃的预转换
触发源尽量使用 PWM 同步触发,采样点相对激励相位固定
模拟前端仪表放大器增益匹配 0.1% 以内,失调电压低于 0.1 mV
保护算法相邻采样差值超过 3 LSB 时启用插值/中值确认

下面是一段适用于任督二脉已经打通、却想快速在 MCU 上实现“多看一拍”的伪代码,可以直接改成 C 或者 Verilog:

uint16_t adc_read_with_settle(adc_channel_t ch) { // 1. 切换模拟 MUX 到目标通道 mux_select(ch); // 2. 等待外部 RC 建立 delay_us(SETTLE_US); // 3. 启动第一次转换,结果丢弃 adc_start_conversion(); adc_wait_conversion_done(); (void)adc_read_result(); // 4. 立即启动第二次有效转换 adc_start_conversion(); adc_wait_conversion_done(); return adc_read_result(); } void sample_sin_cos(uint16_t *sin_code, uint16_t *cos_code) { *sin_code = adc_read_with_settle(ADC_CH_SIN); *cos_code = adc_read_with_settle(ADC_CH_COS); // 如果需要更高精度,这里可以再做一次第二拍采样并取平均 }

这段伪代码看起来平凡,但重点在于:第二次转换才是真正的读数。如果你把这个模式套在自己的代码里,大概率也能复现我在标题里写的那句“多看一拍”的效果。

至于为什么标题里特别强调“正余弦采样要多看一拍”,因为正余弦信号天然存在两个通道交替读取的问题。只要你是交替采样 sin 和 cos,无论内部 ADC 还是外部 MUX,都要面对残留电荷。不要指望靠 PCB 走线或者加去耦电容就能彻底避开,因为问题的根源在 ADC 采样开关和电容阵列本身,你能做的就是把每一次采样之间的“欠债”用额外的时间补上。

我自己的体会是,碰到 ADC 台阶先别急着怪芯片精度不够,很多情况下是我们没给 ADC 足够的时间“忘掉过去”。14 位 ADC 的分辨力已经足够细腻,真正决定系统高不高端的,往往是这些没有人写在数据手册里的细节。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询