第三十八章:电压表实验

本文对应原书第996页起,第五篇「实战篇」。

本章在讲什么?(先看这个)

第三十七章我们用 ILA 观察 AD 采集到的原始数据,那只是一串 0~255 的数字,对初学者来说既不直观也没有实用价值。本章要把它变成一个能用的数字电压表:AD9280 采集输入电压,FPGA 对数据做校准、滤波、换算,最终在 RGB LCD 屏上显示出"12.34"这样的电压值,分辨率 0.01V,每 0.2 秒刷新一次。

本章的核心其实不是 AD 采集本身(上一章已经学过),而是数据处理:如何把 8 位原始数据换算成真实的物理量电压。这套"采原始值→校准→滤波→定点计算→显示"的流程,是所有测量类 FPGA/嵌入式系统的通用套路。

核心概念拆解

硬件基础:7020 开发板 + J4 扩展口

本实验必须使用 7020 领航者开发板,因为 LCD 接口和高速 AD/DA 模块要同时占用扩展口资源(模块接 J4 扩展口,LCD 接 RGB LCD 接口)。AD 部分的接法与上一章完全相同。

0V 电压校准:voltage_calibrator 模块

理想情况下,输入 0V 时 AD9280 输出的数字量应该是 0x80(128,即中值),但实际芯片有偏移误差。voltage_calibrator 模块的任务就是测出这个真实的"0V 码值"(记为 voc_data),方法非常朴素:

  1. 上电后连续采集 2048 个数据;
  2. 丢弃前 1023 个(等待前端电路稳定);
  3. 对后 1024 个数据求和,再做除法求均值。
if (data_cnt < 10'd1023)                 // 丢弃前1023个数据
    data_cnt <= data_cnt + 1'b1;
else                                     // 对后1024个数据累加
    data_sum <= data_sum + ad_data;

均值用右移 10 位(除以 1024)实现,四舍五入的处理很巧妙:累加和的低 10 位如果 ≥512(即 10'b100_0000_0000 的判断),就把右移结果加 1。这是 FPGA 里做定点除法取舍的标准手法,值得记住。

data_aver <= data_sum[18:10] + ((data_sum[9:0] >= 10'b10_0000_0000) ? 1'b1 : 1'b0);

校准完成后输出 voc_done 标志,后续模块才开始工作。

电压换算:voltage_data 模块

拿到校准值后,怎么把任意采样值换算成电压?书上给出了两个定点公式:

  • 正压(采样值 ≥ voc_data):
temp0 <= 5000 * (data_aver - voc_data) / (256 - voc_data);   // 公式①
  • 负压(采样值 < voc_data):
temp0 <= 5000 * (voc_data - data_aver) / (voc_data + 1);     // 公式②

公式里的 5000 代表把电压扩大 1000 倍(即以 mV 为单位计算,5V→5000)。FPGA 不方便做浮点,所以全部用整数乘除法完成。分母取 256-voc_data 或 voc_data+1,是按"满量程 255 对应 5V、0V 对应 voc_data"线性映射出来的,初学者代入几个数验证一下就能理解。

之后是把 temp0 拆成各位数字供数码管/LCD 显示:

temp1 = temp0 / 1000;      // 个位(电压的整数部分,0~5)
temp2 = temp0 / 100 % 10;  // 十分位
temp3 = temp0 % 100;       // 余下两位
// 十分位 = temp2 - temp1*10,百分位 = temp3 / 10

超量程保护:如果计算结果超过 5V,直接显示 99.99,提醒使用者超出量程。

测试电压源:test_voltage 模块

实验需要一个不断变化的电压来做验证,但没有信号发生器怎么办?书上用 test_voltage 模块 + DA 输出自己造:以 50MHz 时钟运行,内部计数器每 0.2 秒让 da_data 从 0 加到 255 循环递增,经 DA 输出就是一个缓慢上升的阶梯电压。把这个电压接到 AD 输入,LCD 上就能看到 0.00V→5.00V 循环爬升的读数,非常直观。

时钟与整体框架

  • ad_clk:AD9280 的驱动时钟,由 50MHz 二分频得到 25MHz(与上一章一致);
  • TIME_200MS = 10_000_000:25MHz 下计 0.2 秒的计数值,控制显示刷新;
  • 顶层 top_dvm 例化:AD 接收、voltage_calibrator、voltage_data、LCD 驱动(复用 LCD 实验的通用模块)、test_voltage 与 DA 输出。

图38.4.2 数字电压表整体结构框图

图片对照

  • 系统结构:图 38.4.1 系统架构图、图 38.4.2 整体框图,看懂数据流"AD→校准→换算→LCD"。
  • 校准模块:图 38.4.3(0V 电压校准模块框图)、图 38.4.4(波形图)、图 38.4.5(仿真波形)——重点看 voc_data 是怎么被测出来的。
  • 换算模块:图 38.4.6(电压数据处理模块框图)、图 38.4.7(波形图)、图 38.4.8(仿真波形)——对照公式①②看乘除法的结果。
  • 整体仿真:图 38.4.9 数字电压表仿真波形。
  • 上板验证:图 38.5.1(RGBLCD 模块 FPC 连接器)、图 38.5.2(开发板连接液晶屏)、图 38.5.3(实验结果图,LCD 显示电压读数)。

初学者容易踩的坑

  1. 误以为 0V 就该读到 128(0x80),没做校准就直接按"128=0V"换算,结果永远有零点几伏的固定偏差。必须先用 voltage_calibrator 测出真实的 voc_data。
  2. 均值没有做四舍五入,直接右移 10 位,导致校准值系统性偏小 0.5LSB。记得把低 10 位 ≥512 的进位补上。
  3. 除零风险:公式②分母是 voc_data+1,为什么 +1?因为 voc_data 理论上可能等于 0,不加 1 会除零。改公式时别把这个保护改丢了。
  4. 显示数字没有拆位:temp0 是 0~5000 的整数(mV),直接往 LCD 送是不行的,必须先除 1000、取余等拆出个位/十分位/百分位。
  5. 刷新太快反而看不清:0.2 秒刷新一次是刻意设计的,若把 TIME_200MS 改小,读数会跳变闪烁。
  6. 用 7010 开发板跑本实验:LCD 与高速模块都需要足够逻辑资源与扩展口,本实验只支持 7020 板,注意确认自己的硬件。

小结 & 下一步

本章把"原始 ADC 数据"升级成了"可读的物理量":校准模块测零点、均值滤波去抖动、定点乘除算电压、拆位送显示。四舍五入右移、整数定点运算、超量程处理这三个技巧,以后做任何测量仪器都会反复用到。

下一步进入第三十九章:同样是 DA 出正弦波,我们将升级成 DDS(直接数字频率合成)架构,用相位累加器实现频率、波形都可以按键随意切换的信号发生器。