如何使用Arduino Nano和OLED显示器构建示波器

发布时间:2023-03-28  

  示波器是一种电子测试设备,可以使用二维图形监控任何电压的稳定变化,其中一个或多个电压随时间的变化位于垂直 Y 轴上。一般来说,每个电子爱好者或对电子产品感兴趣的人都会在某些时候需要示波器。然而,对于学生和业余爱好者来说,它的价格非常昂贵,这就是为什么在本文中我们将讨论如何使用 Arduino 在家里制作迷你示波器。

  在本文中,我们将构建一个简单、低成本的基于 Arduino 的示波器,该示波器具有 1.3“ OLED 显示屏,可用于准确显示波形。该项目的灵感来自于火柴盒项目中的Peter Balch Oscilloscope。我们更改了很少的代码和硬件可以满足我们的要求。

  构建基于 Arduino 的示波器所需的材料

  使用Arduino Nano制作这款便携式迷你示波器需要以下组件。

pYYBAGLNLDuAUDolAABp7RGaVQY347.png

pYYBAGLNLEWAXvaIAABYSkvmXso706.png

  Arduino示波器的电路图

  构建基于 Arduino 的示波器的原理图非常简单,只需要几个部件,您可以查看下面的完整电路图。

pYYBAGLNLEqAWcs4AAFoLVvDr9k427.png

  原理图的主要部分使用单个运算放大器 IC,即LM358,它在单个芯片内包含两个运算放大器。由于输入信号为交流信号,并且我们没有分轨结构,因此有两个运算放大器(来自单个运算放大器 8 引脚封装)用于使信号交流耦合。两个运算放大器都被馈入用于偏移信号的参考电压,并使用模拟输入将其绘制在示波器图上。可以使用电位器(具有 100K 电阻)更改偏移量。两个运算放大器都设置有相同的负反馈和 x5 增益设置。

  除此之外,OLED 通过 A4 连接,A5 是带有 4.7K 上拉电阻的 I2C SCL 和 SDA 引脚。它可以使用简单的 USB 连接器。这些按钮用于设置示波器的参数。我们在性能板上构建了完整的电路,当我完成设置时,它看起来像这样。

poYBAGLNLFGATKKIAAUq8zykBJs947.png

Arduino示波器——代码说明

编码部分很复杂。要了解编码的工作原理,请查看以下代码片段 -

首先,Oscope 的库是从 Peter Balch 的SimpleSH1106.h库中使用的。对于使用 SH1106 芯片组的 OLED,它是一个非常快速的库。

这些库在以下几行中定义。

#include 

#include 

#include "SimpleSH1106.h"

#include 

 


定义和类型定义在下面的行中定义 -


 


ifndef getBit


#define getBit(sfr, bit) (_SFR_BYTE(sfr) & _BV(bit))


#万一

枚举 Tmode {DC5V, AC500mV, AC100mV, AC20mV,

               逻辑逻辑,

               毫伏表,

               最大模式1

               };

常量 Tmode maxMode = maxMode1 - 1;

 


此外,所需的常量和变量在下面声明 -


 


/---------------------------------------------------- ----------------------------

// 全局常量

//------------------------------------------------ -----------------------------------------

布尔 bHasLogic = true;

布尔 bHasFreq = true;

布尔 bHasVoltmeter = true;

布尔 bHasTestSignal = true;

布尔 bHasSigGen = 假;

常量长波特率 = 115200; // UART 的波特率,单位 bps

常量 int 命令延迟 = 10; // ms 等待串行缓冲区的填充

常量 int COMBUFFERSIZE = 4; // 传入数字的缓冲区大小

常量 int testSignalPin = 3;

常量字符确认 = '@'; // 确认通讯命令

常量字节 SampPerA = 5 + 6; // 6 次

#define LoopNops __asm__("nopn nopn nopn nopn nopn nopn")

常量 int SampPerB = 20;

常量 int BtnHorz = 4; // 按钮

常量 int BtnVert = 7; // 按钮

常量 int FreeRunTimeout = 0x10; // 0.5 秒自由运行

//------------------------------------------------ -----------------------------------------

// 全局变量

//------------------------------------------------ -----------------------------------------

Tmode curMode = DC5V;

uint8_t curVref = 1;

uint8_t curPeriod = 200;

uint8_t curPrescaler = 7;

字符命令缓冲区[COMBUFFERSIZE + 1];

布尔 TrigFalling = true;

uint8_t curSweep = 0;

字节 yGraticulePage0,yGraticuleByte0,yGraticulePage1,yGraticuleByte1,yGraticulePage2,yGraticuleByte2;

字节* pxGratLabel;

字节* pyGratLabel;

字节 xGratLabelLen,yGratLabelLen;

字节 yGraticule0,yGraticule1,yGraticule2,xGraticule1,xGraticule2;

TmenuSel sel = sTime;// 用于主菜单

字节 adj[4] = {0, 0, 0, 0}; // 用于主菜单

bool SendingSerial = false;

int curPwmMode = 0;

常量 int ADCBUFFERSIZE = 128;

uint8_t ADCBuffer[ADCBUFFERSIZE];

int ButtonsTimer1 = 0;

长 Vin = 0; // 用于显示电压表

 


菜单上的图像在这里声明 -


 


/---------------------------------------------------- ----------------------------

// 主菜单的图像

//------------------------------------------------ -----------------------------------------

常量字节 imgMainMenuTop[] PROGMEM = {

  128, // 宽度

  2, // 页面

  1, 224, 147, 32, 130, 0, 3, 248, 252, 6, 130, 2, 3, 6, 252, 248, 130, 0, 2, 96, 240, 130, 144, 2, 176, 32、130、0、2、224、240、130、

  16, 3, 48, 32, 0, 130, 246, 130, 0, 130, 254, 130, 0, 130, 254, 130, 0, 2, 224, 240, 130, 16, 2, 240, 224, 130, 0, 2, 96, 240, 130,

  144, 2, 176, 32, 130, 0, 2, 224, 240, 130, 16, 5, 48, 32, 0, 224, 240, 130, 16, 2, 240, 224, 130, 0, 130, 240、130、16、2、240、224、

  130, 0, 2, 224, 240, 130, 80, 2, 112, 96, 130, 0, 149, 32, 2, 224, 255, 149, 0, 3, 1, 3, 6, 130, 4, 3, 6, 3, 1, 130, 0, 2, 2, 6, 130,

  4, 2, 7, 3, 130, 0, 2, 3, 7, 130, 4, 3, 6, 2, 0, 130, 7, 130, 0, 130, 7, 130, 0, 130, 7, 130, 0, 2, 3, 7, 130, 4, 2, 7, 3, 130, 0, 2, 2, 6,

  130, 4, 2, 7, 3, 130, 0, 2, 3, 7, 130, 4, 5, 6, 2, 0, 3, 7, 130, 4, 2, 7, 3, 130, 0, 130, 63, 130, 4, 2, 7, 3, 130, 0, 2, 3, 7, 130, 4, 2,

  6、2、151、0、1、255

};

常量字节 imgMainMenuMid[] PROGMEM = {

  128, // 宽度

  1, // 页面

  1、255、254、0、1、255

};

常量字节 imgMainMenuBot[] PROGMEM = {

  128, // 宽度

  1, // 页面

  1、255、254、128、1、255

};

常量字节 imgBoxTop[] PROGMEM = {

  128, // 宽度

  1, // 页面

  1、248、254、8、1、248

};

常量字节 imgCaret1[] PROGMEM = {

  4, // 宽度

  1, // 页面

  4、255、126、60、24

};

常量字节 imgCaret2[] PROGMEM = {

  7, // 宽度

  1, // 页面

  7、32、48、56、60、56、48、32

};

常量字节 imgTrian[] PROGMEM = {

  14, // 宽度

  2, // 页面

  28, 3,12,48,192,0,0,0,0,0,0,192,48,12,3,128,128,128,128,131,140,176,176,140,131,128,128,128,128};

常量字节 imgSine[] PROGMEM = {

  14, // 宽度

  2, // 页面

  28, 1,2,28,224,0,0,0,0,0,0,224,28,2,1,128,128,128,129,142,144,160,160,144,142,129,128,128,128};

常量字节 imgSquare[] PROGMEM = {

  14, // 宽度

  2, // 页面

  28, 0,0,0,255,1,1,1,1,1,1,255,0,0,0,160,160,160,191,128,128,128,128,128,128,191,160,160,160};

 


图纸和线条在这里声明 -


 


//------------------------------------------------ -----------------------------------------

// 填充条

// 将屏幕列的位从位 y1 填充到位 y2

// 创建一个必须是 'page' 一部分的栏

// 返回柱

//------------------------------------------------ -----------------------------------------

字节填充条(字节 y1,字节 y2,字节页){

  静态字节 lob[] = {0x00、0x01、0x03、0x07、0x0F、0x1F、0x3F、0x7F、0xFF};

  字节条;

  如果(页面 == y1 / 8){

               如果(页面 == y2 / 8)

               bar = lob[(y2 & 7) + 1];

               别的

               条= 0xFF;

               返回栏 - lob[y1 & 7];

  }

  else if (page == y2 / 8)

               返回吊球[(y2 & 7) + 1];

  else if ((page > y1 / 8) & (page < y2 / 8))

               返回 0xFF;

  别的

               返回0;

}

//------------------------------------------------ -----------------------------------------

// 画框

// 在屏幕周围画一个盒子,左上角写着 s

//------------------------------------------------ -----------------------------------------

无效的drawBox(char * s){

  // 清除SH1106();

  DrawImageSH1106(0, 0, imgBoxTop);

  for (int i = 1; i < 7; i++)

               DrawImageSH1106(0, i, imgMainMenuMid);

  DrawImageSH1106(0, 7, imgMainMenuBot);

  DrawCharSH1106(' ', 6, 0, SmallFont);

  DrawStringSH1106(s, 7, 0, SmallFont);

}

//------------------------------------------------ -----------------------------------------

// 画屏

// 像示波器一样绘制图形

// 大约需要 40 毫秒

//------------------------------------------------ -----------------------------------------

无效画屏(无效){

  字节 i, j, k, y, yPrev, bar, page, lastDrawn;

  字节* pxbz;

  字节* pybz;

  字节 pxlenz,pylenz;

  开关(curMode){

               案例毫伏表:

               drawBox("电压表");

               我 = 20;

               如果(Vin == LONG_MAX)

               DrawStringSH1106("++++", i, 3, LargeDigitsFont);

               否则如果(Vin == -LONG_MAX)

               DrawStringSH1106("----", i, 3, LargeDigitsFont);

               别的 {

               i += DrawIntDP2(Vin / 10, i, 3, LargeDigitsFont);

               DrawStringSH1106("伏特", i, 4, SmallFont);

               }

               返回;

               案例 AC100mV:

               对于 ( i = 0; i < ADCBUFFERSIZE; i++ )

               ADCBuffer[i] = ADCBuffer[i] / 4;

               休息;

               默认:

               对于 ( i = 0; i < ADCBUFFERSIZE; i++ )

               ADCBuffer[i] = 63 - ADCBuffer[i] / 4;

  }

if ((curPeriod == 0) && (curMode <= AC20mV)) {

               yPrev = ADCBuffer[0];

               y = ADC缓冲区[1];

               对于 ( i = 1; i < ADCBUFFERSIZE - 1; i++ ) {

               ADCBuffer[i] = (yPrev + y + ADCBuffer[i + 1]) / 3;

               yPrev = y;

               y = ADC缓冲区[i + 1];

               }

  }

  pxbz = pxGratLabel;

  pxlenz = xGratLabelLen;

  pybz = pyGratLabel;

  pylenz = yGratLabelLen;

  for (page = 0; page <= 7; page++) {

               yPrev = ADCBuffer[0];

               最后绘制 = 255;

               设置页面(页面);

               设置科尔(0);

               Wire.beginTransmission(addr);

               Wire.write(0x40); //后面的字节是数据

               对于 (i = 0; i < ADCBUFFERSIZE; i++) {

               如果 (i % 26 == 0) {

               Wire.endTransmission();

               Wire.beginTransmission(addr);

               Wire.write(0x40); //后面的字节是数据

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>