信号完整性与串阻源端匹配实战:一条 25MHz 时钟线上的振铃,逼我把 33Ω 的位置和取值都算了个明白
先给结论,免得有人白看几千字:
- 串阻(源端串联匹配)不是”压振铃的万金油”,而是把驱动器输出阻抗补到传输线特征阻抗上的一次阻抗匹配。取值就是 R_s = Z0 − Z_drv,位置必须贴驱动端。
- 要不要上它,先做传输线判据:走线单向延时 t_pd 小于信号上升时间的 1/6 时,线本来就”看不见反射”,加串阻只会把边沿拖慢、时序余量吃掉。
- 串阻真正的效果不是”把边沿变缓”,而是把源端的二次反射吸收掉。负载端看到的是”延迟约 t_pd 的完整边沿”,源端看到的是”半高台阶,2·t_pd 后补满”。
- 最容易翻车的两个场景:负载端是大电容(排线/多器件),以及已经做了终端匹配还想再串一个。这两种情况加串阻是负收益。
下面是完整的落地过程。
一、现象:一块”看天气”的板子
出问题的是个采集板:STM32 主机通过 SPI 读一颗外部 ADC,25MHz 时钟,走线在 4 层板上从主控拉到板子另一头的连接器,长约 80mm,微带。从连接器再经 20mm 排线进 ADC 小板。
症状很有代表性:
- 常温台面上跑一天,偶尔出现一次数据异常(幅值跳一大截,或者通道错位)。
- 进高低温箱,低温端(−20°C)几乎分钟级复现,常温基本不复现。
- 换一块板子,症状一模一样 —— 说明是设计问题,不是器件问题。
- 降速到 10MHz,问题消失。
“降速就没事”这句话基本就把方向指死了:跟边沿速率有关,跟逻辑功能无关。这类问题十有八九是信号完整性。
探头上去一看(这是关键的一步,没有这一步后面全是猜):
- 时钟线上升沿约 0.9ns,过冲到 5.1V(3.3V 供电)。
- 过一个过冲之后还有清晰的振铃,周期约 2.4ns,大约衰减三四个周期。
- 接收端(ADC 引脚)波形更难看:台阶、双沿、局部低于 0V 的下冲。
到这里,问题已经从”数据偶尔错”变成了”这条线的边沿在 0.9ns 内跑完,而它长 80mm”。
二、先把概念摆正:源端串阻到底在干什么
这一节值得单独拎出来,因为我在现场听过的错误理解实在太多。
先把两个前置概念压缩成一句话:
- 上升时间是”信号自己的事”:t_r 由驱动器输出级决定,3.3V 的 MCU GPIO 在最高速度档下做到 0.5~1.5ns 很常见。
- 线是”有记忆的”:信号沿导线传播的速度约 6.2 ps/mm(FR-4 微带),80mm 就是 0.5ns。信号沿还没走完,驱动器的边沿早就结束了 —— 这时候线上跑的是波,不是电压。
反射的判据就是那个反射系数:
- Γ = (Z_L − Z0) / (Z_L + Z0)
接收端是 CMOS 高阻输入,Z_L 近似无穷大,Γ ≈ +1。也就是说:波到接收端之后全反射回来,回到驱动端,驱动端看到自己的输出阻抗 Z_drv 和线阻抗 Z0 不相等,又产生一次反射,来回弹,这就是振铃。振铃周期 = 4 × t_pd(往返两次)。
源端串阻做的事只有一件:让驱动端看出去的等效源阻抗等于 Z0。
R_s Z0, t_pd
+-----/\/\-----+=====================+------+
| | | |
[Z_drv] | | Z_L = open
| | | |
GND GND GND GND
源端看到的阻抗 = Z_drv + R_s,让它等于 Z0
一旦 R_s + Z_drv = Z0,波回到源端时看到的负载就是完美的 Z0,Γ = 0,不产生二次反射。接收端的第一次全反射仍然存在(开路端无法消除),但因为不再有后续反射叠加,波形在接收端只出现一次台阶,振铃消失。
顺带记住源端匹配的波形特征,不然会上示波器时看不懂:
| 观测点 | 无串阻(源端低阻,未匹配) | 加了正确串阻 |
|---|---|---|
| 驱动端波形 | 干净(自己看的自己的电压) | 先到半高,停 2·t_pd,再补满 |
| 接收端波形 | 过冲 + 振铃 + 台阶 | 延迟约 t_pd 的单次完整边沿 |
| 振铃周期 | 4·t_pd | 基本消失 |
| 额外延时 | 0 | 约 t_pd(纳秒级,量级很小) |
那个”源端半高台阶”是很多人第一次加串阻时最慌的东西:”我明明串了个电阻,怎么驱动端波形变成两台阶了?”——这正是匹配成功的标志。
三、先判断要不要当传输线看:一个判据一张表
串阻不是必贴件。判断规则我固定用这条(偏保守):
走线单向延时 t_pd 大于信号上升时间 t_r 的 1/6 时,按传输线处理,做源端匹配。
(也有人用 1/4 ~ 1/3,那是更宽松的口径。我选 1/6,因为板子后面还要过温、过压、器件离散性,余量留着不亏。微带取 6.2 ps/mm,内层带状线取 6.8 ps/mm。)
按这条规则换算成”允许的走线长度”:
| 驱动器上升时间 t_r | t_r / 6 | 临界线长(微带 6.2 ps/mm) | 典型信号 |
|---|---|---|---|
| 0.5 ns | 83 ps | 13 mm | 高速 GPIO、SDIO、QSPI |
| 1.0 ns | 167 ps | 27 mm | 大多数 MCU 推到 20~50MHz 的外设 |
| 2.0 ns | 333 ps | 54 mm | 中低速外设、SPI 10MHz 档 |
| 5.0 ns | 833 ps | 134 mm | 低速 IO、按键、继电器控制 |
| 10 ns | 1.67 ns | 269 mm | 基本不用管 |
这张表解释了为什么”同一颗芯片,A 板没事 B 板出事”:走线长度差了 30mm,就跨过了判据的那条线。
回到本案:t_r = 0.9ns,阈值 0.15ns,线长 80mm → t_pd = 0.5ns。超了三倍多,必须匹配。 这也就解释了为什么降到 10MHz 会好:频率低了,接收端的采样发生在振铃衰减之后,边沿质量不再是问题;但波反射本身一直在。
四、取值:R_s = Z0 − Z_drv,以及 Z_drv 到底怎么测
公式一行就够:
R_s = Z0 − Z_drv
难点全在 Z_drv。规格书上根本不给这个参数,能查到的 IOL/VOL 是规格下限,反推出来的阻抗严重偏大(3.3V 下常见反推值 50~112Ω),照它算出来的串阻会偏小。我的做法是实测。
4.1 实测 Z_drv:分压法
方法很简单:让 GPIO 输出固定电平,在引脚上对地(或对电源)接一个已知电阻,测节点电压,用分压比反推输出阻抗。
高电平侧: Z_OH = R_load × (V_open − V_node) / V_node
低电平侧: Z_OL = R_load × V_node / (V_open − V_node)
V_open = 空载时实测的电平(用高阻万用表或示波器直流档)
R_load = 已知电阻(220~470Ω,取太小时引脚电流会超限)
几个必须注意的点,我当初全部踩过:
- R_load 别太小。 330Ω 接在 3.3V 上大约是 10mA,短时间测没问题;33Ω 就是 100mA,直接超 MCU 引脚能力,不仅测不准还可能伤片子。
- 高低电平要分开测。 CMOS 输出级上下管不对称,实测常见高电平侧 40~60Ω、低电平侧 20~35Ω。匹配时我们关心的是边沿期间的等效阻抗,实务上取两侧的平均值,并预留一档。
- 拉低测高电平侧,拉高测低电平侧。 接到对侧去(比如对地拉高电平侧),测出来的才是这个方向上的分压。
- 连续翻转测不准。 用直流静态测,不要在翻转中读数。
实测那颗 STM32 的 GPIO(最高速度档,3.3V):
| 侧 | V_open | 接 330Ω 后 V_node | 反推 Z_drv |
|---|---|---|---|
| 高电平侧 | 3.28 V | 2.94 V | 38Ω |
| 低电平侧 | 8 mV | 0.27 V(对 VDD 接 330Ω) | 29Ω |
取平均约 33Ω,再考虑温度和工艺离散(−20°C 时边沿更快、等效阻抗更低),我按 20Ω 这个偏小值来算串阻,这样算出的 R_s 偏大一点,更稳:
R_s = 50 − 20 = 30Ω → 取标准档 33Ω
4.2 用脚本把三件事一次算完
手算容易漏项,我写了个小工具,把”判据 + 取值 + 边沿余量”一次算完(文末附完整代码):
$ python si_series_r.py --z0 50 --zdrv 20 --len 80 --tr 1.0 --cl 15 --freq 25
== 1. 需不需要当传输线看 ==
单向延时 t_pd = 0.50 ns ; 判据阈值 t_r/6 = 0.17 ns
结论: 是 -- 长度超过阈值,必须做源端匹配
== 2. 串阻取值 ==
Z0 - Z_drv = 50.0 - 20.0 = 30.0 ohm
理论值 30.0 ohm -> 取标准档: 33 ohm
== 3. 边沿余量(短走线+大电容场景)==
充电电阻 = Z_drv + R_s = 53 ohm
时间常数 = 53 ohm x 15 pF = 0.80 ns ; 到 90% 约 1.83 ns
半周期 = 20.00 ns ; 边沿占用 9%
结论: 余量充足
注意第 3 项:串阻会增加负载端的充电时间常数,这是它的代价,必须在取值时就一起算。本案 33Ω + 15pF 只占半周期的 9%,代价可以忽略。
4.3 取值的三条纪律
- 别超过 Z0。 R_s + Z_drv 明显大于 Z0 时,进线波幅不足半高,负载端会出现”先到不足幅、再补齐”的台阶,边沿反而更差。
- 向上取一档。 30Ω 取 33Ω,而不是取 27Ω。Z_drv 会随温度、供电电压变化,宁可稍微过阻尼。
- 取不到就保留 0Ω。 板子上先贴 0Ω 或者干脆空焊盘,出板后用波形决定。所有高速单端信号(时钟、SPI CLK/MOSI、SDIO CMD、地址、片选)驱动端都预留串阻焊盘,这是我现在的默认动作,成本几乎为零。
五、位置:为什么必须贴驱动端
这一条是串阻最常见的实施错误。串阻放在走线中间,或者放在接收端,效果完全不同。
- 贴驱动端:串阻与驱动器的输出级合起来构成 Z0,从”源头”就是匹配的,整条线都在匹配状态下工作。串阻到驱动引脚之间的引线要极短(< 5~10mm),这段是唯一的不连续段。
- 放中间:前半段(驱动→串阻)仍是未匹配的低阻源,反射照旧在那一段产生;后半段又被串阻变成了”高阻源驱动”。结果是两头都没匹配好,还可能引入新的阻抗台阶。
- 放接收端:这是我最想强调的误区。串阻串在高阻输入的接收端,不构成任何阻抗匹配——开路端的全反射系数仍然是 +1。它唯一的作用是给接收端的输入电容加了个 RC 低通,波形”看起来”干净了,但线上的反射一点没少,而且把边沿拖慢了。这种改法在示波器上常常”看着有效”,实际是拿时序余量换来的假象。
具体布局要求,我按这几条做:
- 串阻放在驱动器引脚后面第一个器件位,中间不打孔、不分叉、不并行走线。
- 串阻前后不共享其他网络的走线(防止串扰注入到匹配段)。
- 0402 的电阻寄生电感约 0.5~1nH,在 500MHz 时阻抗不到 3Ω,可以忽略;真正的问题是引线长度,所以别为了”好看”把串阻摆到离引脚 20mm 的地方。
六、反面教材:这几种情况加了反而更糟
这一节是本次踩坑的另一个收获。串阻不是免费午餐。
6.1 负载端是大电容
走线短但有大量负载电容(长排线、多器件挂一根线、TVS 结电容堆叠),此时起主导作用的是 RC,不是波反射。串阻直接加进充电回路,把边沿拖慢。把本案的负载电容从 15pF 换成一个 100pF 的排线,速率提到 50MHz,还是盲目取 100Ω:
$ python si_series_r.py --z0 50 --zdrv 20 --len 80 --tr 1.0 --cl 100 --freq 50 --rs 100
== 3. 边沿余量(短走线+大电容场景)==
充电电阻 = Z_drv + R_s = 120 ohm
时间常数 = 120 ohm x 100 pF = 12.00 ns ; 到 90% 约 27.60 ns
半周期 = 10.00 ns ; 边沿占用 276%
结论: 不够 -- 减串阻、降速,或把负载电容降下来
到 90% 需要 27.6ns,而半周期只有 10ns。这种配置下串阻不是”优化”,是”自杀”。正确做法是降串阻、降速率,或者加缓冲器把一根线拆成几段。
6.2 已经做了终端匹配
并联端接(50Ω 到地)或戴维南端接会把线拉到 Z0,这时候再串一个 33Ω,负载端的稳态幅度直接被分压器压下去(开路输入无直流电流,但端接电阻有):
- 33Ω 串阻 + 50Ω 端接:负载幅度 ≈ 50 / (20 + 33 + 50) × VDD ≈ 0.49 VDD
一半的摆幅没了。源端匹配与终端匹配选一种,不要叠加。 电池供电设备基本不考虑并联端接:3.3V/50Ω 常态耗 66mA,还没算信号翻转的功耗。
四种端接方式的取舍,我做成了对照表,选之前先看一眼:
| 方案 | 接法 | 匹配点 | 静态功耗 | 适用场景 |
|---|---|---|---|---|
| 源端串联 | R_s ≈ Z0 − Z_drv,贴驱动端 | 源端 | 仅跳变瞬间 | 点对点、单向、单负载(时钟/SPI/片选),首选 |
| 并联端接 | R = Z0 到 GND 或 VDD | 负载端 | 大(VDD/Z0 常态电流) | 高速、多负载、驱动能力强 |
| 戴维南端接 | R1 + R2 分压到 Z0 | 负载端 | 中等 | 多负载总线,代价是常态电流 |
| AC 端接 | R + C 到 GND | 负载端 | 仅跳变瞬间 | 周期性时钟;数据信号不适用(码型间隔长时会被 C 拉住) |
| 二极管钳位 | 到 VDD/GND | 不改阻抗 | 小 | 只抑制过冲,不解决反射 |
6.3 其他三个不适合加串阻的场景
- 线短于判据:见第三节的表。加了只是白吃边沿和成本。
- 驱动源本身很弱(Z_drv 已接近或超过 Z0):常见于板上已经很长的 I2C 上拉、或者被过度驱动的 GPIO。再串阻会做出台阶。
- 开漏总线(I2C 等):串阻在这里的角色完全变了,不是做阻抗匹配,而是限流、隔离、抑制振铃。取值口径也不同:10~100Ω 比较常见;超过 220Ω 就要重新核两件事 —— 下降沿变慢(R × Cb,100Ω × 200pF ≈ 20ns)和低电平抬升(上拉电流 × R,4.7k 上拉时 100Ω 约抬 66mV)。这一点在《上拉/下拉电阻取值实战》里已经算过账,本篇不重复。
七、量化验证:别靠眼睛说”好像好了”
这是我最想推荐的一个习惯:用固件把误码率打出来。示波器波形是定性的,”从有振铃到没振铃”很容易看出来,但”是否真的不再丢数据”必须用数字说话。
测试代码非常短,关键是把上层所有重试逻辑关掉,否则错误会被重试藏起来:
/* 串阻效果量化:连续读 100000 次器件 ID,不重试,直接统计不符预期次数
* 前提:关掉 SPI 层的重试、关掉 CRC 重传兜底,让错误暴露出来 */
#define ID_LOOP 100000u
#define DEV_ID_WANT 0x1E5Au
static uint32_t si_error_count(void)
{
uint32_t err = 0;
for (uint32_t i = 0; i < ID_LOOP; i++) {
uint32_t id = 0;
/* 每次都重新发起一次完整读,不使用任何缓存/重试 */
(void)spi_read_reg(REG_DEV_ID, (uint8_t *)&id, 2);
if (id != DEV_ID_WANT) {
err++;
}
}
return err; /* 返回 0 才是真的干净 */
}
/* 上层一次性调用并打印,用定点格式,不用 %f */
static void si_verdict(void)
{
char buf[48];
uint32_t e = si_error_count();
/* err 与 LOOP 已知,直接整数打印 */
snprintf(buf, sizeof(buf), "SI: err=%lu / %lu\r\n",
(unsigned long)e, (unsigned long)ID_LOOP);
log_write(buf);
}
改前改后的数字(同一块板,−20°C 箱内,25MHz,跑 3 轮取最差):
| 配置 | 不符预期次数 / 100000 | 过冲峰值 | 振铃 | 上升时间 |
|---|---|---|---|---|
| 无串阻(0Ω) | 137 | 5.10 V | 明显,周期 2.4ns | 0.90 ns |
| 22Ω | 24 | 4.05 V | 减弱 | 1.05 ns |
| 33Ω | 0 | 3.48 V | 基本消失 | 1.30 ns |
| 47Ω | 0 | 3.35 V | 无 | 1.55 ns |
| 100Ω | 0 | 3.32 V | 无 | 2.40 ns(开始吃掉时序余量) |
这张表里最值得看的是最后一行:47Ω 和 100Ω 虽然也不丢数据了,但上升时间从 0.9ns 涨到 2.4ns,时序余量被白白吃掉。“不丢数据”不等于”改到位”,取到匹配值就该停手。
顺带一个收益:边沿从 0.9ns 变缓到 1.3ns,时钟的高频谐波幅度跟着下来,辐射余量也好了一些。这一点在《PCB 布局与 EMC 实战》里提过,串阻在这里是”一石二鸟”——抑制反射的同时顺便降了 EMI。
八、示波器怎么测才可信:别把探头自己的振铃当成电路的
我在这上面白折腾了半天:换了三种串阻,波形上那点振铃死活不消失,最后发现那是探头地线自己造的。
用 10:1 无源探头、6cm 鳄鱼夹地线去量 1ns 边沿,会得到两个假象:
- 地线电感谐振:6cm 地线约 60nH,配探头 10~15pF 输入电容,谐振频率约
1 / (2π√(LC)) ≈ 170MHz—— 正好落在你要观察的振铃频段。你会看到一段”看起来很像反射”的振荡,其实它来自探头。 - 探头负载效应:10pF 挂在快速边沿节点上,本身就会造成下冲和边沿变缓。
我在用的口径:
- 接地弹簧代替鳄鱼夹地线(长度 < 1cm,谐振推到 400MHz 以上)。
- 探头带宽至少是信号带宽的 3 倍。0.9ns 边沿对应带宽 0.35 / 0.9ns ≈ 390MHz,所以 1GHz 探头才舒服,500MHz 勉强。
- 测接收端波形时,探头就接在接收引脚上,不要”接在附近的分支上”——差几毫米,波形就不是那个波形了。
- 触发用单次 + 余辉,或者用无限余辉看振铃包络;平均模式会把随机抖动带来的信息抹平,判断反射时不要用。
- 先在源头量:驱动端(串阻之后)、串阻之前、接收端,三个点都看一遍,才能确认串阻是否真的在起作用。
另外,找反射点有个很实用的技巧:用振铃周期反推反射路径长度。
振铃周期 T = 4 × t_pd = 4 × (d × 6.2 ps/mm)
→ d(mm) ≈ 41.7 × T(ns)
本案实测 T = 2.4ns → d ≈ 100mm。我的 PCB 走线 80mm + 排线 20mm = 100mm。对上了,说明二次反射的路径就是整条链路,不是某个中间过孔。这个技巧在排查”反射点到底在哪”时非常省时间:T = 1.2ns → 50mm,那就去 50mm 附近找阻抗不连续的地方(过孔、连接器、分叉)。
九、串阻还有两个附带作用,顺手用上
- 限流保护:串阻在热插拔、接口误插、ESD 事件时限制流入引脚或负载的电流。这在《ESD/浪涌防护实战》里讨论过,串阻是”成本最低的一层缓冲”,但它不能替代 TVS。
- 多分支拓扑的分支隔离:一根线带多个负载(地址总线、片选)时,在每个分支处串阻可以削弱分支之间的相互反射。代价是每个分支的充电时间常数变大(本分支的 R_s + 分支电容),所以只适合地址/片选这类低速信号,不适合数据和时钟。
最后再说一次落地建议:把所有高速单端信号在驱动端预留串阻焊盘。这次整改最省事的做法不是重新画板,而是板子上本来就有 0Ω 焊盘,换成 33Ω 就完事了。如果没预留,就得动刀割线、飞电阻,评审会上很难看。
十、12 项信号完整性自查清单
下次画板 / 整改之前,对着这张表过一遍:
- ☐ 列出所有边沿时间 < 2ns 的信号,标出它们的走线长度
- ☐ 对每条这样的线做传输线判据(t_pd > t_r / 6?),而不是凭感觉
- ☐ 确认 Z0 是按实际叠层算出来的(不是”我们一向做 50Ω”),微带 6.2 / 带状线 6.8 ps/mm
- ☐ Z_drv 实测过(分压法,高/低电平两侧分开测),不是照抄 datasheet 的 IOL/VOL
- ☐ 串阻按 R_s = Z0 − Z_drv 取值,向上取一档,宁愿略过阻尼
- ☐ 串阻在驱动端,不是走线中间、不是接收端
- ☐ 串阻到驱动引脚的走线 < 10mm,中间不打孔、不分叉
- ☐ 检查负载电容:长排线 / 多器件 / TVS 结电容都算进来,算过 R×C 的边沿占用
- ☐ 没有在已经端接的线上再串电阻(源端匹配与终端匹配不叠加)
- ☐ 用固件统计过误码率(关掉重试兜底),而不是只看波形”好像干净了”
- ☐ 测量链路可信:接地弹簧、探头带宽 ≥ 3× 信号带宽、探头接在接收引脚上
- ☐ 边沿变缓之后,重新核过建立/保持余量(串阻不是免费的)
十一、下一篇
串阻解决的是”点对点、单负载、单向”这一类信号。它背后有个前提:你知道 Z0 是多少。而 Z0 是从叠层和线宽算出来的——这一层,很多项目是直接甩给板厂的,图纸上只写一句”阻抗 50Ω 控制”。
下一篇讲:阻抗控制与叠层设计实战。微带和带状线的阻抗公式怎么快速估,4 层板怎么选叠层才能让 50Ω 落在好画的线宽上,参考平面不连续(跨沟、换层)到底会带来多大的反射,以及怎么在板厂不给阻抗测试报告的情况下自己验一验。
附:串阻取值小工具完整代码
#!/usr/bin/env python3
"""串阻取值 + 传输线判据 + RC 边沿余量,一次算完。
用法:
python si_series_r.py --z0 50 --zdrv 20 --len 80 --tr 1.0 --cl 15 --freq 25
"""
import argparse
# 手头常备的串阻档(比完整 E24 更贴近实际贴料)
STD_R = [0, 10, 15, 22, 27, 33, 39, 47, 56, 68, 82, 100]
def pick_std(v):
for r in STD_R:
if r >= v:
return r
return STD_R[-1]
def main():
p = argparse.ArgumentParser()
p.add_argument("--z0", type=float, default=50.0) # 传输线特征阻抗
p.add_argument("--zdrv", type=float, default=20.0) # 驱动器输出阻抗(实测)
p.add_argument("--len", type=float, default=80.0) # 走线长度 mm
p.add_argument("--tr", type=float, default=1.0) # 驱动器上升时间 ns
p.add_argument("--cl", type=float, default=15.0) # 负载端总电容 pF
p.add_argument("--freq", type=float, default=25.0) # 信号频率 MHz
p.add_argument("--psmm", type=float, default=6.2) # 微带6.2 / 带状线6.8 ps/mm
p.add_argument("--rs", type=float, default=None) # 强制指定串阻(做对比用)
a = p.parse_args()
t_pd = a.len * a.psmm / 1000.0 # ns
need = a.z0 - a.zdrv
rs = a.rs if a.rs is not None else pick_std(max(need, 0))
print("== 1. 需不需要当传输线看 ==")
print("单向延时 t_pd = %.2f ns ; 判据阈值 t_r/6 = %.2f ns" % (t_pd, a.tr / 6))
if t_pd > a.tr / 6:
print("结论: 是 -- 长度超过阈值,必须做源端匹配")
else:
print("结论: 否 -- 短于阈值,串阻只当保险/EMC 手段用,别指望它救波形")
print("\n== 2. 串阻取值 ==")
print("Z0 - Z_drv = %.1f - %.1f = %.1f ohm" % (a.z0, a.zdrv, need))
if a.rs is not None:
print("手工指定 R_s = %d ohm(应为 %d ohm,这里在故意做对比)"
% (rs, pick_std(max(need, 0))))
else:
print("理论值 %.1f ohm -> 取标准档: %d ohm" % (need, rs))
if need <= 0:
print("注意: Z_drv 已 >= Z0,驱动器本身够弱,再加串阻会把边沿拖出台阶")
print("\n== 3. 边沿余量(短走线+大电容场景)==")
r_tot = a.zdrv + rs
tau = r_tot * a.cl / 1000.0 # ns
t90 = 2.3 * tau
half = 500.0 / a.freq # ns
print("充电电阻 = Z_drv + R_s = %.0f ohm" % r_tot)
print("时间常数 = %.0f ohm x %.0f pF = %.2f ns ; 到 90%% 约 %.2f ns"
% (r_tot, a.cl, tau, t90))
print("半周期 = %.2f ns ; 边沿占用 %.0f%%" % (half, 100.0 * t90 / half))
if t90 < half * 0.3:
print("结论: 余量充足")
elif t90 < half * 0.5:
print("结论: 偏紧,需实测复核建立时间")
else:
print("结论: 不够 -- 减串阻、降速,或把负载电容降下来")
print("\n== 4. 反向验证 ==")
print("若示波器测出振铃周期 T,反射点距离 d(mm) ~= 41.7 x T(ns)")
main()
相关阅读: