原创 用labwindows做过的一个串口上位机程序

2012-7-18 00:15 7446 13 19 分类: 测试测量

 

    这是一个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++)

 

 

 

     

PARTNER CONTENT

文章评论6条评论)

登录后参与讨论

用户377235 2015-8-25 09:09

To disable callbacks for a port, pass 0 for the eventMask and/or callbackFunction parameters.

用户377235 2015-7-30 16:55

你好!我是个labwindows新手,最近也是想编一个功能类似的程序,看了你的文章很有启发,但是还是有些地方没明白,感觉少了一些东西?冒昧的问一下不知道能不能给我发份完整的程序学习一下。。非商业用途,只是学习的。如果没少不知道能不能加一下qq请教下!

用户1829338 2015-6-30 17:01

点赞!!!

用户883410 2013-5-23 17:08

大师,真的很牛·!~!~困扰我多个星期的问题似乎这就是答案,能不能留下一个QQ什么之类的啊~~!

用户394057 2012-8-17 17:53

片子已经出去了,就没有在修改了,感觉我们验证思想还要加强,主要是现在的仿真平台随机化不够。

用户377235 2012-8-16 17:21

修下ECO就好了吧

用户1431363 2012-7-24 23:53

见好就收,这是我最本能的反应,谢谢你阿松!!!

用户410706 2012-7-23 09:38

好东西,自己收藏了,谢谢阿松
相关推荐阅读
用户421976 2012-11-18 20:09
AT91SAM7X256学习笔记(1)
         好久没有写点什么了,前段时间工作比较忙,就犯懒了。写点有关atmel 的arm7芯片的东西吧,很简单很基础的。       开发环境用的是keil mdk 4.23  H...
用户421976 2012-08-30 23:09
搞定单片机多字节串口接收
             工作了一年多,写了不少单片机串口程序。感觉串口多字节接收部分的逻辑相对于配置寄存器跟串口回复来说,是有点难度的——寄存器配置基本上都是死的,串口回复多字节跟回...
用户421976 2012-08-03 23:51
让低端FPGA也能跑nios
      第一次接触嵌入软核是公司一个项目中通信用422串口通信。其他的电路板的控制都是单片机,都好办。可是有一块板的控制虽然不太复杂,但必须用FPGA(并行处理能力单片机没法比啊),在通信...
用户421976 2012-06-30 03:35
工作了满一年了
这是我在电子行业从业的第一年。这一年里做的东西真的挺多,挺杂的。 我工作的单位规模并不是很大。研发人员不是很多,这导致我很多方面的技术都接触过。 刚开始做51单片机程序。做电路,之后做过c...
用户421976 2012-04-21 00:55
串行AD芯片ADC12S021的时序问题
  ADC124S021 4 Channel, 50 ksps to 200 ksps, 12-Bit A/D Converter   在接触ADC124S021这个芯片以前,我一...
我要评论
6
13
关闭 站长推荐上一条 /3 下一条