这是一个FPGA的串口通信上位机软件,要求不高,顶多算是个测试用面板。我觉得这就是个小case。可是实际上这不到1千行的代码,却把我折腾了好长时间。
这个软件的大概内容是接受fpga定时发送的数据,把数据保存在一个文本文档里面就基本OK了。让人蛋疼的是通信的速度,每隔10ms fpga就要发送37字节的数据到上位机,波特率采用115200。数据格式为4位帧头,32位数据,1位帧尾(0xa5)。
按照串口的通信协议来说,至少一个起始位(低电平)8个数据位,一个终止位。照这样算,10ms的时间,满打满算才能接收110字节的。而PC机作为串口上位机,为非实时系统,稍微其他什么程序影响一下,延迟了几个毫秒响应中断,很可能就处理不过来数据的。
为了让串口中断不影响程序的运行,就用CmtScheduleThreadPoolFunction函数插入了一个子线程,然后再子线程里面添加串口中断回调函数(InstallComCallback (COM, LWRS_RECEIVE, 37, 0, ComCallback, callbackdata); )。公司里以前做数据采集都是这样插入子线程来做的。令人头痛的是这次居然要在子线程里面插入中断回调函数。退出时居然不能关闭串口,停在CloseCom函数了。以前都是中断完了,直接退出时关闭串口,没有问题。看了CloseCom的函数说明,确实可以自动取消中断并退出的。后来狂看labwindows的帮助文档,发现了用 InstallComCallback (COM, LWRS_RECEIVE, 0, 0, ComCallback, callbackdata); 可以取消回调函数。这样就可以只在退出时调用CloseCom,终于可以运行正常了。
这样程序差不多可以了,试着跑了一下。发现有很小的几率发生串口不能接收到数据的情况。而且,一旦发生,程序就再也不能接收到数据,除非重启。我想可能是因为,串口回调函数里面的程序还是太多了,有时候运行不过来,毕竟是非实时系统嘛。然后我把串口中断里面的程序尽量精简,只是接收到数后就保存在一个大数组里面,还有一些必须进行的操作,其他的都不要了。并且把接收到37字节产生中断,改成了接收到185字节产生中断(InstallComCallback (COM, LWRS_RECEIVE, 185, 0, ComCallback, callbackdata); )等所有的数据都接收完毕,然后再把数据按照通信协议的格式挑出来,并处理保存在文本文档中。
这样就差不多了(虽然就写了这么点,当时我调了好几天呢)。下面附上部分源代码:
#define COM 1
static int panelHandle,pathSelect;
static void*callbackdata;
void ComCallback(int COMport, int eventMask, void *callbackdata);
int CVICALLBACK Lel1Test (void *functionData);
void printspace(int inputdat) ;
void DataTransfer(void);
static int intWorkStatus=0;
int lv1TestID;
static int intTestTimes=0;
int intTotalTimes;
//unsigned char chSaveData[200000000];
FILE *fp;
static long count = 0;
int intPrintData[2000000][17] ;
//int SavePattern; //0默认路径,1是选择的路径
//char dirSlected[260];
static int status[2000000];
unsigned char buffer[200000000];
//在main函数里面打开的串口
int main (int argc, char *argv[])
{
if (InitCVIRTE (0, argv, 0) == 0)
return -1; /* out of memory */
if ((panelHandle = LoadPanel (0, "bit数据采集.uir", PANEL)) < 0)
return -1;
OpenComConfig(COM,"",115200,0,8,1,5120,5120);
FlushInQ (COM);
DisplayPanel (panelHandle);
RunUserInterface ();
DiscardPanel (panelHandle);
return 0;
}
//子线程只插入了串口回调函数
int CVICALLBACK Lel1Test (void *functionData)
{
//char *time;
char *date;
char Dir[260];
char FileName[260];
char DataPrint[260];
int j;
long i;
InstallComCallback (COM, LWRS_RECEIVE, 185, 0, ComCallback, callbackdata);
FlushInQ (COM);
return 0;
}
//串口中断回调函数
void ComCallback(int COMport,int eventMask,void *callbackdata)
{
int process;
long j;
int num;
//获取输入队列长度,有可能会在大于185,不信你仔细看帮助文档
num= GetInQLen(COM);
//读取输入队列存入缓存中
if(intWorkStatus==1)
{
ComRd(COM,&buffer[count],num);
count=count+num;
intTestTimes=intTestTimes+5;
//控制进度条显示
process = intTestTimes*100/(intTotalTimes);
if(process>100)
{
process=100;
}
SetCtrlAttribute (panelHandle, PANEL_NUMERICSLIDE, ATTR_CTRL_VAL,process);
//获取静/动态
GetCtrlAttribute (panelHandle, PANEL_BINARYSWITCH,
ATTR_CTRL_VAL, &status[intTestTimes]);
if(intTestTimes==intTotalTimes+5)
{
intWorkStatus=3;//工作状态3处于终止状态
intTestTimes=0; //把采集次数置
SetCtrlAttribute (panelHandle, PANEL_TIMER, ATTR_ENABLED, 1);
InstallComCallback (COM, LWRS_RECEIVE, 0, 0, ComCallback, callbackdata);
FlushInQ (COM);
}
}
}
//这个函数太二了,printf函数明明可以控制输出格式的。当时忘了。。
//没办法,在这之前我做单片机编程相对比较多,单片机程序从来不用printf
//这个函数的。。。。。。。
void printspace(int inputdata)
{
if(inputdata<10)
fprintf(fp,"%s"," ");
else if(inputdata<100)
fprintf(fp,"%s"," ");
else if(inputdata<1000)
fprintf(fp,"%s"," ");
else if(inputdata<10000)
fprintf(fp,"%s"," ");
else
fprintf(fp,"%s"," ");
}
//最后数据处理的部分让我放到了一个定时器的回调函数中
int CVICALLBACK timer (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
char *time;
char *date;
char Dir[260];
char FileName[260];
char DataPrint[260];
int SaveDefault;
int SelectStatus;
int j;
long i;
int savePattern;
unsigned char last, current;
int errorNum = 0;
switch (event)
{
case EVENT_TIMER_TICK:
SetCtrlAttribute (panelHandle, PANEL_TIMER, ATTR_ENABLED, 0);
SetCtrlAttribute (panelHandle, PANEL_CHECKBOX, ATTR_DIMMED, 0);
// CloseCom(COM);
SetCtrlAttribute (panelHandle, PANEL_COMMANDBUTTON_PAUSE, ATTR_DIMMED, 1);
SetCtrlAttribute (panelHandle, PANEL_COMMANDBUTTON_STOP, ATTR_DIMMED, 1);
SetCtrlAttribute (panelHandle, PANEL_NUMERICSLIDE, ATTR_DIMMED, 1);
SetCtrlAttribute (panelHandle, PANEL_COMMANDBUTTON_START, ATTR_DIMMED, 0);
SetCtrlAttribute (panelHandle, PANEL_COMMANDBUTTON_START, ATTR_LABEL_TEXT,"开始采集");
SetCtrlAttribute (panelHandle, PANEL_NUMERICSLIDE, ATTR_CTRL_VAL,0);
date="\0";
time="\0";
date=DateStr();
time=TimeStr();
DataTransfer();
GetCtrlVal (panelHandle, PANEL_CHECKBOX, &SaveDefault);
if(SaveDefault==0)
{
SelectStatus=DirSelectPopup("","请选择保存路径",1,1,Dir);
if(SelectStatus)
{
sprintf(FileName,"%s\\DataSaved.txt",Dir);
savePattern=1;
}
else
{
GetProjectDir(Dir);
sprintf(FileName,"%s\\DataSaved.txt",Dir);
savePattern=0;
}
}
else
{
GetProjectDir(Dir);
sprintf(FileName,"%s\\DataSaved.txt",Dir);
savePattern=0;
}
fp = fopen (FileName, "w");
//写入文件我用的fprintf函数,也可以用sprinf先把这些转换成字符串再用fwrite
//函数写入
fprintf(fp,"%s ",time);
fprintf(fp,"%s\n\n",date);
fprintf(fp,"%s","编号 ");
fprintf(fp,"%s","通道1 ");
fprintf(fp,"%s","通道2 ");
fprintf(fp,"%s","通道3 ");
fprintf(fp,"%s","通道4 ");
fprintf(fp,"%s","通道5 ");
fprintf(fp,"%s","通道6 ");
fprintf(fp,"%s","通道7 ");
fprintf(fp,"%s","通道8 ");
fprintf(fp,"%s","通道9 ");
fprintf(fp,"%s","通道10 ");
fprintf(fp,"%s","通道11 ");
fprintf(fp,"%s","通道12 ");
fprintf(fp,"%s","通道13 ");
fprintf(fp,"%s","通道14 ");
fprintf(fp,"%s","通道15 ");
fprintf(fp,"%s","通道16 ");
fprintf(fp,"%s","静/动态 \n");
for(i=0;i
{
fprintf(fp,"%d",i+1);
printspace(i+1);
for(j=0;j<17;j++)
{
fprintf(fp,"%d",intPrintData[j]);
printspace(intPrintData[j]);
}
fprintf(fp,"%s","\n");
}
fclose (fp);
if(savePattern)
MessagePopup("数据采集完毕","测试结果已经成功保存至所选目录");
else
MessagePopup("数据采集完毕","测试结果已经成功保存至默认目录");
break;
}
return 0;
}
//把接收到的数据转换成要保存的数据,2字节数代表一个数据,一共16个数据
void DataTransfer(void)
{
long i;
int j;
int k;
int saveNum=0;
i=0;
for(i=0;i {
//0xaa,0x55,0x80,0x20是串口通讯的帧头,0xa5是帧尾
if(buffer==0xaa&&buffer[i+1]==0x55&&buffer[i+2]==0x80&&
buffer[i+3]==0x20&&buffer[i+36]==0xa5)
{
for(j=0;j<16;j++)
{
intPrintData[saveNum][j] =((int)(buffer[i+2*j+4]))*256+buffer[i+2*j+5];
}
intPrintData[saveNum][16]=status[saveNum];
saveNum++;
if(saveNum==intTotalTimes)
break;
}
}
}
;i++)
;i++)
用户377235 2015-8-25 09:09
用户377235 2015-7-30 16:55
用户1829338 2015-6-30 17:01
用户883410 2013-5-23 17:08
用户394057 2012-8-17 17:53
用户377235 2012-8-16 17:21
用户1431363 2012-7-24 23:53
用户410706 2012-7-23 09:38