C语言在测量与控制中的应用实验报告.docx
- 文档编号:18856725
- 上传时间:2023-04-24
- 格式:DOCX
- 页数:34
- 大小:720.74KB
C语言在测量与控制中的应用实验报告.docx
《C语言在测量与控制中的应用实验报告.docx》由会员分享,可在线阅读,更多相关《C语言在测量与控制中的应用实验报告.docx(34页珍藏版)》请在冰豆网上搜索。
C语言在测量与控制中的应用实验报告
HarbinInstituteofTechnology
C语言在测量与控制中的应用
实验报告
专业:
控制科学与工程
班级:
学号:
姓名:
设计时间:
2015年5月12日星期二
实验一AD/DA数据采集实验
一、定时中断程序设计
实验的目的:
1.掌握定时器/计数器8254的工作原理与编程。
2.熟悉中断控制器8259A的工作原理与使用方法。
3.掌握硬件中断程序设计的原理与编程方法。
实验条件:
PC机,WinXP操作系统,TurboC2.0
程序设计要求:
程序运行首先提示输入中断服务的时间间隔T和中断服务次数N,正确输入后,回车,则每间隔指定的时间T会在屏幕上显示一些字符,显示N次后,则不再显示,如果N=0,则会无限显示下去,直到在键盘上按下指定的按键,才停止显示。
停止显示后,按任意键程序结束运行。
1.时间间隔T为以毫秒为单位浮点数,可处理范围至少要0.001毫秒到5000毫秒。
2.屏幕上的显示信息要有助于验证程序运行结果的正确。
3.输入错误信息要有提示,并允许重新输入。
4.编程时要尽量把具有独立功能的代码写成子程序。
5.注意变量的命名要清晰,代码的注释要丰富。
6.后面的三个实验均要在此程序基础上编程、添加代码,注意程序的结构。
设计思路
1.如何实现任意时间间隔?
(附程序流程图)
答:
时间间隔的选择是根据用户需求进行相应设置。
程序第一步用户输入时间间隔T(ms),在程序设计时,根据用户输入T(ms),并通过使用相应的语句对8254的通道0定时器设计数初值,然后将计数初值先写低8位,后写高8位的方式,初始化通道0。
代码实现为:
通过CalIPara()函数来计算计数初值的高低8位:
voidCalIPara(doubleDTimeI,int*ILongCount,unsignedchar*CL8,unsignedchar*CH8){
doubleTotalCounter=DTimeI*1193;
intResidue;
ILongCount[0]=TotalCounter/65536;
Residue=TotalCounter-65536*ILongCount[0];
CH8[0]=Residue>>8;
CL8[0]=Residue&0x0FF;
}
通过SetupTimerInterrupt()函数来对8254的通道0定时器的初始化:
voidSetupTimerInterrupt(void){
disable();
oldint8=getvect(0x08);
outportb(0x43,0x36);
if(LongCount==0){
outportb(0x40,L8);
outportb(0x40,H8);
}
else{
outportb(0x40,0x00);
outportb(0x40,0x00);
}
setvect(0x08,myint8);enable();
}
这样,每隔T(ms)时间,8259A的中断请求信号引起中断服务的执行。
时间间隔是由用户自己选择的,因此这样的设计能够实现任意时间间隔。
程序流程图如下:
2.如何设计显示信息便于验证定时的正确性?
(文字表述及必要的代码)
答:
为了验证定时的正确性,设计采用每次显示字符为中断次数的方法。
首先,可以通过显示字符的频率来粗略验证定时的正确性,当然也可以数规定时间内的字符数计算中断时间间隔,与设定值比对。
其次,中断次数可以验证有限次中断功能的正确性和无限次中断的手动请求停止功能的正确性。
具体实现可以通过中断时打印当前中断次数。
具体代码如下:
printf("%d\t",CurrentInterruptNum);
3.如何实现错误提示?
(文字表述及必要的代码)
答:
为了避免程序执行过程中发生错误,在用户每输入完一项数据后都要进行必要的正确性检验。
如果输入不符合要求,则在必要的友好提示下,循环要求用户重新输入,直至输入正确数据。
程序中,在InputInterruptPara函数中完成此项功能。
具体代码如下所示。
printf("Please input time interval of interrupt events (milisecond):
");
do{
scanf("%le",&TimeInterval);
if(TimeInterval>5000 || TimeInterval<0.001) {
getchar();
printf("\n\tInvalid input!
Time interval should be in the domain of [0.001,5000].");
printf("\n\tPlease input again:
");
}
else break;
}while
(1);
printf("\nPlease input the times of interrupt:
");
do{
scanf("%d",&InterruptNum);
if(InterruptNum<0) {
getchar();
printf("\n\tInvalid input!
Interrupt times should be no smaller than 0.");
printf("\n\tPlease input again:
");
}
4.如何实现按键停止定时中断服务?
(文字表述及必要的代码)
答:
当用户输入中断次数为0时,中断将会一直执行,直至用户手动输入键盘上特定的按键以申请手动停止中断。
具体实现上,通过kbhit()函数获取按键信号,通过bioskey(0)函数获得键值。
具体代码如下所示。
(1)在中断函数myint8中获得按键及键值
if(kbhit()){
EndStroke=bioskey(0);}
(2)在主函数中判断按键属性
/*Deadrecyclesdependingonthevalueofuser-inputInterruptNum*/if(InterruptNum!
=0)
while(CurrentInterruptNum else /*PressCtostop*/ while(EndStroke! =11875); 5.程序编写过程中遇到过的问题及解决办法。 答: 出现的问题: 在实现错误提示过程中,使用do-while函数进行循环判断时,错误输入数据后程序进行死循环,没有出现相应的输入提示。 解决办法: 事实证明由于没有进行相关函数的缓冲,在do-while函数增加getchar()函数对输入进行缓冲,即解决这个问题。 程序运行结果截图(不同运行参数条件下) 1.错误提示界面截图: 2.中断时间: 100ms,中断次数: 50 截图如下: 3.中断时间: 200ms,中断次数: 0(无限次中断),图示为按C键后结果,截图如下: 二、数据处理与绘图程序设计 实验的目的: 1.掌握数据文件的建立、存储和读取方法。 2.掌握C语言绘图程序设计方法。 3.掌握DosBox的使用方法。 实验条件: PC机,WinXP操作系统,TurboC2.0,DosBox 程序设计要求: 程序运行前,配置数据文件(包括中断服务的时间间隔T,中断服务次数N,正弦曲线的振幅、周期,坐标系在屏幕上的位置,横(时间)纵(幅度)坐标显示范围及刻度等),运行后,首先,在屏幕上设定位置绘制坐标系,然后每间隔指定的时间生成设定的正弦曲线上的一个点,保存到指定数据文件并绘制在坐标系里,超过横坐标显示范围后,会滚动显示,显示N个点后,则曲线停止,如果N=0,则会无限显示下去,直到在键盘上按下指定的按键,才停止显示。 停止显示后,按任意键程序结束运行。 1.横坐标标注为时间,纵坐标标注为幅度。 2.曲线动态显示 3.配置文件如有错误要能提示,不会造成程序运行报系统错误。 4.编程时要尽量把具有独立功能的代码写成子程序。 5.注意变量的命名要清晰,代码的注释要丰富。 6.后面的数据采集要在此程序基础上编程、添加代码,注意程序的结构。 设计思路 1.如何实现配置文件参数的读取? (文字表述及必要的代码) 答: 通过编写Setup.txt文件来实现,其中文件内容根据回车分行,依次为坐标系在屏幕上的位置、横(时间)纵(幅度)坐标显示范围及刻度、参考横线、网格、中断时间间隔及中断次数。 具体说明如下图: 读取文件数据时,采用fscanf()函数读取,通过ReadSetupFromFile()函数实现,代码如下: voidReadSetupFromFile(void){ FILE*fp1; fp1=fopen("Setup.txt","r");fscanf(fp1,"%d,%d,%d,%d\n",&IWXMin,&IWXMax,&IWYMin,&IWYMax);fscanf(fp1,"%le,%le,%le,%le\n",&DXMin,&DXMax,&DYMin,&DYMax);fscanf(fp1,"%le,%le\n",&DXStep,&DYStep);fscanf(fp1,"%le\n",&DReferenceLine); fscanf(fp1,"%d\n",&IGrid); fclose(fp1); } 2.如何编写绘制坐标系的子程序? (文字表述及必要的代码) 答: 首先,设计GetScreenX()函数、GetScreenY()函数以及GetInterval()函数来实现将用户配置的坐标系显示范围与刻度转化为实际屏幕上的点的位置的功能,然后根据坐标系在屏幕上的位置求出首尾点,通过画直线将坐标系画出来,即由 DTrail()函数来实现,代码如下: intGetScreenX(doubleRealX,intWXMin,intWXMax,doubleXMin,doubleXMax){ intWX=(WXMax-WXMin)/(XMax-XMin)*(RealX-XMin)+WXMin; returnWX; } intGetScreenY(doubleRealY,intWYMin,intWYMax,doubleYMin,doubleYMax){ intWY=WYMax-(WYMax-WYMin)/(YMax-YMin)*(RealY-YMin); returnWY; } intGetInterval(doubleRealInterval,intWRegion,doubleRegion){ intInterval=WRegion/Region*RealInterval; returnInterval; } /*Drawthetrail*/ voidDTrail(intWXMin,intWXMax,intWYMin,intWYMax,doubleXMin,doubleXMax,doubleYMin,doubleYMax,doubleXStep,doubleYStep,doubleRefLine,intGrid){ charLabel[10]; intWYRefLine; intWDot; intDot; inti; /*Drawtheframe*/l ine(WXMin,WYMin,WXMax,WYMin);/*top*/ line(WXMin,WYMin,WXMin,WYMax);/*left*/ line(WXMin,WYMax,WXMax,WYMax);/*Bottom*/ line(WXMax,WYMin,WXMax,WYMax);/*right*/ /*Drawreferenceline*/ WYRefLine=GetScreenY(RefLine,WYMin,WYMax,YMin,YMax);line(WXMin,WYRefLine,WXMax,WYRefLine); gcvt(RefLine,5,Label); outtextxy(WXMax+1,WYRefLine-2,Label); /*DrawXStep*/ i=0; while (1){ Dot=XMin+i*XStep;WDot=GetScreenX(Dot,WXMin,WXMax,XMin,XMax);if(WDot>WXMax){ break; } if(Grid==1){ line(WDot,WYMin,WDot,WYMax); } else{ line(WDot,WYMax,WDot,WYMax-5); } gcvt(Dot,5,Label); outtextxy(WDot-5,WYMax+8,Label); i++; } /*DrawyStep*/ i=0; while (1){ Dot=YMin+i*YStep;WDot=GetScreenY(Dot,WYMin,WYMax,YMin,YMax);if(WDot break; } if(Grid==1){ line(WXMin,WDot,WXMax,WDot); } else{ line(WXMin,WDot,WXMin+5,WDot); } gcvt(Dot,5,Label); if(Dot<0){ outtextxy(WXMin-30,WDot-2,Label); } else{ outtextxy(WXMin-20,WDot-2,Label); } i++; } } 3.如何保存数据文件,数据文件的格式? (文字表述及必要的代码) 答: 读取文件数据后先提示输入保存数据文件名称,将模拟得到的数据保存在该文件中,保存数据时,采用文件写入功能,加制表符分割,将数据写入。 格式如图所示: 代码如下: fprintf(fp,"%d\t%d\t%le\t%le\n",Index,IData,RealTime,DData); 4.如何将生成的正弦数据动态显示成曲线? (文字表述及必要的代码) 答: 每次的中断过程中,根据中断时间和当前中断次数计算出当前时间,代入到模拟的数据采集正弦函数中,则可求得当前点的纵坐标,再根据已有的前一点数据,连接两点得到一较短线段,当时中断次数较大时,就能够打正弦曲线绘制出来,由于中断过程是不断进行的,因此在坐标系上看实现了动态效果。 同时,实际中使用中可能会出现幅值超过坐标显示范围的部分,因此对这部分也需要进行一定的处理,因此,考虑到超出部分是由于坐标系的Y轴坐标不够大,这可以对Setup.txt文件中进行相应的修改即可,因此超出坐标轴的部分不进行显示。 代码如下: /*DrawXStep*/ i=0; while (1){ Dot=XMin+i*XStep; WDot=GetScreenX(Dot,WXMin,WXMax,XMin,XMax); if(WDot>WXMax){ break; } if(Grid==1){ line(WDot,WYMin,WDot,WYMax); } else{ line(WDot,WYMax,WDot,WYMax-5); } gcvt(Dot,5,Label); outtextxy(WDot-5,WYMax+8,Label); i++; } /*DrawyStep*/ i=0; while (1){ Dot=YMin+i*YStep; WDot=GetScreenY(Dot,WYMin,WYMax,YMin,YMax); if(WDot break; } if(Grid==1){ line(WXMin,WDot,WXMax,WDot); } else{ line(WXMin,WDot,WXMin+5,WDot); } gcvt(Dot,5,Label); if(Dot<0){ outtextxy(WXMin-30,WDot-2,Label); } else{ outtextxy(WXMin-20,WDot-2,Label); } i++; } 5.如何实现滚动? (文字表述及必要的代码) 答: 由于连续实时显示,实际绘图可能超过坐标轴显示范围。 设计要求指出,当在图像超出x轴显示范围时,将在新的一张坐标轴中显示。 所以,绘制图像时,当判断到当前点的横坐标超出x轴范围时,刷新坐标系,通过计算当前点与前一点连线与y轴的交点,在新的坐标系中仅显示交点到当前点部分线段。 同时,横坐标刻度也相应累加。 以此实现动态效果。 具体代码如下: if(RealTime>DXMax){ XRegion=DXMax-DXMin; DXMin=DXMax; DXMax=DXMin+XRegion; cleardevice();DrawCoordinate(IWXMin,IWXMax,IWYMin,IWYMax,DXMin,DXMax,DYMin,DYMax,DXStep,DYStep,DReferenceLine,IGrid); LastWDotX=IWXMin-1; } if(RealTime<=DXMax){ WDotX=GetScreenX(RealTime,IWXMin,IWXMax,DXMin,DXMax);WDotY=GetScreenY(DData,IWYMin,IWYMax,DYMin,DYMax);if(LastWDotX line(WDotX,WDotY,WDotX,WDotY); } else{ line(WDotX,WDotY,LastWDotX,LastWDotY); } LastWDotX=WDotX; LastWDotY=WDotY; } 程序运行结果截图(不同运行条件下) 中断时间是100ms,幅值为50,程序运行如下: 中断时间是400ms,幅值为50,程序运行如下: 三、AD/DA数据采集与模拟量输出实验 实验的目的: 1.熟悉AD/DA卡的工作原理 2.掌握AD/DA卡的安装、配置及编程方法。 3.了解实时数据采集和模拟量输出的常用方案。 实验条件: 1.具有ISA总线插槽和USB接口的PC系列微型计算机,操作系统使用DOS或Windows98,装有TurboC2.0,DosBox。 2.超低频信号发生器。 3.超低频示波器。 4.HY1232模入/模出接口板。 程序设计要求: 程序运行前,配置数据文件,运行后,首先,在屏幕上设定位置绘制坐标系,然后每间隔指定的时间通过A/D采集信号发生器的输出信号,保存到指定数据文件、绘制在坐标系里、并通过D/A输出, 1.能通过配置文件来改变程序的运行模式,不必修改代码即可让程序在启动时或者工作于实验二方式或者工作于实验三方式。 2.A/D采集过程得到的数字量及解码后得到模拟量均要保存到数据文件中 设计思路 1.A/D转换代码的实现? (文字表述及必要的代码) 答: 进行从端口读入AD12位AD值,使用位运算组合后,乘上对应的系数,就得到了AD采样值。 代码如下: outportb(0x280,0x00); while((inportb(0x285)&0x80)==0){ ; } IData=inportb(0x281)|((inportb(0x282)&0x0f)<<8); DData=5-(4095-IData)*10./4096; 2.解码的原理和实现? (文字表述及必要的代码) 答: 12位AD对应-5V到+5V之间的电压变化,产生212=4096个量,所以该12位AD的分辨率为: 10/4096=0.00244140625V。 代码为: outportb(0x280,0x00); while((inportb(0x285)&0x80)==0){ ; } IData=inportb(0x281)|((inportb(0x282)&0x0f)<<8); DData=5-(4095-IData)*10./4096; DData就是实际的电压值。 3.D/A转换的数字量与A/D转换的数字量如何变换? D/A的实现? (文字表述及必要的代码) 答: D/A转换时,把12位A/D转换的结果通过位运算分解为两个8位数字量,通过端口控制D/A输出。 程序如下: if(IData<4095){ IDAData=IData+1; } else{ IDAData=IData; } outportb(0x283,IDAData&0x0ff); outportb(0x284,(IDAData>>8)&0x0f); 4.如何实现实验二和实验三的切换? (文字表述及必要的代码) 答: 通过在设置一个变量ADDA,当ADDA==0时,进行实验二,ADDA==1时,进行实验三。 从而可以实现实验二和实验三的切换。 在Process()函数中插入代码: if(RealTimeShow==1){ outportb(0x280,0x00); do{ ; }while(! (inportb(0x285)&0x80)); IData=inportb(0x281)|((inportb(0x282)&0x0f)<<8);/*A/D*/ DData=5-(4095-IData)*10.0/(pow(2,12)); ADDA=(DealedData-(-5))*pow(2,12)/10;/*D/A*/ outportb(0x283,(ADDA&0x00ff)); outportb(0x284,((ADDA>>8)&0x0f)); } elseif(RealTimeShow==0){ IData=Index%10; DData=50*sin(Index); } 5.程序运行结果截图 实验二串口测试界面实验 实验目的: 1、掌握C++Builder软件的使用方法; 2、学习C++类的使用,以及多线程类的使用; 3、了解串口的工作原理以及基本通信参数配置。 实验条件: PC机,winXP操作系统,C++Builder6.0 程序设计要求 使用C++Builder画出基本的串口测试界面,通过界面上的控制接口实现打开、关闭串口;串口通信参数: 波特率、奇偶校验位、停止位、位数的配置;发送数据的进制选择;实现串口之间的通信。 1、串口不可以被重复打开,错误的时候要实现错误信息的提示; 2、
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言 测量 控制 中的 应用 实验 报告