您现在的位置: 网站首页 >  新闻资讯 > 技术文章

气体智能涡轮流量计下位机与上位机的串口通信

气体智能涡轮流量计下位机与上位机的串口通信在石油、天然气、煤矿等工业生产实践中,要用流量计对输送管道流体进行流量检测,用PC机与仪表相连进行实时监控和自动控制。通常要求PC机能在用户界面上具有数据采集、数据处理以及控制信号的产生与传输功能。在这种特殊的环境下,PC机要与过程控制的实时信号相联系,就要求能对PC机的串口进行直接操作。工业现场分布着大量的现场仪表,这些仪表通常采用485串行通讯方式向上位机(工控机)发送数据或从上位机接收命令。这些仪表来自不同的厂家,使用不同的协议,但其通讯过程大同小异,主要区别就在于数据帧的结构和数据处理方式不一样。文中以一种气体智能涡轮流量计为例,介绍下位机与上位机的串口通信,并用VC++编程实现。
      1 通讯协议
      通讯协议是进行串口通讯的基础,不同厂家仪表使用不同的协议,因此弄清仪表的通讯协议是首先要做的第一件事。当通讯命令发送至仪表时,符合相应地址码的设备接受通讯命令,并除去地址码,读取信息,如果没有出错,则执行相应的任务;然后把执行结果返送给发送者。返送的信息中包括地址码、执行动作的功能码、执行动作后结果的数据及校验数据。如果出错就不发送任何信息。
      1.1 通信接口
      本例中的气体智能涡轮流量计(定义为下位机)采用RS485串口通信,因此要与上位机进行通信,就必须通过RS-232/RS-485转换模块连接到上位机的串口上,如图1。
      1.2 数据帧结构
      在串口通信中,数据帧通常由以下几个部分组成:
      引导符地址码功能码数据区校验码引导符:仪表厂家不同,使用的引导符也会不同,例如工业通用的MODBUS协议使用“@”为引导符。引导符代表数据帧的开始,在本例中,使用UU(十六进制为55H 55H)为引导符。一般把上位机发送到下位机的帧称为下行帧,而下位机发送给上位机的帧则称为上行帧地址码:每个下位机都具有唯一的地址码,地址码表明由用户设定地址码的下位机将接收由上位机发送来的信息。上位机发送的地址码表明将发送到的下位机地址,而下位机回送的地址码表明该下位机的地址。
      功能码:作为上位机请求发送,通过功能码告诉下位机执行什么动作。作为下位机响应,它发送的功能码与从上位机发送来的功能码一样,并表明下位机已响应上位机进行操作。如果下位机发送的功能码的最高位为1,则表明没有响应操作或发送出错。
      数据区:数据区是根据不同的功能码而有所不同。如果是上位机请求发送,数据区可以是功能码需要的附带参数。如果是下位机响应,数据区可以是实际测量值(例如流量、压力、温度)、设置值、上位机发送给下位机或下位机发送给上位机的地址等。在本例中,数据区由以下两部分组成:
      (1)数据长度Len,1个字节,表示它后面有Len个字节的数据;
      (2)数据,有Len个字节的数据。
      校验码:上位机或下位机可用校验码进行判别接收信息是否出错。有时,由于电子噪声或其它一些干扰信息在传输过程中会发生细微的变化。错误校验码保证了上位机或下位机在传送过程中出错的信息不起作用,这样增加了系统的安全和效率。本例中校验码为1个字节,其数值是从地址码起到校验码之前的全部数据之和的低8位数据。
      本例中数据帧可以表示成如下形式:
      55H,55H,ADDR,CMD,Len,Data(0),Data(1),...Data(Len-1),ChkSum
      ADD地址码,1个字节;CMD功能码,1个字节;Len数据长度,1个字节;ChkSum校验码,1个字节。
      1.3 数据类型
      本通信协议中使用的数据有以下四种类型:字节、字符、字和浮点数:
      (1)字节:8位,十六进制,用B(Byte)来标记;
      (2)字符:8位,ASCII码,表示1个英文字母,用ASC标记;
      (3)字:16位,十六进制,2个字节,低字节在前,高字节在后,用w(Word)表示;
      (4)浮点数:32位,4个字节,用F(Float)表示。
表2 数据区代码说明 
代码
数据区的变量
[2-0]
DevStatus(B,流量计状态码),DevErr(B,流量计故障码)
[2-1]
null(B,保留),upassword(W,用户密码),mpassword (W,厂家密码)
[2-2]
Qm(F,工况流量),Qo(F,标况流量),t(F,温度),P(F,压力),slim(F,累积量)
      浮点数通常用来表示现场测量参数值,要在上位机上显示出来,还需要经过转换。在本例中浮点数占4个字节,依次为P,SMH,MM,ML,其中:P为阶码,1个字节,以十六进制补码的形式表示;
      SMH为尾数的高字节,1个字节,最高位(第7位)为符号位S,S=1表示数据为负,S=0则数据为正;其余7位为浮点数尾数的高7位,第0到6位;
      MM为尾数的中间字节,1个字节,第7到14位;
      ML为尾数的低字节,1个字节,第15到23位;
      转换成单精度二进制浮点数为:FloatData=±0.MH-MM-ML*2p。
      2 串口通讯程序
      2.1 初始化串口
      在VC++++中,对串口操作与对文件操作一样,所以可以从CFile派生新类CComPort,创建CComPort类的成员函数OpenPort()函数,用打开文件的类似方法打开并初始化串口。应该注意的是,在打开串口之前,要检查一下串口是否已经打开。如果串口已经打开,必须先关闭,然后再重新打开。
      通信会话从调用CreateFile开始。CreateFile以读、写或读写方式打开串口。按照Windows惯例,CreateFile返回一个句柄,以后对串口的操作都是通过此句柄。
      打开串口:
      LPCTSTR lpPort=“COM1”:
      HANDLE hPort=::CreateFile(lpPort,//打开COM1口 
      GENERIC_READIGENERIC_WRITE,//端口访问类型为可读可写
      0,//comm devices must be opened w/exclusive-access
      NULL,//no security attributes
      OPEN_EXISTING,//comm devices must use OPEN-EXISTING
      0,//not overlapped I/O
      NULL//hTemplate must be NULL for comm devices);
      配置串口:
      定义设备控制块(Device Control Block,即DCB)
      DCB dcb;
      GetCommState(hPon,&dcb)
      dcb.BaudRate=CBR_1200;//波特率设为1200
      dcb.ByteSize=8;//端口使用的数据位数
      dcb.Parity=NOPARITY;//无奇偶校验位
      dcb.StopBits=ONESTOPBIT;//一个停止位
      if(!SetCommState(hPort,&dcb))
            {
                  return GetLastError();
            }
      调用GetCommState(hPort,&dcb)读取当前串口的DCB设置
      调用SetCommState(hPort,&dcb)函数把修改后的串口设置写入
      2.2 发送通讯命令
      可以根据通讯命令简表及数据帧结构,向流量计发送需要的命令。例如要读取流量计的测量参数,那么上位机向流量计发送的数据帧就是m_csOrder=“55 55 17 02 00 19”,注意这里的数据帧是字符串不是十六进制数。
      因此要把字符串转化为十六进制数并放人输出缓冲区中,可以这样实现
      char lpOrder[50];
      MEMSet(lpOrder,0,sizeof(lpOrder));
      while(m_csOrder.GetLength()>0)
            {
                  CString csTmp=m_csOrder.Left(2);
                  lpData[i++]=(char)strtoul(csTmp.GetBufer(csTmp.GetLe-ngth()),NULL,16);
                  m_csOrder=m_csOrder.Right(m_esOrder.GetLength()-3);
                  m_csOrder.TrimLeft();
            }
      数据缓冲区里有了数据,而且串口已经打开,就可以往串口写人数据了
      WriteFile(Handle hPort,(LPCVOID)lpOrder,dwDataLen,&dwDataSend,NULL)
      在本例中,因发送是可以控制的,但接收信号的到来是不确定的。所以在接收到任何信息的时候,Windows都应通知。这就需要采用事件驱动I/O方式,由Windows在某些信号改变后发出通知。为了添加或修改Windows所报告的事件列表,可以使用函数
      SetCommMask(hPort,EV_TXEMPTY|EV_RXCHAR)
      EV_TXEMPTY表示输出缓冲区中的最后一个字节,并发出去;
      EV_RXCHAR表示接收到一个字节,并放人输入缓冲区中。
      在用SetCommMask指定了有用的事件后,应用程序就调用WaitCommEvent(hcomm,&dwEvent,&overlapped)来等待事件发生。
      2.3 接收流量计响应数据
      当EV_RXCHAR事件发生,即有数据返回的时候,就可以从数据输入缓冲区中读取返回的数据了。对于不同的通讯命令,返回的数据长度及数值都不一样。就本例来讲,因为发送的功能码是2,由通讯命令简表可知道,返回的数据块长度为22。
      char buf[255];
      memset(buf,0,sizeof(buf));
      ReadFile (hPort,(LPVOID)buf,nDataLen,&dwDataRead,NULL)
      读取到的数据放到buf数组中,hPort为前面打开串口的句柄,nDataLen指定要取到的数据的长度,以字节为单位,dwDataRead是实际返回的数据的长度,以字节为单位。
      Buf中存储的数据,根据数据区代码说明表(表2)可知,数据中的信息包括五个流量参数,即工况流量、标况流量、温度、压力、累计量。但是数据类型为字符型,四个字节表示一个流量参数,所以需要把每四个字符的信息转化为浮点数。
      2.4 流量计数据处理
      在2.3节中已知浮点数为四个字节,依次为P,SMH,MM,ML,用F(Float)表示;其计算公式为FloatData=±0.MH-MM-ML*2p。由这个公式就可以把buf里存储的每四个字符数据转化为一个浮点数,并在用户界面上显示出来,至此通讯过程就算结束了。
      程序如下:
      CalcFloatValue(char bur[])
            {
                  int nHighBit=buf[0]&128;
                  int p;//阶码
                        if(nHighBit=0)
                        P=buf[0];
            else
            {
                  P=-(~buf[0]+1);
            }
            nHighBit=buf[1]&128;
            double p1,p2,p3;
            p1=0;
            p2=0;
            p3=0;
            p1=(bur[1]&127)*0.1;
            p2=buf[2]*0.01;
            p3=buf[3]*0.001;
            double dValue=pow(2,p);
            dValue= dValue*(p1+p2+p3);
            if(nHighBit!=0)
            dValue=-dValue ;
            return dValue;
      介绍了一种涡轮流量计的通讯协议,并用VC++++实现了其与上位机的串口通讯。以上程序已在油田多个天然气输气站上投入运行三个月,系统稳定可靠,计算精度高,运行效果良好。对其他工业仪表与上位机的串口通讯具有一定的借鉴意义。
点击次数:  更新时间:2017-06-03 18:05:01  【打印此页】  【关闭