1. CAN FD总线物理层波形观测与眼图分析
任务目标
用示波器(或差分探头)捕获 CAN FD 总线 CAN_H/CAN_L 差分信号,测量显性/隐性差分电平、上升/下降时间,并基于多次位跳变叠加生成眼图,评估信号质量。本案例给出可复现的 Python 分析脚本:输入示波器导出的差分波形 CSV,输出电平、边沿时间与眼图 PNG。
环境与依赖
- Python 3.9+,依赖:
numpy、matplotlib - 示波器导出 CSV:两列(时间 s、差分电压 V),或单列差分电压加固定采样率
- 建议采样率 ≥ 1 GSa/s,带宽 ≥ 500 MHz,使用差分探头或 CH1-CH2 数学运算
测量原理
CAN FD 差分电压 Vdiff = CAN_H − CAN_L。显性位(逻辑 0)差分约 1.5~3 V,隐性位(逻辑 1)差分约 0 V。上升/下降时间按 10%~90% 幅度定义。眼图通过在每个位边界处对齐、叠加多个位窗口得到,用于观察抖动与噪声裕量。
核心代码
import numpy as np
import matplotlib.pyplot as plt
def load_waveform(path, fs=None):
data = np.loadtxt(path, delimiter=',', skiprows=1)
if data.ndim == 1:
v = data
t = np.arange(len(v)) / fs
else:
t, v = data[:, 0], data[:, 1]
return t, v
def measure_levels(v):
vmax, vmin = np.percentile(v, 99), np.percentile(v, 1)
vth = (vmax + vmin) / 2
dominant = v[v > vth].mean()
recessive = v[v < vth].mean()
return dominant, recessive, vth
def edge_times(t, v, vth, dominant, recessive):
hi = recessive + 0.9 * (dominant - recessive)
lo = recessive + 0.1 * (dominant - recessive)
tr_list, tf_list = [], []
for i in range(1, len(v)):
if v[i-1] < lo and v[i] >= hi:
tr_list.append(t[i] - t[i-1])
elif v[i-1] > hi and v[i] <= lo:
tf_list.append(t[i] - t[i-1])
return np.array(tr_list), np.array(tf_list)
def eye_diagram(t, v, vth, bit_time, n_traces=200):
idx = np.where(np.diff((v > vth).astype(int)) != 0)[0]
if len(idx) < 2:
return None, None
edges = t[idx]
span = int(bit_time / (t[1] - t[0]))
traces = []
for e in edges[:n_traces]:
i0 = np.searchsorted(t, e)
if i0 + span < len(v):
traces.append(v[i0:i0+span])
if not traces:
return None, None
traces = np.array(traces)
x = np.linspace(0, bit_time, traces.shape[1])
return x, traces
if __name__ == "__main__":
t, v = load_waveform("canfd_diff.csv", fs=1e9)
dom, rec, vth = measure_levels(v)
tr, tf = edge_times(t, v, vth, dom, rec)
print(f"显性电平: {dom:.3f} V, 隐性电平: {rec:.3f} V")
print(f"上升时间均值: {tr.mean()*1e9:.2f} ns, 下降时间均值: {tf.mean()*1e9:.2f} ns")
bit_time = 1e-6 # 1 Mbps 数据段位时间
x, traces = eye_diagram(t, v, vth, bit_time)
if traces is not None:
plt.figure(figsize=(6, 4))
for tr_ in traces:
plt.plot(x * 1e9, tr_, color='steelblue', alpha=0.15)
plt.axhline(dom, color='r', ls='--', lw=0.8)
plt.axhline(rec, color='g', ls='--', lw=0.8)
plt.xlabel("时间 (ns)"); plt.ylabel("差分电压 (V)")
plt.title("CAN FD 眼图")
plt.tight_layout(); plt.savefig("canfd_eye.png", dpi=150)
print("眼图已保存: canfd_eye.png")
else:
print("边沿不足,无法生成眼图")
运行与验证
- 将示波器导出的差分波形保存为
canfd_diff.csv(首行表头,两列:时间、电压)。 - 运行
python canfd_analysis.py。 - 观察终端输出:显性电平应落在 1.5~3 V,隐性电平接近 0 V;上升/下降时间应小于位时间的 10%。
- 检查生成的
canfd_eye.png:眼图张开度越大表示信号质量越好;若眼图闭合或边沿时间过长,说明终端匹配、线缆长度或驱动能力存在问题。
通过条件:脚本无报错运行,输出电平与边沿时间数值合理,眼图 PNG 成功生成且可见清晰的张开区域。