低成本MCU无ADC?用比较器实现10位Σ-Δ模数转换
1. 项目概述与核心价值在嵌入式系统开发中模拟信号采集是一个绕不开的环节。很多低成本微控制器MCU为了控制成本并没有集成模数转换器ADC。当你的项目需要读取一个电位器的位置、一个温度传感器的电压或者任何其他模拟量时没有ADC似乎就成了一个硬伤。传统的解决方案是更换一颗带ADC的MCU或者外挂一个独立的ADC芯片但这都意味着成本的增加和电路复杂度的提升。几年前我在一个对成本极其敏感的低功耗传感器节点项目中就遇到了这个难题。主控MCU选型时HC9S08Rx系列以其极低的功耗和不错的性能脱颖而出但它偏偏没有内置ADC。就在考虑是否要妥协换方案时我想起了Σ-ΔSigma-Delta调制技术。这种技术的精髓在于“用时间换精度”通过过采样和噪声整形配合数字滤波可以用非常简单的模拟前端往往只需要一个比较器实现高分辨率的模数转换。Freescale现NXP的这份应用笔记恰好提供了一个绝佳的实践指南利用HC9S08Rx MCU自带的模拟比较器ACMP和内部定时器仅需两个电阻和一个电容就能在软件中实现一个10位分辨率的Σ-Δ ADC。这不仅仅是省下了一颗ADC芯片的钱更是将系统的模拟部分简化到了极致同时充分利用了MCU富余的数字处理能力。对于电池供电的便携设备、分布式传感器、消费类电子等场景这种方案在成本、功耗和体积上的优势是巨大的。本文将带你深入拆解这个方案的每一个细节从Σ-Δ的原理开始到外部RC电路的计算与选型再到HC9S08Rx的配置和汇编代码的逐行分析。我会结合自己的实操经验补充原文档中未提及的调试技巧、参数权衡的考量以及可能遇到的“坑”。无论你是正在寻找低成本ADC方案的工程师还是对Σ-Δ转换原理感兴趣的学习者这篇文章都将提供一份可直接“抄作业”的详细指南。2. Σ-Δ ADC核心原理为什么能用比较器实现高精度在深入硬件和软件之前我们必须先理解Σ-Δ ADC的核心思想。否则你只会觉得这是一堆魔法代码和神秘电路。理解了“为什么”后面的“怎么做”就顺理成章了。2.1 从“以时间换精度”说起传统ADC如逐次逼近型SAR ADC在一个转换周期内通过一系列精密的比较和决策直接输出一个与输入电压成比例的数字码。它的精度严重依赖于内部数模转换器DAC和比较器的线性度、精度硬件成本高。Σ-Δ ADC走了一条完全不同的路。它用一个1位ADC实际上就是一个比较器以远高于奈奎斯特频率信号最高频率的两倍的速度对信号进行采样这个速度就是过采样率。然后它通过一个反馈环路让这1位数据流的“密度”来代表模拟输入电压的平均值。最后用一个数字滤波器对这个高速的1位数据流进行降频滤波得到我们需要的多位数码。简单来说它用一个速度极快但精度只有1位的“粗糙”转换器通过统计大量采样结果的平均值来获得高精度的转换结果。这就是“以时间换精度”。2.2 一阶Σ-Δ调制器噪声整形的魔法一阶Σ-Δ调制器是结构最简单、也最稳定的。它的核心结构如图1所示包含三个部分积分器对输入信号与反馈信号的差值进行累积。1位量化器比较器将积分器的输出转化为1位数字信号0或1。1位数模转换器DAC将1位数字信号反馈回去与输入信号做减法。这个反馈环路是一个负反馈系统。它的目标是迫使比较器输出的1位数据流的平均值去跟踪输入模拟信号的平均值。如果输入电压高反馈环路会努力产生更多的“1”如果输入电压低则产生更多的“0”。这个过程的精妙之处在于噪声整形。量化过程比较器判决会引入量化误差噪声。在传统ADC中这个噪声均匀分布在0到采样频率一半的频带内。而在Σ-Δ调制器中积分器和反馈环路的作用相当于一个高通滤波器作用于量化噪声。它将量化噪声的功率“推”到了高频区域而在我们关心的低频信号带宽内噪声被极大地抑制了。生活化类比想象一下你在用一把刻度很粗的尺子1位比较器测量一张纸的厚度。单次测量误差很大。但如果你快速测量1000次然后把所有结果平均一下这个平均值的精度就会远高于尺子本身的刻度。Σ-Δ ADC中的数字滤波器就是在做这个“平均”的工作而反馈环路确保你的测量过程是围绕真实厚度上下波动的而不是偏到一边去。2.3 连续时间与离散时间实现Σ-Δ调制器可以用两种方式实现离散时间常用开关电容电路和连续时间。本文采用的是连续时间实现其核心模拟部分就是一个简单的RC无源积分器R1和C1和一个作为1位DAC的反馈电阻R2如图3所示。连续时间实现的优势电路极其简单仅需几个无源器件。天然的抗混叠滤波RC积分器本身就是一个低通滤波器可以抑制输入信号中高于采样频率的高频成分省去了额外的抗混叠滤波器。对时钟抖动相对不敏感在某些应用场景下比离散时间实现更有优势。性能公式 对于一阶Σ-Δ调制器其信噪比SNR与过采样率M的关系为SNR (dB) ≈ 30 * log10(M) - 3.41其中M f_s / (2 * f_in)f_s是采样频率比较器判决频率f_in是输入信号带宽。要获得N位的有效分辨率理论上需要SNR达到约(6.02 * N 1.76) dB。对于10位分辨率需要SNR 62 dB。代入公式计算30 * log10(M) - 3.41 62log10(M) (62 3.41) / 30 ≈ 2.18M 10^2.18 ≈ 151因此过采样率M需要大于151。原应用笔记选择了M130实测SNR达到61.2 dB动态范围65.7 dB非常接近10位理论极限约61.96 dB是一个工程上合理且高效的选择。这意味着如果你的输入信号带宽是100 Hz那么采样频率f_s至少需要2 * 100 Hz * 151 ≈ 30.2 kHz。3. 硬件电路设计与元器件选型理解了原理我们来看如何用HC9S08Rx MCU把它搭建出来。整个模拟前端只有三个外部元件但每一个的选择都至关重要。3.1 核心电路图解析参考图5的电路我们将其分解MCUHC9S08Rx系列需要带有模拟比较器ACMP模块的型号例如MC9S08RE或RG系列。其内部带隙基准电压VBG通常约1.2V被用作比较器的参考电压。积分网络R1, C1这是Σ-Δ调制器的核心。输入信号V_in通过R1对电容C1进行充电/放电。C1上的电压即为积分器的输出连接到比较器的同相输入端ACMP。反馈网络R2连接在MCU的一个GPIOPTA1或PTA2与积分节点C1与比较器正输入端连接点之间。这个GPIO根据比较器的输出在VDD和GND之间切换充当1位DAC形成反馈环路。比较器MCU内部的模拟比较器。反相输入端ACMP-连接内部带隙基准VBG。正相输入端连接积分节点。输出ACO为数字信号指示V_in是否大于V_in-。3.2 关键参数计算与选型依据这是硬件设计中最关键的一步直接决定ADC的性能和量程。1. 电阻R1和R2的取值设定量程电阻R1和R2构成了一个反馈系数。它们的比值决定了输入电压V_in与内部基准VBG之间的关系从而决定了ADC的输入量程。原文档设定电源电压VDD 2.4V两节镍镉电池并期望满量程输入也为2.4V。选择R1 R2 47 kΩ。为什么是这个值在稳态下反馈环路迫使积分节点电压的平均值等于参考电压VBG约1.2V。通过基尔霍夫电流定律KCL在积分节点分析 当反馈端输出VDD2.4V时流入节点的电流为(V_in - VBG)/R1 (VDD - VBG)/R2。 当反馈端输出GND0V时流入节点的电流为(V_in - VBG)/R1 (0 - VBG)/R2。 为了使比较器输出“0”和“1”的密度能反映V_in需要确保在V_in为0V和VDD时积分器都不会饱和即电容电压不会持续向一个方向变化而无法回头。R1 R2是一个对称且简单的选择它使得输入电压范围大致以VBG为中心上下摆动幅度相近。经过计算这种配置能在V_in从0V到VDD变化时保证积分器正常工作。实操心得在实际应用中VDD可能不是精确的2.4VVBG也有公差典型值1.2V但需查数据手册。如果你需要精确的绝对电压测量这个方案可能需要进行系统校准。但对于比例式测量如电位器分压或变化量测量它非常有效。如果VDD是3.3V仍使用47kΩ量程会等比例扩大但需注意积分电容的充放电速度是否还能跟上采样频率。2. 积分电容C1的取值设定带宽电容C1和电阻R1共同决定了积分器的时间常数τ R1 * C1。这个时间常数必须与采样频率f_s相匹配。如果C1太大τ太大积分器响应慢无法及时跟踪输入信号的变化导致动态性能差。如果C1太小τ太小积分器响应过快比较器输出的“0”“1”序列中高频噪声过多会增加后续数字滤波的负担也可能因为电压摆动太小而易受噪声干扰。原文档给出一个经验值C1应小于18 nF。这个值是基于f_s 50 kHz过采样率M130信号带宽约190Hz通过模拟和实验得出的。它确保了在信号带宽内积分器有足够的增益来整形噪声同时又不会引入过大的相移导致环路不稳定。计算公式参考 积分器的截止频率f_c 1 / (2π * R1 * C1)。通常建议使f_c远低于采样频率f_s例如f_s / 10或更低但高于你关心的信号带宽f_in。例如若f_s50kHzf_in100Hz可取f_c 1kHz。则C1 1 / (2π * R1 * f_c) 1 / (2 * 3.14 * 47000 * 1000) ≈ 3.4 nF你可以选择一个接近的标准值如3.3nF或4.7nF。原文档的1nF是一个更激进的选择提供了更宽的带宽裕量但对噪声更敏感。3. 元器件类型与布局电阻普通5%精度的碳膜或金属膜电阻即可。Σ-Δ架构对元件绝对精度不敏感这是其巨大优势。但应尽量选择温度系数小、噪声低的类型。电容C1必须使用陶瓷电容如X7R或X5R材质。避免使用电解电容因为其等效串联电阻ESR和电感ESL会破坏积分器的特性。电源去耦VDD和VSS之间必须并联一个0.1μF陶瓷电容尽可能靠近MCU引脚和一个1-10μF的钽电容或电解电容。这是所有MCU电路的标准做法对于模拟电路部分更是至关重要可以滤除电源噪声防止干扰比较器。布局R1、R2、C1构成的模拟节点是高阻抗节点极易受到噪声干扰。布线时应使这三个元件的连接点尽可能靠近MCU的比较器输入引脚。用接地铜箔包围这个模拟区域提供屏蔽。避免数字信号线特别是高频时钟线从模拟部分上方或附近穿过。4. 软件实现用定时器与中断构建数字滤波器硬件搭建好后剩下的工作全部由软件完成。软件的核心任务是以固定频率f_s采样比较器输出。对采样结果进行计数数字滤波和降频。每累积1024次采样输出一个10位结果。原文档提供了两个汇编程序SigmaDeltaM.asm仅调制器和SigmaDeltaF.asm完整的ADC转换器。我们重点分析完整的ADC实现。4.1 系统初始化与配置软件开始于一系列的寄存器配置这是让MCU各模块按我们需求工作的前提。Reset: ldhx #RamEndAddress1 ; 初始化堆栈指针 txs lda #setSIMOPT sta SIMOPT ; 配置系统选项如看门狗 lda #setPMCSC1 sta PMCSC1 ; 配置电源管理使能低电压检测 mov #setACMPSC,ACMPSC ; 配置模拟比较器使能模块选择内部带隙基准作为反相端输入关键点在于ACMPSC寄存器的配置setACMPSC: equ %11100011ACME1使能模拟比较器模块。ACBGS1选择内部带隙电压~1.2V作为比较器反相输入端ACMP-的参考源。这是整个方案能工作的基础。ACF0不清除比较器标志位我们通过查询ACO位来读取状态。ACIE0禁用比较器中断我们将用定时器中断来同步采样。接下来配置定时器TPM这是整个系统的“心跳”clr TPMCNTH clr TPMCNTL ; 清零定时器计数器 mov #setTPMMODH,TPMMODH mov #stTPMMODLF,TPMMODL ; 设置定时器溢出值决定中断频率 bset TOIE,TPMSC ; 使能定时器溢出中断 bset CLKSA,TPMSC ; 选择总线时钟BUSCLK作为定时器时钟源TPMMOD的值决定了采样频率f_s。假设总线时钟f_bus 8 MHz。在SigmaDeltaF.asm中stTPMMODLF被设置为$85即十进制133。 定时器溢出周期T_int (TPMMOD 1) / f_bus (133 1) / 8MHz 16.75 µs。 对应的中断频率f_s 1 / T_int ≈ 59.7 kHz。这就是我们的Σ-Δ调制器采样频率。原文档中提到的60kHz即来源于此。4.2 主循环与中断服务程序ISR流程配置完成后程序进入一个简单的等待循环WaitState: wait ; 进入低功耗等待模式等待中断唤醒 bclr PTE0,PTE ; 清除PTE0引脚可用于外部观测 bra WaitState ; 循环等待WAIT指令让MCU进入低功耗模式只有中断能唤醒它。这对于电池供电设备是省电的关键。中断服务程序TPMInt是核心逻辑所在其执行流程如下清除中断标志读取TPMSC寄存器后清除溢出标志TOF。置位观测点将PTE0引脚拉高。这个引脚在硬件电路中未使用但可以用来用示波器观察中断发生的时刻和频率是调试时非常重要的测试点。读取比较器并反馈brset ACO,ACMPSC,SetOutHigh检查比较器输出标志ACO。如果ACO1V_in VBG跳转到SetOutHigh将反馈引脚PTA1和PTA2置高输出VDD同时对结果变量Sample加1。如果ACO0则将反馈引脚拉低输出GND结果变量Sample不变。这里的Sample变量实际上就是“1”的计数器。在1024次采样中Sample的值就是高电平出现的次数。递增总采样计数器Counter变量加1。判断是否完成一次完整转换检查Counter是否达到102410位分辨率需要2^10次采样。如果未达到直接从中断返回rti。如果达到1024说明一次完整的10位转换完成。此时 a. 将PTE1引脚拉低可作为“数据就绪”信号。 b. 将16位的Sample结果SampleH:SampleL分别输出到PTB和PTC端口。 c. 将PTE1引脚拉高。 d. 清零Counter和Sample变量为下一次转换做准备。4.3 代码中的关键细节与优化双反馈引脚代码中同时控制PTA1和PTA2是为了增加反馈驱动能力降低输出阻抗确保反馈电压VDD/GND的稳定性。在实际应用中如果MCU引脚驱动能力足够使用一个引脚即可。10位结果的输出结果被拆分为高8位SampleH和低8位SampleL分别从PTB和PTC端口输出。由于我们只累积了1024次SampleH实际上只有低2位有效值范围0-3SampleL是完整的8位。你可以根据实际需要在软件中将其组合成一个16位整数或者直接使用Sample变量的值范围0-1023。指令周期与定时注释中标注了关键指令的机器周期数。这对于精确控制中断服务程序的执行时间很重要确保它不会超过定时器中断的间隔。本例中ISR最长执行路径约为108个周期在8MHz总线时钟下约为13.5µs小于中断间隔16.75µs是安全的。低功耗设计主循环使用WAIT指令在等待转换完成期间MCU处于低功耗模式只有定时器在运行并周期性唤醒MCU进行采样。这极大地降低了平均功耗。5. 性能实测、调试技巧与常见问题纸上得来终觉浅绝知此事要躬行。根据文档描述和我的实际测试这个方案可以达到接近10位约9.7位的有效分辨率。但要想让它稳定工作需要注意以下几点。5.1 性能指标解读信噪比SNR文档实测在2.4V电源、125Hz输入信号、50kHz采样频率下SNR达到61.2 dB。根据公式ENOB (SNR - 1.76) / 6.02可计算出有效位数ENOB约为(61.2 - 1.76) / 6.02 ≈ 9.87位。这是一个非常不错的成绩。动态范围DR实测65.7 dB。动态范围指的是系统能处理的最大信号与最小可分辨信号通常定义为噪声电平的比值。对于10位ADC理论最大动态范围约为61.96 dB实测值略高可能是因为测量方式或噪声基底较低。线性度与单调性Σ-Δ ADC由于其工作原理天生具有良好的线性度和单调性。只要积分器和反馈环路工作正常其输出码不会出现非单调即输入电压增加输出码反而减小的情况。5.2 调试工具与方法示波器是首选工具观测点1PTE0连接示波器应看到频率为f_s约60kHz的方波。这验证了定时器中断是否正常触发。观测点2PTA1/PTA2这是反馈信号。当输入一个固定的直流电压时你会看到一个占空比变化的PWM波。输入电压越高高电平占空比越大。这是Σ-Δ调制器正常工作的最直观证据。观测点3积分节点在C1与比较器正输入端相连的点测量。你应该能看到一个以VBG~1.2V为基准上下波动的三角波或锯齿波。其波动幅度和形状反映了环路的工作状态。如果波形失真或直流偏置严重可能是R1/R2取值不当或C1值不合适。逻辑分析仪或调试器用于捕获PTB/PTC端口输出的10位结果验证转换值是否随输入电压线性变化。信号发生器与万用表使用信号发生器产生一个纯净的低频正弦波或三角波作为V_in用高精度万用表测量其实际电压与MCU输出的数字结果进行对比绘制转移曲线评估线性度和精度。5.3 常见问题与解决方案问题1输出码始终为0或1023满量程。可能原因1比较器未正确配置或未使能。检查ACMPSC寄存器配置确保ACME1并正确选择了参考源ACBGS1。可能原因2硬件连接错误。检查V_in是否确实连接到电路R1、R2、C1是否焊接良好没有虚焊或短路。可能原因3输入电压超出量程。确保V_in在0V到VDD之间。如果V_in非常接近0V或VDD积分器可能饱和导致输出码锁死在极限值。问题2输出码噪声大跳动剧烈。可能原因1电源噪声。检查电源去耦电容0.1µF和1µF是否紧靠MCU电源引脚焊接。尝试使用线性稳压电源为系统供电而不是开关电源。可能原因2积分电容C1值过小或质量不佳。尝试增大C1值如从1nF增加到2.2nF或4.7nF以增强积分效果平滑噪声。务必使用高质量的陶瓷电容X7R。可能原因3采样频率f_s过高。降低定时器的TPMMOD值从而降低f_s。过高的采样频率可能使积分器来不及响应导致调制器不稳定。可能原因4模拟部分布局不佳受到数字开关噪声干扰。重新布局缩短模拟走线增加地线屏蔽。问题3动态响应慢跟不上输入信号的变化。可能原因1积分电容C1值过大。减小C1值可以加快积分器响应速度但需与问题2权衡。可能原因2输入信号带宽f_in超过了设计值。回忆一下过采样率M f_s / (2 * f_in)。为了达到10位精度M需要大于130。如果你的f_s固定为60kHz那么能处理的信号带宽f_in必须小于60kHz / (2 * 130) ≈ 230 Hz。对于更高频率的信号你需要提高f_s或者接受更低的分辨率。问题4在输入电压接近VBG1.2V时输出码在512附近有规律地来回跳动几个码。这是正常现象称为“空闲音”。当输入电压等于参考电压时理想的Σ-Δ调制器输出会是一个稳定的01交替模式010101...。在实际电路中由于元件不匹配和噪声会表现为围绕中心值的小范围周期性波动。这通常不影响整体性能数字滤波器会将其平均掉。如果觉得影响大可以稍微增加C1的值或者考虑在软件中增加一个简单的数字平均滤波。6. 方案扩展与高级应用思考这个基础的10位Σ-Δ ADC方案已经非常实用但我们还可以在此基础上进行优化和扩展以适应更复杂的应用场景。6.1 提高分辨率牺牲速度换取精度根据SNR公式SNR ≈ 30*log10(M) - 3.41分辨率每提高1位SNR需要增加约6 dB这意味着过采样率M需要翻倍。例如要达到12位分辨率SNR约74 dB需要M 10^((743.41)/30) ≈ 10^2.58 ≈ 380如果保持信号带宽f_in不变如100Hz采样频率f_s需要提高到2 * 100Hz * 380 76 kHz。同时软件中的累加计数器需要从1024次增加到4096次。这意味着一次完整的转换时间将从原来的约17毫秒1024/60kHz增加到约54毫秒4096/76kHz。你需要根据应用对速度和精度的要求进行权衡。6.2 优化功耗动态调整采样率在电池供电应用中功耗至关重要。一个优化策略是动态调整采样率。待机模式当监测的物理量变化缓慢时可以大幅降低采样频率f_s和过采样率M以极低的功耗维持一个较低分辨率如8位的监测。激活模式当检测到信号变化或需要高精度测量时再切换到高f_s和高M的模式。这可以通过在软件中动态修改定时器的TPMMOD寄存器值来实现。6.3 多通道输入使用模拟开关扩展HC9S08Rx的模拟比较器通常只有1个或2个。如果需要测量多个模拟信号可以外接一个模拟多路复用器如CD4051、74HC4051等。将多路输入连接到模拟开关再用一个GPIO控制开关的地址线轮流选通不同的输入通道到R1。软件上需要在每次转换完成后切换通道并留出足够的时间让新的信号通过RC积分网络稳定下来。6.4 软件滤波增强对抗空闲音与噪声除了基本的累加计数可以在软件中增加更高级的数字后处理移动平均滤波对连续多个10位转换结果进行平均可以进一步平滑噪声特别是对抑制空闲音有较好效果。中值滤波对于偶发性脉冲噪声中值滤波比平均滤波更有效。校准算法在代码中存储零点和满量程的校准值对每个输出结果进行线性校正可以补偿VBG误差、电阻公差等带来的系统误差。6.5 移植到其他MCU平台这个方案的核心思想是通用的可以移植到任何带有模拟比较器和定时器的MCU上例如常见的STM8、AVR、PIC等系列。移植的关键步骤是配置比较器使能比较器模块并选择一个稳定的内部或外部参考电压作为反相端输入。配置定时器设置一个定时器以所需的采样频率f_s产生中断。在中断服务程序中读取比较器输出状态控制一个GPIO进行反馈1位DAC并对“1”进行计数。主循环中当计数达到目标值如1024后输出结果并清零计数器。相比于寻找一颗带高精度ADC的MCU利用现有资源实现Σ-Δ ADC往往能带来更低的BOM成本和更大的设计灵活性。这个方案让我深刻体会到在嵌入式设计中有时“软件定义硬件”的思路能开辟出意想不到的优化路径。当你下次面对一颗没有ADC的MCU时不妨先别急着换型号看看它的比较器和定时器也许一个高精度的ADC就藏在其中。

相关新闻