做硬件这行谁没想过手里有一台自己的示波器台式机占地方带出门又嫌重买一台几百块的玩具示波器性能又太捉急。前两年我被项目折腾得够呛每次跨实验室调板子都得扛着公司的示波器跑后来索性自己动手用一片FPGA加一颗AD9288从零搭了一台能触发的便携式示波器。这篇文章就是那次折腾的完整记录包含方案怎么选、电路怎么搭、逻辑怎么写、板子怎么调还有我踩过的一堆坑。不管你是FPGA入门学习者还是想低成本给实验室添一台备用示波器照着这个思路走都能做出来。1. 为什么非要自己造示波器方案选型与核心思路1.1 先弄清楚示波器到底在干什么示波器的本质是一个“高速采集 存储 显示”的数字系统。信号从探头进来经过模拟前端调理送到ADC进行模数转换转换结果存入存储器再由控制逻辑处理成波形最终在屏幕上画出来。这里面最要命的就是ADC的采样率。根据奈奎斯特采样定理采样率至少是信号最高频率的2倍否则频谱就会混叠。但实际做示波器2倍根本不够看工程上讲究采样率是信号带宽的5到10倍才能在波形上看出失真、毛刺和细节。举个生活中类比你拍视频想拍清楚风扇叶片转动的轨迹每秒拍2帧肯定不行至少得拍10帧、20帧才能大致还原运动过程。信号采样也是这个道理。所以一台数字示波器好不好第一个指标就是ADC位宽和采样率。位宽决定垂直分辨率也就是一个波形幅度能分多少个台阶采样率决定水平分辨率也就是波形能看得多细。这两项基本锁死了示波器的定位和上限。1.2 AD9288这颗芯片到底什么水平我选AD9288主要是看中了它三点双通道8位、单通道最高100MSPS采样率做便携入门示波器非常合适数据输出是并行CMOS电平直接接FPGA的普通IO不需要高速串行解串器无需软件配置寄存器上电就能工作对FPGA初学者特别友好AD9288是双通道ADC每个通道8bit最大采样率100MSPS供电3.3V典型功耗在几十毫瓦级别。8bit意味着幅值被分成256份做10MHz以内信号的波形观察完全够用100MSPS采样率按5倍采样率反推可以做到20MHz带宽日常调试STM32输出的PWM、I2C总线、SPI时序、电源纹波、音频信号绰绰有余。双通道也非常实用。很多调试场景需要同时看两个信号的关系比如串口TX和RX、PWM输入和输出、运算放大器的同相和反相输出。用单通道示波器看两路信号只能来回切换探头麻烦得很。AD9288双通道解决了我一大痛点。和它对比的一些方案是这样的方案优势劣势MCU内置ADC DMA电路最简单代码好写采样率一般不超过几MSPS带宽太低带USB接口的数字示波器模块即插即用有现成上位机价格不低无法定制触发和显示AD9288 FPGA可学习、可定制、成本低工作量最大硬件软件都要自己搞如果你只想快速测波形直接买成品模块最省事。但如果你想从底层搞懂示波器原理还想把FPGA练熟那我强烈推荐第三方案。用FPGA做示波器本质上就是把采样、存储、触发、显示这几大块全部用Verilog写出来做完这一遍你对FPGA的理解会上一个台阶。1.3 FPGA在示波器里扮演的角色有人可能会问我用几十块钱的STM32做不行吗只谈采样部分的话STM32G474内置的ADC可以到5MSPS做音频频段差不多但想上20MHz带宽就完全不行了。外部挂AD9288虽然也可以但ADC一秒钟输出1亿个8bit数据MCU的IO翻转速度和DMA带宽根本扛不住更别说同时做触发判断和实时显示。这就是FPGA的核心优势并行处理天然适合这种高速数据流。FPGA在示波器里承担四件事提供稳定的采样时钟用PLL对晶振倍频驱动ADC实时接收ADC的并行数据完成采样和降速处理实现触发逻辑识别信号的上升沿、下降沿或指定电平驱动屏幕显示或者通过串口/USB把数据打包上传到PC我用的FPGA是Intel Cyclone IV E系列的EP4CE10E22C8约1万个逻辑单元片内有M9K块RAM可以做FIFO有PLL可以做时钟管理价格只要几十块钱。这套组合在国产高云FPGA、Xilinx Artix-7上也都能实现逻辑代码思路完全通用。2. 系统架构拆解一台便携示波器的完整硬件链路2.1 模拟前端信号调理才是成败关键很多DIY示波器只做数字部分模拟前端随便拿个电阻分压就完事这样出来波形往往不忍直视。示波器的第一个环节是模拟前端它决定了你能测什么信号、测得多准。输入级需要做三件事高阻输入一般是1MΩ并联10pF左右电容减少对被测电路的影响幅度调理把毫伏级到几十伏的信号都映射到ADC输入范围AD9288差分输入满量程约1Vp-p过压保护防止被测信号太大把ADC烧掉我做了三个档位×1、×10、×100用模拟开关切换分压电阻网络。最前面用一对BAT54S做输入钳位保护把电压限制在0V到3.3V之内。这样就算误测12V电源也不会立刻报废芯片。触发电平比较器我直接放在FPGA内部做不额外加硬件比较器。方法很简单在Verilog里持续判断采样值和触发电平寄存器的大小关系上一个点小于电平、当前点大于等于电平就认为捕获到一个上升沿。这样做的好处是不需要调电位器触发电平可以在软件层面精确到1/256满量程。这里有个经验之谈模拟前端的元器件布局对信号质量影响极大。我第一次打板为了追求小体积把分压电阻和ADC输入脚拉得很远结果波形上高频细节明显衰减后来重新布局才好转。原则就是尽量缩短模拟信号路径地线要短而粗。2.2 FPGA选型和资源规划FPGA选型不必追求高性能够用就好。我的资源使用大概是这样模块资源消耗说明采样捕获约几十个LE只有寄存器和组合逻辑异步FIFO1到2个M9K块每块9Kbit4096深度需要4到8块触发逻辑约百个LE比较器 状态机显示驱动约千个LESPI屏刷新控制PLL1个产生几十MHz采样时钟用Quartus Prime Lite开发编译后资源占用不到50%留了不少余量做扩展。如果第一次做我建议直接用学习板的FPGA型号比如黑金或小梅哥的EP4CE10开发板外接一片AD9288模块先把逻辑调通再自己画PCB也不迟。2.3 显示与人机交互便携性从哪来屏幕我选了3.5寸TFT彩色液晶屏SPI接口320×480像素。SPI屏功耗低、接线少但刷新率有限适合显示频率较低的波形。如果你想让波形刷新更流畅建议用RGB接口屏幕加SDRAM做显存但那对FPGA逻辑写码要求高不少。屏幕只负责显示人机交互靠一个旋转编码器加两个按键。旋转编码器用来调节时基比如每格1us、10us、100us和电压档位每格0.1V、1V、10V按键用来切换菜单、冻结波形。这些都是非常成熟的外设驱动重点是逻辑上别搞得太复杂保证易用即可。还有一个非常实用的小方案先不做屏幕把采样数据通过串口发到电脑上位机用Python画波形。这样能快速验证采集和触发好不好用等整机逻辑没坑了再投入精力搞屏幕。这个思路我在后面实操章节详细说。2.4 电源树设计小细节毁掉大项目便携设备自然是USB供电5V进来后分两路3.3V给数字电路FPGA IO、ADC的数字电源、显示屏逻辑1.2V给FPGA内核ADC的模拟电源AVDD必须单独滤波。我一开始偷懒AVDD和DVDD直接从同一个3.3V LDO出来结果屏幕上波形噪声接近±1个LSB怎么看怎么不舒服。后来改成AVDD用LP5907超低噪声LDO单独供电波形立刻干净了很多。ADC的参考电压VREF也一定要加好电容滤波它直接决定转换精度。电源布局的一点心得模拟电源和数字电源用磁珠或0欧电阻做单点连接模拟地和数字地在ADC芯片底下汇合别让大电流数字信号穿越模拟区域。这个原则和做音频DAC的电源布局是一样的。3. 核心逻辑实操从采样、FIFO、触发到时基显示3.1 ADC采样逻辑把并行数据稳稳抓进来AD9288的工作模式分两种单倍数据率SDR和双倍数据率DDR。SDR模式下每个采样时钟上升沿输出一组数据DDR模式下上下沿各输出一组速度翻倍但时序约束更紧。第一次做示波器建议只用SDR模式100MSPS已经足够。采样时钟由FPGA的PLL产生接到AD9288的时钟引脚同时送入内部逻辑作为采样数据同步时钟。这里有个容易忽略的细节ADC输出数据相对于采样时钟有一定延迟FPGA采样时不能在时钟边沿卡得太紧否则数据不稳定。我一般用PLL产生一个相位偏移45到90度的时钟来采ADC数据或者用IO时序约束工具自动调整保证数据稳定。最基础的采样代码长这样module adc_capture( input wire adc_clk, // PLL输出的采样时钟 input wire [7:0] adc_data_a,// 通道A数据 input wire [7:0] adc_data_b,// 通道B数据 output reg [7:0] sample_a, output reg [7:0] sample_b ); always (posedge adc_clk) begin sample_a adc_data_a; sample_b adc_data_b; end endmodule就这么简单。但要注意的是AD9288并行输出可能有多根数据线一定要对照数据手册检查引脚编号接反一根就是满屏乱码。3.2 跨时钟域与FIFO别让亚稳态坑了你采样时钟是100MHz级别而屏幕刷新是几十Hz到几kHz这中间有巨大的时钟鸿沟。你不可能让屏幕直接跟着100MHz的采样数据跑必须把数据先存储起来一帧存完再慢慢刷到屏幕。这个过程就需要异步FIFO。异步FIFO的核心难点是跨时钟域。写时钟域是采样时钟读时钟域是显示时钟两个时钟完全异步。处理不好就会读到半新半旧的数据违反同步器规则还会造成亚稳态。亚稳态是个很抽象的概念简单理解就是触发器在时钟边沿时刻没有采到稳定的数据输出端进入一种非0非1的中间状态然后随机向0或1塌缩。FPGA硬件设计中跨时钟域信号不经过同步直接使用就是埋雷。我直接用Quartus的FIFO IP核选择异步读时钟和写时钟深度设为4096足够存储一帧波形。IP核内部已经把格雷码跨时钟域同步做好了比自己写安全得多。如果你是Xilinx环境就选FIFO Generator IP核原理一样。FIFO使用流程大概是触发条件到来时通过写使能往FIFO里写采样数据写入固定长度比如一帧2048个点后停止写读侧按显示刷新率读数据送给屏幕驱动3.3 触发逻辑示波器能不能稳住看这里示波器如果只是傻乎乎地连续采样你看到的波形会在屏幕上乱滚这就是没有触发。触发的意思是让采样过程在某个特征点开始这样才能一帧一帧稳定显示同一个波形。最简单的触发逻辑是上升沿电平触发reg [7:0] prev_data; reg trig_flag; parameter [7:0] TRIG_LEVEL 8d128; always (posedge adc_clk) begin if (prev_data TRIG_LEVEL adc_data_a TRIG_LEVEL) trig_flag 1b1; else trig_flag 1b0; prev_data adc_data_a; end这段代码的意思是上一个采样点低于触发电平当前采样点等于或高于触发电平认为信号出现上升沿产生一个触发脉冲。触发产生后控制器开始写FIFO存够一帧数据就去显示。实际使用中还需要两个优化触发抑制holdoff触发一次后暂停一段时间不再响应触发防止抖动造成重复触发触发电平调节通过旋转编码器改变TRIG_LEVEL值通常设成满量程的50%左右触发不稳定是最常见的调试问题。我做第一版时方波信号触发正常换成不规则的串口信号就疯狂乱跳。后来加了触发抑制观察立刻稳定下来。如果你遇到波形抖动、滚屏先怀疑触发参数别急着改采样率。3.4 时基控制与波形映射时基指的是屏幕上水平一格代表多少时间。时基换算公式采样点数量 采样率 × 时基 × 屏幕格数比如采样率是40MSPS屏幕显示10格要显示每格10us也就是整个屏幕宽100us那么一帧需要4000个采样点。如果屏幕水平分辨率是320个像素每个像素点对应12.5个采样点。这时有几种做法每12.5个点抽一个点保留峰值和谷值做平均用均值填充用最大值和最小值画包络类似余辉显示我建议做峰值包络因为示波器本来就是为了找异常平均反而把毛刺抹掉了。实现就是每N个采样点取最大值和最小值两个点画在同一列像素上。这样既能保证水平分辨率不丢失尖峰毛刺显示效果也好。时基改变有两种思路改变PLL输出采样时钟频率或者在固定采样率下做抽取。前者适合慢信号长时基后者适合快速切换实际项目中两种方案可以同时存在。我为了省事采用固定40MSPS采样率加抽取的方式能覆盖1us到1s的时基范围完全够用。4. 从原理图到调通完整实操过程与踩坑记录4.1 准备工作先把最小系统点亮PCB打样回来后不要急着把芯片全焊上去。我的习惯是分三步走第一步只焊电源和FPGA最小系统包括晶振、滤波电容、JTAG下载电路用LED闪烁程序验证FPGA工作正常PLL能锁定。FPGA的PLL锁定后有一个locked信号最好拉到一个LED上方便观察。第二步焊AD9288接一个低频信号源用万用表量ADC的电源是否正常然后写一个最简单的采样程序把采样值的高4位接到LED上通过调电位器改变输入电压观察LED变化确认数据线没有接反。第三步接上显示屏或者串口调整体逻辑。这个阶段最常见的错误是FPGA引脚分配错误。你看着原理图明明接对了但可能PIN分配时填错一个引脚号结果数据输出完全混乱。所以一定要在Quartus里重新核对引脚约束文件.qsf用编译报告里的引脚表逐一确认。4.2 先用数码管和串口验证采集热词里总有人搜“fpga 实现数码管动态显示”“cd4511控制七段数码管fpga”其实在调试示波器时这个思路特别实用。我第一版调试时就是把采样值发到四位数码管显示。给ADC输入端加一个缓慢变化的直流电压数码管上数据跟着变说明ADC采集链路通了。然后输入1kHz正弦波数码管刷新肉眼看不到具体波形但至少能看出数据在跳动。更推荐的做法是接串口。FPGA每采样一组数据打包成帧通过UART发到电脑。电脑上我用一个简单的Python脚本接收并画图import serial import matplotlib.pyplot as plt ser serial.Serial(COM3, 921600, timeout1) buf [] while len(buf) 1024: data ser.read(1024 - len(buf)) for b in data: buf.append(b) plt.plot(buf) plt.ylim(0, 255) plt.show()当时在电脑屏幕上看到一串完整的正弦波曲线时心里特别踏实。这个方法的价值在于剥离了显示问题单独验证采集和传输链路。如果这一步波形都不对先不要碰屏幕问题一定在模拟前端或ADC采集逻辑上。4.3 接上TFT屏幕把波形画出来串口验证通过后我开始调TFT屏幕。屏幕驱动可以用SPI总线速度比较慢但代码简单。我做了一个显存区域把一帧1024个采样点映射到240列像素上。刷新策略是先清屏再画波形点再画网格。画竖线很简单每个采样点对应一列在这一列上把幅值对应的像素点亮。但如果每列只点一个点信号锯齿感很强。我采用的办法是先算好每一列的包络最高点和最低点然后连续画一列垂直线条这样波形看起来饱满得多。这里给一个显示映射的公式横坐标 x (采样点索引 / 总采样点数) × 屏幕宽度纵坐标 y 屏幕高度 - (采样值 / 255) × 波形显示区域高度两个坐标都是整数除法注意别让计算溢出先用乘法再用除法避免精度损失。4.4 PCB布局和硬件细节的心得打完第二次板子之后我总结了几条PCB设计经验AD9288模拟输入走线尽量短不要在输入脚附近走数字时钟线或数据线FPGA和AD9288之间数据线长度尽量一致等长设计有助于信号时序稳定每一组电源引脚附近都放0.1uF和10uF两级退耦电容ADC的AVDD和DVDD分开走线晶振尽量靠近FPGA晶振底下不铺地减少寄生电容探头输入BNC座的焊接点要和外壳地做好连接屏蔽效果会好很多另外DIY示波器最容易让人忽略的是探头。普通示波器探头有10倍衰减输入电容大约10到20pF对高频信号有补偿作用。自制的示波器如果不加探头直接用BNC线输入效果也不错但测量高频信号时要注意引入的负载电容。5. 常见问题排查与实测性能评估5.1 典型故障速查表我调试过程中遇到的最典型问题整理成了一张速查表现象可能原因排查思路屏幕上全是乱码数据线接反、FPGA引脚分配错误用数码管或串口逐位检查数据波形毛刺大ADC模拟电源噪声、输入悬空输入信号接地看底噪换LDO滤波波形抖动滚动触发条件没满足、电平不对调触发电平增加触发抑制时间波形错位断裂异步FIFO读写时钟配置错误检查FIFO IP核时钟设置确认写入停止条件显示刷新慢SPI屏刷屏速度限制降低分辨率显示或换并行RGB屏采样值满偏或全零输入信号超出ADC范围检查模拟前端分压和钳位保护FPGA程序下载不稳定JTAG电源不稳、电平不匹配检查下载器供电USB线换短线这些问题里最诡异的是“波形偶发断裂一帧”。查了很久发现是FIFO写侧触发后还没写够一帧就被读侧读空了。解决办法是加一个FIFO写满标志后再允许读或者把FIFO深度做大。异步逻辑就是这么折磨人看起来现象是显示问题根因在存储控制逻辑。5.2 实测性能它能测什么不能测什么整机调好后我用信号发生器做了几个简单测试输入1MHz正弦波幅值调在1Vpp左右波形显示正常没有明显台阶感输入10MHz方波上升沿能看到完整过程但边沿有一定平滑说明带宽够用但不算富余输入STM32的UART信号115200波特率触发稳定能看到每个字节的起始位和数据位测3.3V电源输出底噪在1个LSB左右能看清纹波的大致幅度这套配置的定位很清楚便携、低成本、适合现场快速查看信号不能指望它替代几万一台的宽带示波器。8bit分辨率约等于48dB动态范围看数字信号和电源纹波完全够用但想分析音频谐波、射频包络这些垂直分辨率就不太够了。ADC满量程只有256级小信号测量误差会比较明显。一句话总结性能边界20MHz带宽以内的调试场景它都能应对超过这个范围建议换更高性能的ADC。5.3 后续升级路线从示波器到多功能仪器做完这台示波器你可以顺着几个方向继续升级增加信号发生器功能。FPGA里做一个DDS模块输出正弦波、方波、三角波到DA芯片示波器和信号发生器二合一测试时免去外接仪器升级ADC。换成12bit、250MSPS的串行LVDS接口ADC虽然逻辑复杂不少但可以对付更高频信号加逻辑分析仪通道。并行数据采样是FPGA的强项加几个输入通道就能抓数字总线调试协议非常方便把上位机做成虚拟示波器。通过USB或以太网把采样数据传到PC用上位机做显示和存储硬件就可以做得更小集成电池和触摸屏进一步提升便携性顺便提一句FPGA里有很多相关方向可以继续深入比如LVDS高速接口、MIPI摄像头接收、ISP图像处理、以太网通信等。如果你做完了示波器还想找练手项目这些方向都是很好的下一步。6. 一些关于学习和实践的心里话说点掏心窝子的体会。做这个项目之前我FPGA的底子属于“能写流水灯、能跑个状态机”的水平。对采样定理、FIFO、跨时钟域这些概念书上都看过但理解停留在做题层面。这次从零做示波器才真正把这些概念变成每天都得打交道的东西。比如握手信号、亚稳态、异步FIFO的格雷码处理在示波器里全是实际需求不是课本上抽象的名词。调试过程中最深的感悟是很多问题不是逻辑写错而是时序关系没理清。比如信号到来和时钟边沿差那么几个纳秒触发就采不到FIFO的读时钟没稳定数据就乱。FPGA调试跟软件调试的最大不同就是你不能只盯着代码看得同时看时钟、看数据、看复位时序。如果再让我做一次我会在立项阶段就先想清楚屏幕方案。第一次做的时候我把大量时间花在调试SPI屏幕刷新上后来发现这部分完全可以先放一放用串口上位机验证更高效。等采集和触发都稳定了再做屏幕就是水到渠成的事。这篇文章写下来的所有代码和思路其实都是围绕一个核心问题展开的怎样把一秒钟一亿次的采样变成屏幕上一条稳定的、能触发、能看懂幅值的波形曲线。这个问题理解透了示波器也就做出来了。如果你也正打算用FPGA做点有实际用途的东西这个项目绝对是回报率最高的选择之一。材料不贵逻辑不算太难成就感却非常足。