这一篇在干嘛?

上一篇用 12 位 DA902 单通道生成了正弦波,本实验换成 10 位双通道 DAC 芯片 DA9763:FPGA 同时向两路 DAC 输送数据,一次实验就能输出两路独立的模拟波形。硬件连接上,扩展板插在 J1 口,示波器探头接两个 SMA 端子即可分别观察双路输出,实测输出为锯齿波信号。

实验目标与原理

实验目的

双通道 10bit DA。

为什么是「双通道」

很多应用需要同时产生两路有关系的模拟信号:比如示波器的 X-Y 显示模式需要一路驱动 X 轴、一路驱动 Y 轴;通信里的 I/Q 两路基带信号也是成对产生的。单通道 DAC 一次只能出一刀,双通道 DAC(如本实验的 DA9763)内部有两套转换电路、共享一个参考,FPGA 用两根时钟、两组数据线分别驱动,就能同步输出两路波形。

DA9763 是 10 位 DAC,分辨率比 12 位的 DA902 粗一些:满量程被分成 2¹⁰ = 1024 级,每级占满量程的约 0.1%。对波形发生器来说,位数决定了波形「台阶」的细腻程度,位数越少量化误差越大、波形上的锯齿越明显——这一点在示波器放大观察时能直观看到。

波形是怎么「走」出来的

回顾上一篇的 DDS 思想:把波形一个周期的幅值量化后存进 ROM,地址计数器循环递增,逐点读出送给 DAC。锯齿波不需要 ROM——它的幅值随时间线性上升,到顶后立刻回零,本质上就是地址计数器本身:计数器从 0 加到 1023 再归零,把计数值直接当作 10 位数据送给 DA,输出就是一个完美锯齿波;若让计数值到顶后回折递减,输出就是三角波;换成 ROM 存正弦表,输出正弦波。所以波形发生器的「核心」只是一个计数器加一个查表选择,硬件成本极低。

本实验的顶层结构如原文 RTL 原理图所示:时钟进来,分成两路通道逻辑(各含地址/数据生成),分别驱动 DA1 与 DA2 两路转换,最后从两个 SMA 端子输出模拟信号。

图 1 RTL 原理图

逻辑分析仪抓取的 DA 实时波形如图 2,可以看到两路数据随时间阶梯式递增的过程:

图 2

10 位数据在示波器上长什么样

以 1024 级满量程为例,锯齿波从 0 线性爬升到 1023 共 1024 个台阶:

  • 若地址节拍为 1MHz,一个锯齿周期 = 1024 × 1μs ≈ 1.024ms,输出频率约 977Hz;
  • 每个台阶持续 1μs、高度为满量程的 1/1024 ≈ 0.1%,示波器放大后能看到「楼梯」,正常观察时被平滑成一条直线;
  • 回落沿在 1μs 内从满幅跳回 0,是整条波形里变化最快的地方——用示波器观察时要注意探头带宽,否则回落沿会显得「糊」。

锯齿波的三个可调参数

用计数器法生成锯齿波,波形完全由三个参数决定,都可在 FPGA 里用常量或寄存器调节:

  1. 幅度:数据输出的最大值。10 位数据最大 1023,若只想输出半幅波形,把计数值右移一位(或限制上限)即可;
  2. 频率:地址计满 0~1023 一轮的时间就是一个周期。对计数节拍做数控分频,分得越细周期越长、频率越低;也可以让计数器每次加 2、加 4(步进递增),频率成倍提高,代价是台阶变粗;
  3. 回零速度:锯齿波的「回落」是瞬间完成的(计数器溢出归零),所以下降沿极陡,这正是它与三角波的区别。

与 DA902 实验的对照

项目DA902(上一篇)DA9763(本篇)
分辨率12 位(4096 级)10 位(1024 级)
通道数单通道双通道
波形来源ROM 查表(正弦)计数器直出(锯齿)
数据线da_data[11:0]da1_dat[9:0]、da2_dat[9:0]

两块芯片的驱动方式相同:FPGA 给时钟和并行数据,芯片在每个时钟沿完成一次转换。区别只在位宽与通道数——理解了一个,另一个只是「改位数、复制一份通道」。上板核对波形时,先看单路锯齿是否干净,再开双踪比对两路台阶是否逐拍对齐,最后再调试两路幅度差,按这个顺序排查最不容易绕弯路。

如果想继续玩下去

在计数器直出的基础上,稍加改造就能玩出花样:

  • 加一个波形选择开关:用拨码开关在锯齿波、三角波、ROM 正弦表之间做二选一,FPGA 里就是一个多路选择器(呼应「多路选择器」实验);
  • 加频率控制字:地址计数器改为「每次加 N」,N 由拨码开关给定,输出频率随之连续可调;
  • 两路出李萨如图形:一路送 X、一路送 Y,把示波器切到 X-Y 模式,调整两路频率比即可观察李萨如图形。

双通道同步输出小技巧

要让两路输出严格同步,最简单的办法是让两路共用同一个地址计数器:同一个计数值送到两路不同的波形表(或不同的幅度缩放),两路波形在时间上就完全对齐。这也是本实验硬件上两路共用一个时钟源的用意——只要 da1_clk 与 da2_clk 同源同相,双路波形就不会错位。

波形发生器的应用场景

锯齿波看似简单,用处却不少:

  • 示波器/显像管的扫描:屏幕上光点从左到右匀速移动靠的就是一路锯齿波驱动偏转,行扫描、场扫描各一路——这正是「双通道」的经典应用;
  • 模数转换的基准:斜坡式 ADC 用一路锯齿波与被测电压比较,通过计数比较时间得到数字量;
  • 音频扫频与测量:频率随时间线性变化的信号源可用于测量电路的频响特性。

理解了这三个用途,就明白为什么本实验特意做成双通道:一路扫 X、一路扫 Y,两路波形的时间配合可以画出李萨如图形等更复杂的图案。

硬件准备

扩展板上一颗 DA9763 同时提供两路独立的 DAC 通道,两个 SMA 端子分别对应通道 1 和通道 2 的模拟输出,示波器探头可以逐路观察,也可以用双踪模式同时对比两路波形的时间关系。

引脚分配

每路通道一个独立的转换时钟、一组 10 位并行数据,全部约束如下。原文分两段给出,均一字不差保留:

set_pin_assignment { clk50mhz } { LOCATION = R7; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; } set_pin_assignment { da1_clk } { LOCATION = H15; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; } set_pin_assignment { da1_dat[0] } { LOCATION = H16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; } set_pin_assignment { da1_dat[1] } { LOCATION = K15; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; } set_pin_assignment { da1_dat[2] } { LOCATION = J16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; } set_pin_assignment { da1_dat[3] } { LOCATION = G16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; } set_pin_assignment { da1_dat[4] } { LOCATION = G14; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; } set_pin_assignment { da1_dat[5] } { LOCATION = F14; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; } set_pin_assignment { da1_dat[6] } { LOCATION = F15; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; } set_pin_assignment { da1_dat[7] } { LOCATION = D16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; } set_pin_assignment { da1_dat[8] } { LOCATION = E15; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }

set_pin_assignment { da1_dat[9] } { LOCATION = L10; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da2_clk } { LOCATION = M16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da2_dat[0] } { LOCATION = T15; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da2_dat[1] } { LOCATION = R16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da2_dat[2] } { LOCATION = R15; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da2_dat[3] } { LOCATION = P16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da2_dat[4] } { LOCATION = P15; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da2_dat[5] } { LOCATION = N16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da2_dat[6] } { LOCATION = N14; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da2_dat[7] } { LOCATION = M15; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da2_dat[8] } { LOCATION = L16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da2_dat[9] } { LOCATION = K16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }

信号含义:clk50mhz(R7)是 50MHz 板载时钟;通道 1 用 da1_clk(H15)和 da1_dat[9:0] 共 11 个引脚;通道 2 用 da2_clk(M16)和 da2_dat[9:0] 共 11 个引脚。每路都是 10 位数据加 1 根转换时钟。

10 位数据别按 12 位接

本实验 DA 是 10 位(da1_dat/da2_dat 都只有 [9:0]),与上一篇 DA902 的 12 位不同。如果把代码里数据位宽写成 [11:0] 或照搬上一篇的引脚约束,多余的两位会悬空或接到错误引脚,输出波形幅度和台阶都会不对。对照引脚表逐位核对后再编译,是 DA 类实验省时间的最好习惯。

硬件连接

实验的硬件连接如图 3:扩展板接插在 J1 口上,示波器正极连接 2 个 SMA 端,分别观察两路输出。

图 3

实验结果

两路通道输出的锯齿波信号(原文写作「巨齿波」,即锯齿波)如下:

26-6

示波器上可以看到随时间线性上升、到顶后瞬间回落的锯齿波。对照逻辑分析仪波形(图 2)看:数据线上每个时钟递增一级、计满归零,与示波器上锯齿的「慢爬快落」一一对应——数字侧的计数规律,就是模拟侧的波形形状。

把上一篇和本篇放在一起看:ROM + 正弦表出正弦波,计数器直接出锯齿波/三角波,两路数据线出双通道——波形发生器的三块积木就凑齐了。后续想输出任意复杂波形,只需换 ROM 表内容或加入频率控制字,硬件架构完全不变。

自测