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

如何使用Arduino Nano和OLED显示器构建示波器,第1张

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

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

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

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

如何使用Arduino Nano和OLED显示器构建示波器,pYYBAGLNLDuAUDolAABp7RGaVQY347.png,第2张

如何使用Arduino Nano和OLED显示器构建示波器,pYYBAGLNLEWAXvaIAABYSkvmXso706.png,第3张

  Arduino示波器的电路

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

如何使用Arduino Nano和OLED显示器构建示波器,pYYBAGLNLEqAWcs4AAFoLVvDr9k427.png,第4张

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

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

如何使用Arduino Nano和OLED显示器构建示波器,poYBAGLNLFGATKKIAAUq8zykBJs947.png,第5张

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__("nop\n nop\n nop\n nop\n nop\n nop\n")
常量 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); //后面的字节是数据
               }
               y = ADC缓冲区[i];
               如果 (yPrev > y + 1) {
               if (yPrev == lastDrawn)
               yPrev--;
               bar = FillBar(y + 1, yPrev, page);
               最后绘制 = yPrev + 1;
               } 别的 {
               bar = FillBar(yPrev, yPrev, page);
               lastDrawn = yPrev;
               }
               // }
               if ((page == 0) && (bar == 0x01) && (i & 1))
               条 = 0;
               if ((page == 7) && (bar == 0x80) && (i & 1))
               条 = 0;
               if (page == yGraticulePage0) {
               如果 (i & 8)
               酒吧 = 酒吧 | y刻度字节0;
               }
               否则 if (page == yGraticulePage1) {
               如果 (i < pylenz)
               {
               酒吧 |= *pybz;
               pybz++;
               }
               否则如果 (i % 4 == 0)
               条形|= yGraticuleByte1;
               }
               否则 if (page == yGraticulePage2) {
               如果 (i % 4 == 0)
               条 |= yGraticuleByte2;
               }
               如果 ((i == xGraticule1) | (i == xGraticule2))
               酒吧 = 酒吧 | 0x22;
               if ((page == 7) && (i > xGraticule2 - pxlenz - 2) && (i < xGraticule2 - 1)) {
               条形 |= *pxbz;
               pxbz++;
               }
               Wire.write(bar);
               yPrev = y;
               }
               Wire.endTransmission();
  }
}

 

ADC 在这里声明 -

 

//------------------------------------------------ -----------------------------------------
// 初始化ADC()
//------------------------------------------------ -----------------------------------------
无效初始化ADC(无效){
  如果(curMode > AC20mV)
               返回;
  ACSR = 0x10;
  ADCSRA = 0x97;
  ADCSRB = 0x0 ; //ADC控制和状态寄存器B
  // 0 位 6 – ACME:模拟比较器多路复用器使能
  // 000 位 2:0 – ADTSn:ADC 自动触发源 [n = 2:0] 自由运行模式
  ADMUX = 0x20 + (curVref << 6) + curMode;// ADC 多路复用器选择寄存器
  // rr 位 7:6 – REFSn:参考选择 = Vcc
  // 1 位 5 – ADLAR:ADC 左调整结果
  // aaaa 位 3:0 – MUXn:模拟通道选择
  DIDR0 = 0x3F;// 数字输入禁用寄存器 0
  // ADC0D=1, ADC1D=1, ADC2D=1, ADC3D=1, ADC4D=1, ADC5D=1, ADC6D=0, ADC7D=0
}

 

屏幕上信号的扫描在下面声明 -

 

//------------------------------------------------ -----------------------------------------
// 设置扫描
// 设置周期和 ADC 预分频器
//------------------------------------------------ -----------------------------------------
无效 setSweep(字节扫描){
  诠释 x;
  长 t;
  如果(扫一扫 == 255){
               如果(curSweep == 0)
               curSweep = 6;
               别的
               curSweep——;
  } 别的
               curSweep = 扫描;
  开关(curSweep){
               情况 0:curPeriod = 0;curPrescaler = 2; t = 100;pxGratLabel = &ax0_1[0]; xGratLabelLen = sizeof(ax0_1); 休息;
               案例1:curPeriod = 4;curPrescaler = 2; t = 400;pxGratLabel = &ax0_4[0]; xGratLabelLen = sizeof(ax0_4); 休息;
               案例2:curPeriod = 11;curPrescaler = 3; t = 1000;pxGratLabel = &ax1[0]; xGratLabelLen = sizeof(ax1); 休息;
               案例 3:curPeriod = 24;curPrescaler = 3; t = 2000;pxGratLabel = &ax2[0]; xGratLabelLen = sizeof(ax2); 休息;
               案例4:curPeriod = 62;curPrescaler = 4; t = 5000;pxGratLabel = &ax5[0]; xGratLabelLen = sizeof(ax5); 休息;
               案例5:curPeriod = 125;curPrescaler = 4; t = 10000;pxGratLabel = &ax10[0]; xGratLabelLen = sizeof(ax10); 休息;
               案例6:curPeriod = 255;curPrescaler = 5; t = 20000;pxGratLabel = &ax20[0]; xGratLabelLen = sizeof(ax20); 休息;

  }
  如果(curSweep == 0)
               x = t;
  别的
               x = 16 * t / (curPeriod * SampPerA + SampPerB);
  x格线1 = x / 2;
  x格线2 = x;
  发送确认();
}
//------------------------------------------------ -----------------------------------------
// 扫一扫
// 连续扫描 siggen freq
// 整个扫描需要 n mS
// SDC regs 被保存和恢复
// 当接收到一个串行字符时停止
//------------------------------------------------ -----------------------------------------
无效扫描(int n){
  字节旧ACSR = ACSR;
  字节旧ADCSRA = ADCSRA;
  字节老ADCSRB = ADCSRB;
  字节老ADMUX = ADMUX;
  字节老DIDR0 = DIDR0;
  字节老DIDR1 = DIDR1;
  整数 fmin,fmax;
  fmin = calcFreq(freqSGLo);
  fmax = calcFreq(freqSGHi);
  诠释我=0;
  做 {
               长 f = exp((log(fmax) - log(fmin))*i/(n-1) + log(fmin)) +0.5;
               SG_freqSet(f, waveType);
               延迟(1);
               我++;
               如果 (i >= n) i = 0;
  } 而 (!Serial.available());
  SG_freqSet(calcFreq(freqSGLo), waveType);
  ACSR = 旧ACSR;
  ADCSRA = 旧ADCSRA;
  ADCSRB = 旧ADCSRB;
  ADMUX = oldADMUX;
  DIDR0 = 旧DIDR0;
  DIDR1 = 旧DIDR1;
}

 

按钮增量和模式设置在下面完成 -

 

//------------------------------------------------ -----------------------------------------
// INC模式
// 递增模式
// 从最大值环绕
// 跳过不允许的模式
//------------------------------------------------ -----------------------------------------
int incMode(int mode) {
  模式++;
//if ((mode == mLogic) && (!bHasLogic)) mode++;
// if ((mode == mFreqLogic) && ((!bHasFreq) || (!bHasLogic))) mode++;
// if ((mode == mFreqAC) && (!bHasFreq)) mode++;
  if ((mode == mVoltmeter) && (!bHasVoltmeter)) mode++;
  如果(模式 > 最大模式)
               返回DC5V;
  别的
               返回模式;
}
//------------------------------------------------ -----------------------------------------
// 设置模式
// 设置模式和 Vref
//------------------------------------------------ -----------------------------------------
无效 setMode(int 模式){
  诠释我;
  如果(模式 == 255){
               curMode = incMode(curMode);
  } 别的
               curMode = 模式;
  开关(curMode){
               案例 DC5V:
               curVref = 1;
               i = (long)4000 * 64 / readVcc();
               如果(我 <= 63){
               y刻度1 = 63 - i;
               y 刻度 2 = 63 - i / 2;
               y刻度线0 = 255;
               pyGratLabel = &ax4V[0];
               yGratLabelLen = sizeof(ax4V);
               } 别的 {
               y刻度2 = 63 - i;
               y 刻度 1 = 63 - i / 2;
               y刻度线0 = 255;
               pyGratLabel = &ax2V[0];
               yGratLabelLen = sizeof(ax2V);
               }
               休息;
               案例 AC500mV:
               curVref = 3;
               i = (字节)(0.5 / 1.1 * 256 / 4);
               y刻度1 = 32 - i;
               y刻度2 = 32 + i;
               y刻度线0 = 32;
               pyGratLabel = &ax0_5[0];
               yGratLabelLen = sizeof(ax0_5);
               休息;
               案例 AC100mV:
               curVref = 3;
               i = (字节)(0.1 / 1.1 * (R1 + R2) / R2 * 256 / 4);
               y刻度1 = 32 - i;
               y刻度2 = 32 + i;
               y刻度线0 = 32;
               pyGratLabel = &ax0_1[0];
               yGratLabelLen = sizeof(ax0_1);
               休息;
               案例AC20mV:
               curVref = 3;
               i = (字节)(0.02 / 1.1 * (R1 + R2) / R2 * (R1 + R2) / R2 * 256 / 4);
               y刻度1 = 32 - i;
               y刻度2 = 32 + i;
               y刻度线0 = 32;
               pyGratLabel = &ax20[0];
               yGratLabelLen = sizeof(ax20);
               休息;
               默认:
               curVref = 1;
               y 格线 1 = 255;
               y 格线 2 = 255;
               y刻度线0 = 255;
               pyGratLabel = &ax20[0];
               yGratLabelLen = sizeof(ax20);
               休息;
  }

 

主菜单的绘制是使用以下代码片段完成的 -

 

无效drawMainMenu(无效){
  int ofs, x, yVcc, pg;
  开关(选择){
               案例 sMode: ofs = -1; 休息;
               案例 sTrigger: ofs = -2; 休息;
               案例 sTestSig: ofs = -5; 休息;
               案例 sSigGen: ofs = bHasTestSignal ? -7:-5;休息;
               默认值:ofs = 0;
  }
  // DrawImageSH1106(0,ofs,imgMainMenu);
  DrawImageSH1106(0, ofs + 0, imgMainMenuTop);
  对于 (x = 2; x < 14; x++)
               DrawImageSH1106(0, ofs + x, imgMainMenuMid);
  DrawImageSH1106(0, ofs + 10 + bHasTestSignal * 2 + bHasSigGen * 2, imgMainMenuBot);
  DrawImageSH1106(6, 3 + sel * 2 + ofs, imgCaret1);
  粗体SH1106 = 真;
  pg = 3 + ofs;
  DrawStringSH1106("时间:", 12, pg, SmallFont); pg += 2;
  DrawStringSH1106((adj[1] <= AC20mV ? "Gain:" : "Mode:"), 12, pg, SmallFont); pg += 2;
  DrawStringSH1106("触发器:", 12, pg, SmallFont); pg += 2;
  如果(bHasTestSignal){
               DrawStringSH1106("测试签名:", 12, pg, SmallFont); pg += 2;
               如果(bHasSigGen){
               DrawStringSH1106("信号发生器", 12, pg, SmallFont); pg += 2;
               }
               DrawStringSH1106("Vcc:", 12, pg, SmallFont); yVcc = pg; pg += 2;
  } 别的 {
               如果(bHasSigGen){
               DrawStringSH1106("Vcc:", 12, pg, SmallFont); yVcc = pg; pg += 2;
               DrawStringSH1106("信号发生器", 12, pg, SmallFont); pg += 2;
               } 别的 {
               DrawStringSH1106("Vcc:", 12, pg, SmallFont); yVcc = pg; pg += 2;
               }
  }
  粗体SH1106 = 假;
  x = 62;
  pg = 3 + ofs;
  开关(adj[0]){
               案例 0: DrawStringSH1106("1mS", x, pg, SmallFont); 休息;
               案例一:DrawStringSH1106("2mS", x, pg, SmallFont); 休息;
               案例2:DrawStringSH1106("5mS", x, pg, SmallFont); 休息;
               案例 3:DrawStringSH1106("10mS", x, pg, SmallFont); 休息;
               案例4:DrawStringSH1106("20mS", x, pg, SmallFont); 休息;
               案例5:DrawStringSH1106("50mS", x, pg, SmallFont); 休息;
               案例 6:DrawStringSH1106("100mS", x, pg, SmallFont); 休息;
  }
  pg += 2;
  开关(adj[1]){
               案例 DC5V: DrawStringSH1106("5V DC", x, pg, SmallFont); 休息;
               案例 AC500mV: DrawStringSH1106("0.5V​​ AC", x, pg, SmallFont); 休息;
               案例 AC100mV: DrawStringSH1106("0.1V AC", x, pg, SmallFont); 休息;
               案例 AC20mV: DrawStringSH1106("20mV AC", x, pg, SmallFont); 休息;
               //case mLogic: DrawStringSH1106("Logic", x, pg, SmallFont); 休息;
               //case mFreqLogic: DrawStringSH1106("频率逻辑", x, pg, SmallFont); 休息;
               //case mFreqAC: DrawStringSH1106("Freq AC", x, pg, SmallFont); 休息;
               case mVoltmeter: DrawStringSH1106("Voltmeter", x, pg, SmallFont); 休息;
  }
  pg += 2;
 开关(adj[2]){
               案例一:DrawStringSH1106("Fall", x, pg, SmallFont); 休息;
               默认值:DrawStringSH1106("Rise", x, pg, SmallFont);
  }
  pg += 2;
  如果(bHasTestSignal){
               开关(adj[3]){
               案例一:DrawStringSH1106("31250Hz 32uS", x, pg, SmallFont); 休息;
               案例2:DrawStringSH1106("3906Hz 256uS", x, pg, SmallFont); 休息;
               案例3:DrawStringSH1106("977Hz 1024uS", x, pg, SmallFont); 休息;
               案例4:DrawStringSH1106("488Hz 2048uS", x, pg, SmallFont); 休息;
               案例5:DrawStringSH1106("244Hz 4096uS", x, pg, SmallFont); 休息;
               案例6:DrawStringSH1106("122Hz 8192uS", x, pg, SmallFont); 休息;
               案例 7:DrawStringSH1106("31Hz 32768uS", x, pg, SmallFont); 休息;
               默认值:DrawStringSH1106("Off", x, pg, SmallFont);
               }
               pg += 2;
  }
  如果(bHasSigGen)
               pg += 2;
  如果(yVcc <= 7){
               x += DrawIntDP2(readVcc() / 10, x, yVcc, SmallFont);
               DrawCharSH1106('V', x, yVcc, SmallFont);
  }
}

 

下面使用的按钮处理程序 -

 

无效检查按钮(无效){
  常量字节超时 = 70;// 1 秒显示菜单
  静态int prevHorz = HIGH;
  静态int prevVert = HIGH;
  诠释我;
  如果(数字读取(BtnHorz)== 低){
               if (prevHorz == HIGH) {
               开关(curMode){
               //case mFreqLogic:
               // 案例 mFreqAC:
               案例毫伏表:
               执行菜单();
               返回;
               }
               按钮定时器1 = 0;
               我的延迟(15);
               设置扫描(255);
               prevHorz = 低;
               } 别的 {
               if (ButtonsTimer1 > timeout) {
               执行菜单();
               返回;
               }
               }
  } 别的 {
               prevHorz = 高;
  }
  如果(数字读取(BtnVert)== 低){
               if (prevVert == HIGH) {
               按钮定时器1 = 0;
               我的延迟(15);
               设置模式(255);
               prevVert = 低;
               } 别的 {
               if (ButtonsTimer1 > timeout) {
               执行菜单();
               返回;
               }
               }
  } 别的 {
               prevVert = 高;
  }
}

 

频率测量是使用下面的复杂定时器逻辑完成的 -

 

//================================================= ==========================
// Timer1 每 65536 个计数溢出
//================================================= ==========================
ISR (TIMER1_OVF_vect)
{
  FC_overflowCount++;
}
//================================================= ==========================
// Timer1 捕捉中断
// 由比较器调用
//读取当前timer1捕获值
// 用于频率计
//================================================= ==========================
ISR (TIMER1_CAPT_vect) {
  // 在计数器值发生任何变化之前获取它
  无符号整数 timer1CounterValue = ICR1; // 参见数据表,第 117 页(访问 16 位寄存器)
  无符号长溢出复制 = FC_overflowCount;
  无符号长 t;
  静态无符号长prevT;
  // 如果刚刚错过了溢出
  if ((TIFR1 & bit(TOV1)) && timer1CounterValue < 0x7FFF)
               溢出复制++;
  t = (overflowCopy << 16) + timer1CounterValue;
  if ((!FC_firstAC) && (t-prevT > 100) && (t-prevT > FC_MaxPeriodAC))
               FC_MaxPeriodAC = t-prevT;
  上一页T = t;
  FC_firstAC = 假;
}
//================================================= ==========================
// Timer0 中断服务由硬件 Timer0 每 1ms = 1000 Hz 调用一次
// 由频率计数器使用
// 每 1mS 调用一次
//================================================= ==========================
ISR(TIMER0_COMPA_vect){
  if (FC_Timeout >= FC_LogicPeriod) { // 门时间结束,测量准备就绪
               TCCR1B &= ~7; // 门关闭/计数器 T1 停止
               位清除(TIMSK0,OCIE0A);// 禁用 Timer0 中断
               FC_OneSec = 真;// 设置结束计数周期的全局标志
               // 现在计算频率值
               FC_freq = 0x10000 * FC_overflowCount;// mult #overflows by 65636
               FC_freq += TCNT1; // 添加 counter1 的值
  }
  FC_超时++;// 计算中断事件的数量
  if (TIFR1 & 1) { // 如果定时器/计数器 1 溢出标志
               FC_overflowCount++; // 计数 Counter1 溢出的次数
               位集(TIFR1,TOV1);// 清除定时器/计数器 1 溢出标志
  }
}
//================================================= ==========================
// FC_InitLogic
// 计算 D5 在 mS 周期内的上升沿数
//================================================= ==========================
无效 FC_InitLogic() {
  无中断();
  TIMSK0 = 0x00;
  延迟微秒(50);// 等待是否有任何 int 待处理
  FC_OneSec = 假;// 重置周期测量标志
  FC_Timeout = 0; // 重置中断计数器
  TCCR1A = 0x00;//定时器输出关闭
  TCCR1B = 0x07;// T1 引脚上的外部时钟源。时钟在上升沿。
  TCNT1 = 0x00;// 计数器 = 0
  TCCR0A = 0x02;// 比较输出关闭;最大计数 = OCRA
  TCCR0B = 0x03;// 输入时钟为 16M/64
  TCNT0 = 0x16;// counter = 0 - 为什么这不是 0?设置时间的成本?
  TIMSK0 = 0x00;
  OCR0A = 248; // 最大计数值 = CTC 除以 250 = 1mS
  GTCCR = 0x02;// 重置预分频器
  FC_overflowCount = 0;
  位集(TIMSK0,OCIE0A);// 启用 Timer0 中断
  中断();
}
//================================================= ==========================
// FC_InitAC
// ACfreqAdcPin = 0..5 - 使用该 ADC 多路复用器并使用 Timer1 测量周期
//================================================= ==========================
无效 FC_InitAC() {
  无中断();
  FC_disable();
  TCCR1A = 0;// 重置定时器 1
  TCCR1B = 位(CS10)| 位(ICES1);// 无预分频器,输入捕捉边沿选择
  TIFR1 = 位 (ICF1) | 位(TOV1);// 清除标志,这样我们就不会收到虚假中断
  TCNT1 = 0; // Timer1归零
  FC_overflowCount = 0; // 对于 Timer1 溢出
  TIMSK1 = 位(TOIE1)| 位(ICIE1);// 定时器 1 溢出和输入捕捉中断
  ADCSRA = 0;
  DIDR1 = 1;// D6的数字输入关闭
  ADMUX = ACfreqAdcPin;
  ACSR = 位(ACI)| 位(ACIC) | (B10 << ACIS0); //“清除”中断标志;比较器的定时器捕捉;下降沿
  ADCSRB = 位(ACME);// 比较器连接到 ADC 多路复用器
  FC_firstAC = 真;
  FC_Timeout = 0;
  FC_MaxPeriodAC = 0;
  中断();
}
//================================================= ==========================
// FC_disable
//关闭频率计数器中断
//================================================= ==========================
无效 FC_disable() {
  TCCR0A = 0x03;// 没有比较输出;高达 0xFF 的快速 PWM
  TCCR0B = 0x03;// 没有输出比较;预分频器 = 16MHz/64;大约每 1mS 溢出一次
  TIMSK0 = 0x00;// 中断屏蔽寄存器 = 无
  GTCCR = 0x00;//控制寄存器=无
  OCR0A = 0x00;// 输出比较寄存器 A = none
  OCR0B = 0x00;// 输出比较寄存器 B = none
  TCCR1A = 0xC0;
  TCCR1B = 0x05;
  TCCR1C = 0x00;
  TIMSK1 = 0x00;
}
//================================================= ==========================
// FC_OneSecPassed
// 1 秒过去了吗?
//================================================= ==========================
bool FC_OneSecPassed() {
  静态字节 prevTimer1 = 0;
  字节 i;
  静态无符号长 t = 0;
  if (bitRead(TIFR0, TOV0)) // 每 1mS 溢出
               FC_超时++;
  位集(TIFR0,TOV0);
  返回 FC_Timeout > 1000;
}
//================================================= ==========================
// FC_CheckLogic
//频率测量器
// 重复调用
// 超时返回true
// 结果为 FC_freq
//================================================= ==========================
布尔 FC_CheckLogic() {
  返回 FC_OneSec;
}
//================================================= ==========================
// FC_CheckAC
//频率测量器
// 重复调用
// 超时返回true
// 结果为 FC_freq
//================================================= ==========================
布尔 FC_CheckAC() {
  无符号长 FC_elapsedTime;
  如果(FC_OneSecPassed()){
               如果(FC_MaxPeriodAC > 0)
               FC_freq = 100 * F_CPU*1.004 / FC_MaxPeriodAC;// 乘以 100 所以可以显示 2 dp
               别的
               FC_freq = 0;
               FC_InitAC();
               返回真;
  }
  返回假;
}
//------------------------------------------------ -----------------------------------------
// 我的延迟
// 延迟大约 mS 毫秒
// 不使用任何计时器
// 不影响中断
//------------------------------------------------ -----------------------------------------
无效我的延迟(int毫秒){
  对于 (int j = 0; j < mS; j++)
               延迟微秒(1000);
}
//------------------------------------------------ -----------------------------------------
// 测量电压表
// 以 mV 为单位测量 Vin 处的电压表
//假设电阻器已连接到引脚:
// Ra 从引脚到 5V
// Rb 从引脚到 0V
// Rc 从引脚到 Vin
//------------------------------------------------ ----------------------------

 

在设置中,UART、ADC、OLED 启动,内存缓冲区设置,I2C 开始。

 

无效设置(无效){
  // 打开串口,波特率为 BAUDRATE b/s
  Serial.begin(波特率);
  // 清除缓冲区
  memset( (void *)commandBuffer, 0, sizeof(commandBuffer) );
  // 激活中断
  sei();
  初始化ADC();
  Serial.println("ArdOsc " __DATE__); // 编译日期
  Serial.println("OK");
  设置模式(0);// y 增益 5V
  设置扫描(5);
  setPwmFrequency(testSignalPin, 3); // 测试信号 976Hz 1024uS
  pinMode(BtnHorz,INPUT_PULLUP);
  pinMode(BtnVert,INPUT_PULLUP);
  pinMode(LED_BUILTIN,输出);
  Wire.begin(); // 加入 i2c 总线作为主机
               TWBR = 1; // 频率=888kHz 周期=1.125uS
  初始化SH1106();
}

 

在 void 循环中,循环取决于它所在模式的开关状态,按下按钮选择模式。

 

//------------------------------------------------ -----------------------------------------
// 主要例程
// 环形
//------------------------------------------------ -----------------------------------------
无效循环(无效){
  静态 int ButtonsTimer2 = 0;
  开关(curMode){
               案例毫伏表:
               如果(检查电压表())
               画屏();
               休息;
               默认:
               if (!SendingSerial) {
               发送ADC();
               开关(扫描类型){
               案例 sw20Frames: 案例 sw100Frames: 案例 sw500Frames:
               SG_StepSweep();
               }
               }
  }
  检查按钮();
}

 

  Arduino示波器工作

  所有组件都焊接在板上并使用 USB 电缆供电,并针对输入测试不同的波。

  以下是正弦波、方波和三角波的图像。

如何使用Arduino Nano和OLED显示器构建示波器,pYYBAGLNLKeAf3iLAAVD0AoZYUQ921.png,第6张

  这个项目的完整工作可以在视频中找到,链接在这个页面的底部。此外,该项目还可以修改和改进为基于 BNC 连接器的微型示波器,具有电池供电 *** 作。如果您有更多想法,请将它们放在评论部分,并且有任何问题,您可以使用我们的论坛

  代码

 


 

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

// 版权所有 2018 彼得·巴尔奇

// 遵守 GNU 通用公共许可证

// 在 SH1106 屏幕上显示样本作为示波器

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

#include

#include

#include "SimpleSH1106.h"

#include

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

// 定义和类型定义

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

// 获取寄存器位 - 更快:不会把它变成 0/1

#ifndef 获取比特

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

#万一

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

            逻辑逻辑,

            毫伏表,

            最大模式1

           };

常量 Tmode maxMode = maxMode1 - 1;

枚举 TmenuSel {sTime, sMode, sTrigger, sTestSig, sSigGen};

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

// 全局常量

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

布尔 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__("nop\n nop\n nop\n nop\n nop\n nop\n")

常量 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; // 用于显示电压表

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

// SigGen 中使用的全局变量

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

常量字节 numberOfDigits = 6; // 频率的位数

字节频率SGLo[numberOfDigits] = {0, 0, 0, 1, 0, 0}; // 1000Hz SelSG = 0..numberOfDigits-1

字节频率SGHi[numberOfDigits] = {0, 0, 0, 0, 2, 0}; // 20kHz SelSG = numberOfDigits..2*numberOfDigits-1

字节 SelSG = numberOfDigits-1;

常量字节 SelSGSweep = 2*numberOfDigits;

常量字节 SelSGSine = 2*numberOfDigits+1;

常量 int wSine = 0b0000000000000000;

const int wTriangle = 0b0000000000000010;

常量 int wSquare = 0b0000000000101000;

枚举 TsweepType {swOff,sw20Frames,sw100Frames,sw500Frames,sw1Sec,sw5Sec,sw20Sec};

int waveType = wSine;

TsweepType sweepType = swOff;

常量 int SG_fsyncPin = 2;

常量 int SG_CLK = 13;

常量 int SG_DATA = 12;

int SG_iSweep,SG_nSweep;

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

// 频率计数器中使用的全局变量

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

volatile boolean FC_OneSec;

volatile boolean FC_firstAC;

volatile unsigned long FC_overflowCount;

volatile unsigned long FC_MaxPeriodAC;

无符号长 FC_Timeout = 0;

无符号长 FC_freq;

常量 int ACfreqAdcPin = 3;

常量 int FC_LogicPeriod = 1006; // mS 略长于 1 秒进行校准

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

// 刻度标签

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

常量 int R1 = 100;

常量 int R2 = 27;

常量字节 ax2V[] = {98, 81, 73, 70, 0, 3, 28, 96, 28, 3};

常量字节 ax4V[] = {24, 22, 127, 16, 0, 3, 28, 96, 28, 3};

常量字节 ax0_1[] = {62, 65, 65, 62, 0, 64, 0, 2, 127 };

常量字节 ax0_2[] = {62, 65, 65, 62, 0, 64, 0, 98, 81, 73, 70 };

常量字节 ax0_4[] = {62, 65, 65, 62, 0, 64, 0, 24, 22, 127, 16 };

常量字节 ax0_5[] = {62, 65, 65, 62, 0, 64, 0, 47, 69, 69, 57};

常量字节 ax1[] = {2, 127 };

常量字节 ax2[] = {98, 81, 73, 70 };

常量字节 ax4[] = {24, 22, 127, 16 };

常量字节 ax5[] = {47, 69, 69, 57};

常量字节 ax10[] = {2, 127, 0, 62, 65, 65, 62 };

常量字节 ax20[] = {98, 81, 73, 70, 0, 62, 65, 65, 62 };

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

//主菜单的图像

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

常量字节 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;

}

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

// 画框

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

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

无效的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); //后面的字节是数据

      }

      y = ADC缓冲区[i];

        如果 (yPrev > y + 1) {

          if (yPrev == lastDrawn)

            yPrev--;

          bar = FillBar(y + 1, yPrev, page);

          最后绘制 = yPrev + 1;

        } 别的 {

          bar = FillBar(yPrev, yPrev, page);

          lastDrawn = yPrev;

        }

     // }

      if ((page == 0) && (bar == 0x01) && (i & 1))

        条 = 0;

      if ((page == 7) && (bar == 0x80) && (i & 1))

        条 = 0;

      if (page == yGraticulePage0) {

        如果 (i & 8)

          酒吧 = 酒吧 | y刻度字节0;

      }

      否则 if (page == yGraticulePage1) {

        如果 (i < pylenz)

        {

          酒吧 |= *pybz;

          pybz++;

        }

        否则如果 (i % 4 == 0)

          条形|= yGraticuleByte1;

      }

      否则 if (page == yGraticulePage2) {

        如果 (i % 4 == 0)

          条 |= yGraticuleByte2;

      }

      如果 ((i == xGraticule1) | (i == xGraticule2))

        酒吧 = 酒吧 | 0x22;

      if ((page == 7) && (i > xGraticule2 - pxlenz - 2) && (i < xGraticule2 - 1)) {

        条形 |= *pxbz;

        pxbz++;

      }

      Wire.write(bar);

      yPrev = y;

    }

    Wire.endTransmission();

  }

}

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

// 初始化ADC()

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

无效初始化ADC(无效){

  如果(curMode > AC20mV)

    返回;

  ACSR = 0x10;

  ADCSRA = 0x97;

  ADCSRB = 0x0 ; //ADC控制和状态寄存器B

  // 0 位 6 – ACME:模拟比较器多路复用器使能

  // 000 位 2:0 – ADTSn:ADC 自动触发源 [n = 2:0] 自由运行模式

  ADMUX = 0x20 + (curVref << 6) + curMode;// ADC 多路复用器选择寄存器

  // rr 位 7:6 – REFSn:参考选择 = Vcc

  // 1 位 5 – ADLAR:ADC 左调整结果

  // aaaa 位 3:0 – MUXn:模拟通道选择

  DIDR0 = 0x3F;// 数字输入禁用寄存器 0

  // ADC0D=1, ADC1D=1, ADC2D=1, ADC3D=1, ADC4D=1, ADC5D=1, ADC6D=0, ADC7D=0

}

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

// 打印状态

//打印各种寄存器值

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

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

// 设置PwmFrequency

// 定时器模式=1 模式=2 模式=3 模式=4 模式=5 模式=6 模式=7

// 引脚=5 0 f=62500/1 f=62500/8 f=62500/64 f=62500/256 f=62500/1024

// 引脚=6 0 f=62500/1 f=62500/8 f=62500/64 f=62500/256 f=62500/1024

// 引脚=9 1 f=31250/1 f=31250/8 f=31250/64 f=31250/256 f=31250/1024

// 引脚=10 1 f=31250/1 f=31250/8 f=31250/64 f=31250/256 f=31250/1024

// 引脚=3 2 f=31250/1 f=31250/8 f=31250/32 f=31250/64 f=31250/128 f=31250/256 f=31250/1024

// 引脚=11 2 f=31250/1 f=31250/8 f=31250/32 f=31250/64 f=31250/128 f=31250/256 f=31250/1024

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

void setPwmFrequency(int pin,字节模式){

  发送确认();

  curPwmMode = 模式;

  如果(模式 == 0){

    类比写入(引脚,0);

  } 别的 {

    类比写入(引脚,128);

    if (pin == 5 || pin == 6 || pin == 9 || pin == 10) {

      如果(针 == 5 || 针 == 6){

        TCCR0B = TCCR0B & 0b11111000 | 模式;

      } 别的 {

        TCCR1B = TCCR1B & 0b11111000 | 模式;

      }

    } else if (pin == 3 || pin == 11) {

      TCCR2B = TCCR2B & 0b11111000 | 模式;

    }

  }

}

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

// 开始定时器1

// TIFR1 在之后变为非零

// 溢出*1024/16000000 秒

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

void StartTimer1(字溢出){

  TCCR1A = 0xC0;// 在比较匹配上设置 OC1A

  TCCR1B = 0x05;// 预分频器 = 1024

  TCCR1C = 0x00;// 没有 pwm 输出

  OCR1AH = 高字节(溢出);

  OCR1AL = 低字节(溢出);

  OCR1BH = 0;

  OCR1BL = 0;

  TIMSK1 = 0x00;// 没有中断

  TCNT1H = 0; // 必须先写

  TCNT1L = 0; //清空计数器

  TIFR1 = 0xFF;// 清除所有标志

}

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

// 发送确认

// 如果发送串行然后发送@

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

无效发送确认(无效){

  如果(发送序列)

    序列号.print(ack);

}

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

// 读取Vcc

// 结果为 mV

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

长读Vcc(无效){

  长结果;

  ACSR = 0x10;

  ADCSRA = 0x97;

  ADCSRB = 0x0;

  // 根据 AVcc 读取 1.1V 参考

  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);

  我的延迟(2);

  ADCSRA |= _BV(ADSC); // 兑换

  而(bit_is_set(ADCSRA,ADSC));

  结果 = ADCL;

  结果 |= ADCH << 8;

  结果 = 1125300L / 结果;// 反算以 mV 为单位的 AVcc

  初始化ADC();// 设置下一次扫描

  返回结果;

}

无效GetADCSamples(无效){

  uint8_t d;

  uint8_t* p;

  const int 迟滞 = 2;

  布尔触发;

  Trig = TrigFalling ^ (curMode != AC100mV);

  初始化ADC();

  ADCSRA = 0x80 + (curPrescaler & 7); // ADC 控制和状态寄存器 A

  // 1 位 7 – ADEN:ADC 使能

  // 0 位 6 – ADSC:ADC 开始转换

  // 0 位 5 – ADATE:ADC 自动触发启用

  // 0 位 4 – ADIF:ADC 中断标志

  // 0 位 3 – ADIE:ADC 中断使能

  // nnn 位 2:0 – ADPSn:ADC 预分频器选择 [n = 2:0]

  开始定时器1(0);// 没有超时

  for (d = 0; d < 10; d++) { // 确保 ADC 正在运行

    位集(ADCSRA,ADSC);// 开始 ADC 转换

    而(!getBit(ADCSRA,ADIF));// 等待 ADC

    位集(ADCSRA,ADIF);// 清除标志

  }

  if (curPeriod == 0) { // 1Msps

    而 (Trig ? (ADCH >= 0x80 - 迟滞) : (ADCH < 0x80 + 迟滞)) {

      d = TCNT1L;// 强制读取 TCNT1H

      if (TCNT1H > FreeRunTimeout) 转到 freeRunFast;

      位集(ADCSRA,ADSC);

    }

    而(触发?(ADCH < 0x80 + 滞后):(ADCH >= 0x80 - 滞后)){

      d = TCNT1L;// 强制读取 TCNT1H

      if (TCNT1H > FreeRunTimeout) 转到 freeRunFast;

      位集(ADCSRA,ADSC);

    }

自由奔跑:

    对于(p = ADCBuffer;p < ADCBuffer + ADCBUFFERSIZE;p++){

      *p = ADCH;

      __asm__("nop"); // 填充到 16 条指令

      __asm__("nop"); // 填充到 16 条指令

      位集(ADCSRA,ADSC);

    }

  } else { // 慢于 1Msps

    do { // 等待比较器低电平

      位集(ADCSRA,ADSC);// 开始 ADC 转换

      对于 (d = 0; d < curPeriod; d++ )

        循环数;

      d = TCNT1L;// 强制读取 TCNT1H

      if (TCNT1H > FreeRunTimeout) 转到 freeRunSlow;

    } while (Trig ? (ADCH >= 0x80 - 迟滞) : (ADCH < 0x80 + 迟滞));

    do { // 等待比较器高电平

      位集(ADCSRA,ADSC);// 开始 ADC 转换

      对于 (d = 0; d < curPeriod; d++ )

        循环数;

      d = TCNT1L;// 强制读取 TCNT1H

      if (TCNT1H > FreeRunTimeout) 转到 freeRunSlow;

    } while (Trig ? (ADCH < 0x80 + 迟滞) : (ADCH >= 0x80 - 迟滞));

自由运行慢:

    位集(ADCSRA,ADSC);// 开始 ADC 转换

    对于 ( p = ADCBuffer; p < ADCBuffer + ADCBUFFERSIZE; p++) {

      对于 (d = 0; d < curPeriod; d++ )

        循环数;

      *p = ADCH;

      位集(ADCSRA,ADSC);// 开始 ADC 转换

    }

  }

}

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

// 发送ADC

// 使用 curPrescaler,curPeriod,curMode,curVref

//

// 读取并发送一个充满样本的缓冲区

// 预分频器

// 7 128

// 6 64

// 5 32

// 4 16

// 3 8

// 2 4

// 1 2

// 0 2

// 周期:采样周期

// 0: 16 个时钟

// n: n*SampPerA+SampPerB 时钟

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

无效发送ADC(){

  memset( (void *)ADCBuffer, 0, sizeof(ADCBuffer) );

  无中断();

// if (curMode == mLogic)

// GetLogicSamples();

// 别的

GetADCSamples();

  中断();

  数字写入(LED_BUILTIN,高);

  如果(发送序列){

    串行写入((uint8_t)0xAA);

    Serial.write((uint8_t)0xBB);

    Serial.write((uint8_t)0xCC);

    Serial.write((uint8_t *)ADCBuffer, ADCBUFFERSIZE);

  }

  画屏();

  数字写入(LED_BUILTIN,低);

  诠释 t = TCNT1L; // 强制读取 TCNT1H

  按钮Timer1 += TCNT1H;

}

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

// 设置扫描

// 设置周期和 ADC 预分频器

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

无效 setSweep(字节扫描){

  诠释 x;

  长 t;

  如果(扫一扫 == 255){

    如果(curSweep == 0)

      curSweep = 6;

    别的

      curSweep——;

  } 别的

    curSweep = 扫描;

  开关(curSweep){

    情况 0:curPeriod = 0;curPrescaler = 2; t = 100;pxGratLabel = &ax0_1[0]; xGratLabelLen = sizeof(ax0_1); 休息;

    案例1:curPeriod = 4;curPrescaler = 2; t = 400;pxGratLabel = &ax0_4[0]; xGratLabelLen = sizeof(ax0_4); 休息;

    案例2:curPeriod = 11;curPrescaler = 3; t = 1000;pxGratLabel = &ax1[0]; xGratLabelLen = sizeof(ax1); 休息;

    案例 3:curPeriod = 24;curPrescaler = 3; t = 2000;pxGratLabel = &ax2[0]; xGratLabelLen = sizeof(ax2); 休息;

    案例4:curPeriod = 62;curPrescaler = 4; t = 5000;pxGratLabel = &ax5[0]; xGratLabelLen = sizeof(ax5); 休息;

    案例5:curPeriod = 125;curPrescaler = 4; t = 10000;pxGratLabel = &ax10[0]; xGratLabelLen = sizeof(ax10); 休息;

    案例6:curPeriod = 255;curPrescaler = 5; t = 20000;pxGratLabel = &ax20[0]; xGratLabelLen = sizeof(ax20); 休息;

  }

  如果(curSweep == 0)

    x = t;

  别的

    x = 16 * t / (curPeriod * SampPerA + SampPerB);

  x格线1 = x / 2;

  x格线2 = x;

  发送确认();

}

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

// INC模式

// 递增模式

// 从最大值环绕

// 跳过不允许的模式

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

int incMode(int mode) {

  模式++;

//if ((mode == mLogic) && (!bHasLogic)) mode++;

// if ((mode == mFreqLogic) && ((!bHasFreq) || (!bHasLogic))) mode++;

// if ((mode == mFreqAC) && (!bHasFreq)) mode++;

  if ((mode == mVoltmeter) && (!bHasVoltmeter)) mode++;

  如果(模式 > 最大模式)

    返回DC5V;

  别的

    返回模式;

}

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

// 设置模式

// 设置模式和 Vref

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

无效 setMode(int 模式){

  诠释我;

  如果(模式 == 255){

    curMode = incMode(curMode);

  } 别的

    curMode = 模式;

  开关(curMode){

    案例 DC5V:

      curVref = 1;

      i = (long)4000 * 64 / readVcc();

      如果(我 <= 63){

        y刻度1 = 63 - i;

        y 刻度 2 = 63 - i / 2;

        y刻度线0 = 255;

        pyGratLabel = &ax4V[0];

        yGratLabelLen = sizeof(ax4V);

      } 别的 {

        y刻度2 = 63 - i;

        y 刻度 1 = 63 - i / 2;

        y刻度线0 = 255;

        pyGratLabel = &ax2V[0];

        yGratLabelLen = sizeof(ax2V);

      }

      休息;

    案例 AC500mV:

      curVref = 3;

      i = (字节)(0.5 / 1.1 * 256 / 4);

      y刻度1 = 32 - i;

      y刻度2 = 32 + i;

      y刻度线0 = 32;

      pyGratLabel = &ax0_5[0];

      yGratLabelLen = sizeof(ax0_5);

      休息;

    案例 AC100mV:

      curVref = 3;

      i = (字节)(0.1 / 1.1 * (R1 + R2) / R2 * 256 / 4);

      y刻度1 = 32 - i;

      y刻度2 = 32 + i;

      y刻度线0 = 32;

      pyGratLabel = &ax0_1[0];

      yGratLabelLen = sizeof(ax0_1);

      休息;

    案例AC20mV:

      curVref = 3;

      i = (字节)(0.02 / 1.1 * (R1 + R2) / R2 * (R1 + R2) / R2 * 256 / 4);

      y刻度1 = 32 - i;

      y刻度2 = 32 + i;

      y刻度线0 = 32;

      pyGratLabel = &ax20[0];

      yGratLabelLen = sizeof(ax20);

      休息;

    默认:

      curVref = 1;

      y 格线 1 = 255;

      y 格线 2 = 255;

      y刻度线0 = 255;

      pyGratLabel = &ax20[0];

      yGratLabelLen = sizeof(ax20);

      休息;

  }

  开关(curMode){

    案例毫伏表:

      Vin = 0;

      画屏();

      休息;

    默认值:FC_disable();

  }

  yGraticulePage0 = yGraticule0 / 8;

  yGraticuleByte0 = 1 << (yGraticule0 % 8);

  yGraticulePage1 = yGraticule1 / 8;

  yGraticuleByte1 = 1 << (yGraticule1 % 8);

  yGraticulePage2 = yGraticule2 / 8;

  yGraticuleByte2 = 1 << (yGraticule2 % 8);

  发送确认();

}

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

// 绘制主菜单

// 为 sel 和 adj 的值绘制主菜单

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

无效drawMainMenu(无效){

  int ofs, x, yVcc, pg;

  开关(选择){

    案例 sMode: ofs = -1; 休息;

    案例 sTrigger: ofs = -2; 休息;

    案例 sTestSig: ofs = -5; 休息;

    案例 sSigGen: ofs = bHasTestSignal ? -7:-5;休息;

    默认值:ofs = 0;

  }

  // DrawImageSH1106(0,ofs,imgMainMenu);

  DrawImageSH1106(0, ofs + 0, imgMainMenuTop);

  对于 (x = 2; x < 14; x++)

    DrawImageSH1106(0, ofs + x, imgMainMenuMid);

  DrawImageSH1106(0, ofs + 10 + bHasTestSignal * 2 + bHasSigGen * 2, imgMainMenuBot);

  DrawImageSH1106(6, 3 + sel * 2 + ofs, imgCaret1);

  粗体SH1106 = 真;

  pg = 3 + ofs;

  DrawStringSH1106("时间:", 12, pg, SmallFont); pg += 2;

  DrawStringSH1106((adj[1] <= AC20mV ? "Gain:" : "Mode:"), 12, pg, SmallFont); pg += 2;

  DrawStringSH1106("触发器:", 12, pg, SmallFont); pg += 2;

  如果(bHasTestSignal){

    DrawStringSH1106("测试签名:", 12, pg, SmallFont); pg += 2;

    如果(bHasSigGen){

      DrawStringSH1106("信号发生器", 12, pg, SmallFont); pg += 2;

    }

    DrawStringSH1106("Vcc:", 12, pg, SmallFont); yVcc = pg; pg += 2;

  } 别的 {

    如果(bHasSigGen){

      DrawStringSH1106("Vcc:", 12, pg, SmallFont); yVcc = pg; pg += 2;

      DrawStringSH1106("信号发生器", 12, pg, SmallFont); pg += 2;

    } 别的 {

      DrawStringSH1106("Vcc:", 12, pg, SmallFont); yVcc = pg; pg += 2;

    }

  }

  粗体SH1106 = 假;

  x = 62;

  pg = 3 + ofs;

  开关(adj[0]){

    案例 0: DrawStringSH1106("1mS", x, pg, SmallFont); 休息;

    案例一:DrawStringSH1106("2mS", x, pg, SmallFont); 休息;

    案例2:DrawStringSH1106("5mS", x, pg, SmallFont); 休息;

    案例 3:DrawStringSH1106("10mS", x, pg, SmallFont); 休息;

    案例4:DrawStringSH1106("20mS", x, pg, SmallFont); 休息;

    案例5:DrawStringSH1106("50mS", x, pg, SmallFont); 休息;

    案例 6:DrawStringSH1106("100mS", x, pg, SmallFont); 休息;

  }

  pg += 2;

  开关(adj[1]){

    案例 DC5V: DrawStringSH1106("5V DC", x, pg, SmallFont); 休息;

    案例 AC500mV: DrawStringSH1106("0.5V​​ AC", x, pg, SmallFont); 休息;

    案例 AC100mV: DrawStringSH1106("0.1V AC", x, pg, SmallFont); 休息;

    案例 AC20mV: DrawStringSH1106("20mV AC", x, pg, SmallFont); 休息;

    //case mLogic: DrawStringSH1106("Logic", x, pg, SmallFont); 休息;

    //case mFreqLogic: DrawStringSH1106("频率逻辑", x, pg, SmallFont); 休息;

    //case mFreqAC: DrawStringSH1106("Freq AC", x, pg, SmallFont); 休息;

    case mVoltmeter: DrawStringSH1106("Voltmeter", x, pg, SmallFont); 休息;

  }

  pg += 2;

  开关(adj[2]){

    案例一:DrawStringSH1106("Fall", x, pg, SmallFont); 休息;

    默认值:DrawStringSH1106("Rise", x, pg, SmallFont);

  }

  pg += 2;

  如果(bHasTestSignal){

    开关(adj[3]){

      案例一:DrawStringSH1106("31250Hz 32uS", x, pg, SmallFont); 休息;

      案例2:DrawStringSH1106("3906Hz 256uS", x, pg, SmallFont); 休息;

      案例3:DrawStringSH1106("977Hz 1024uS", x, pg, SmallFont); 休息;

      案例4:DrawStringSH1106("488Hz 2048uS", x, pg, SmallFont); 休息;

      案例5:DrawStringSH1106("244Hz 4096uS", x, pg, SmallFont); 休息;

      案例6:DrawStringSH1106("122Hz 8192uS", x, pg, SmallFont); 休息;

      案例 7:DrawStringSH1106("31Hz 32768uS", x, pg, SmallFont); 休息;

      默认值:DrawStringSH1106("Off", x, pg, SmallFont);

    }

    pg += 2;

  }

  如果(bHasSigGen)

    pg += 2;

  如果(yVcc <= 7){

    x += DrawIntDP2(readVcc() / 10, x, yVcc, SmallFont);

    DrawCharSH1106('V', x, yVcc, SmallFont);

  }

}

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

// incAdj

// 增加 adj 的值

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

无效incAdj(无效)​​{

  if (sel == sSigGen)

    返回;

  if (sel == sMode) {

    adj[1] = incMode(adj[1]);

  } 别的 {

    adj[选择]++;

    如果 (adj[0] > 6) adj[0] = 0;

    如果 (adj[2] > 1) adj[2] = 0;

    如果 (adj[3] > 7) adj[3] = 0;

  }

  绘制主菜单;

}

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

// incSel

// 增加 sel 的值

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

无效incSel(无效){

  如果(bHasTestSignal){

    如果(bHasSigGen){

      if (sel == sSigGen)

        sel = sTime - 1;

    } 别的 {

      if (sel == sTestSig)

        sel = sTime - 1;

    }

  } 别的 {

    如果(bHasSigGen){

      if (sel == sSigGen)

        sel = sTime - 1;

      if (sel == sTrigger)

        sel = sSigGen - 1;

    } 别的 {

      if (sel == sTrigger)

        sel = sTime - 1;

    }

  }

  选择 = 选择 + 1;

  绘制主菜单;

}

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

// DrawIntDP2

// 以 12.34 格式绘制 int 1234

// 在 x, 页

// 返回绘制的宽度

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

int DrawIntDP2(int i, byte x, byte page, word Font) {

  诠释开始;

  开始 = x;

  如果 (i < 0) {

    我 = - 我;

    x += DrawCharSH1106('-', x, page, 字体);

  }

  x += DrawIntSH1106(i / 100, x, page, 字体);

  x += DrawCharSH1106('.', x, page, 字体);

  x += DrawIntSH1106((i / 10) % 10, x, page, 字体);

  x += DrawIntSH1106(i % 10, x, page, 字体);

  返回 x - 开始;

}

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

// drawSigGenMenu

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

无效的drawSigGenMenu(无效){

  字节 x,y,i;

  drawBox("信号发生器");

  if (sweepType == swOff) {

    x = 20;

    y = 3;

    for (i = numberOfDigits - 1; i < numberOfDigits; i--) {

      如果 (i == SelSG)

        DrawImageSH1106(x+2, y+2, imgCaret2);

      x += DrawIntSH1106(freqSGLo[i], x, y, LargeDigitsFont);

    }

  } 别的 {

    x = 60;

    y = 2;

    DrawStringSH1106("最大频率:", 12, y, SmallFont);

    for (i = numberOfDigits - 1; i < numberOfDigits; i--) {

      if (i == SelSG-numberOfDigits)

        DrawImageSH1106(x-2, y+1, imgCaret2);

      x += DrawIntSH1106(freqSGHi[i], x, y, SmallFont);

    }

    DrawStringSH1106("Hz", x, y, SmallFont);

    x = 60;

    y = 4;

    DrawStringSH1106("最小频率:", 12, y, SmallFont);

    for (i = numberOfDigits - 1; i < numberOfDigits; i--) {

      如果 (i == SelSG)

        DrawImageSH1106(x-2, y+1, imgCaret2);

      x += DrawIntSH1106(freqSGLo[i], x, y, SmallFont);

    }

    DrawStringSH1106("Hz", x, y, SmallFont);

  }

  x = 12;

  y = 6;

  如果(SelSG == SelSGSine)

    DrawImageSH1106(x-6, y, imgCaret1);

// 开关 (waveType) {

// case wSine: DrawStringSH1106("Sine", x, y, SmallFont); 休息;

// case wTriangle: DrawStringSH1106("Triangle", x, y, SmallFont); 休息;

// case wSquare: DrawStringSH1106("Square", x, y, SmallFont); 休息;

// }

  对于 (x=12;x<40;x+=14)

  开关(波型){

    案例 wSine: DrawImageSH1106(x, y, imgSine); 休息;

    案例 wTriangle: DrawImageSH1106(x, y, imgTrian); 休息;

    案例 wSquare: DrawImageSH1106(x, y, imgSquare); 休息;

  }

  x = 54;

  y = 6;

  开关(扫描类型){

    case swOff: DrawStringSH1106("常量", x, y, SmallFont); 休息;

    case sw1Sec: DrawStringSH1106("Sweep 1 Sec", x, y, SmallFont); 休息;

    case sw5Sec: DrawStringSH1106("Sweep 5 Sec", x, y, SmallFont); 休息;

    case sw20Sec: DrawStringSH1106("扫描 20 秒", x, y, SmallFont); 休息;

    case sw20Frames: DrawStringSH1106("Swp 20 帧", x, y, SmallFont); 休息;

    case sw100Frames: DrawStringSH1106("Swp 100 帧", x, y, SmallFont); 休息;

    case sw500Frames: DrawStringSH1106("Swp 500 帧", x, y, SmallFont); 休息;

  }

  如果(SelSG == SelSGSweep)

    DrawImageSH1106(x-6, y, imgCaret1);

}

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

//返回 10^y

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

无符号长幂(int y){

  无符号长 t = 1;

  对于(字节 i = 0;i < y;i++)

    t = t * 10;

  返回 t;

}

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

//根据数组计算频率。

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

无符号长计算频率(字节*频率SG){

  无符号长 i = 0;

  对于(字节 x = 0;x < numberOfDigits;x++)

    i = i + freqSG[x] * 幂(x);

  返回我;

}

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

// SG_WriteRegister

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

无效SG_WriteRegister(字数据){

  数字写入(SG_CLK,低);

  数字写入(SG_CLK,高);

  数字写入(SG_fsyncPin,低);

  对于(字节 i = 0;i < 16;i++){

    如果 (dat & 0x8000)

      数字写入(SG_DATA,高);

    别的

      数字写入(SG_DATA,低);

    dat = dat << 1;

    数字写入(SG_CLK,高);

    数字写入(SG_CLK,低);

  }

  数字写入(SG_CLK,高);

  数字写入(SG_fsyncPin,高);

}

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

// SG_Reset

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

无效 SG_Reset() {

  延迟(100);

  SG_WriteRegister(0x100);

  延迟(100);

}

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

// SG_freqReset

// 重置 SG regs 然后设置频率和波形类型

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

void SG_freqReset(长频率,整波){

  长 fl = 频率 * (0x10000000 / 25000000.0);

  SG_WriteRegister(0x2100);

  SG_WriteRegister((int)(fl & 0x3FFF) | 0x4000);

  SG_WriteRegister((int)((fl & 0xFFFC000) >> 14) | 0x4000);

  SG_WriteRegister(0xC000);

  SG_WriteRegister(波);

  波型 = 波;

}

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

// SG_freqSet

// 设置 SG 频率调节器

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

void SG_freqSet(长频率,整波){

  长 fl = 频率 * (0x10000000 / 25000000.0);

  SG_WriteRegister(0x2000 | wave);

  SG_WriteRegister((int)(fl & 0x3FFF) | 0x4000);

  SG_WriteRegister((int)((fl & 0xFFFC000) >> 14) | 0x4000);

}

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

// SG_StepSweep

// 增加 FG 频率

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

无效SG_StepSweep(无效){

  如果(SG_iSweep > SG_nSweep)SG_iSweep = 0;

  长 f = exp((log(calcFreq(freqSGHi)) - log(calcFreq(freqSGLo)))*SG_iSweep/SG_nSweep + log(calcFreq(freqSGLo))) +0.5;

  SG_freqSet(f, waveType);

  SG_iSweep++;

}

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

// 扫一扫

// 连续扫描 siggen freq

// 整个扫描需要 n mS

// SDC regs 被保存和恢复

// 当接收到一个串行字符时停止

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

无效扫描(int n){

  字节旧ACSR = ACSR;

  字节旧ADCSRA = ADCSRA;

  字节老ADCSRB = ADCSRB;

  字节老ADMUX = ADMUX;

  字节老DIDR0 = DIDR0;

  字节老DIDR1 = DIDR1;

  整数 fmin,fmax;

  fmin = calcFreq(freqSGLo);

  fmax = calcFreq(freqSGHi);

  诠释我=0;

  做 {

    长 f = exp((log(fmax) - log(fmin))*i/(n-1) + log(fmin)) +0.5;

    SG_freqSet(f, waveType);

    延迟(1);

    我++;

    如果 (i >= n) i = 0;

  } 而 (!Serial.available());

  SG_freqSet(calcFreq(freqSGLo), waveType);

  ACSR = 旧ACSR;

  ADCSRA = 旧ADCSRA;

  ADCSRB = 旧ADCSRB;

  ADMUX = oldADMUX;

  DIDR0 = 旧DIDR0;

  DIDR1 = 旧DIDR1;

}

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

// incSelSG

// SigGen 菜单的递增数字

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

无效incSelSG(无效){

  如果(SelSG == SelSGSine){

    开关(波型){

      案例 wSine:waveType = wTriangle;休息;

      案例 wTriangle:waveType = wSquare;休息;

      案例 wSquare:waveType = wSine;休息;

    }

  } 别的

  如果(SelSG == SelSGSweep){

    if (sweepType == sw20Sec)

      扫描类型 = swOff;

    别的

      扫描类型=扫描类型+1;

  } 别的

  如果(SelSG < numberOfDigits){

    if (freqSGLo[SelSG] >= 9)

      频率SGLo[SelSG] = 0;

    别的

      频率SGLo[SelSG]++;

  } 别的

  如果(扫描类型!= swOff){

    if (freqSGHi[SelSG-numberOfDigits] >= 9)

      freqSGHi[SelSG-numberOfDigits] = 0;

    别的

      freqSGHi[SelSG-numberOfDigits]++;

  }

  drawSigGenMenu();

}

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

// incAdjSG

// 增加 SigGen 菜单的插入符号位置

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

无效incAdjSG(无效){

  如果(SelSG == 0)

    SelSG = SelSGSine;

  别的

    塞尔SG——;

  if ((SelSG >= numberOfDigits) && (SelSG < 2*numberOfDigits) && (sweepType == swOff))

    SelSG = numberOfDigits-1;

  drawSigGenMenu();

}

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

// ExecSigGenMenu

// SigGen 菜单

// 用户按下 sel 或 Adj 按钮

// 如果 2 秒内没有按钮,则返回

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

无效ExecSigGenMenu(无效){

  静态int prevHorz = 0;

  静态int prevVert = 0;

  诠释我;

  常量字节超时 = 0xC0; // 3 秒退出

  FC_disable();

  drawSigGenMenu();

  而 ((digitalRead(BtnHorz) == LOW) || (digitalRead(BtnVert) == LOW))

    ; // 等到按钮关闭

  开始定时器1(0);

  SG_iSweep = 0;

  做 {

    i = 数字读取(BtnVert);

    如果(我!= prevVert){

      prevVert = i;

      如果(我 == 低){

        incSelSG();

        drawSigGenMenu();

        SG_freqReset(calcFreq(freqSGLo), waveType);

      }

      我的延迟(100);

      开始定时器1(0);

    }

    i = 数字读取(BtnHorz);

    if (i != prevHorz) {

      上一页Horz = i;

      如果(我 == 低){

        incAdjSG();

        drawSigGenMenu();

      }

      我的延迟(100);

      开始定时器1(0);

    }

    如果(扫描类型!= swOff){

      开关(扫描类型){

        case sw1Sec: SG_nSweep = (long) 1000*65/100; 休息;

        case sw5Sec: SG_nSweep = (long) 5000*65/100; 休息;

        case sw20Sec: SG_nSweep = (long)20000*65/100; 休息;

        case sw20Frames: SG_nSweep = 20; 休息;

        case sw100Frames: SG_nSweep = 100; 休息;

        case sw500Frames: SG_nSweep = 500; 休息;

      }

      SG_StepSweep();

    }

    我 = TCNT1L; // 强制读取 TCNT1H

  } while ((TCNT1H < timeout) || (sweepType >= sw1Sec));

}

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

// 执行菜单

// 显示主菜单

// 用户按下 sel 或 Adj 按钮

// 如果 2 秒内没有按钮,则返回

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

无效执行菜单(无效){

  静态int prevHorz = 0;

  静态int prevVert = 0;

  诠释我;

  常量字节超时 = 0x80; // 2 秒退出

  adj[0] = curSweep;

  adj[1] = curMode;

  adj[2] = TrigFalling;

  adj[3] = curPwmMode;

  FC_disable();

  drawMainMenu();

  而 ((digitalRead(BtnHorz) == LOW) || (digitalRead(BtnVert) == LOW))

    ; // 等到按钮关闭

  开始定时器1(0);

  做 {

    i = 数字读取(BtnVert);

    如果(我!= prevVert){

      prevVert = i;

      如果(我 == 低){

        incSel();

        drawMainMenu();

      }

      我的延迟(100);

      开始定时器1(0);

    }

    i = 数字读取(BtnHorz);

    if (i != prevHorz) {

      上一页Horz = i;

      如果(我 == 低){

        if (sel == sSigGen)

          休息;

        incAdj();

        if (sel == sTestSig)

          setPwmFrequency(testSignalPin, adj[3]);

        drawMainMenu();

      }

      我的延迟(100);

      开始定时器1(0);

    }

    我 = TCNT1L; // 强制读取 TCNT1H

  } 而 (TCNT1H < 超时);

  if (sel == sSigGen) {

    选择 = 时间;

    ExecSigGenMenu();

  }

  setSweep(adj[0]);

  setMode(adj[1]);

  TrigFalling = adj[2] != 0;

  清除SH1106();

}

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

// 检查按钮

// 如果 BtnVert 按钮被按下,改变增益

// 如果 BtnHorz 按钮被按下,改变扫描

// 如果按钮按住 2 秒,则显示菜单

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

无效检查按钮(无效){

  常量字节超时 = 70;// 1 秒显示菜单

  静态int prevHorz = HIGH;

  静态int prevVert = HIGH;

  诠释我;

  如果(数字读取(BtnHorz)== 低){

    if (prevHorz == HIGH) {

      开关(curMode){

        //case mFreqLogic:

       // 案例 mFreqAC:

        案例毫伏表:

          执行菜单();

          返回;

      }

      按钮定时器1 = 0;

      我的延迟(15);

      设置扫描(255);

      prevHorz = 低;

    } 别的 {

      if (ButtonsTimer1 > timeout) {

        执行菜单();

        返回;

      }

    }

  } 别的 {

    prevHorz = 高;

  }

  如果(数字读取(BtnVert)== 低){

    if (prevVert == HIGH) {

      按钮定时器1 = 0;

      我的延迟(15);

      设置模式(255);

      prevVert = 低;

    } 别的 {

      if (ButtonsTimer1 > timeout) {

        执行菜单();

        返回;

      }

    }

  } 别的 {

    prevVert = 高;

  }

}

//================================================= ==========================

// Timer1 每 65536 个计数溢出

// 用于频率计

//================================================= ==========================

ISR (TIMER1_OVF_vect)

{

  FC_overflowCount++;

}

//================================================= ==========================

// Timer1 捕捉中断

// 由比较器调用

//读取当前timer1捕获值

// 用于频率计

//================================================= ==========================

ISR (TIMER1_CAPT_vect) {

  // 在计数器值发生任何变化之前获取它

  无符号整数 timer1CounterValue = ICR1; // 参见数据表,第 117 页(访问 16 位寄存器)

  无符号长溢出复制 = FC_overflowCount;

  无符号长 t;

  静态无符号长prevT;

  // 如果刚刚错过了溢出

  if ((TIFR1 & bit(TOV1)) && timer1CounterValue < 0x7FFF)

    溢出复制++;

  t = (overflowCopy << 16) + timer1CounterValue;

  if ((!FC_firstAC) && (t-prevT > 100) && (t-prevT > FC_MaxPeriodAC))

    FC_MaxPeriodAC = t-prevT;

  上一页T = t;

  FC_firstAC = 假;

}

//================================================= ==========================

// Timer0 中断服务由硬件 Timer0 每 1ms = 1000 Hz 调用一次

// 由频率计数器使用

// 每 1mS 调用一次

//================================================= ==========================

ISR(TIMER0_COMPA_vect){

  if (FC_Timeout >= FC_LogicPeriod) { // 门时间结束,测量准备就绪

    TCCR1B &= ~7; // 门关闭/计数器 T1 停止

    位清除(TIMSK0,OCIE0A);// 禁用 Timer0 中断

    FC_OneSec = 真;// 设置结束计数周期的全局标志

    // 现在计算频率值

    FC_freq = 0x10000 * FC_overflowCount;// mult #overflows by 65636

    FC_freq += TCNT1; // 添加 counter1 的值

  }

  FC_超时++;// 计算中断事件的数量

  if (TIFR1 & 1) { // 如果定时器/计数器 1 溢出标志

    FC_overflowCount++; // 计数 Counter1 溢出的次数

    位集(TIFR1,TOV1);// 清除定时器/计数器 1 溢出标志

  }

}

//================================================= ==========================

// FC_InitLogic

// 计算 D5 在 mS 周期内的上升沿数

//================================================= ==========================

无效 FC_InitLogic() {

  无中断();

  TIMSK0 = 0x00;

  延迟微秒(50);// 等待是否有任何 int 待处理

  FC_OneSec = 假;// 重置周期测量标志

  FC_Timeout = 0; // 重置中断计数器

  TCCR1A = 0x00;//定时器输出关闭

  TCCR1B = 0x07;// T1 引脚上的外部时钟源。时钟在上升沿。

  TCNT1 = 0x00;// 计数器 = 0

  TCCR0A = 0x02;// 比较输出关闭;最大计数 = OCRA

  TCCR0B = 0x03;// 输入时钟为 16M/64

  TCNT0 = 0x16;// counter = 0 - 为什么这不是 0?设置时间的成本?

  TIMSK0 = 0x00;

  OCR0A = 248; // 最大计数值 = CTC 除以 250 = 1mS

  GTCCR = 0x02;// 重置预分频器

  FC_overflowCount = 0;

  位集(TIMSK0,OCIE0A);// 启用 Timer0 中断

  中断();

}

//================================================= ==========================

// FC_InitAC

// ACfreqAdcPin = 0..5 - 使用该 ADC 多路复用器并使用 Timer1 测量周期

//================================================= ==========================

无效 FC_InitAC() {

  无中断();

  FC_disable();

  TCCR1A = 0;// 重置定时器 1

  TCCR1B = 位(CS10)| 位(ICES1);// 无预分频器,输入捕捉边沿选择

  TIFR1 = 位 (ICF1) | 位(TOV1);// 清除标志,这样我们就不会收到虚假中断

  TCNT1 = 0; // Timer1归零

  FC_overflowCount = 0; // 对于 Timer1 溢出

  TIMSK1 = 位(TOIE1)| 位(ICIE1);// 定时器 1 溢出和输入捕捉中断

  ADCSRA = 0;

  DIDR1 = 1;// D6的数字输入关闭

  ADMUX = ACfreqAdcPin;

  ACSR = 位(ACI)| 位(ACIC) | (B10 << ACIS0); //“清除”中断标志;比较器的定时器捕捉;下降沿

  ADCSRB = 位(ACME);// 比较器连接到 ADC 多路复用器

  FC_firstAC = 真;

  FC_Timeout = 0;

  FC_MaxPeriodAC = 0;

  中断();

}

//================================================= ==========================

// FC_disable

//关闭频率计数器中断

//================================================= ==========================

无效 FC_disable() {

  TCCR0A = 0x03;// 没有比较输出;高达 0xFF 的快速 PWM

  TCCR0B = 0x03;// 没有输出比较;预分频器 = 16MHz/64;大约每 1mS 溢出一次

  TIMSK0 = 0x00;// 中断屏蔽寄存器 = 无

  GTCCR = 0x00;//控制寄存器=无

  OCR0A = 0x00;// 输出比较寄存器 A = none

  OCR0B = 0x00;// 输出比较寄存器 B = none

  TCCR1A = 0xC0;

  TCCR1B = 0x05;

  TCCR1C = 0x00;

  TIMSK1 = 0x00;

}

//================================================= ==========================

// FC_OneSecPassed

// 1 秒过去了吗?

//================================================= ==========================

bool FC_OneSecPassed() {

  静态字节 prevTimer1 = 0;

  字节 i;

  静态无符号长 t = 0;

  if (bitRead(TIFR0, TOV0)) // 每 1mS 溢出

    FC_超时++;

  位集(TIFR0,TOV0);

  返回 FC_Timeout > 1000;

}

//================================================= ==========================

// FC_CheckLogic

//频率测量器

// 重复调用

// 超时返回true

// 结果为 FC_freq

//================================================= ==========================

布尔 FC_CheckLogic() {

  返回 FC_OneSec;

}

//================================================= ==========================

// FC_CheckAC

//频率测量器

// 重复调用

// 超时返回true

// 结果为 FC_freq

//================================================= ==========================

布尔 FC_CheckAC() {

  无符号长 FC_elapsedTime;

  如果(FC_OneSecPassed()){

    如果(FC_MaxPeriodAC > 0)

      FC_freq = 100 * F_CPU*1.004 / FC_MaxPeriodAC;// 乘以 100 所以可以显示 2 dp

    别的

      FC_freq = 0;

    FC_InitAC();

    返回真;

  }

  返回假;

}

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

// 我的延迟

// 延迟大约 mS 毫秒

// 不使用任何计时器

// 不影响中断

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

无效我的延迟(int毫秒){

  对于 (int j = 0; j < mS; j++)

    延迟微秒(1000);

}

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

// 测量电压表

// 以 mV 为单位测量 Vin 处的电压表

//假设电阻器已连接到引脚:

// Ra 从引脚到 5V

// Rb 从引脚到 0V

// Rc 从引脚到 Vin

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

长测量电压表(int pin){

  常量长 Ra = 120; //从引脚到5V

  常量长 Rb = 150; //从引脚到0V

  常量长 Rc = 470; // 从引脚到 Vin

  const int calibrateZero = +20; //校准所以0V =“0V”

  常量 int calibrateVolts = 1024; // 校准所以 10V = "10V"

  长Aa,Vb;

  字节旧ACSR = ACSR;

  字节旧ADCSRA = ADCSRA;

  字节老ADCSRB = ADCSRB;

  字节老ADMUX = ADMUX;

  字节老DIDR0 = DIDR0;

  字节老DIDR1 = DIDR1;

  Vb = readVcc();

  Aa = 模拟读取(引脚);//设置ADC

  我的延迟(1);// 高输入阻抗,因此允许稳定

  Aa = 模拟读取(引脚);// 读取 ADC

  ACSR = 旧ACSR;

  ADCSRA = 旧ADCSRA;

  ADCSRB = 旧ADCSRB;

  ADMUX = oldADMUX;

  DIDR0 = 旧DIDR0;

  DIDR1 = 旧DIDR1;

  如果 (Aa > 1023 - 10)

    返回 LONG_MAX;

  如果 (Aa < 10)

    返回-LONG_MAX;

  返回 (Aa * Vb - Rc * ((Vb * 1024 - Aa * Vb) / Ra - Aa * Vb / Rb)) / calibrateVolts + calibrateZero;

}

//================================================= ==========================

// 检查电压表

// 电压表测量器

//================================================= ==========================

布尔检查电压表(){

  常量字节 n = 50;

  静态字节 i = 0;

  静态长总和 = 0;

  长 v;

  我的延迟(5);

  v = 测量电压表(6);

  我++;

  如果((v |总和)&0x40000000){

    总和 = v;

    Vin = v;

  } 别的 {

    总和 += v;

    Vin =总和/我;

  }

  如果 (i >= n) {

    总和 = 0;

    我 = 0;

    返回真;

  }

  返回假;

}

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

// InitSigGen

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

无效 InitSigGen(无效){

  pinMode(SG_DATA,输出);

  pinMode(SG_CLK,输出);

  pinMode(SG_fsyncPin,输出);

  数字写入(SG_fsyncPin,高);

  数字写入(SG_CLK,高);

  SG_Reset();

  SG_freqReset(calcFreq(freqSGLo), waveType);

}

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

// 主要例程

// 设置函数

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

无效设置(无效){

  // 打开串口,波特率为 BAUDRATE b/s

  串行.开始(波特率);

  // 清除缓冲区

  memset( (void *)commandBuffer, 0, sizeof(commandBuffer) );

  // 激活中断

  sei();

  初始化ADC();

  Serial.println("ArdOsc " __DATE__); // 编译日期

  Serial.println("OK");

  设置模式(0);// y 增益 5V

  设置扫描(5);

  setPwmFrequency(testSignalPin, 3); // 测试信号 976Hz 1024uS

  pinMode(BtnHorz,INPUT_PULLUP);

  pinMode(BtnVert,INPUT_PULLUP);

  pinMode(LED_BUILTIN,输出);

  Wire.begin(); // 加入 i2c 总线作为主机

    TWBR = 1; // 频率=888kHz 周期=1.125uS

  初始化SH1106();

}

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

// 主要例程

// 环形

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

无效循环(无效){

  静态 int ButtonsTimer2 = 0;

  开关(curMode){

    案例毫伏表:

      如果(检查电压表())

        画屏();

      休息;

    默认:

      if (!SendingSerial) {

        发送ADC();

        开关(扫描类型){

          案例 sw20Frames: 案例 sw100Frames: 案例 sw500Frames:

            SG_StepSweep();

        }

      }

  }

  检查按钮();

}

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/dianzi/2419186.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-08-01
下一篇 2022-08-01

发表评论

登录后才能评论

评论列表(0条)

保存