此为用C进行仪器控制系列数字万用表的第4篇文章,在此之前已经编写了3篇相关文章了,可参见: 1)用C进行仪器控制系列数字万用表(1):该篇文章介绍了如何从零开始创建一个控制台应用程序,实现Keysight数字万用表的电压测量,并将结果通过控制台展示; 2)用C进行仪器控制系列数字万用表(2):该篇文章在第1篇文章的基础上,以窗体应用程序的形式重写了万用表电压测量程序,并能够自动识别仪器地址,以及能够通过按钮控制测量的开始和停止,电压的显示等。 3)用C进行仪器控制系列数字万用表(3):该篇文章在第2篇文章的基础上,通过代码的简单优化,实现了不同测量类型的下拉选择,根据下拉选项执行对应类型的测量功能(电压、电流、电阻、二极管、导通、频率测试等)。 此篇文章是在第3篇文章的基础上,添加波形图表显示功能。优化需求:添加波形图表显示功能,用以观测数据的变化趋势 通过添加波形图表功能,将测量数据实时传递给波形图表,使其能够在界面中显示测量的历史数据,就可以观测到数据的变化趋势了。最终想要实现的效果如下图所示。 需求实现效果优化实现过程 1)窗体界面的优化从工具箱中找到VisualStudio自带的Chart图表控件,并将其拖拽至窗体面板中。 Chart控件选中新添加的Chart控件,在其属性窗口中找到图表的Series属性,然后点击该属性右侧的3个小圆点组成的按钮进入Series属性设置页,如下图所示 图表的Series属性修改数据序列的名称(Name)为Data,修改图表类型(ChartType)为Line,即修改图表类型为折线图显示。 修改Series属性设置完图表属性后,调整窗体上相关控件的布局,调整后如下图所示。 调整窗体布局 2)代码优化 主要涉及如下3块代码段的修改:在窗体的构造函数中增加曲线图样式的设置语句,包括曲线样式、曲线颜色的设置。publicForm1(){InitializeComponent();设置曲线图样式WaveChart。Series〔0〕。ChartTypeSeriesChartType。Line;WaveChart。Series〔0〕。ColorSystem。Drawing。Color。Blue;}在开始测试的事件响应代码段添加清除曲线代码,实现开始测试时重新绘图。if(btnStart。Text开始测试){btnStart。Text停止测试;清空曲线图数据WaveChart。Series〔0〕。Points。Clear();在测量线程代码段,增加将实时测量数据传递给波形图表的代码。循环读取万用表数据while(true){发送采集命令messageBasedSession。WriteString(:READ?,true);读取返回值stringresponsemessageBasedSession。ReadString();在控件和图表中输出结果this。Invoke((MethodInvoker)delegate{resultTextBox。TextChangeDataToD(response。Trim());WaveChart。Series〔0〕。Points。AddY(double。Parse(resultTextBox。Text));});}优化效果验证 程序优化后的运行效果如下图所示,可通过下拉选择测量类型,实现不同的测试需求,然后通过TextBox显示控件和Chart波形图表显示数据。 运行效果 通过验证,可以完美的实现测量需求。 附:本文中涉及到的完整源代码usingSystem;usingSystem。Resources;usingSystem。Windows。Forms;usingIvi。Visa。Interop;usingSystem。Threading;usingSystem。Linq。Expressions;usingSystem。Windows。Forms。DataVisualization。Charting;namespaceWinFormComPorts{publicpartialclassForm1:Form{privateIvi。Visa。Interop。ResourceManagerresourceManager;privateFormattedIO488messageBasedSession;privateThreadmeasurementThread;publicForm1(){InitializeComponent();设置曲线图样式WaveChart。Series〔0〕。ChartTypeSeriesChartType。Line;WaveChart。Series〔0〕。ColorSystem。Drawing。Color。Blue;}region端口号下拉选择框下拉事件响应代码summary端口号下拉选择框下拉事件响应代码summaryparamnamesenderparamparamnameeparamprivatevoidcboComPortsDropDown(objectsender,EventArgse){try{清除下拉列表this。cboComPorts。Items。Clear();获取设备的所有串口资源string〔〕comNamesSystem。IO。Ports。SerialPort。GetPortNames();添加可用串口号到下拉框中foreach(stringsincomNames){this。cboComPorts。Items。Add(s);}创建VISA资源对象实例Ivi。Visa。Interop。ResourceManagerrmnewIvi。Visa。Interop。ResourceManager();获取设备的所有仪器资源string〔〕usbNamesrm。FindRsrc(?INSTR);添加可用USB资源到下拉框中foreach(stringsinusbNames){if(s。Contains(0x))this。cboComPorts。Items。Add(s);}}catch(Exception){MessageBox。Show(系统内不存在此类仪器,ERROR);}}endregionregion开始测试停止测试按钮事件响应代码summary开始测试停止测试按钮事件响应代码summaryparamnamesenderparamparamnameeparamprivatevoidbtnStartClick(objectsender,EventArgse){try{if(btnStart。Text开始测试){btnStart。Text停止测试;清空曲线图数据WaveChart。Series〔0〕。Points。Clear();创建ResourceManager和MessageBasedSession对象resourceManagernewIvi。Visa。Interop。ResourceManager();messageBasedSessionnewFormattedIO488();打开万用表连接,万用表的USB通讯地址通过端口号下拉列表的值传递过来messageBasedSession。IO(IMessage)resourceManager。Open(cboComPorts。Text。ToString(),AccessMode。NOLOCK,5000,);获取测量类型,根据测量类型选择相应的采集指令stringConfigCommand;switch(cboMeasType。Text){caseDCVoltage:ConfigCommand:CONF:VOLT:DCAUTO,DEF;break;caseACVoltage:ConfigCommand:CONF:VOLT:ACAUTO,DEF;break;caseDCCurrent:ConfigCommand:CONF:CURR:DCAUTO,DEF;break;caseACCurrent:ConfigCommand:CONF:CURR:ACAUTO,DEF;break;caseResistance2wire:ConfigCommand:CONF:RESAUTO;break;caseResistance4wire:ConfigCommand:CONF:FRESAUTO;break;caseDiode:ConfigCommand:CONF:DIOD;break;caseContinuity:ConfigCommand:CONF:CONT;break;caseFrequency:ConfigCommand:CONF:FREQ;break;default:ConfigCommand:CONF:VOLT:DCAUTO,DEF;break;}设置采集模式和测量范围messageBasedSession。WriteString(ConfigCommand,true);创建线程执行连续测量measurementThreadnewThread(newThreadStart(MeasureThread));measurementThread。Start();}else{btnStart。Text开始测试;停止测量线程if(measurementThread!nullmeasurementThread。IsAlive){measurementThread。Abort();measurementThread。Join();}关闭连接if(messageBasedSession!null){messageBasedSession。IO。Close();messageBasedSession。Dispose();}if(resourceManager!null){resourceManager。Dispose();MessageBox。Show(资源释放);}}}catch(Exceptionex){MessageBox。Show(发生错误:ex。Message);}}endregionregion测量线程代码summary测量线程,读取万用表的数据,并将结果反馈至resultTextBox控件summaryprivatevoidMeasureThread(){try{循环读取万用表数据while(true){发送采集命令messageBasedSession。WriteString(:READ?,true);读取返回值stringresponsemessageBasedSession。ReadString();在控件和图表中输出结果this。Invoke((MethodInvoker)delegate{resultTextBox。TextChangeDataToD(response。Trim());WaveChart。Series〔0〕。Points。AddY(double。Parse(resultTextBox。Text));});}}catch(ThreadAbortException){线程被中断MessageBox。Show(线程被中断);查询错误messageBasedSession。WriteString(:SYST:ERR?);读取返回的错误信息stringresponsemessageBasedSession。ReadString();若仪器返回了410错误(读取万用表读数时被中断导致)或无错误,则忽略if(response。Contains(410)response。Contains(Noerror)){}else{MessageBox。Show(response);}}catch(Exceptionex){MessageBox。Show(发生错误:ex。Message);}}endregionregion数据显示格式转换代码summary将科学计数法表示的字符串数据转换为double型数据显示格式summaryparamnamestrDataparamreturns返回double型数据格式的字符串returnsprivatestringChangeDataToD(stringstrData){DoubledData0。0;if(strData。Contains(E)){dDataDouble。Parse(strData);}returndData。ToString();}endregion}}