Altium Designer画的原理图和PCB图如下:(51hei附件中可下载工程文件)
制作出来的实物图如下:
单片机源程序如下:
/* Mini DSO with STC MCU 操作说明: 主界面-选项设置模式: 单击编码器:切换开始/暂停采样 双击编码器:切换波形水平滚动模式/选项设置模式 长按编码器:进入设置界面 旋转编码器:修改当前参数 按下同时旋转编码器:在参数间切换 主界面-波形水平滚动模式: 单击编码器:切换开始/暂停采样 双击编码器:切换波形水平滚动模式/选项设置模式 长按编码器:进入设置界面 旋转编码器:水平滚动波形(在暂停采样状态下有效) 按下同时旋转编码器:垂直滚动波形(仅在暂停采样状态下有效) 设置界面: 单击编码器:无效 双击编码器:无效 长按编码器:返回主界面 旋转编码器:修改当前选项 按下同时旋转编码器:切换选项 主界面参数: 横轴时间区间:"500ms", "200ms", "100ms", "50ms", "20ms", "10ms","5ms", "2ms", "1ms", "500us", "200us", "100us"(100us仅在自动触发模式下可用) 纵轴电压区间:设定电压量程上限,最大30V,顺时针旋转编码器将电压上限设置为0则进入自动量程,逆时针旋转进入手动量程 触发值:设定触发电压值,触发位置被放在屏幕中心 触发方向:上下箭头,分别代表上升沿触发和下降沿触发 触发方式: Auto(自动触发) :连续采样,指示灯始终点亮,通过单击编码器控制启停;触发成功时,触发点位于屏幕中心,触发失败则波形无规律滚动,屏幕左侧显示Fail标志 Normal(普通触发):等待预采样,待指示灯亮起表示预采样结束,这时可以输入信号; 触发成功后,显示波形,并继续等待下次触发; 如果有新的触发成功,则自动显示新的波形,如没有新的触发成功则屏幕波形保持并一直等待下次触发; 需注意设置正确的触发值,否则一直处于等待触发状态,屏幕不会显示波形 Single(单次触发):等待预采样,待指示灯亮起表示预采样结束,这时可以输入信号; 触发成功后,显示波形,并停止采样,需用户单击编码器开始下次采样 需注意设置正确的触发值,否则一直处于等待触发状态,屏幕不会显示波形 主界面状态显示: Run:正在采样 Stop:停止采样 Fail:自动模式下,触发值超出波形范围引起触发失败 Auto:自动量程 设置界面选项: PMode(绘图模式): Vector:波形以矢量显示 Dots :波形以点阵显示 LSB(采样系数):分压系数的100倍,比如用10k加2k的电阻进行分压,分压系数为(10+2/2)=6,LSB理论取值为6x100=600,根据实际电阻精度进行微调 BRT:OLED亮度 保存设置:长按编码器退出设置界面时,设置界面和主界面的所有参数被保存到EEPROM 2020-04-30更新 1. 增加单次触发和普通触发功能,可自定义触发点用来捕捉波形变化 2. 按照规格书要求将ADC采样端口设置为高阻输入 2020-5-3更新 1. 增加波形频率显示 2. 使用编码器水平/垂直滚动波形 3. 使用编码器垂直缩放波形 4. 用户可设定触发电平 */ #include <reg51.h> #include "global.h" #include "delay.h" #include "adc_stc8.h" #include "config_stc.h" #include "settings.h" #include "chart.h" #include "ssd1306.h" // #include "uart.h" // #include "stdio.h" #define VBAT_UPDATE_FREQ 40 //电池电压信息的更新频率 VBAT_UPDATE_FREQ*25ms #define VBAT_LSB 600 //电池电压采样分压系数*100 #define INT_0 0 //外部中断0序号,编码器旋转 #define INT_1 2 //外部中断1序号,编码器按下 #define TIMER_0 1 //定时器0中断序号 void init() { InSettings = 0; //初始化开机界面 OptionInSettings = 0; //初始化设置界面的选项编号 OptionInChart = 0; //初始化主界面的选项标号 WaveScroll = 0; //清零波形滚动标志 OptionChanged = 0; //清零设置修改标志 ADCRunning = 1; //置位采样标志 ADCInterrupt = 0; //清零ADC中断标志 ADCComplete = 0; //清零ADC完成标志 WaveUpdate = 0; //清零波形更新标志 ClearWave = 0; //清零清除波形标志 ClearDisplay = 1; //置位清空屏幕标志 EC11PressAndRotate = 0; //清零编码器旋转标志 UpdateVbat = 0; //清零采样电池电压标志 ADCComplete = 0; //清零ADC完成采样标志 WaveFreq = 0; //清零波形频率 TriPos = 50; //初始化触发点位置 TriPosOffset = 0; //初始化触发点偏移量 TriFail = 0; //清零触发失败标志 VMax = 0; //清零波形电压最大值 VMin = 0; //清零波形电压最小值 WaveLengthSumNum = 0; //清零波形长度求和次数 WaveLengthSum = 0; //清零波形长度求和 /* 波形滚动模式下正在采样则禁用外部中断0(编码器旋转) Disable external interrupt 0(Encoder rotation) in waveform scroll mode when sampling*/ if (ADCRunning && WaveScroll) EX0 = 0; /* 获取内部参考电压值 Get internal reference voltage */ BGV = GetBGV(); } void runWhenInSettrings() { P_Ready = 0; ClearDisplay = 1; while (InSettings) { /* 更新电池电压信息 Update voltage infor of battery */ if (UpdateVbat) { UpdateVbat = 0; VBat = GetVoltage(ADC_BAT, VBAT_LSB); } /* 更新屏幕亮度 Update OLED Brightness */ if (OLED_BrightnessChanged) { OLED_BrightnessChanged = 0; OLED_Write_Command(0x81); OLED_Write_Command(OLED_Brightness * 10); } /* 更新参数 Update parameters */ if (DisplayUpdate) { DisplayUpdate = 0; PlotSettings(); OLED_Display(); } } /* 保存设置 Save Settings */ EA = 0; if (OptionChanged) { OptionChanged = 0; ClearDisplay = 0; /* 保存设置并显示保存状态 Save settings and display saving status*/ PlotSaveStatus(Save_Options()); OLED_Display(); Delay1000ms(); } /* 退出settings后刷新屏幕 Redraw display after exit settings */ ClearDisplay = 1; PlotChart(); OLED_Display(); IE0 = 0; IE1 = 0; EA = 1; } /* ADCRuning=1 | WaveUpdate=1 | | ClearWave=1 | | ClearWave=0 | WaveUpdate=0 ADCRuning=0 DisplayUpdate=1 WaveUpdate=1 | ClearWave=1 | ClearWave=0 WaveUpdate=0 */ void runWhenADCInterrupt() { ADCInterrupt = 0; P_Ready = 0; /* ADC采样运行 ADC Sampling Running */ if (ADCRunning) { EX0 = 0; if (WaveUpdate) { WaveUpdate = 0; ClearDisplay = 1; /* 清空波形缓存 Clear waveform in buffer*/ if (ClearWave) { ClearWave = 0; memset(ADCbuf, 0x00, SAMPLE_NUM * 2); WaveFreq = 0; TriFail = 0; VMax = 0; VMin = 0; PlotChart(); } /* 显示缓存中的波形 Display waveform in buffer*/ else { AnalyseData(); PlotChart(); PlotWave(); } } else { ClearDisplay = 0; PlotChart(); } OLED_Display(); /* ADCRuning和WaveScroll同时置位时,不开启外部中断0(编码器旋转) Not to enable external interrupt 0(Encoder Rotation) when both of ADCRunning and WaveScroll set. */ if (!WaveScroll) { IE0 = 0; EX0 = 1; } } /* ADC采样停止 ADC Sampling Stopped */ else { while (!ADCRunning && !InSettings) { if (DisplayUpdate) { EX0 = 0; DisplayUpdate = 0; if (WaveUpdate) { WaveUpdate = 0; /* 分析缓存中完成采样的采样数据 Analyse completed sampling data in buffer */ AnalyseData(); ClearDisplay = 1; /* 清空屏幕,绘制波形 Clear display and draw waveform */ if (ScaleH == ScaleH_tmp) { /* 采样点数有限,不支持横向缩放波形,只有时间区间与完成采样的时间区间相同才绘制波形 Since the sampling points limitation, scaling waveform along horizontal is not support. Show waveform only when time scale is same as that when sampling completed */ PlotChart(); PlotWave(); } /* 清空屏幕,不绘制波形 Clear display and no waveform */ else { WaveFreq = 0; //清零WaveFreq,绘制界面时,频率位置会显示****Hz PlotChart(); } } /* 只更新屏幕上的参数,波形不变 Update parameters on display only, keep waveform */ else { ClearDisplay = 0; PlotChart(); } OLED_Display(); IE0 = 0; EX0 = 1; } } } } void runWhenADCComplete() { /* ADC采样完成 - 单次触发 ADC Sampling Complete - Single Trigger Mode */ if (TriMode == 2) { EX0 = 0; ADCInterrupt = 1; DisplayUpdate = 0; WaveUpdate = 0; ADCRunning = 0; //清零ADC运行标志,停止采样 AnalyseData(); ClearDisplay = 1; PlotChart(); PlotWave(); OLED_Display(); IE0 = 0; EX0 = 1; } /* ADC采样完成 - 自动触发或普通触发 ADC Sampling Complete - Auto or Normal Trigger Mode */ else { //EX0 = 0; ClearDisplay = 1; //置位清空屏幕标志 AnalyseData(); //分析采样数据 PlotChart(); //绘制主界面 PlotWave(); //绘制波形 OLED_Display(); //IE0 = 0; //EX0 = 1; } } /* 程序入口 Program Entry */ void main() { /* 将ADC端口P0.4,P0.6设置为高阻 Set ADC port P0.4, P0.6 as high-impedance */ P0M1 |= 0x50; P0M0 &= ~0x50; /* 设置指示灯端口P1.1为推挽输出 Set indicator port P1.1 as push-pull output */ P0M1 &= ~0x02; P0M0 |= 0x02; /* 设置编码器端口P3.2,P3.3,P3.4为准双向 Set Encoder port P3.2, P3.3, P3.4 as quasi bidirectional */ P3M1 &= ~0x1C; P3M0 &= ~0x1C; /* 设置OLED端口P2.3,P2.4,P2.5,P2.6,P2.7为准双向 Set OLED port P2.3, P2.4, P2.5, P2.6 as quasi bidirectional */ P2M1 &= ~0xF8; P2M0 &= ~0xF8; /* 编码器旋转外部中断 Interrupt for rotating of Encoder */ IT0 = 0; //外部中断0触发方式,上升沿和下降沿 PX0 = 1; //外部中断0高优先级,可打断按键中断,用于判断按下同时旋转 EX0 = 1; //开启外部中断0 /* 编码器按键外部中断 Interrupt for clicking of Encoder */ IT1 = 1; //外部中断1触发方式,下降沿 PX1 = 0; //外部中断1低优先级 EX1 = 1; //开启外部中断1 /* 定时器0,为更新电池电压信息计时 Timer 0, for updating voltage of battery */ AUXR &= 0x7F; //定时器时钟12T模式 TMOD &= 0xF0; //设置定时器模式 TL0 = 0x46; //设置定时初值 //27MHz,25ms TH0 = 0x24; //设置定时初值 TF0 = 0; //清除TF0标志 ET0 = 1; //允许定时器0中断 /* 开总中断 Enable global interrupt */ EA = 1; //UartInit(); Read_Options(); init(); OLED_Init(); OLED_SetFontWidth(6); PlotChart(); OLED_Display(); while (1) { P_Ready = 0; /* 采样波形 Sampling*/ GetWaveData(); /* 进入设置界面 Enter Settings Interface */ if (InSettings) { runWhenInSettrings(); } /* ADC采样中断 ADC Sampling Interrupt */ else if (ADCInterrupt) { runWhenADCInterrupt(); } /* ADC采样完成 ADC Sampling Complete */ else { runWhenADCComplete(); } } } /* 分析编码器旋转 Analyse Rotation of Encoder */ void Scan_EC11(void) { /* 延时去抖动 Delay to remove jitter */ Delay500us(); /* 正转 Clockwise */ if (EC11_A != EC11_B) { Change_Val(1); } /* 反转 Anticlockwise */ else if (EC11_A == EC11_B) { Change_Val(0); } } /* 编码器旋转中断 Interrput for Encoder Rotated */ void INT0_interrupt(void) interrupt INT_0 { Delay500us(); /* 编码器是否被按下 Whether the Encoder is pressed */ if (!EC11_KEY) EC11PressAndRotate = 1; else EC11PressAndRotate = 0; Scan_EC11(); ADCInterrupt = 1; DisplayUpdate = 1; IE1 = 0; //清零外部中断1标志位 IE0 = 0; //清零外部中断0标志位 } /* 编码器点击中断 Interrput for Encoder Pressed */ void INT1_interrupt(void) interrupt INT_1 { Delay50ms(); if (!EC11_KEY) { EC11PressAndRotate = 0; /* 长按编码器按键 - 切换主界面和设置界面 Long presse Encoder - Switch main interface and settings interface */ if (Delay800ms_Long_Press()) { InSettings = ~InSettings; /* 进入设置界面 Enter Settings */ if (InSettings) { DisplayUpdate = 1; UpdateVbat = 1; TF0 = 0; //清零定时器0溢出标志 TR0 = 1; //定时器0开始计时,开始电池电压信息更新计时 IE0 = 0; //清零外部中断0中断标志 EX0 = 1; //开启外部中断0(编码器旋转) } /* 回到主界面 Retrurn to main interface */ else { TR0 = 0; //清零定时器0溢出标志 TF0 = 0; //定时器0停止计时,停止电池电压信息更新计时 WaveFreq = 0; TriFail = 0; VMax = 0; VMin = 0; DisplayUpdate = 1; WaveUpdate = 1; ClearWave = 0; } ADCInterrupt = 1; } /* 按住编码器同时旋转 Rotate Encoder while pressing */ else if (EC11PressAndRotate) { /* 由编码器旋转中断执行操作 Operations Performed by Interrupt of Encoder Rotation */ } /* 双击编码器按键 - 在主界面,滚动波形和选项模式之间切换 Double click Encoder - Switch between Waveform Scroll Mode and Parameter Mode in Main Interface */ else if (Delay300ms_Double_Click()) { /* 主界面 Main Interface*/ if (!InSettings) { WaveScroll = ~WaveScroll; OptionChanged = 1; ADCInterrupt = 1; DisplayUpdate = 1; ClearWave = 0; } } /* 单击编码器按键 - 在主界面,切换Stop/Run状态 Single click Encoder - Switch Run/Stop in main interface */ else if (!InSettings) { EX0 = 0; ADCRunning = ~ADCRunning; if (ADCRunning) { WaveUpdate = 1; ClearWave = 1; } else { DisplayUpdate = 1; WaveUpdate = 1; } ADCInterrupt = 1; IE0 = 0; IE1 = 0; } } IE1 = 0; } /* 为更新Vbat计时 Timer for updating Vbat */ void TIMER0_interrupt(void) interrupt TIMER_0 { static uint8 n; if (++n >= VBAT_UPDATE_FREQ) { n = 0; UpdateVbat = 1; DisplayUpdate = 1; } }
复制代码