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

示波器是一种电子测试设备,可以使用二维图形监控任何电压的稳定变化,其中一个或多个电压随时间的变化位于垂直 y 轴上。一般来说,每个电子爱好者或对电子产品感兴趣的人都会在某些时候需要示波器。然而,对于学生和业余爱好者来说,它的价格非常昂贵,这就是为什么在本文中我们将讨论如何使用 arduino 在家里制作迷你示波器。
在本文中,我们将构建一个简单、低成本的基于 arduino 的示波器,该示波器具有 1.3“ oled 显示屏,可用于准确显示波形。该项目的灵感来自于火柴盒项目中的peter balch oscilloscope。我们更改了很少的代码和硬件可以满足我们的要求。
构建基于 arduino 的示波器所需的材料
使用arduino nano制作这款便携式迷你示波器需要以下组件。
arduino示波器的电路图
构建基于 arduino 的示波器的原理图非常简单,只需要几个部件,您可以查看下面的完整电路图。
原理图的主要部分使用单个运算放大器 ic,即lm358,它在单个芯片内包含两个运算放大器。由于输入信号为交流信号,并且我们没有分轨结构,因此有两个运算放大器(来自单个运算放大器 8 引脚封装)用于使信号交流耦合。两个运算放大器都被馈入用于偏移信号的参考电压,并使用模拟输入将其绘制在示波器图上。可以使用电位器(具有 100k 电阻)更改偏移量。两个运算放大器都设置有相同的负反馈和 x5 增益设置。
除此之外,oled 通过 a4 连接,a5 是带有 4.7k 上拉电阻的 i2c scl 和 sda 引脚。它可以使用简单的 usb 连接器。这些按钮用于设置示波器的参数。我们在性能板上构建了完整的电路,当我完成设置时,它看起来像这样。
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 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 xgraticule2 - pxlenz - 2) && (i ac20mv) 返回; acsr = 0x10; adcsra = 0x97; adcsrb = 0x0 ; //adc控制和状态寄存器b // 0 位 6 – acme:模拟比较器多路复用器使能 // 000 位 2:0 – adtsn:adc 自动触发源 [n = 2:0] 自由运行模式 admux = 0x20 + (curvref <= 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 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 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 0) fc_freq = 100 * f_cpu*1.004 / fc_maxperiodac;// 乘以 100 所以可以显示 2 dp 别的 fc_freq = 0; fc_initac(); 返回真; } 返回假;}//------------------------------------------------ -----------------------------------------// 我的延迟// 延迟大约 ms 毫秒// 不使用任何计时器// 不影响中断//------------------------------------------------ -----------------------------------------无效我的延迟(int毫秒){ 对于 (int j = 0; j 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 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 xgraticule2 - pxlenz - 2) && (i 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 < freeruntimeout) 转到 freerunfast;
      位集(adcsra,adsc);
    }
自由奔跑:
    对于(p = adcbuffer;p = 0x80 - 迟滞) : (adch < 0x80 + 迟滞));
    do { // 等待比较器高电平
      位集(adcsra,adsc);// 开始 adc 转换
      对于 (d = 0; d freeruntimeout) 转到 freerunslow;
    } while (trig ? (adch = 0x80 - 迟滞));
自由运行慢:
    位集(adcsra,adsc);// 开始 adc 转换
    对于 ( p = adcbuffer; p < adcbuffer + adcbuffersize; p++) {
      对于 (d = 0; d 最大模式)
    返回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] 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 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 = 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 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 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 0)
      fc_freq = 100 * f_cpu*1.004 / fc_maxperiodac;// 乘以 100 所以可以显示 2 dp
    别的
      fc_freq = 0;
    fc_initac();
    返回真;
  }
  返回假;
}
//------------------------------------------------ -----------------------------------------
// 我的延迟
// 延迟大约 ms 毫秒
// 不使用任何计时器
// 不影响中断
//------------------------------------------------ -----------------------------------------
无效我的延迟(int毫秒){
  对于 (int j = 0; j 1023 - 10)
    返回 long_max;
  如果 (aa = 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();
        }
      }
  }
  检查按钮();
}

HDI PCB的一阶,二阶和三阶是如何区分的?详细案例说明
示波器升维破解CAN错误帧/BusOff的经验分享
2010全国LED驱动及电源技术研讨会邀请函
三大新技术能否改善IC设计中的功耗、性能和面积?
5G规模化发展推动MEC迎来产业爆发期
如何使用Arduino Nano和OLED显示器构建示波器
MAX9668 10位可编程gamma基准系统,为TFT L
工业以太网有哪些好处_现场总线和工业以太网的区别
红外对管控制电路错误输出?原因解析与解决方案
发行数字货币很容易吗
苹果回应中国产iPhone15供欧美传言
高效、可靠的电源开关和驱动器芯片
为什么说直流电的电压有点任性?
150万年内一颗恒星将与太阳系相撞
锦欣生殖如何打造国际VIP医疗服务体系
云计算将成为ERP未来重要研发方向
日本日置推出功能全面精华版记录仪MR8875
WDM波分复用器件的结构组成介绍
安森美扩展蓝牙低功耗微控制器(MCU)系列到汽车无线应用
简单的介绍100G光模块的相关信息