资讯动态

三阶连续时间Delta-Sigma ADC的Matlab行为级建模与仿真实践

发布时间:2026/10/19 10:02:18 来源:尧图企业网站定制
简介这份资源是面向模拟集成电路与信号处理学习者的3阶连续时间Delta-Sigma ADC Matlab仿真模型适合具备一定ADC理论基础、希望深入理解CTSDM架构与量化噪声整形机制的中高级读者。压缩包共10个文件约3.14MB包含3个m脚本、3篇pdf参考论文、1个mdl Simulink模型、1个xlsx计算表、1个txt说明及1张png操作示意图覆盖时序仿真、频域分析与功率谱评估等核心环节。资源围绕三阶积分器、3位量化与20倍过采样、400MHz采样率展开可帮助读者复现调制器动态行为、观察噪声整形效果并评估SNDR等性能指标。目前已有960人学习下载配套论文与说明文档便于对照理论推导与工程实现适合用于课程设计、科研入门或ADC性能优化参考。1. 从一次跑不通的仿真说起这个三阶连续时间 Delta-Sigma ADC 到底能干什么如果你正在做混合信号电路方向的研究或者刚接手一个 ADC 建模任务大概率会遇到这样的场景论文里写满了传递函数、噪声整形、系数缩放但真到动手搭一个能跑出频谱的模型时却不知道从哪下手。这份三阶连续时间 Delta-Sigma ADC 的 Matlab 仿真代码解决的正是这个问题。它把连续时间调制器的环路滤波器、量化器、反馈 DAC、时钟抖动这些环节用脚本串起来跑完直接出功率谱密度和信噪比。适合谁适合正在做 ADC 行为级建模的研究生、需要快速验证架构可行性的模拟 IC 工程师以及想搞懂 Delta-Sigma 调制器内部信号流向的自学者。Matlab 在这个场景里不是万能的但它能让你在写 Verilog-A 之前先把架构层面的坑踩一遍。2. 架构拆解三阶连续时间环路到底由哪几块拼起来2.1 连续时间 Delta-Sigma 调制器的信号链路一个三阶连续时间 Delta-Sigma ADC 的核心思路是用过采样加噪声整形把量化噪声推到高频段再在数字端滤掉。整个链路可以拆成几个关键环节输入信号先进入环路滤波器环路滤波器通常由三个级联的积分器构成每个积分器处理连续时间信号积分器的输出进入量化器量化器一般用多比特 Flash 结构把模拟量转成数字码数字码再通过反馈 DAC 送回输入端形成闭环。这个闭环结构决定了噪声传递函数和信号传递函数也决定了整个调制器的稳定性。在 Matlab 里实现这条链路有两种常见做法。一种是直接用 Simulink 搭模块另一种是用脚本写差分方程或状态空间方程。这份代码走的是脚本路线好处是参数调整方便跑批量仿真时不用反复打开模型文件。代码里用状态空间矩阵描述环路滤波器每个积分器的状态变量在时间步进中更新量化器用一个简单的比较函数实现反馈 DAC 则根据量化输出更新输入端的反馈量。2.2 环路滤波器的系数映射与状态空间实现连续时间环路滤波器的系数不是随便填的它们决定了噪声传递函数的零极点位置。常见做法是先设计一个满足稳定性要求的噪声传递函数比如 Butterworth 或 Chebyshev 型再通过脉冲响应不变法或双线性变换把离散域的系数映射回连续时间域。这份代码里系数以向量形式给出每个积分器的增益、反馈路径的权重都单独定义方便你替换成自己算出来的值。状态空间实现的好处是把微分方程写成矩阵形式时间步进时只需要做矩阵乘法。代码里用A、B、C、D四个矩阵描述环路滤波器状态更新公式是x[n1] x[n] dt*(A*x[n] B*u[n])其中dt是仿真时间步长u[n]是当前输入。这个前向欧拉法虽然简单但在步长足够小的时候精度够用。如果你要做更精确的仿真可以换成梯形法或龙格库塔法代码结构不用大改只替换状态更新那一行就行。2.3 量化器与反馈 DAC 的建模细节量化器在行为级仿真里通常用一个量化函数表示输入是连续幅值输出是离散电平。这份代码里量化器的位数可配置默认是 4 比特输出范围从 -8 到 7步长是 1。量化误差被建模成加性白噪声这是行为级仿真的常见简化虽然不完全准确但在架构评估阶段足够用。反馈 DAC 则把量化输出乘以一个增益系数再送回环路滤波器的输入端。这个增益系数很关键它直接影响闭环增益和稳定性代码里用一个单独的变量dac_gain表示方便你扫描不同值看效果。时钟抖动是连续时间 Delta-Sigma ADC 的一个痛点这份代码里用随机相位扰动来模拟。具体做法是在每个采样时刻加一个服从高斯分布的小扰动扰动幅度由jitter_rms参数控制。这个建模方式不算精确但能让你快速看到抖动对信噪比的影响趋势。如果你要做更严格的抖动分析可以把扰动加到反馈 DAC 的更新时刻上而不是采样时刻。3. 跑起来从零开始把仿真脚本跑出频谱3.1 环境准备与文件结构说明这份代码不需要额外的工具箱Matlab 基础版本就能跑。我一般会先确认 Matlab 版本在 R2016b 以上因为代码里用了隐式扩展老版本可能会报维度不匹配。文件结构很简单主脚本一个里面包含参数定义、环路滤波器系数、仿真循环和频谱分析四部分。没有额外的函数文件所有逻辑都在一个脚本里方便你直接复制到自己的工程目录下。跑之前建议先清空工作区避免旧变量干扰。命令很简单clear; close all; clc;这三行不是可有可无的尤其是clear因为代码里有些变量名比较通用比如fs、N如果工作区里有同名变量可能会覆盖掉脚本里的定义导致仿真结果莫名其妙。血泪经验有一次我忘了清空跑出来的频谱跟预期完全对不上排查了半天才发现是旧变量在捣乱。3.2 关键参数配置采样率、过采样比与量化位数参数配置集中在脚本开头几个关键参数需要你根据实际需求调整。采样率fs决定了仿真时间步长通常设为目标信号带宽的几百倍。过采样比OSR决定了噪声整形的效果三阶调制器一般取 32 或 64。量化位数nbits影响量化噪声底位数越高信噪比越好但仿真时间也会增加。fs 1e6; % 采样率单位 Hz OSR 64; % 过采样比 nbits 4; % 量化器位数 fin 10e3; % 输入信号频率单位 Hz Ain 0.5; % 输入信号幅度归一化值 N 2^16; % 仿真点数这几个参数之间有关联。fs和OSR决定了信号带宽fs/OSR就是奈奎斯特带宽。N决定了频率分辨率点数越多频谱越平滑但跑得也越慢。我一般会先用N 2^14快速跑一遍看趋势确认没问题再加到2^16出最终结果。Ain不要设得太大超过 0.7 容易让调制器进入不稳定区输出频谱会出现杂散。3.3 仿真主循环与频谱分析代码逐段讲主循环是整个脚本的核心每一轮迭代做四件事更新环路滤波器状态、计算量化器输入、执行量化、更新反馈。代码结构如下% 初始化状态变量 x zeros(3, 1); % 三个积分器的状态 y zeros(1, N); % 量化输出序列 u Ain * sin(2*pi*fin*(0:N-1)/fs); % 输入信号 % 主循环 for n 1:N % 计算环路滤波器输出 v C * x D * u(n); % 量化 q quantizer(v, nbits); % 反馈 DAC fb dac_gain * q; % 状态更新 x x dt * (A * x B * (u(n) - fb)); % 保存输出 y(n) q; end逐段说明。x是 3×1 的状态向量对应三个积分器的输出。u是输入正弦信号频率和幅度由前面的参数决定。循环里先算v这是量化器的输入等于状态向量的线性组合加上前馈输入。quantizer是一个自定义函数输入连续值输出离散电平具体实现后面会讲。fb是反馈量等于量化输出乘以dac_gain。状态更新那一行是前向欧拉法dt 1/fs。最后把量化输出存到y里。跑完循环后频谱分析用pwelch或fft都行。代码里用的是fft加窗函数避免频谱泄漏。具体做法是先对y加 Hann 窗再做 FFT然后取单边谱归一化后画图。信噪比的计算用信号功率除以噪声功率噪声功率是除去信号频点外的总功率。% 加窗 w hann(N); yw y .* w; % FFT Y fft(yw); P abs(Y).^2 / N; % 单边谱 P_half P(1:N/21); P_half(2:end-1) 2*P_half(2:end-1); f (0:N/2) * fs / N; % 画图 figure; plot(f, 10*log10(P_half)); xlabel(频率 (Hz)); ylabel(功率谱密度 (dB)); grid on;这段代码里hann(N)生成长度为N的 Hann 窗yw是加窗后的信号。fft之后取平方得到功率再归一化。单边谱的处理要注意除了直流和奈奎斯特频点其他频点要乘以 2因为双边谱的能量要折算到单边。画图用10*log10转成 dB这样噪声整形的那条斜率线看起来更直观。三阶调制器的噪声整形斜率是 60 dB/decade在频谱上应该能看到一条明显的上升线。4. 避坑与排查跑仿真时最容易翻车的几个地方4.1 频谱出现异常杂散现象跑出来的频谱在低频段出现不该有的尖峰或者噪声底明显偏高。原因通常是量化器输入过载或者反馈增益设得不对。三阶调制器的稳定输入范围比一阶窄很多Ain超过 0.6 就可能不稳定。解决方法是先把Ain降到 0.3 跑一遍确认频谱正常后再逐步加大找到稳定边界。另外检查dac_gain是否和环路滤波器系数匹配不匹配会导致闭环增益异常。4.2 仿真速度慢到无法接受现象N 2^16时跑一次要等好几分钟。原因主要是 Matlab 的循环效率低尤其是状态更新那一行每次都在做矩阵乘法。解决方法是把循环里的矩阵运算向量化或者用for循环但提前分配好数组。另一个技巧是把N降到2^14先调参数最后再跑高分辨率。如果还是慢可以考虑用parfor并行跑多个参数组合但要注意状态变量不能并行更新。4.3 信噪比计算结果和理论值差太多现象算出来的 SNR 比理论值低十几 dB。原因可能是频谱分析的窗函数选得不对或者噪声功率的计算范围没排除直流和信号频点。解决方法是检查pwelch或fft的参数确保窗长和重叠点数合理。另外信号频点附近的几个 bin 要排除在噪声功率之外否则信号泄漏会被算成噪声。我一般会排除信号频点前后各 3 个 bin这个数字可以根据窗函数的主瓣宽度调整。4.4 时钟抖动参数改了但结果没变化现象调整jitter_rms后频谱和 SNR 几乎不变。原因是抖动建模的位置不对或者抖动幅度太小被量化噪声淹没了。解决方法是把抖动加到反馈 DAC 的更新时刻上而不是采样时刻因为连续时间调制器对反馈路径的抖动更敏感。另外jitter_rms的量级要和采样周期1/fs可比如果设成1e-12而1/fs是1e-6那确实看不出变化。4.5 量化器函数输出电平数不对现象量化输出只有几个离散值和设定的位数不匹配。原因是量化函数的步长计算错了或者输入范围没对齐。解决方法是检查quantizer函数里的step和offset参数确保输出电平数等于2^nbits。如果输入是归一化的输出也要归一化否则反馈增益会出错。这个坑很隐蔽因为频谱看起来可能正常但 SNR 会偏低。5. 二次开发把这套代码改成你自己的架构验证工具5.1 替换环路滤波器系数的正确姿势这份代码的环路滤波器系数是硬编码的如果你想验证自己设计的噪声传递函数需要替换A、B、C、D四个矩阵。常见做法是先用tf2ss把传递函数转成状态空间再把得到的矩阵填进去。注意连续时间传递函数和离散时间传递函数的区别tf2ss默认是离散的如果你有连续时间传递函数要用tf2ss的连续版本或者手动推导状态空间。替换系数后先跑一个短点数仿真看输出频谱的噪声整形斜率是否和预期一致。三阶调制器的斜率应该是 60 dB/decade如果斜率不对说明系数映射有问题。另外检查闭环极点是否都在左半平面否则系统不稳定。5.2 加入非理想因素运放有限增益与电容失配行为级仿真的价值在于快速评估非理想因素的影响。你可以在状态更新那一行加入运放有限增益的建模常见做法是把积分器的理想增益A乘以一个衰减因子1/(11/Aol)其中Aol是运放的开环增益。电容失配可以用随机扰动模拟每个积分器的增益加一个服从高斯分布的误差项。% 运放有限增益 Aol 60; % 开环增益dB gain_err 1 / (1 1/(10^(Aol/20))); A_eff A * gain_err; % 电容失配 mismatch 0.001; % 失配标准差 A_eff A_eff .* (1 mismatch * randn(size(A_eff)));这两段代码可以直接插在状态更新之前。Aol设成 60 dB 是常见值再低就会明显影响 SNR。mismatch设成 0.1% 是典型工艺水平再大就会在频谱上看到杂散。跑完对比理想情况和非理想情况的 SNR就能量化这些因素对性能的影响。5.3 用脚本批量扫描参数并自动出图二次开发的一个实用技巧是把参数扫描自动化。比如你想看OSR从 16 到 128 变化时 SNR 怎么变可以写一个外层循环每次改OSR跑一次仿真把结果存到数组里最后画一条曲线。这样比手动改参数跑多次效率高得多。OSR_list [16, 32, 64, 128]; SNR_list zeros(size(OSR_list)); for k 1:length(OSR_list) OSR OSR_list(k); % 这里调用仿真函数返回 SNR SNR_list(k) run_simulation(OSR, nbits, Ain); end figure; plot(OSR_list, SNR_list, o-); xlabel(过采样比); ylabel(SNR (dB)); grid on;这段代码的关键是把仿真逻辑封装成函数run_simulation输入参数输出 SNR。封装的时候注意把状态变量和输出序列都放在函数内部避免污染工作区。跑完画图理论上 OSR 每翻一倍SNR 提升 9 dB 左右三阶调制器因为噪声整形斜率更陡提升会更明显。如果曲线不符合预期回去检查系数映射和量化器建模。从那以后我每次改环路系数都会先跑一个短点数仿真确认噪声整形斜率再跑长点数出最终结果。这个习惯帮我省了很多次重新跑几十分钟的麻烦。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑