手持示波器这东西,听起来像是测量仪器里的入门款,但真要把采样率、带宽、触发、便携性和成本揉在一起,做一台能认真测信号的版本,工程量一点不比台式机小。“再战”两个字,说明我已经踩过一轮坑,这次重新设计,目标不是“能亮波形”,而是“在现场能替代台式机的关键功能”。这次我把整条链路重新捋了一遍:模拟前端怎么衰减、ADC怎么采样、触发怎么稳定、固件怎么画波形、标定怎么校准,每一个环节都走了几轮验证。如果你也想低成本做一台能看10MHz以内信号、带双通道、能随身携带的示波器,这篇文章应该能给你一套完整可参考的思路。
1. 为什么再做一台:从需求倒推方案
1.1 第一版踩过的坑
第一版我做的是“能出波形”的示波器,硬件堆得很简单:一颗带ADC的单片机直接采样,把数据丢到屏幕上。实际拿来测一个1kHz方波还好,一旦测PWM的边沿、看电源纹波、或者量一个串口波形,问题全冒出来了。
首先是带宽,单片机内置ADC的采样率看着不低,但实际采样保持电路的有效采样带宽很有限,信号频率到几兆赫兹之后,幅值就急剧衰减,看到的波形已经严重失真。其次是触发电平,第一版没有任何硬件触发,完全靠软件在数据流里找上升沿,结果就是波形在屏幕上不停晃动,触发点不固定,根本看不了一个稳定的周期波形。再就是电池续航,直接用USB供电,现场用不方便,而且模拟前端和数字部分的电源没有分开处理,噪声全部耦合到屏幕上了。
这一轮让我清楚一件事:示波器不是画波形,而是一个“信号采集+触发+显示”的闭环系统。任何一环偷懒,最后测出来的数据都不可信。
1.2 本次的目标指标
有了上一版的痛苦,这次在设计之前先列了一个指标表,不是拍脑袋定的,而是根据日常调试里真正会遇到的信号来定的。
| 参数 | 第一版实际表现 | 本次目标 |
|---|---|---|
| 模拟带宽 | 约2MHz | 10MHz |
| 采样率 | 约10MSPS(实际不稳定) | 40MSPS |
| 通道数 | 1 | 2 |
| 输入电压范围 | ±1V | ±50V(含10:1探头) |
| 触发方式 | 软件触发 | 硬件触发+软件辅助 |
| 存储深度 | 4K点 | 256K点 |
| 供电 | USB供电 | 锂电池供电,约5小时 |
| 校准 | 无 | 可选参考源校准 |
为什么要定10MHz带宽?因为大部分嵌入式调试场景,像SPI时钟、UART波特率、PWM频率、电源开关瞬态,基频都不会超过10MHz,谐波能看到三四次就足够定位问题了。40MSPS采样率配合10MHz带宽,刚好满足“采样率至少四倍于信号最高频率”的经验法则,用来重建波形不会出现严重的混叠。
1.3 方案选型的核心思路
这次没有追求很夸张的规格,选型原则是“成熟、好调试、成本可控”。
- 主控选了一颗基于Cortex-M3内核的MCU,主频可以跑到72MHz,带DMA和FSMC接口,可以外扩SRAM,关键是优秀的驱动无源LCD也没有明显卡顿。
- ADC采用外置并行接口的高速芯片,8位分辨率、40MSPS,虽然位数不高,但对示波器来说,8位刚好可以满足大多数肉眼观察需求,成本也远低于12位高速ADC。
- FIFO/逻辑控制用了一片CPLD做接口桥接,负责把ADC的高速采样数据暂存,再把MCU需要的数据段按照请求送出去。如果不用CPLD,MCU的中断根本扛不住40MSPS的采样率。
- 模拟前端用了分立元件构建:输入保护、分压衰减、阻抗变换、驱动ADC。
这套方案最关键的逻辑是:高速采样不能依赖MCU,MCU只负责“调度”。ADC在CPLD的控制下连续采样,CPLD内部做一个环形缓冲,MCU每帧只需要读取触发后的有效数据。这样既保证了采样不丢点,又不占用MCU大量时间。
正好也是因为“再战”,我对FPGA/CPLD这类器件没有那么排斥了。第一版总想用纯MCU解决,后来发现强行让MCU做它不擅长的高速数据搬运,纯属给自己挖坑。
2. 硬件架构与核心模块拆解
2.1 模拟前端:从探头到ADC的完整链路
模拟前端是示波器的“手感”来源。如果前端做不好,ADC再快看到的也是噪声和衰减后的波形。
前端链路设计为:探头BNC接口 → 输入保护电路 → 分压衰减网络 → 缓冲放大 → 可变增益 → ADC驱动级。
输入阻抗必须做高,标准示波器输入阻抗是1MΩ,并联约10~15pF电容,这样探头才能与示波器输入形成正确的10:1分压补偿。如果输入阻抗太低,探头接到被测电路上会改变原电路的工作状态,测出来的波形本身就失真了。
分压网络我采用的是“粗衰减+细衰减”两级结构。粗衰减继电器切换到1:1或10:1档位,细衰减用电阻网络配合模拟开关调整增益。对于±50V输入,10:1档下ADC输入端看到的最大电压也就在±5V以内,再通过一级运放偏置到ADC适合的0~5V范围内。
这里有个容易忽视的细节:分压电阻必须并联补偿电容。示波器探头里的RC补偿网络是为了让高频分压比和低频分压比一致。如果只用电阻分压,信号频率高了之后,电阻分压会因为寄生电容的存在变得不均匀,波形的高频分量幅度就会失真。我最初就是没加补偿电容,用方波测试时明显看到边沿变圆,后来加上可调微调电容,对1kHz方波进行补偿,直到波形顶部完全平直。
2.2 采集核心:ADC、采样时钟与数据缓冲
ADC部分我选的是8位并行接口的高速ADC,采样时钟由MCU的一个定时器引脚产生,允许通过软件切换采样率。最高40MSPS,最低可以降到几十kSPS,以满足不同时基下的波形显示。
采样时钟的生成要特别注意“抖动”问题。虽然MCU定时器引脚可以直接输出时钟,但如果定时器中断或其他任务干扰,导致时钟周期不均匀,采样间格就不等间距,重建波形会产生一系列虚假毛刺。我用的是主控的定时器PWM模式,让硬件自动切换引脚电平,不依赖中断,实测时钟抖动控制在可接受范围。
ADC输出8位并行数据,直接送往CPLD。CPLD内部构建了一个深度为8192点的FIFO,采样过程中一直往里写数据,一旦触发条件满足,再把触发点前后的数据完整保留下来。MCU需要波形时,通过FSMC读取CPLD的缓冲寄存器,一次读走一帧数据。这样MCU完全不需要介入高速采样过程,采样率的高低也不会拖慢系统。
存储深度的意义在于:采样率很高时,如果FIFO太浅,能观察的时间窗口就很短。256K点是我用外扩SRAM实现的,时基在每格20ms以上时,屏幕还能显示完整的低频波形,而不需要降低采样率。对于手持仪器,256K点已经相当够用来检查协议波形和低频瞬态。
2.3 触发单元:让波形稳定的关键
第一版没有硬件触发,导致波形乱跳,这次我把触发单元单独设计出来。
触发链路是:模拟前端输出信号 → 比较器电平比较 → 上升或下降沿检测 → 触发脉冲送入CPLD。
触发电平通过一片DAC由MCU控制,用户可以手动调节,也可以启用自动档。自动档的原理是:MCU持续读取一段时间内的信号平均值,把触发电平设置为信号中位值附近,再滚动更新。
比较器输出会有毛刺问题,尤其是信号在触发电平附近徘徊时。解决办法是给比较器加上正反馈,形成迟滞窗口。比如设置触发电平为1V,迟滞窗口为±20mV,那么只有信号从0.98V升到1.02V时才真正触发,避免噪声造成误触发。迟滞窗口太小不行,太大则触发点偏移明显,一般设置为满量程的1%左右比较合适。
触发模式上,我实现了上升沿、下降沿和脉冲宽度触发。其中最常用的是上升沿触发,测串口、SPI、PWM信号都够用。
2.4 供电与整机结构:不只是装上电池那么简单
手持仪器的电源设计,难点在于模拟部分对噪声很敏感。ADC和运放共用一个电源时,采样瞬间产生的电流会通过电源内阻耦合到模拟输入端,屏幕上会出现周期性的横条纹“滚动干扰”。
这次采用了锂电池 → 升压到±5.5V → 正负LDO稳压到±5V的方案。数字部分单独用一组3.3V电源,并用磁珠和模拟电源隔离。正负LDO的好处是地平面规整,信号调理电路可以按照标准运放双电源方案工作,这对于输入信号在0V附近波动时特别重要,可以避免单电源方案里信号被硬生生抬高后产生偏置误差。
外壳我用3D打印,后部设计了一个电池仓,按键集中在左手握持区,屏幕在正面。这里有一个手持设备容易犯的共性问题:外壳里的数字线和模拟线如果靠得太近,屏幕排线的数据翻转会对模拟前端产生干扰。解决方式是让屏幕排线从顶部走线,模拟前端集中在底部,中间用地隔离带切分。
3. 固件与算法:把数据变成可以观察的波形
3.1 采样控制与数据搬运
固件部分的核心是“不要让MCU频繁中断”。我采用CPLD作为数据接口后,MCU只做两层工作:配置采样率和触发模式,然后以DMA方式从CPLD读取波形数据。
读取一帧波形的流程大概是:
- MCU配置CPLD的控制寄存器,设定采样率和触发源。
- CPLD启动采样,信号经过比较器产生的触发脉冲到来时,CPLD在FIFO里标记触发点。
- 触发后,CPLD继续采样一小段(用于显示触发后的信息),然后停止写入并置位“有数据”标志。
- MCU检测标志后,启动FSMC+DMA读取FIFO内容到SRAM。
- 读取完成后,MCU在图形处理中将原始电压码值转换成实际电压值并绘制。
实际代码里,关键是读取时序不能和DMA冲突。FSMC模式下,CPLD像外部存储器一样被访问,地址线取值有效时,数据已被准备好;MCU只需要连续读地址即可。我的配置是16位外设宽度,但ADC只有8位,所以低字节有效,高字节忽略。DMA每次搬运一字节或半字,搬运总量由波形显示区域的需要决定。
/* 伪代码:等待CPLD采样完成标志 */ while (read_adapter_flag() == 0) { /* 超时保护 */ } /* 通过DMA读取波形数据 */ dma_config(DMA_PERIPH_FSMC, DMA_MEMORY_BUFFER, WAVEFORM_SIZE); start_dma_transfer(); wait_dma_done(); /* 数据处理:把码值转换为电压,存入波形数组 */ for (uint32_t i = 0; i < WAVEFORM_SIZE; i++) { uint8_t adc_code = raw_buffer[i] & 0xff; wave_data[i] = ((float)adc_code / 255.0f); }这里有个实际经验:MCU从CPLD读取数据时,最好关闭全局中断,或者保证读取操作不被更高优先级的中断打断。否则DMA读取过程中一旦被定时器中断插入,FSMC时序可能出现一个额外的等待周期,影响数据传输稳定性。我后来就把DMA完成中断设为最高优先级,其他任务都排在它后面。
3.2 波形绘制与显示
波形绘制是另一件容易低估的工程。40MSPS采样得到的原始数据是密集的点,如果直接把所有点投到屏幕上,很多数据是重叠的;如果简单抽帧,又丢失毛刺细节。
我用的显示方案是“分块映射”:首先根据当前时基决定一帧数据对应的实际时间长度,然后换算成屏幕上每个像素对应的时间;每个像素列内,将属于该时间片内的所有采样点做“最大值-最小值”统计,然后绘制一个垂直线段,把峰谷都画出来。
这样做的原理是:示波器要忠实显示脉冲顶部底部之间的包络,而不是平均后的平滑曲线。比如1MHz方波,40MSPS下每个周期有40个采样点,屏幕上一个像素可能覆盖一个完整周期,如果不做包络,只取其中一个点,可能会得到错误的高电平或低电平。包络绘制能保证屏幕显示的最高“闪烁线”对应信号真实峰峰范围。
滚动模式我也实现了,用于观察低频缓变信号。滚动模式下,新数据从屏幕右缘推入,老数据向左移动,效果像心电图。这里注意显示帧率不要过高,30fps左右足够流畅,否则MCU同步刷新LCD的负载会大到影响采样任务。
3.3 参数测量与自动校准
读数功能是手持示波器比较实用的部分。我实现了频率、周期、峰峰值、平均值、有效值的测量,全部基于当前显示的波形数据计算。
频率测量除了常规的过零点检测外,还预设了脉冲宽度测量模式:测量两个连续上升沿之间的点数,乘以采样间隔得到周期,再取倒数得到频率。对于占空比测量,再统计一个周期内高电平点数占比。要注意的是,如果信号本身带有较大噪声,过零点的位置会上下跳动,导致频率读数不稳。此时可以先让波形通过数字均值滤波,或者开启模拟前端的带宽限制,比如限制到1MHz,再测量频率。
校准则采用了“内置参考源”方案:在主板上留出一个参考方波输出,幅值标定为1V、频率10kHz。每次开机或用户触发校准时,系统把模拟通道切换到参考源,以它为标准计算出增益误差和零点偏移,然后把补偿系数保存在EEPROM中。这个设计比每次手动用外部信号发生器校准实用得多,至少现场不会因为运放温漂问题影响测试结果。
校准公式很简单:实际电压 = (码值 / 255) * 满量程电压 * 增益误差系数 + 零点偏移。开机自检时会抓取多个周期的参考方波,计算低电平平均值和高电平平均值,然后反推增益误差系数。这里要注意高低电平的统计窗口不要取成一个周期,而是取多个周期,避免触发点占空比不稳定的情况。
4. 实操过程与问题排查实录
4.1 组装与调试顺序
这一版基本功扎实,但真正组装调试时还是有几个步骤容易出错。我总结了一套“由下往上”的顺序:
先焊电源部分,确认正负5V、3.3V、参考电压都正常,尤其是LDO的压差不能过大,否则电池电压下降后稳压会掉出调节区,导致电压跌落。然后焊模拟前端,先用信号发生器从BNC输入一个低频正弦波,观察提升放大后的各级节点波形,确认没有自激振荡。再焊ADC和CPLD部分,先测试数据线、时钟线和FIFO读写时序,不急于接屏幕。等整个采样链路都通了,再上LCD和按键。
电源部分最容易出问题:我一开始把模拟负电源的滤波电容和数字3.3V的电容共用了一个引脚,导致开机后屏幕显示正常,但测量直流电压时有明显的50Hz工频纹波。后来把模拟负电源独立铺设,并增加一个10μF钽电容后纹波才消失。
4.2 实测中抓到的典型问题
实测过程中有三个问题最具有代表性。
第一个是高阻抗输入端的自激振荡。用探头连接目标信号前,单独用BNC线连接信号发生器,波形正常;但接上10:1探头后,屏幕出现了一团高频毛刺。查了很久,发现问题是探头补偿电容的调整旋钮松动后,输入电容和运放电路形成了轻微谐振。正常情况下,高输入阻抗本来就会对环境噪声敏感,但只要补偿电容和前端运放的相位裕度足够,不会自激。后来我收紧旋钮,重新用1kHz方波补偿,毛刺消失。
第二个是触发抖动的干扰。硬件触发模式看似正常,但波形偶尔会左右偏移1个采样点。排查后发现问题出在DAC输出的触发电平上。DAC电源上有一个微小噪声,使得比较器翻转时刻略微提前或延后。解决办法是在DAC输出端加了一枚RC滤波器,截止频率1kHz,同时将延时调整到100mV,抖动就消失了。
第三个是宽幅信号测量不准。测试±12V电源时,使用10:1分压,理论上ADC端最大只有2.4V,但实测数值比实际偏高约3%。检查后发现问题不在分压电阻精度,而是ADC参考电压偏低。给ADC供电的LDO输出不是标准的5V,而是4.88V,导致满量程值被压缩。更换更高精度基准源后,误差降到1%以内。
4.3 常见问题速查表
| 现象 | 可能原因 | 解决步骤 |
|---|---|---|
| 开机无波形,屏幕只显示栅格 | 模拟前端电源没开或ADC时钟没启动 | 检查正负5V,用逻辑分析仪查看ADC时钟引脚 |
| 波形边沿明显变圆 | 输入电容补偿不正确 | 接入1kHz方波,调整补偿电容直到顶部平直 |
| 波形存在周期刷新横纹干扰 | 数字电源与模拟电源耦合 | 磁珠隔离、模拟部分加大去耦电容 |
| 触发点抖动,波形不稳定 | 比较器迟滞太小或DAC噪声 | 增大迟滞窗口,DAC输出加低通滤波 |
| 高频信号幅度偏低 | 运放增益带宽积不足或探头带宽不够 | 更换更高带宽运放,确认探头在10:1档 |
| 电池续航不足 | 电源转换效率低或待机电流大 | 关闭不需要的外设,用深度睡眠模式 |
这个表是我实际调试中反复用到的“排障地图”,每次遇到问题先对着表定位,效率比自己胡乱翻原理图快很多。记住,示波器调试首先怀疑电源和时钟,其次是参考电压,最后才怀疑固件逻辑。顺序反了很容易浪费半天时间。
5. 再战一轮后的经验
最后分享几个很个人的体会。第一,做手持示波器,不要一开始就想着把规格顶满。你现在需要的可能是10MHz带宽、40MSPS采样率,但实际做成后你会发现,真正高频的故障排查往往靠的是一根好的探头和一个稳定的触发。规格表只是参考,仪器是否可信,体现在每一次测量重复性是否一致。
第二,模拟前端是真正拉开“玩具示波器”和“工具示波器”差距的地方。业余DIY里最容易忽略的是输入保护。我第一次做的时候没有加TVS管和串联限流电阻,一次误测高压就把ADC烧了。这次我在输入端并联了两个反向TVS管,并在分压电阻前串联了一个100Ω电阻,虽然会稍微增加输入噪声,但安全性大大提升。
第三,固件里的DMA和触发协作是最花时间的环节。如果你也打算复刻类似方案,建议先把CPLD的FIFO结构想清楚,再写MCU代码。不要一上来就写显示界面。先把“采样→触发→读出→串口打印数据”这条链路跑通,比任何其他步骤都重要。
这个项目后续还可以扩展的方向很多:增加串行协议解码功能,在屏幕上直接分析UART/I2C/SPI波形;加一个USB接口配合上位机,把波形导出成CSV或图片;或者用一个更快的逻辑分析仪实现频率计功能。不过这些都是后话,能把基础的手持示波器做得稳定、可重复、抗干扰,就已经值回“再战”投入的所有精力了。