步骤 1:以每秒 1M 个样本进行采样


火柴盒示波器的制作

 

 

我们称这个示波器为“ArdOsc”(因为这是 INO 文件的名称)。

 

六年前,Cristiano Lino Fontana 为他的 Girino 设计发布了一个 Instructable。

 

它有点工作但有问题。特别是,它的最大可靠采样率约为 37ksps(75ksps 偶尔会冻结)并且触发器似乎无法正常工作。它也没有显示。

 

Girino 很慢,因为它使用中断。由于保存和恢复寄存器所需的代码,中断很慢。中断是危险的,因为它们可能导致软件出现错误,而且很少发生错误。我已经编写了 40 年的嵌入式代码,我尽可能避免中断。轮询好,中断不好。注意我的话。

 

因此,ArdOsc 禁用所有中断,进入紧密循环并在需要时从 ADC 中获取数据。如果 ADC 没有完成:太糟糕了 - 只要给我你所拥有的。它抓取 1000 个样本(每个一个字节),然后重新启用中断并通过串行端口以 115200 波特率将字节发送到 PC - 或者它抓取 128 个样本并将其显示在屏幕上。

 

Girino Instructable 详细描述了 Arduino ADC。如果您有兴趣,请阅读并阅读 Atmega328p 数据表。我只想告诉你大纲。

 

Arduino ADC 使用“逐次逼近”。它测量最重要的位 - 是 0 还是 1?得到它之后,它将其“到目前为止的答案”与输入电压进行比较并测量下一个最重要的位。接下来。等等 10 位。这些位存储在 ADCH 寄存器(前 8 位)和 ADCL(后两位。我只想要 8 位,所以我忽略了 ADCL。

 

ADC 测量全部 10 位时设置一个标志。但我只想要 8 位,所以我忽略了标志并读取 ADCH 是否 ADC 完成。我原本以为这意味着我会得到“答案到目前为止”但我没有。到目前为止的“答案”存储在其他地方,我们得到的是上传到 ADCH 的最后一个答案。这意味着在 1Msps 模式下,每个连续的 4 个样本集都是相同的。 Arduino 草图使它们平滑,因此它们看起来很好但不会被愚弄:你看到 250ksps。 (感谢 AndrewJ177 指出这一点 - 请参阅下面的讨论。)


测量每个位需要时间。该定时脉冲来自于将 Atmega 的时钟(16MHz)除以“预分频器”值:2,4,8,16,32,64 或 128. 如果将预分频器设置为 2,则为 0.125uS,这对于 ADC 正确地进行比较 - 质量很差。预分频器= 4,表示 0.25uS 哪种工作 - 结果有噪声。预分频器= 8,表示 0.5uS,对于 8 位非常合理。一般来说,每位给 ADC 的时间越长,工作效果就越好。

 

但如果你允许 ADC,比如每位 1uS 那么它每个字节需要 8uS,即 125ksps - 相当慢。如果将预分频器设置得太低,则只能正确转换前几位,并且得到的图形具有大的锯齿状步长。如果将预分频器设置得过高,则需要等待很长时间才能进行转换。

 

因此,这是每比特时间和每秒采样之间的权衡。

 

我们还必须考虑输入信号需要多长时间才能改变 ADC 采样保持电容的电压。我们不会在每次转换之前更改通道,因此充电时间不必与 Atmel 文档建议的时间一样长,但仍有效果。示波器可以达到 20kHz,但响应会下降。您可以看到一个 50kHz 的正弦波,但它的大小应该是它的四分之一。

 

ArdOsc 代码只有一个完全正确的长度,以 1Msps 的速度采样 - 即它需要 16 个时钟周期环。另一个更复杂的循环会占用更长的采样时间。

 

第 2 步:最简单的示波器

 

 

 

 


最简单的 ArdOsc 由 Arduino Nano(328p 16MHz)4 个电阻和 3 个电容组成。

 

示波器由 USB 连接供电并传输帧通过 USB 将数据传送到 PC。

 

输入信号被送入 ADC A0 引脚。在极端电压情况下,10k 电阻可为 Atmega 提供一些保护。 

 

Atmega 引脚具有二极管,可防止其输入超过 Vcc(5V)或低于 0V。二极管可以传导高达 1mA 的电流,因此示波器的输入信号可以安全地在 -10V 和+ 15V 之间变化。 ADC 引脚的输入阻抗约为 100M 和 14pF,因此额外的 10k 对 ADC 的精度影响不大。

 

ADC 使用 Vcc 作为参考电压读取 A0 引脚电压 - 因此测量范围为 0V 至 5V。 (实际上它是 0 到 Vcc * 254/255。)不幸的是,Vcc 很少是 5V,所以程序读取 Vcc 的实际值并适当地绘制“示波器显示”的格线。

 

输入是也通过一个 100nF 电容馈入 ADC A1 引脚。 A1 通过 1M 电阻连接到 0.55V。因此,A1 引脚看到输入信号的交流分量以 0.55V 为中心。

 

ADC 使用内部 1.1V 参考电压读取 A1 引脚电压 - 因此测量值为 -0.55V 至+ 0.55V。

 

0.55V 是由 Nano 的 3V3 引脚的分压器产生的。 3V3 引脚电压比 USB 连接的“5V”稳定得多。 3V3 引脚的输出不完全是 3.3V,因此您必须修整分压器以提供 0.55V 的电压。将示波器输入连接到地,然后查看 AC 范围显示的“电压”。调整 R1 直到线位于屏幕中心 - 我需要 R1 = 33k。

 

我已经展示了电路的条形板布局。条形板与 Arduino Nano 的尺寸相同,形成三明治。 Nano 的底面位于条形板的铜侧(因此在图中,Nano 从下方显示)。将一些引脚焊接到条板上,然后将 Nano 安装在引脚上并将它们焊接到 Nano 上。在我的图表中,条形板的铜以青色显示。红线是条形板上的导线或柔性导线从电路板上传出信号和电源。


步骤 3:放大信号

 

 

 


“最简单”示波器有两个输入范围:


0V 至 5V


-0.55 V 至+ 0.55V


但我们感兴趣的许多信号都小于此值。因此我们可以添加两个放大级。


LM358 双运算放大器放大 A1 的 AC 信号。运算放大器采用交流耦合,两个输入均以 0.55V 为中心。两个运算放大器级的增益都不到 5 倍。它们的输出转到 A2 和 A3,因此 Atmega 可以选择要采样的信号。

 

示波器现在有四个输入范围:


0V 到 5V


-0.55V 至+ 0.55V


-117mV 至+ 117mV


-25mV 至+ 25mV

 

它使用相同的 INO 文件并且作为“最简单”的执行。

 

将交流信号对中在 0.55V 附近的优点是运算放大器信号保持低电平。 LM258 输出不能在 Vcc 的 1.5V 范围内;所以它的范围是 0V 到 3.5V - 可怕。

 

我已经展示了电路的条形板布局。有两个条板 - 一个用于 Nano,另一个用于 LM358。他们应该形成三明治。电路板从元件侧显示。精细的柔性电线连接两块板。将电路板与粘垫,焊接支架或其他任何东西连在一起。在我的图表中,条形板的铜以青色显示。红线是条形板上的线链或将板连接在一起的柔性线。我没有显示“测试引线”。

 

再一次,你可能需要修整分压器给 0.55V。将示波器输入连接到地并调整 R9 直到线位于屏幕中央 - 我需要 R9 = 33k。

 

LM358 可能存在问题。如果信号大于 LM358 可以处理 LM358 的输出失真。您应该使用较高增益设置来查看小信号。如果你在大信号上使用它们,它们就会变形。你可以尝试更好的芯片 - 如果你有一个 - LM358 是一个相当差的芯片。

 

第 4 步:逻辑显示

 


通常你在处理逻辑电平 - 示波器是否可以显示几个“逻辑”通道。是的 - 这比搞乱 ADC 更容易。

 

值得吗?可能不是,但很容易这样做为什么不呢?


示波器现在有五个输入范围:


0V 到 5V


-0.55V 到+ 0.55V


-117mV 至+ 117mV


-25mV 至+ 25mV

 

逻辑


在“逻辑”中“模式,四个逻辑通道可以连接到 Arduino 引脚 D8,D9,D10 和 D11。它们在显示屏上显示为四行。

 

D8 至 D11 对应于 Atmega328p 芯片的端口 B 引脚 0 至 3. 芯片将整个端口 B 读入其采样缓冲器而不是 ADC 输出在 ADCH 寄存器中。

 

在 1Msps 模式下,你能看到的理论最大频率是 500kHz - 但你得到的只是一个“状态变化”的实心条。实际上,250kHz 信号更容易看到。

 

如果您不想要“逻辑”输入,则不要将连接器包括在 D8 到 D11 中。在 INO 文件中,将 bool 常量 bHasLogic 设置为 false。 (我尝试重新编写代码以使用#define 而不是 bool const 但是它很乱。)

 

步骤 5:触发器

 


假设您正在查看重复波形,例如正弦波。如果示波器在每次扫描时在屏幕上的相同位置显示它,那就太好了。因此,示波器扫描应该被启动,就像波从负变为正。

 

首先我尝试使用比较器来触发扫描(即开始收集数据)吉里诺做到了。它似乎很理想,但结果却有缺点。我决定使用 0.55V 的固定触发电压 - 交流信号的中间位置。 Atmel 允许您将比较器连接到当前 ADC 通道。听起来不错。但您必须关闭 ADC,并在触发发生时再次将其重新打开。 ADC 需要一段时间才能启动。不太好。

 

所以我采取简单的方法 - 运行 ADC 并观察它产生的值。当它们从中途下降到中途时,开始扫描。

 

在“逻辑”模式下,D8 用作触发器。

 

如果没有信号则示波器应该是自由运行的。在等待一段时间后开始扫描。我选择了最长等待 250 毫秒。程序初始化 Timer1(一个 16 位定时器),然后等待,直到它计算了足够数量的滴答。我只是看着 Timer1 的计数器 - 应该有一个更好的方法来使用标志,但它非常复杂,我无法让它 100%可靠地工作。

 

步骤 6:测试信号输出

 


您偶尔需要一个信号来测试您正在构建的任何电路。很多人已经有了信号发生器


ArdOsc 电路可以提供以下频率的方波:


31250/1 = 31250Hz


31250/8 = 3906Hz


31250/32 = 977Hz


31250/64 = 488Hz


31250/128 = 244Hz


31250/256 = 122Hz


31250/1024 = 31Hz


测试信号在引脚 D3 上生成。


如果您不想要“测试信号”输出,则不要包含连接器 D3。在 INO 文件中,将 bool 常量 bHasTestSignal 设置为 false。


步骤 7:串行协议

 


示波器通过 USB 电缆将数据帧传输到 PC,就好像它是 115200 波特,8 位,无奇偶校验的串行数据流一样。

 

PC 可以将两种命令发送到示波器。小写命令是单个字节:


‘a’将 x 轴设置为“1mS”= 1Msps


‘b’将 x 轴设置为“2mS”


‘c’将 x 轴设置为“5mS”


‘d’将 x 轴设置为“10mS”


‘e’将 x 轴设置为“ 20mS“


‘f’将 x 轴设置为”50mS“


‘g’将 x 轴设置为”100mS“


‘j’set y -axis to 5V


‘k


’设置 y 轴为 0.5V


‘l’设置 y 轴为 0.1V‘m’设置 y 轴为 200mV


‘n’将模式设置为“逻辑”


‘p’将触发设置为下降


‘q’将触发设置为上升


‘r ’设置测试信号关闭


‘s’设置测试信号 31250Hz


‘t’设置测试信号 3906Hz


‘u’设置测试信号 976Hz


‘v’设置测试信号 488Hz


‘w’设置测试信号 244Hz


‘x’设置测试信号 122Hz


‘y’设置测试信号 30Hz


‘z’扫描并发送数据

 

扫描数据发送如下:


0xAA


0xBB


0xCC


1000 字节数据


程序通过发送响应每个命令一个 Ack 字节 - “@”。

 

Arduino 串行库使用中断来读取串行输入。扫描期间输入关闭,因此输入字节保留在 Atmega 的串行输入寄存器中。扫描结束时,串行库收集字节,程序可以读取它。但是如果在扫描期间到达第二个字节,它将被丢弃。

 

大写命令可以包含几个字节,因此如果在扫描期间发送,则会损坏。在发送大写命令之前,停止发送‘z’并等待结果。大写命令仅用于调试和测试。有几个可以包含整数十进制值‘n’:


‘A’n 将 ADC 通道设置为 n


‘B’报告“电池”电压= Vcc


‘D’报告状态


‘F’n 设置频率 pwm


‘R’n 设置 Vref 为 ADC


‘T’n 触发上升或下降


‘U’n 设置预分频器并发送扫描数据


‘V’n 设置 ADC 的采样周期

 

步骤 8:添加显示

 

 

 

 

 

 


示波器可以有自己的内置显示器 --1.3“OLED。虽然 1.3”听起来很小,但这些显示器非常清晰。

 

显示器有 1.3“OLE 运行在 3.3V 时,由 SH1106 芯片通过 I2C 总线控制。(SPI 版本可用,但我使用 Arduino SPI 引脚作为“逻辑”。)

 

我需要一个非常快速的 Arduino 库和它最好是小的 .U8glib 库很慢而且很大,所以我自己编写。它只有很少的命令所以它叫做“SimpleSH1106”。

 

SH1106 有一个内置缓冲区,每像素一位。它被安排为 128 列 7 个条带(其他尺寸可用)。每个样本高 8 像素,顶部有 lsb。在 SH1106 文档中,swathes 称为“pages”,但“swathe”是计算机图形学中的标准术语。您可以写入的最小单位是一个字节 - 从 8 像素边界开始的 8 像素列。

 

我的库在 Arduino 上没有屏幕缓冲区所以所有命令都基于将整个字节写入页面。它不太方便,但你获得 1k 的 RAM。

 

Atmel328p 内置 I2C 驱动程序连接到 A4(SDA)和 A5(SCL)引脚。 SDA 和 SCL 需要上拉电阻;内置的 I2C 驱动程序使用 Atmel328p 弱上拉电阻约 50kohm。 50k 上拉电阻在低速下工作,但上升沿的速度不够快,因此我在 Nano 的 3V3 引脚上增加了 1k 的上拉电阻。

 

Arduino IDE 有一个名为 Wire.h 的 I2C 驱动程序库。这是一个不错的小型快速库,但正如您对 Arduino 所期望的那样,文档记录很少。该库初始化 I2C 硬件以 100kHz 运行,但我想要更快。因此在调用 Wire.begin()之后,我将 Atmel328p TWBR 寄存器设置为较小的值。

 

生成的库很快 - 示波器的扫描显示在 40mS 内绘制。可以使用以下命令:


void clearSH1106()用 0 字节(黑色)填充屏幕。


void DrawByteSH1106 绘制一个字节(一个 8 像素的列)。


int DrawImageSH1106 绘制图像。


int DrawCharSH1106 绘制一个角色。


int DrawStringSH1106 绘制一个字符串。


int DrawIntSH1106 绘制一个整数。


图像在程序存储器(PROGMEM)中声明。提供了一个 Windows 程序,用于将 BMP 文件转换为 SimpleSH1106 的游程编码图像。

 

库中给出了完整的描述。

 

我展示了一个条形板电路布局。有三个条形板 - 一个用于 Nano,一个用于显示器,一个用于 LM358。他们应该形成三明治。电路板从元件侧显示。精细的柔性电线连接两块板。将电路板与焊接的支架连接在一起。在我的图表中,条形板的铜以青色显示。红线是条形板上的线链或将板连接在一起的柔性线。我没有展示“测试线索”。

 

有些显示器的引脚似乎有不同的顺序。检查它们。

 

我购买的 0.9“显示器的像素的 x 坐标从 x = 0 到 127 运行。使用 1.3”显示器它们是从 x = 2 到 129. 该库包含一个常量“colOffset”,允许您调整显示器的偏移量。

 

我已经为 SM PCB 附加了 Gerber 文件和 EasyPC 源文件。这些尚未经过测试,因此使用它们需要您自担风险。

 

步骤 9:不同的屏幕

 

 


模拟显示屏显示带有刻度的波形。横轴表示时间 im mS。纵轴表示伏特,虚线为 4V,0.5V,0.1V 和 20mV;在 DC 模式下,0V 位于底部;在 AC 模式下,0V 在中间显示为虚线。

 

逻辑显示屏显示四个通道位。 D8 是顶部通道,D11 是底部通道。横轴表示以 mS 为单位的时间。

 

有两个按钮:一个用于调整时基轴的“水平”按钮和一个用于调整增益轴的“垂直”按钮。如果您按住任一按钮 1 秒钟,则会出现一个菜单屏幕。

 

当菜单显示时,“垂直”按钮会滚动浏览不同的设置,“水平”按钮会设置每个设置的值。 。如果您没有按任何一个按钮 2 秒钟,程序将返回显示波形。

 

步骤 10:频率计数器

 

 


通过使用 Timer1 和 Timer2,ArdOsc 也可以充当频率计数器。频率计有两种工作方式:在一秒钟内计算上升沿的数量,或者测量从一个上升沿到下一个上升沿的时间。

 

再一次,值得吗?也许。我不记得我需要一个频率计数器的时间。为什么不这样做很容易?

 

为了计算 D8“逻辑”信号的边沿,程序选择 D5 作为 Timer1 的时钟输入(16 位计数器 / 定时器)。 D5 外部连接到 D8 - “逻辑”输入之一。 Timer0(一个 8 位定时器)设置为每毫秒溢出一次。每次 Timer0 溢出时,都会产生中断。 1000 次中断后,Timer1 中的计数显示为“频率”。如果 Timer1 计数超过 65536,则会产生中断并记录此类中断的数量。

 

要测量从一个边沿到下一个 AC 信号的时间,定时器 1 被设置为空闲 - 运行 16MHz 计时器。 ICR1 寄存器设置为在比较器变为高电平时捕获 Timer1 中的当前值。比较器的负输入连接到 A3,正输入连接到 D6。每次比较器变为高电平时,都会产生中断。一个中断和下一个中断之间的时间是周期(1/ 频率)。程序显示一秒钟内测量的平均频率。比较器意味着在上升沿触发,但没有杂音,因此如果有噪声,下降沿将被视为上升沿。这在低频时尤其重要,因此,例如,10Hz 信号将被报告为 20Hz。

 

同时,Timer2 可能产生“测试信号”。因此没有定时器可用,我们不能使用正常的 Arduino 函数 delay(),millis()等。

 

频率计数器的代码基于 Nick Gammon 的优秀网页。


如果您不想输入“频率计数器”,则不需要从 D5 到 D8 的连接。在 INO 文件中,将 bool 常量 bHasFreq 设置为 false。

 

步骤 11:电压表

 

 


示波器还可以作为电压表,可以测量大约 -20V 和+ 20V 之间的电压。它使用 Atmega328p 的内置带隙电压参考,因此相当准确。

 

值得吗?功能的数量越来越荒谬。好的,为什么不呢?


在 A6 测量电压,ADC 使用 Vcc(即大约 5V)作为参考。因为“5V”是近似值,我们还通过将其与 1.1V 带隙进行比较来测量 Vcc 的实际值。根据数据表,带隙仅为 10%准确,但我尝试的几个接近 1.1V。

 

要测量的输入电压通过电阻网络。我选择了上面显示的值


Ra = 120k


Rb = 150k


Rc = 470k


你会在 MeasureVoltage()函数的开头附近找到这些常数。


Rc 告诉你电压表的输入阻抗。与便宜的数字万用表相比,470k 是低的,但是足够高,可以使用。

 

电压表可以测量的最低电压是


-5 * Rc/Ra = -19.6V


它可以测量的最高值是


5 * Rc/Rb + 5 = 20.7V


如果需要,可以选择不同的电阻。


如果超过这些电压怎么办?没事的。如果 Arduino 输入引脚上的电压高于 Vcc 或低于 0V,保护二极管可以承受 1mA 电流。使用 470k 意味着理论上可以测试 470V 的电压。但是我不相信在 470V 的条形板绝缘,你不应该玩这样的电压和这种原油电路。

 

如果想要精确的测量,你需要校准电压表。将电压表输入“探头”连接到 0V,看看电压表报告的内容。调整 calibrateZero 常数,直到电压表读数为“0.00V”。现在将电压表输入连接到已知电压源 - 如果你有一个不错的万用表,那么测量一个 9V 电池的电压。调整 calibrateVolts 常数直到电压表给出正确的答案。

 

如果您不想要“电压表”输入,那么您不需要连接到 D6 的电阻。在 INO 文件中,将 bool 常量 bHasVoltmeter 设置为 false。

 

步骤 12:测试引线

 


示波器通常具有花哨的测试引线。我通常使用无焊接面包板,所以我只是附加了一种用于面包板的插入式电线。由于示波器由 5V 供电,我将其连接到我在面包板上使用的任何 5V 和 0V 电源以及更多的插入式电线。

 

步骤 13:添加信号发生器

 

 

 

 

 


信号发生器是一个非常有用的部分测试装备这个使用 AD9833 模块。我在这里描述了一个独立的版本;此步骤描述如何将一个添加到 ArdOsc。 (此步骤是对此原始 Instructable 的编辑。)

 

AD9833 可以生成 0.1 MHz 至 12.5 MHz 的正弦波,三角波和方波 - 本项目中的软件限制为 1Hz 至 100kHz。它可以用作扫描发生器。扫频发生器有助于测试滤波器,放大器等的频率响应。

 

我选择的 AD9833 模块与此类似。我不是说这是最好或最便宜的供应商,但你应该买一张看起来像那张照片。

 

模块之间的连接是:


地面连接在一起


5V = AD9833 的 Vcc


D2 = FSync


D13 = Clk


D12 =数据


上面的示意图是步骤 8 中的原理图的补充。您可以使用另一块 stripboard 将另一层添加到步骤 8 中描述的三明治中。

 

我已在步骤中更新了 INO 文件 8 包括控制 AD9833 的代码。如果添加 AD9833,则应将 bHasSigGen 变量设置为 true(我将其保留为 false,因为大多数人没有 AD9833)。


新菜单控制 AD9833。它允许您选择频率和波形以及是否扫描频率。

 

扫描发生器在 1,5 或 20 秒内反复输出逐渐增加的频率。它以“min”频率开始,1,5 或 20 秒后处于“最大”频率。频率变化是对数的,并且每毫秒改变一次。在扫描频率时,示波器无法显示频率。

 

在不同模式下,扫描发生器输出频率,显示示波器输入,更改频率,显示示波器输入等。频率从“min”变为“max”超过这些步骤的 20,100 或 500(或称为“帧”)。频率变化比“扫描”模式更加粗糙,但您可以观察正在发生的事情。

 

步骤 14:未来发展

可能是电池供电?是的,只需添加连接到 Nano 的 RAW 引脚的 9V PP3。它通常使用 25mA。

 

它可以由单个锂电池供电吗?这不是那么简单,因为 3.7V 可能还不够。在 DC 模式下显示电压的代码已经读取 Vcc 电压,因此它将调整刻度位置。 Nano 可以在 3.7V(进入“5V”引脚)上运行。但是,3V3 输出可能不会达到 3.3V;监管机构的辍学率太大了。你可以直接从锂电池的 3.7V 开始运行显示器,但是你从哪里获得 0.55V 的参考电压?它需要稳定。也许您可以使用 LM317(如果将其 Adjust 引脚连接到 0V,则可提供稳定的 1.25V 电压 - 该电流下降应该足够低)。或者您可以使用 LED 作为齐纳。或者,只要您汲取微小电流,就可以使用 Vref 引脚上的电压 - 将其连接到发射极跟随器。您可能需要将 LM358 替换为工作电压较低的运算放大器。

 

触发器可以更好吗?数字示波器在触发之前将数据收集到循环缓冲区中。触发水平可以变化吗?你能一次扫一扫吗?是的,你可以做所有这些,但你可能更好只需购买“适当的”示波器。

 

你能使用 Pro Mini 吗?是的,但这不值得。您需要为显示器和 0.55V 参考电压制作自己的 3V3 信号。如果您要将数据发送到 PC,那么您将需要一个串口转 USB。只需使用 Nano。

 

它可以是无线的吗?是。添加自己的蓝牙与 HC-05(Instructables 可用)并连接到 PC 或 Android 手机。 ESP 8266 对于这个项目来说会更麻烦。

 

你能用更大的显示器吗?是的,但为什么这么麻烦,质量不是那么好。只需买一台示波器。

 

你能做得比 LM358 好吗?是。如果您的组件抽屉中有各种运算放大器,请尝试使用它们。让我知道哪一个效果最好。