HART命令帧格式
- 格式:doc
- 大小:600.50 KB
- 文档页数:26
HART 协议1.HART 协议概况HART 协议是美国Rosemount 公司于1986年提出的一项标准通讯协议,其全称为Highway Addressable Remote Transducer,即可寻址远程传感器数据公路。
就现场总线的意义上说,HART 协议的特点是具有与现场总线类似的体系结构,具有总线式的数字通讯,但HART 并不是真正的现场总线,它只是现场总线的雏形,是从模拟控制系统向现场总线过渡的一块踏脚石。
所谓“过渡”,是指它在4~20mA模拟信号上叠加FSK(Frequency Shift Keying,频移键控,详见第二节)数字信号,既可进行4~20mA 模拟信号的传输,又能进行数字通讯,同时,数字通讯信号不影响模拟信号的传输。
显然,这是一项4~20mA模拟信号与数字通讯相兼容的标准。
作为一个开放性协议,经过十多年的发展,HART 协议已成为智能仪表事实上的工业标准。
HART 通讯协议参照“ISO/OSI”的模型标准,简化并引用其中的1,2,7三层制定而成,即:物理层,数据链路层和应用层,对应关系如下:2.HART 协议的物理层本节主要描述了HART 协议的物理层的信号模式和传输介质等,这些都是与OSI 协议参考模型的物理层的规定相一致的。
(1)FSK 频移键控HART 协议采用了Bell202 标准的FSK 频移键控信号。
它在4~20mA的模拟信号上叠加幅度为0.5mA的正弦调制波,1200Hz 代表逻辑“1”,2200Hz代表逻辑“0”,如下图所示。
由于所叠加的正弦信号平均值为0,所以不会影响4-20mA的输出电流。
因此,模拟仪表在数字通讯时仍可以照常工作,这是HART标准的重要优点之一。
二进制数据传送的速率为1200波特。
这就意味着“1”由1200Hz 的一个周期表示,而“0”大约由2200Hz 的两个周期表示。
信号频率和传送速率的选择依据美国Bell202标准,该标准用于在电话网上传送数字信息。
HART通信协议VER 一.概述HART(Highway Addressable Remote Transducer)协议采用基于Bell202标准的FSK 频移键控信号,在低频的4-20mA模拟信号上叠加幅度为的音频数字信号进行双向数字通讯,数据传输率为1200bps。
由于FSK信号的平均值为0,不影响传送给控制系统模拟信号的大小,保证了与现有模拟系统的兼容性。
在HART协议通信中主要的变量和控制信息由4-20mA 传送,在需要的情况下,另外的测量、过程参数、设备组态、校准、诊断信息通过HART协议访问。
在应用层,HART 规定了一系列命令,按命令方式工作。
它有三类命令,第一类称为通用命令,这是所有设备都理解、执行的命令;第二类称为普通应用命令,所提供的功能可以在许多现场设备(尽管不是全部)中实现;第三类称为设备专用命令,以便于工作在某些设备中实现特殊功能,这类命令既可以在基金会中开放使用,又可以为开发此命令的公司所独有。
二.含义1、主机(Master):分为第一主机和第二主机,它能连接在HART网络上,发出命令与从机通讯;2、从机(Field Device):连接在过程控制现场中,具有测量、计算多种变量功能,并能与主机进行HART通讯的设备;3、长帧(Long Frame):地址Address由5字节组成的HART数据帧,所有的HART命令都支持长帧数据帧。
长帧地址各字节含义如下图所示:制造商ID 号(Manufacturer ID )由HART 基金会分配;在生产中,同类型设备序列号唯一,并且永不重复。
广播地址使用长帧结构,低38 BIT 全为0。
厂商指定的设备类型(Decive Type )与制造商ID 满足下表要求;设备类型代码指定原则4、 短帧(Short Frame ):地址Address 由1字节组成的HART 数据帧,短帧只有通用命令Command 0能够使用,短帧字节含义如下:所有的HART 从机都支持长、短帧。
HART 通信协议VER 1.6 一.概述HART ( Highway Addressable Remote Transducer )协议采用基于Bell202 标准的FSK 频移键控信号,在低频的4-20mA 模拟信号上叠加幅度为0.5mA 的音频数字信号进行双向数字通讯,数据传输率为1200bps。
由于FSK信号的平均值为0,不影响传送给控制系统模拟信号的大小,保证了与现有模拟系统的兼容性。
在HART 协议通信中主要的变量和控制信息由4-20mA 传送,在需要的情况下,另外的测量、过程参数、设备组态、校准、诊断信息通过HART 协议访问。
在应用层,HART 规定了一系列命令,按命令方式工作。
它有三类命令,第一类称为通用命令,这是所有设备都理解、执行的命令;第二类称为普通应用命令,所提供的功能可以在许多现场设备(尽管不是全部)中实现;第三类称为设备专用命令,以便于工作在某些设备中实现特殊功能,这类命令既可以在基金会中开放使用,又可以为开发此命令的公司所独有。
二.含义1、主机( Master ):分为第一主机和第二主机,它能连接在HART 网络上,发出命令与从机通讯;2、从机( Field Device ):连接在过程控制现场中,具有测量、计算多种变量功能,并能与主机进行HART 通讯的设备;3、长帧( Long Frame ):地址Address 由5 字节组成的HART 数据帧,所有的HART 命令都支持长帧数据帧。
长帧地址各字节含义如下图所示:Q Secondary MasteiZ1 Primary Master制造商ID号(Manufacturer ID )由HART基金会分配;在生产中,同类型设备序列号唯一,并且永不重复。
广播地址使用长帧结构,低38 BIT全为0。
厂商指定的设备类型(Decive Type)与制造商ID满足下表要求;4、短帧(Short Frame ):地址Address由1字节组成的HART数据帧,短帧只有通用命令Comma nd 0能够使用,短帧字节含义如下:所有的HART从机都支持长、短帧。
HART数据格式解析2014-10-141)前导码FF :5~20个字节2)定界符:只有1字节。
此字段的最高位决定该帧是长型帧还是短型帧;低三位表示了不同的帧的类型,其余位保留。
3)地址:①长帧格式地址:共5个字节(40位)。
长帧格式地址建立在设备唯一标识符的基础上,设备唯一标识符标志了每个从设备或现场仪表。
它分为三个部分:(1). 第一字节的最高位是主设备地址位,该位为1时表示基本主设备,为0时表示副主设备。
(2). 第一字节的第六位是成组模式设备,该位为1时,表示从设备处于成组模式,为0时表示从设备没有处于成组模式。
(3). 长地址的其他部分是设备的惟一标识符,共38位。
均为0表示广播地址。
②短帧格式地址:只有1字节。
为保证向后兼容性,HART协议规定只有0号通用命令支持短帧格式。
它使协议可动态地将链路上每个设备与一个短帧地址相关联。
短帧地址可在链路初始化时使用,以便快速检测链路地址空间。
短帧地址分四部分:最高位是主设备地址: 该位为1时表示主设备,为0时表示副主设备;第六位为成组模式设备位:为1时表示从设备处于成组模式,为0时,表示从设备没处于成组模式;第四、五位必须置为0;最低四位表示巡检地址。
4)命令字节:为1字节它表明该帧所封装的HART命令,命令字节值在从设备响应中原封不动地返回。
5)字节计数:为1字节它表明此字节与最后的帧校验字节之间的数据字节个数。
用于识别帧的结束。
字节长度范围应该是在0-27。
6)数据域:由整数字节的用户数据构成,长度为0-25字节7)校验字节:1个字节,一般为除开前导码和校验字节之外所有字节的异或之和8)响应码:2字节,只在从-主帧中出现。
响应码第一字节指明通信状态。
第二字节表示现场设备工作状态。
该帧数据只有在响应码第一字节的位7(b7)为0时才有效。
①响应码的第一个字节的第7位如果置位,则剩余的位包含了有关通信错误的信息;第一个字节的第7位如果复位,则剩余的位用来表示命令响应信息。
HART通信协议一、引言HART(Highway Addressable Remote Transducer)通信协议是一种用于工业自动化领域的数字通信协议,旨在实现智能仪表与控制系统之间的双向通信。
本协议旨在确保HART设备之间的互操作性,并提供一致的通信标准。
二、范围本协议适合于使用HART通信协议的仪表和控制系统,包括但不限于以下应用领域:1. 工业过程控制系统中的传感器和执行器;2. 监测和控制系统中的仪表设备;3. 数据采集和监控系统中的仪表设备。
三、术语和定义以下术语和定义适合于本协议:1. HART主站:指能够发送和接收HART通信协议消息的设备,通常是控制系统或者监测系统中的中央处理单元。
2. HART从站:指能够响应HART通信协议消息的设备,通常是工业仪表设备。
3. HART通信:指通过HART通信协议进行的双向数字通信。
4. HART命令:指HART通信协议中用于发送和接收数据的指令。
5. HART变量:指通过HART通信协议传输的数据或者参数。
四、通信协议规范1. 物理层规范a. HART通信协议使用标准的4-20mA摹拟电流回路进行通信。
b. 通信电缆应符合工业标准,以确保信号传输的可靠性和稳定性。
c. 通信距离应根据具体应用需求进行设计和配置。
2. 数据帧格式a. HART通信协议使用数据帧进行信息传输。
b. 数据帧由起始字符、命令字节、校验字节和住手字符组成。
c. 起始字符和住手字符用于标识数据帧的开始和结束。
d. 命令字节用于指示数据帧的类型和目的。
e. 校验字节用于验证数据帧的完整性和准确性。
3. HART命令集a. HART通信协议定义了一套标准的HART命令集,用于实现不同类型的数据传输和设备配置。
b. HART命令集包括读取变量、写入变量、配置设备等功能。
c. HART从站应支持协议规定的必要命令,并根据具体设备的功能需求实现相应的扩展命令。
4. HART变量定义a. HART通信协议定义了一套标准的HART变量,用于描述和传输不同类型的数据。
HART通信协议VER1。
6一.概述HART(Highway AddressableRemoteTransducer)协议采用基于Bell202标准得FSK频移键控信号,在低频得4—20mA模拟信号上叠加幅度为0。
5mA得音频数字信号进行双向数字通讯,数据传输率为1200bps、由于FSK信号得平均值为0,不影响传送给控制系统模拟信号得大小,保证了与现有模拟系统得兼容性。
在HART协议通信中主要得变量与控制信息由4-20mA传送,在需要得情况下,另外得测量、过程参数、设备组态、校准、诊断信息通过HART协议访问。
在应用层,HART 规定了一系列命令,按命令方式工作。
它有三类命令,第一类称为通用命令,这就是所有设备都理解、执行得命令;第二类称为普通应用命令,所提供得功能可以在许多现场设备(尽管不就是全部)中实现;第三类称为设备专用命令,以便于工作在某些设备中实现特殊功能,这类命令既可以在基金会中开放使用,又可以为开发此命令得公司所独有、二、含义1、主机(Master):分为第一主机与第二主机,它能连接在HART网络上,发出命令与从机通讯;2、从机(Field Device):连接在过程控制现场中,具有测量、计算多种变量功能,并能与主机进行HART通讯得设备;3、长帧(LongFrame):地址Address由5字节组成得HART数据帧,所有得HART命令都支持长帧数据帧。
长帧地址各字节含义如下图所示:制造商ID号(Manufacturer ID)由HART基金会分配;在生产中,同类型设备序列号唯一,并且永不重复。
广播地址使用长帧结构,低38 BIT全为0。
厂商指定得设备类型(DeciveType)与制造商ID满足下表要求;设备类型代码指定原则4、短帧(ShortFrame):地址Address由1字节组成得HART数据帧,短帧只有通用命令Command0能够使用,短帧字节含义如下:所有得HART从机都支持长、短帧、5、请求帧(STX):由主机(如手抄器)向从机(现场设备)发出请求数据命令;6、应答帧(ACK):从机回复给主机得数据;7、前导符(Preamble):发起命令(数据)传输得设备在传输数据帧前发送得0xFF字符,发送该字符得数量一般为5—20个,该数量可以通过命令来改变;前导符得使用就是为了使数据接收端在硬件电路上产生CD载波检测信号,以实现数据通讯得同步。
前段时间做了一部分有线HART的解析,整理了一下基本的帧结构,在此做个笔记HART帧结构:[cpp]view plain copy1.|-------------------------------------------------------------------|2.| PREAMBLE[5..20] | START | ADDR | COM | BCNT | STATUS | DATA | CHK |3.|-------------------------------------------------------------------|4.5.6.FF FF FF FF FF 82 A6 06 B2 BF 01 0F 00 211. PREAMBLE引导码, 一般是5..20个0xFF, 他是一组同步传输的同步信号, 用以保证信息的同步.在开始通讯的时候,使用的是20个FF引导码, 从机应答0信号时将告之主机他“希望”接收几个字节的引导码, 另外主机也可以用59号命令告诉从机应答时应用几位引导码.2. START(1Byte)起始字节, 说明结构为“长”还是“短”, 消息源, 是否是“突发”模式消息.[cpp]view plain copy1.0x02: 主机到从机的短帧2.0x82: 主机到从机的长帧3.0x06:从机到主机的短帧4.0x86: 从机到主机的长帧5.0x01: 突发模式的短帧6.0x81: 突发模式的长帧一般设备进行通讯接收到2个FF字节后, 就表示数据位的接收已经同步, 就将侦听起始位.3. ADDR(1/5Bytes)地址字节, 他包含了主机地址和从机地址, 短结构中占1字节, 长结构中占5字节.不论长短帧结构, HART协议中允许2个主机存在, 所以我们用首字节的最高位来进行区分,值为1表示第一主机地址, 第二主机用0表示.“突发”模式是特例, 0,1值将交替出现, 也就是说, 在该模式下, 赋予2个主机的机会均等. 次高位为1表示为“突发”模式, 短结构用首字节的0~4位表示值为0~15的从机地址, 第5,6位赋0.长结构用后6位表示从机的生产厂商的代码, 第2个字节表示从机设备型号代码,后3~5个字节表示从机的设备序列号, 构成“唯一”标志码.MA: 主机地址BM: 突发模式0 0SA 从 SA机SA 地 SA 址短帧地址结构另外,长结构的低38位如果都是0的话表示的是广播地址,即消息发送给所有的设备。
HART 协议是由Rosemount (罗斯蒙特)
公司提出的一种用于现场智能仪表和控制室设备之间的通信协议,它是4~20mA 模拟信号向全数字信号过渡的一种协议,在4~20mA 标准的模拟信号上叠加一个数字信号,原模拟信号仍然有效,且数字信号和模拟信号互不影响。
HART 协议的有线部分参照了ISO 的OSI 七层模型中的标准物理层、数据链路层和应用层这三层。
HART 协议有线部分的物理层规定了信号的传输方法和传输媒介,它采用基于Bell 202标准的频移键控技术(FSK)来把数字信号叠加到4~20mA 电流回路中。
它用1200Hz 的正弦波代表逻辑1,用2200Hz 的正弦波代表逻辑0,电流峰峰值为正负0.5mA 。
如下图所示:
+0.5 mA
–0.5 mA
0 mA
20 mA
4 mA
A 啊
HART 协议有线部分的数据链路层规定了HART 有线协议帧的格式。
请求帧和响应帧格式如下所示:
图1 HART 帧格式
HART 协议的应用层包括三个类别的HART 命令,用HART 命令来操作数据,包括通用命
令、常用命令和特殊命令,目前最新版本HART协议的各类命令的命令号范围如下表所示:
文章转载自沈阳中科博微自动化技术有限公司工业通讯大课堂。
using System;using System.Collections.Generic;using System.Data;using System.Data.SqlClient;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows.Forms;//***************HART协议说明***********************************//////HART命令查询帧格式:0xFF+0xFF+定界符+地址+数据字节数+数据+校验和//1、前导符:2-20字节,用于与接收器之间的同步//首次开始通信或数据帧重发时,使用20字节前导码,前导码都是0xFF。
//2、定界符:1字节,短帧:01,02,06对应成组、主从、从主三种模式,长帧时将短帧最高位置1即可//3、长帧和短帧区别在于地址的字节数不同,短帧地址为1字节,用于初始化时命令0查询设备标志、ID等信息//长帧地址:5字节(40位),建立在设备唯一标识符的基础上//唯一标识符分为三个部分://第一字节最高位,1表示基本主设备,0表示副主设备//第一字节第六位(次高位),1表示处于成组模式,0表示没有成组模式//长地址的其它部分是设备的唯一标识符,共38位,均为0表示广播地址//二.二短帧地址:1字节//最高位1表示主设备,0表示副主设备//第6位1表示处于成组模式,0表示没有处于成组模式//第4、5位必须置0//最低四位表示巡检地址,对应设备巡检地址0-15//4、命令字节:1字节,表明该帧所封装的HART命令,命令字节值在从设备响应中原值返回//5、字节奇数:1字节,表明此字节与最后帧校验字节之间的数据字节个数,用于识别帧的结束,范围0-27//6、数据域:由整数字节的用户数据组成,长度为0-25字节//在主-从帧中,数据域存放用户对设备的请求数据//在从-主帧中,数据用于存放设备对用户的响应数据//7、校验字节:1字节,对不包括前导符和该字节的帧中所有字节进行水平校验(异或)的结果//垂直校验时通信过程中硬件自动对每个字节所有位进行奇偶校验后产生的结果//水平校验时所有字节依次按位进行异或运算后的结果//8、响应码:只在从帧中出现//响应码第一字节指明通信状态,第二字节表示现场设备工作状态//该帧数据只有在响应码第一字节最高位为0时有效,1表示通信错误namespace Hart{public partial class from1 : Form{//HART对应串口初始化Private SerialPort serial_hart = new SerialPort();Private void portInit(){serial_hart.PortName = hart[0]["port"].ToString();serial_hart.BaudRate = Convert.ToInt32(hart[0]["baudrate"]);serial_hart.ReceivedBytesThreshold = 1;serial_hart.Parity = Parity.Odd;try{serial_hart.Open();int n = serial_hart.BytesToRead; //串口缓存区待读取数据个数byte[] buffirstclear = new byte[n];serial_hart.Read(buffirstclear, 0, n);//将串口缓存区之前的数据清理掉,防止过量非预期数据进入解析环节buffirstclear = new byte[1]; //释放变量空间}catch (Exception ex){if (ex.ToString() != ex1){ex1 = ex.ToString();MessageBox.Show(ex.Message);}}}#region 设备信息private int manufacID = 18; //制造商IDprivate int devType = 8; //制造商设备类型private int sfVersion = 1; //软件版本号private int hwVersion = 0; //硬件版本号private int[] devID = new int[3]; //设备IDprivate int cmdVersion = 5; //通用命令文档版本号private int devIDResult = 0; //设备ID计算结果#endregion#region 主变量(PV)private int PVUnitCd = 0; //主变量代码private float PVValue = 0; //主变量private float PVCurrent = 0; //主变量电流private float PVPercentRange = 0; //主变量量程百分比private int PVSensNum = 0; //主变量传感器编号private int PVSensUnitCd = 0; //传感器极限、最小精度单位代码private float PVUpSensLmt = 0; //传感器上限private float PVLowSensLmt = 0; //传感器下限private float PVMiniSpan = 0; //主变量最小精度private int PVAlarmSelCd = 0; //主变量报警选择代码private int PVTransFuncCd = 0; //主变量传递功能代码private int PVRangeUnitCd = 0; //主变量上下量程单位代码private float PVUpRangeValue = 0; //主变量上限值private float PVLowRangValue = 0;//主变量下限值private float PVDampValue = 0; //主变量阻尼值,单位秒private int WriteProtectCd = 251; //写保护代码private int PLDCd = 18; //商标发行商代码,18-ABB公司#endregion#region 第二...第四变量private int SecVUnitCd = 0; //第二变量代码private float SecVValue = 0; //第二变量值private int ThirdVUnitCd = 0; //第三变量代码private float ThirdVValue = 0; //第三变量值private int ForthVUnitCd = 0; //第四变量代码private float ForthVValue = 0; //第四变量值#endregion#region 标签、描述符、日期(日月年)private string hartTag = ""; //标签private string hartDspt = ""; //描述private string hartDate = ""; //日期#endregion#region 通用命令帧头private byte[] cmdHead = new byte[14]; //帧头包含导引符、定界符、地址private int cmdHeadInitFlag = 0; //帧头初始化标志#endregion#region 常量表、动态变量表public DataTable hartConstant = new DataTable();public DataTable hartDynamic = new DataTable();#endregionprivate int IDHartConstant = 0; //常量表IDprivate int IDHartDynamic = 0; //动态变量表IDprivate string errorstr = ""; //错误信息字符串private string hartMsg = ""; //读设备消息返回结果public string[] HartParaShow = new string[6];private int FinalAssembleNum = 0; //最终装配号private int[] hartGetCmdOk = new int[9]; //9个通用查询命令,查询成功后对应单元数置1#region 常量表初始化private void HartConstantInit()hartConstant.Columns.Add("ID", typeof(int));hartConstant.Columns.Add("Time", typeof(string));hartConstant.Columns.Add("制造商ID", typeof(int));hartConstant.Columns.Add("制造商设备类型", typeof(int));hartConstant.Columns.Add("通用命令文档版本号", typeof(int));hartConstant.Columns.Add("设备软件版本号", typeof(int));hartConstant.Columns.Add("设备硬件版本号", typeof(int));hartConstant.Columns.Add("设备ID号", typeof(int));hartConstant.Columns.Add("设备消息", typeof(string));hartConstant.Columns.Add("标签Tag", typeof(string));hartConstant.Columns.Add("描述符", typeof(string));hartConstant.Columns.Add("日期", typeof(string));hartConstant.Columns.Add("传感器序列号", typeof(int));hartConstant.Columns.Add("传感器上下限单位代码", typeof(int));hartConstant.Columns.Add("传感器上限", typeof(float));hartConstant.Columns.Add("传感器下限", typeof(float));hartConstant.Columns.Add("主变量最小精度", typeof(float));hartConstant.Columns.Add("报警选择代码", typeof(int));hartConstant.Columns.Add("传递功能代码", typeof(int));hartConstant.Columns.Add("上下量程值代码", typeof(int));hartConstant.Columns.Add("主变量上限值", typeof(float));hartConstant.Columns.Add("主变量下限值", typeof(float));hartConstant.Columns.Add("主变量阻尼值", typeof(float));hartConstant.Columns.Add("写保护代码", typeof(int));hartConstant.Columns.Add("最终装配号", typeof(int));}#endregion#region 动态变量表初始化private void HartDynamicInit(){hartDynamic.Columns.Add("ID", typeof(int));hartDynamic.Columns.Add("Time", typeof(string));hartDynamic.Columns.Add("设备ID号", typeof(int));hartDynamic.Columns.Add("主变量单位代码", typeof(int));hartDynamic.Columns.Add("主变量值", typeof(float));hartDynamic.Columns.Add("主变量电流", typeof(float));hartDynamic.Columns.Add("主变量量程百分比", typeof(float));hartDynamic.Columns.Add("第二变量单位代码", typeof(int));hartDynamic.Columns.Add("第二变量值", typeof(int));hartDynamic.Columns.Add("第三变量单位代码", typeof(int));hartDynamic.Columns.Add("第三变量值", typeof(int));hartDynamic.Columns.Add("第四变量单位代码", typeof(int));hartDynamic.Columns.Add("第四变量值", typeof(int));#endregion#region 发送命令0(读标识码)查询请求private void hartSendCmd0() //发送命令0(读标识码)查询请求{if (hartopenflag == 1){byte[] cmd = new byte[13];for (int i = 0; i < 8; i++){cmd[i] = 0xff; //第一次通信前导符为20个0xFF}cmd[8] = 0x02; //短帧,主从模式cmd[9] = 0x80; //主设备cmd[10] = 0x00; //命令0cmd[11] = 0x00; //数据长度0cmd[12] = Convert.ToByte( cmd[8] ^ cmd[9] ^ cmd[10] ^ cmd[11]); //水平校验serial_hart.Write(cmd, 0, 13);}}#endregion#region 发送无请求数据的长帧通用方法private void hartLongFrameCmdsend(byte cmdn) //发送无请求数据的长帧通用方法{byte[] cmd = new byte[17];if (cmdHeadInitFlag == 1) //获取到设备信息后{for (int i = 0; i < 14; i++){cmd[i] = cmdHead[i]; //帧头赋值}cmd[14] = 1; //命令1cmd[15] = cmdn; //无请求数据cmd[16] = cmd[8];for (int i = 9; i < 16; i++){cmd[16] = Convert.ToByte(cmd[16] ^ cmd[i]); //校验和}serial_hart.Write(cmd, 0, 17); //发送请求}}#endregion#region 无请求数据的长地址查询private void hartSendCmd1() //发送通用命令1(读主变量)查询请求{hartLongFrameCmdsend(1);}private void hartSendCmd2() //发送通用命令2(读主变量电流值和百分比)查询请求{hartLongFrameCmdsend(2);}private void hartSendCmd3() //发送通用命令3(读动态变量和主变量电流)查询请求{hartLongFrameCmdsend(3);}private void hartSendCmd12() //发送通用命令12(读消息)查询请求{hartLongFrameCmdsend(12);}private void hartSendCmd13() //发送通用命令13(读标签Tag)查询请求{hartLongFrameCmdsend(13);}private void hartSendCmd14() //发送通用命令14(读主变量传感器信息)查询请求{hartLongFrameCmdsend(14);}private void hartSendCmd15() //发送通用命令15(读主变量输出信息)查询请求{hartLongFrameCmdsend(15);}private void hartSendCmd16() //发送通用命令16(读最终装配号)查询请求{hartLongFrameCmdsend(16);}private void hartSendCmd48() //发送常用命令48(读附加器件状态)查询请求{hartLongFrameCmdsend(48);}#endregion#region 命令0返回值解析(标识码)private void hartGetCmd0() //获取命令0设备返回的数据并解析{//预期接收到设备返回的数据为://FF FF FF FF FF FF FF FF 06 80 00 0E 00 00 FE 12 08 08 05 00 01 00或01 00 00 设备ID高字节设备ID低字节int n = serial_hart.BytesToRead;int begin = 0;byte[] buf;if (n >24){buf = new byte[n];serial_hart.Read(buf, 0, n);for (int i = 0; i < 9; i++){if ((buf[i] == 0x06) & (buf[i + 1] == 0x80) & (buf[i + 2] == 0x0) &(buf[i + 3] == 12)){begin = i;if ((buf[i + 4] == 0) & (buf[i + 5] == 0)){manufacID = buf[i + 7];devType = buf[i + 8];cmdVersion = buf[i + 10];sfVersion = buf[i + 12];hwVersion = buf[i + 13];devID[0] = buf[i + 15];devID[1] = buf[i + 16];devID[2] = buf[i + 17];devIDResult = devID[1] * 256 + devID[2];hartGetCmdOk[0] = 1; //返回值接收成功标志置1cmdHeadInit(); //初始化命令帧头}else{errorstr = "数据长度有误!";pictureBox_Normal.Image = Properties.Resources.异常;label_Right.Text = "异常";label_Hint.Text = "Hart通用命令0" + errorstr;hartSendCmd0(); //未能获取到设备信息则重新发送请求}}}}}#endregion#region 数据解析查找帧头相关变量private int headbegin = 0; //分界符位置private int headFindFlag = 0; //找到分解符标志#endregion#region 判断返回值是否正确,并找到帧头(分界符位置)//判断帧头(定界符)位置private void findFrameHead(byte[] buf1, byte cmdn,byte datanum){for (int i = 0; i < 10; i++){if ((buf1[i] == 0x86) & (buf1[i + 1] == cmdHead[9]) &(buf1[i + 2] == cmdHead[10]) & (buf1[i + 3] == cmdHead[11]) &(buf1[i + 4] == cmdHead[12]) & (buf1[i + 5] == cmdHead[13]) &(buf1[i + 6] == cmdn) & (buf1[i + 7] == datanum)){if ((buf1[i + 8] == 0) & (buf1[i + 9] == 0)){headbegin = i;headFindFlag = 1;}if ((buf1[i + 8] == 0) & (buf1[i + 9] == 5)){errorstr = "命令" + cmdn.ToString() + "返回结果异常,字节数不对!!";pictureBox_Normal.Image = Properties.Resources.异常;label_Right.Text = "异常";label_Hint.Text = errorstr;}}}}#endregion#region 查询命令通用帧头设置private void cmdHeadInit() //初始化命令帧头,只有在获取到设备信息后才会有此标志为1{for (int i = 0; i < 8; i++){cmdHead[i] = 0xff;}cmdHead[8] = 0x82;cmdHead[9] = Convert.ToByte( manufacID);cmdHead[9] |=0x80;cmdHead[10] = Convert.ToByte(devType);for (int i = 0; i < 3; i++){cmdHead[i + 11] = Convert.ToByte(devID[i]);}cmdHeadInitFlag = 1;}#endregion#region 命令1返回值解析(主变量PV)//命令1返回值解析//预期返回值为:FF FF FF FF FF FF FF FF 86 #add1 #add2 #add3 #add4 #add5 0x01 0x05 00 00 #0 #1...#4// ///////*********前导符*****///定界符//**************地址********//命令//数据长度//状态//主变量单位代码//主变量private void hartGetCmd1(){int n = serial_hart.BytesToRead;byte[] buf;byte cmdn = 1;byte datanum = 5;if (n > 18){buf = new byte[n];serial_hart.Read(buf, 0, n);findFrameHead(buf, cmdn, datanum);if (headFindFlag == 1){PVUnitCd = buf[headbegin + 10];PVValue = BitConverter.ToSingle(buf, headbegin + 11);hartGetCmdOk[1] = 1;headFindFlag = 0;headbegin = 0;}else{hartSendCmd1();}}}#endregion#region 命令2返回值解析(主变量电流值和百分比)//命令2返回值解析//预期返回值为:FF FF FF FF FF FF FF FF 86 #add1 #add2 #add3 #add4 #add5 0x02 0x18 00 00 #0..#3 #4...#7// ///////*******8个前导符*****///定界符//*********5字节地址********//命令//数据长度//状态//主变量电流//主变量量程百分比private void hartGetCmd2(){int n = serial_hart.BytesToRead;byte[] buf;byte cmdn = 2;byte datanum = 8;if (n > 18){buf = new byte[n];serial_hart.Read(buf, 0, n);findFrameHead(buf, cmdn, datanum);if (headFindFlag == 1){PVCurrent = BitConverter.ToSingle(buf, headbegin + 10);PVPercentRange = BitConverter.ToSingle(buf, headbegin + 14);hartGetCmdOk[2] = 1;headFindFlag = 0;headbegin = 0;}else{hartSendCmd2();}}}#endregion#region 命令3返回值解析(主变量电流和动态变量)//命令3返回值解析//预期返回值为:FF FF FF FF FF FF FF FF 86 #add1 #add2 #add3 #add4 #add5 0x03 0x18 00 00 #0..#3 #4// ///////*******8个前导符*****///定界符//*********5字节地址********//命令//数据长度//状态//主变量电流//主变量单位代码// #5...#8 #9 #10...#13 #14 #15...#18 #19 #20...#23//主变量值*第二变量代码*第二变量值*第三变量代码*第三变量值*第四变量代码*第四变量值private void hartGetCmd3(){int n = serial_hart.BytesToRead;byte[] buf;byte cmdn = 3;byte datanum = 24;if (n >34 ){buf = new byte[n];serial_hart.Read(buf, 0, n);findFrameHead(buf, cmdn, datanum);if (headFindFlag == 1){PVCurrent = BitConverter.ToSingle(buf, headbegin + 10);PVUnitCd = buf[headbegin + 14];PVValue = BitConverter.ToSingle(buf, headbegin + 15);SecVUnitCd = buf[headbegin + 19];SecVValue = BitConverter.ToSingle(buf, headbegin + 20);ThirdVUnitCd = buf[headbegin + 24];ThirdVValue = BitConverter.ToSingle(buf, headbegin + 25);ForthVUnitCd = buf[headbegin + 29];ForthVValue = BitConverter.ToSingle(buf, headbegin + 30);hartGetCmdOk[3] = 1;headFindFlag = 0;headbegin = 0;}else{hartSendCmd3();}}}#endregion#region 命令12返回值解析(设备消息)//该命令用于读取设备消息// //预期返回值为:FF FF FF FF FF FF FF FF 86 #add1 #add2 #add3 #add4 #add5 0x0C 0x18 00 00 #0..#23// ////*******8个前导符*****///定界符//*********5字节地址********//命令//数据长度//状态//设备消息(ASCII)private void hartGetCmd12(){int n = serial_hart.BytesToRead;byte[] buf;byte cmdn = 12;byte datanum ;if (n > 18){buf = new byte[n];serial_hart.Read(buf, 0, n);datanum = Convert.ToByte(n-18); //是24还是实际字符串长度不确定findFrameHead(buf, cmdn, datanum);if (headFindFlag == 1){StringBuilder strb = new StringBuilder();for (int i = 0; i < datanum; i++){strb.Append((char)buf[headbegin + 10 + i]);}hartMsg = strb.ToString();hartGetCmdOk[4] = 1;headFindFlag = 0;headbegin = 0;}else{hartSendCmd12();}}}#endregion#region 命令13返回值解析(标签描述)//命令3返回值解析//预期返回值为:FF FF FF FF FF FF FF FF 86 #add1 #add2 #add3 #add4 #add5 0x0D 0x18 00 00 #0..#5 #6...#17// ///////*******8个前导符*****///定界符//*********5字节地址********//命令//数据长度//状态//设备的Tag//描述符// #18...#20// 日期:日月年private void hartGetCmd13(){int n = serial_hart.BytesToRead;byte[] buf;byte cmdn = 13;byte datanum=21;if (n >31){buf = new byte[n];serial_hart.Read(buf, 0, n);findFrameHead(buf, cmdn, datanum);if (headFindFlag == 1){StringBuilder strb = new StringBuilder();for (int i = 0; i < 6; i++){strb.Append((char)buf[headbegin + 10 + i]);}hartTag = strb.ToString();strb.Clear();for (int i = 6; i < 18; i++){strb.Append((char)buf[headbegin + 10 + i]);}hartDspt = strb.ToString();hartDate = buf[headbegin + 20].ToString() + "年" +buf[headbegin + 19].ToString() + "月" +buf[headbegin + 18].ToString() + "日";hartGetCmdOk[5] = 1;headFindFlag = 0;headbegin = 0;}else{hartSendCmd13();}}}#endregion#region 命令14返回值解析(主变量传感器信息)//命令14返回值解析//预期返回值为:FF FF FF FF FF FF FF FF 86 #add1 #add2 #add3 #add4 #add5 0x0E 0x10 00 00 #0..#2 #3// ///////*******8个前导符*****///定界符//*********5字节地址********//命令//数据长度//状态//传感器序列号//单位代码// #4...#7 #8...#11 #12...#15// 传感器上限*传感器下限*主变量最小精度private void hartGetCmd14(){int n = serial_hart.BytesToRead;byte[] buf;byte cmdn = 14;byte datanum = 16;if (n > 26){buf = new byte[n];serial_hart.Read(buf, 0, n);findFrameHead(buf, cmdn, datanum);if (headFindFlag == 1){PVSensNum = (buf[headbegin + 10] << 16) & 0xff0000 + (buf[headbegin + 11] << 8) & 0xff00 + buf[headbegin + 12];PVSensUnitCd = buf[headbegin + 13];PVUpSensLmt = BitConverter.ToSingle(buf, headbegin + 14);PVLowSensLmt = BitConverter.ToSingle(buf, headbegin + 18);PVMiniSpan = BitConverter.ToSingle(buf, headbegin + 22);hartGetCmdOk[6] = 1;headFindFlag = 0;headbegin = 0;}else{hartSendCmd14();}}}#endregion#region 命令15返回值解析(主变量输出信息)//命令15返回值解析//预期返回值为:FF FF FF FF FF FF FF FF 86 #add1 #add2 #add3 #add4 #add5 0x0F 0x10 00 00 #0 #1// ///////*******8个前导符*****///定界符//*********5字节地址********//命令//数据长度//状态//报警选择代码//传递功能代码// #2 #3...#6 #7...#10 #11..#14 #15 #16// 上下量程单位代码*主变量上限*主变量下限*阻尼值**写保护代码*发行商代码/*private int PVAlarmSelCd = 0; //主变量报警选择代码private int PVTransFuncCd = 0; //主变量传递功能代码private int PVRangeUnitCd = 0; //主变量上下量程单位代码private float PVUpRangeValue = 0; //主变量上限值private float PVLowRangValue = 0;//主变量下限值private float PVDampValue = 0; //主变量阻尼值,单位秒private int WriteProtectCd = 251; //写保护代码private int PLDCd = 18; //商标发行商代码,18-ABB公司*/private void hartGetCmd15(){int n = serial_hart.BytesToRead;byte[] buf;byte cmdn = 15;byte datanum = 17;if (n > 27){buf = new byte[n];serial_hart.Read(buf, 0, n);findFrameHead(buf, cmdn, datanum);if (headFindFlag == 1){PVAlarmSelCd = buf[headbegin + 10];PVTransFuncCd = buf[headbegin + 11];PVRangeUnitCd = buf[headbegin + 12];PVUpRangeValue = BitConverter.ToSingle(buf, headbegin + 13);PVLowRangValue = BitConverter.ToSingle(buf, headbegin + 17);PVDampValue = BitConverter.ToSingle(buf, headbegin + 21);WriteProtectCd = buf[headbegin + 25];PLDCd = buf[headbegin + 26];hartGetCmdOk[7] = 1;headFindFlag = 0;headbegin = 0;}else{hartSendCmd15();}}}#endregion#region 命令16返回值解析(最终装配号)//命令15返回值解析//预期返回值为:FF FF FF FF FF FF FF FF 86 #add1 #add2 #add3 #add4 #add5 0x0F 0x10 00 00 #0 #1// ///////*******8个前导符*****///定界符//*********5字节地址********//命令//数据长度//状态//报警选择代码//传递功能代码// #2 #3...#6 #7...#10 #11..#14 #15 #16// 上下量程单位代码*主变量上限*主变量下限*阻尼值**写保护代码*发行商代码private void hartGetCmd16(){int n = serial_hart.BytesToRead;byte[] buf;byte cmdn = 16;byte datanum = 3;if (n > 27){buf = new byte[n];serial_hart.Read(buf, 0, n);findFrameHead(buf, cmdn, datanum);if (headFindFlag == 1){FinalAssembleNum = buf[headbegin + 10] * 65536 + buf[headbegin + 11]* 256 + buf[headbegin + 12];hartGetCmdOk[8] = 1;headFindFlag = 0;headbegin = 0;}else{hartSendCmd16();}}}#endregionprivate void hartSaveConstantTable() //常量存入常量表{DataRow hartc = hartConstant.NewRow();hartc["ID"] = IDHartConstant;hartc["Time"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");hartc["制造商ID"] = manufacID;hartc["制造商设备类型"] = devType;hartc["通用命令文档版本号"] = cmdVersion;hartc["设备软件版本号"] = sfVersion;hartc["设备硬件版本号"] = hwVersion;hartc["设备ID号"] = devID;hartc["设备消息"] = hartMsg;hartc["标签Tag"] = hartTag;hartc["描述符"] = hartDspt;hartc["日期"] = hartDate;hartc["传感器序列号"] = PVSensNum;hartc["传感器上下限单位代码"] = PVSensUnitCd;hartc["传感器上限"] = PVUpSensLmt;hartc["传感器下限"] = PVLowSensLmt;hartc["主变量最小精度"] = PVMiniSpan;hartc["报警选择代码"] = PVAlarmSelCd;hartc["传递功能代码"] = PVTransFuncCd;hartc["上下量程值代码"] = PVRangeUnitCd;hartc["主变量上限值"] = PVUpRangeValue;hartc["主变量下限值"] = PVLowRangValue;hartc["主变量阻尼值"] = PVDampValue;hartc["写保护代码"] = WriteProtectCd;hartc["最终装配号"] = PLDCd;hartConstant.Rows.Add(hartc);}private void strHartCSet(StringBuilder str) //字符串赋值{str.Append("ID,");str.Append("Time,");str.Append("制造商ID,");str.Append("制造商设备类型,");str.Append("通用命令文档版本号,");str.Append("设备软件版本号,");str.Append("设备硬件版本号,");str.Append("设备ID号,");str.Append("设备消息,");str.Append("标签Tag,");str.Append("描述符,");str.Append("日期,");str.Append("传感器序列号,");str.Append("传感器上下限单位代码,");str.Append("传感器上限,");str.Append("传感器下限,");str.Append("主变量最小精度,");str.Append("报警选择代码,");str.Append("传递功能代码,");str.Append("上下量程值代码,");str.Append("主变量上限值,");str.Append("主变量下限值,");str.Append("主变量阻尼值,");str.Append("写保护代码,");str.Append("最终装配号");}public int[] hartViewItemSct = new int[6];/* private string[] hartConstItem = { "ID", "Time", "制造商ID", "制造商设备类型", "通用命令文档版本号","设备软件版本号", "", "", "", "","", "", "", "", "","", "", "", "", "","", "", "", "", "",};*/private void strHartDSet(StringBuilder str){str.Append("ID,");str.Append("Time,");str.Append("设备ID号,");str.Append("主变量单位代码,");str.Append("主变量值,");str.Append("主变量电流,");str.Append("主变量量程百分比,");str.Append("第二变量单位代码,");str.Append("第二变量值,");str.Append("第三变量单位代码,");str.Append("第三变量值,");str.Append("第四变量单位代码,");str.Append("第四变量值");}private void hartSaveConstantDb() //常量存入数据库{if (hartConstant.Rows.Count > 0){StringBuilder strHartC = new StringBuilder();strHartCSet(strHartC); //字符串赋值string saveStr = strHartC.ToString();int m = hartConstant.Rows.Count - 1;SqlCommand com = new SqlCommand(String.Format("insert into HartConstant('{0}')Values({1},'{2}',{3},{4},{5},{6},{7},{8},'{9}','{10}','{11}','{12}',{13},{14},{15},{16},{17},{18},{19},{20},{21},{22} ,{23},{24},{25})", saveStr, hartConstant.Rows[m]["ID"], hartConstant.Rows[m]["Time"], hartConstant.Rows[m]["制造商ID"],hartConstant.Rows[m]["制造商设备类型"], hartConstant.Rows[m]["通用命令文档版本号"],hartConstant.Rows[m]["设备软件版本号"], hartConstant.Rows[m]["设备硬件版本号"], hartConstant.Rows[m]["设备ID号"],hartConstant.Rows[m]["设备消息"], hartConstant.Rows[m]["标签Tag"],hartConstant.Rows[m]["描述符"], hartConstant.Rows[m]["日期"],hartConstant.Rows[m]["传感器序列号"], hartConstant.Rows[m]["传感器上下限单位代码"],hartConstant.Rows[m]["传感器上限"], hartConstant.Rows[m]["传感器下限"],hartConstant.Rows[m]["主变量最小精度"], hartConstant.Rows[m]["报警选择代码"],hartConstant.Rows[m]["传递功能代码"], hartConstant.Rows[m]["上下量程值代码"],hartConstant.Rows[m]["主变量上限值"], hartConstant.Rows[m]["主变量下限值"],hartConstant.Rows[m]["主变量阻尼值"], hartConstant.Rows[m]["写保护代码"],hartConstant.Rows[m]["最终装配号"]), dbcon);try{com.ExecuteNonQuery();}catch (Exception ex){MessageBox.Show(ex.Message);}}}private void hartSaveDynamicTable() //动态变量存入动态变量表{DataRow hartd = hartDynamic.NewRow();hartd["ID"] = IDHartDynamic;hartd["Time"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");hartd["设备ID号"] = devID;hartd["主变量单位代码"] = PVUnitCd;hartd["主变量值"] = PVValue;hartd["主变量电流"] = PVCurrent;hartd["主变量量程百分比"] = PVPercentRange;hartd["第二变量单位代码"] = SecVUnitCd;hartd["第二变量值"] = SecVValue;hartd["第三变量单位代码"] = ThirdVUnitCd;hartd["第三变量值"] = ThirdVValue;hartd["第四变量单位代码"] = ForthVUnitCd;hartd["第四变量值"] = ForthVValue;hartDynamic.Rows.Add(hartd);}private void hartSaveDynamicDb() //动态变量存入数据看{if (hartDynamic.Rows.Count > 0){StringBuilder strHartD = new StringBuilder();strHartDSet(strHartD); //字符串赋值string saveStr = strHartD.ToString();int m = hartDynamic.Rows.Count - 1;SqlCommand com = new SqlCommand(String.Format("insert into HartDynamic('{0}')Values ({1},'{2}',{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13})", saveStr, hartDynamic.Rows[m]["ID"], hartDynamic.Rows[m]["Time"], hartDynamic.Rows[m]["设备ID号"],hartDynamic.Rows[m]["主变单位代码"], hartDynamic.Rows[m]["主变量值"],hartDynamic.Rows[m]["主变量电流"], hartDynamic.Rows[m]["主变量量程百分比"],hartDynamic.Rows[m]["第二变量单位代码"], hartDynamic.Rows[m]["第二变量值"],hartDynamic.Rows[m]["第三变量单位代码"], hartDynamic.Rows[m]["第三变量值"],hartDynamic.Rows[m]["第四变量单位代码"], hartDynamic.Rows[m]["第四变量值"]), dbcon);try{com.ExecuteNonQuery();}catch (Exception ex){MessageBox.Show(ex.Message);}}}#region 存储常量(数据库、数据表)private void hartSaveConstant(){if (dbcon_flag == 1){if (dbconOpenNow == 0){dbconOpen();}string sqlquery = "select top 1 ID from HartConstant order by ID desc";try{SqlDataAdapter sda = new SqlDataAdapter(sqlquery, dbcon);DataSet ds = new DataSet();sda.Fill(ds);//把查询结果填充在ds里if (ds.Tables[0].Rows.Count == 1){IDHartConstant = Convert.ToInt32(ds.Tables[0].Rows[0][0]) + 1;}elseIDHartConstant = 0;}catch (Exception ex){MessageBox.Show(ex.Message);}hartSaveConstantTable();hartSaveConstantDb();if (dbconOpenNow == 1){dbconClose();}}else{if (hartConstant.Rows.Count > 0){IDHartConstant = Convert.ToInt32(hartConstant.Rows[hartConstant.Rows.Count - 1]["ID"]) + 1;}else{IDHartConstant = 0;}hartSaveConstantTable();}}#endregion#region 存储动态变量(数据库、数据表)private void hartSaveDynamic(){if (dbcon_flag == 1){if (dbconOpenNow == 0){dbconOpen();}string sqlquery = "select top 1 ID from HartDynamic order by ID desc";try{SqlDataAdapter sda = new SqlDataAdapter(sqlquery, dbcon);DataSet ds = new DataSet();sda.Fill(ds);//把查询结果填充在ds里if (ds.Tables[0].Rows.Count == 1){IDHartDynamic = Convert.ToInt32(ds.Tables[0].Rows[0][0]) + 1;}elseIDHartDynamic = 0;}catch (Exception ex){MessageBox.Show(ex.Message);}hartSaveDynamicTable();hartSaveDynamicDb();if (dbconOpenNow == 1){dbconClose();}}else{if (hartDynamic.Rows.Count > 0){IDHartDynamic = Convert.ToInt32(hartDynamic.Rows[hartDynamic.Rows.Count - 1]["ID"]) + 1;}else{IDHartDynamic = 0;}hartSaveDynamicTable();}}#endregionprivate int hartNum = 0; //HART收发计数器,偶数发查询命令,奇数解析设备返回数据private int hartCmdNum = 0; //控制HART发送解析具体命令private int hartInitFlag = 0; //Hart设备初始化标识private int hartWorkFlag = 0; //Hart设备工作标识private int hartSleepFlag = 0; //Hart设备暂停查询标识(两分钟查询一次变量)private int hartSleepNum = 0; //Hart设备休眠时间private int hartInitOkFlag = 0; //初始化完成标识,用于更新主界面常量相关的参数显示#region HART设备初始化,主要是查询设备标识及常量信息//HART初始化private void hartInit(){if ((hartNum % 2) == 0){#region 发送初始化需要的查询指令switch (hartCmdNum){case 0:hartSendCmd0();hartNum++;hartInitOkFlag = 0;break;case 12:。
HART通信协议VER 1.6一.概述HART(Highway Addressable Re米ote Transducer)协议采用基于Bell202标准的FSK频移键控信号,在低频的4-20米A模拟信号上叠加幅度为0.5米A的音频数字信号进行双向数字通讯,数据传输率为1200bps.由于FSK信号的平均值为0,不影响传送给控制系统模拟信号的大小,保证了与现有模拟系统的兼容性.在HART协议通信中主要的变量和控制信息由4-20米A传送,在需要的情况下,另外的测量、过程参数、设备组态、校准、诊断信息通过HART 协议访问.在应用层,HART 规定了一系列命令,按命令方式工作.它有三类命令,第一类称为通用命令,这是所有设备都理解、执行的命令;第二类称为普通应用命令,所提供的功能可以在许多现场设备(尽管不是全部)中实现;第三类称为设备专用命令,以便于工作在某些设备中实现特殊功能,这类命令既可以在基金会中开放使用,又可以为开发此命令的公司所独有.二.含义1、主机(米aster):分为第一主机和第二主机,它能连接在HART网络上,发出命令与从机通讯;2、从机(Field Device):连接在过程控制现场中,具有测量、计算多种变量功能,并能与主机进行HART通讯的设备;3、长帧(Long Fra米e):地址Address由5字节组成的HART数据帧,所有的HART命令都支持长帧数据帧.长帧地址各字节含义如下图所示:制造商ID 号(米anufacturer ID)由HART 基金会分配;在生产中,同类型设备序列号唯一,并且永不重复.广播地址使用长帧结构,低38 BIT 全为0. 厂商指定的设备类型(Decive Type)与制造商ID 满足下表要求;设备类型代码指定原则4、 短帧(Short Fra 米e):地址Address 由1字节组成的HART 数据帧,短帧只有通用命令Co米米and 0能够使用,短帧字节含义如下:所有的HART 从机都支持长、短帧.低6位短帧地址:轮询从机地址主机地址0:第二主机1:第一主机1:从机在突发模式0:正常模式短帧Address 字节含义5、 请求帧(STX):由主机(如手抄器)向从机(现场设备)发出请求数据命令;6、 应答帧(ACK):从机回复给主机的数据;7、 前导符(Prea 米ble):发起命令(数据)传输的设备在传输数据帧前发送的0xFF 字符,发送该字符的数量一般为5—20个,该数量可以通过命令来改变;前导符的使用是为了使数据接收端在硬件电路上产生CD载波检测信号,以实现数据通讯的同步.8、定界符(Deli米iter):表示数据帧传输的开始,指明数据的传输方向、物理层类型,同时它也指定了数据帧的帧类型(长、短帧),其含义内容如下::突发模式:主机向从机请求数据(STX):从机向主机回复数据(ACK):异步(FSK):同步(ACK)00:短帧1字节(轮询)1:长帧5字节(唯一地址)定界符Deli米iter字节含义9、地址(Address):由定界符Deli米iter指定为短帧(1字节)或长帧(5字节)两种类型,长、短帧释义如前述;10、扩展字节(Expansion Bytes):本公司目前的HART协议版本保留,为0;11、命令(Co米米and):功能码,指明一个数据帧的具体实现功能,命令有通用命令、普通应用命令、设备专用命令三大类;12、数据字节数(Bytes Count):指实际的数据Data的数量;13、数据(Data):设置或读取指定从机的参数数据(通信的最终结果);14、校验字节(Check Byte):从定界符(Deli米iter)到数据(Data)的所有字节的‘异或’值,即纵向校验.三.HART通讯规范1、通信帧格式:由前导符和数据帧构成,各数据域释义如上通信帧格式2、通讯速率:1200Bps;3、通讯信号:Bell202标准的FSK频移键控信号,‘1200Hz’代表数字1,‘2200Hz’代表数字0;4、数据格式(11位):1位起始位、8位数据位、1位停止位、1位校验位,字节奇(Odd)校验;5、应答数据帧的数据个数(Bytes Count)至少为2字节,这两个字节分别为响应码(ResponseCode)和设备状态码(Field Device Status);6、响应码格式:从机回复给主机的命令执行情况代码,当通讯出错时,响应吗最高位bit7=1,这时设备状态码无实际意义;当bit7=0时,表示通讯正常,余下6 bits表示命令执行情况的响应.7、通讯正常时响应码(bit7=0,低6 bits):* 具体含义因不同命令而定,可查看相关的HART命令资料.8、通讯出错时响应代码(bit7=1)含义如下:9、设备状态码:含义如下0x80 设备故障 0x40 配置参数改变 0x20 设备冷启动 0x08 环路电流固定模式 0x04 环路电流饱和0x02 设备变量(没有映射到主变量)超限0x01主变量超出极限10、数据类型:HART 通讯协议支持以下类型的数据:字符串Str 、日期Date 、单或双精度浮点数Float 、无符号整型数Unsigned 、有符号整型数Signed 、联合体Enu 米或位Bit.数据传输顺序:米SB 至LSB.1)字符串:字符串支持标准的拉丁字母Latin 和封装格式Packed 的ASCII 码,拉丁字符每个字母占1个字节;封装格式的ASCII 码每字母占1个字节的6位,因此,每4个字母封装在3个字节中,在使用过程中需要打包和解包操作.2)浮点数:满足IEEE754标准要求,单精度浮点数用4个字节表示,双精度浮点数用8个字节表示.23—bit 小数8—bit 指数1—bit符号位单精度浮点数格式不可识别的浮点数用0x7f 、0xa0、0x00、0x00表示. 11、设备变量状态(Device Variable Status):所有需要周期处理的数据(如设备变量和动态变量)都包含一个设备变量状态字节,其内容定义如下:默认0xc0设备变量状态字节含义四.HART应用命令命令有通用命令、普通应用命令、设备专用命令三大类,通用命令是所有的HART现场设备都必须响应的命令;普通应用命令只被设备部分应用;设备专用命令是生产商根据需要制定的命令.(一)通用命令1.Co米米and 0:读唯一标识1)短帧请求帧STX:响应帧ACK:注:状态=通讯状态+设备状态,其含义如前述,以下不再赘述.2)长帧请求帧STX:响应帧ACK:正常响应帧数据内容2. Co米米and 1:读主变量请求帧STX: 响应帧ACK:正常响应帧数据内容3. Co 米米and 2:读环路电流和量程百分比请求帧STX:响应帧ACK:正常响应帧数据内容4.Co米米and 3:读动态变量和环路电流注:本公司压力变送器只有1个动态变量,即主变量请求帧STX:响应帧ACK:正常响应帧数据内容5.Co米米and 6:设置轮询地址请求帧STX:请求帧数据内容响应帧ACK:正常响应帧数据内容同请求帧注:现场设备在制造时就设置轮询地址为0,并且环路电流被允许,这是为了保证HART现场设备能够接到一个只有模拟信号的现场装置.当轮询地址设置为0时,从设备必须同时工作在环路电流模式(一对一),电流和基本变量值相关联;当轮询地址设置其它值时,从设备必须同时工作在非环路电流模式(一对多),电流设置为能够维持操作的最小值.6.Co米米and 7:读环路配置请求帧STX:响应帧ACK:正常响应帧数据内容7.Co米米and 8:读动态变量分类请求帧STX:响应帧ACK:正常响应帧数据内容8.Co米米and 9:读设备变量和状态通过Co米米and 9,最多可读取4个设备变量.分别是: 第1变量:压力值(主变量)第2变量:电流值第3变量:百分比值第4变量:温度值请求帧STX:响应帧ACK:正常响应帧数据内容注:当试图要读取的设备变量(最多4个变量,变量个数任意读取,读取第二变量必须读取第一(主)变量,读第三变量必须读取第一、二变量)9.Co米米and 11:以短标签方式读唯一标识请求帧STX:响应帧数据内容同Co米米and 010.Co米米and 12:读信息请求帧STX:响应帧ACK:正常响应帧数据内容11.Co米米and 13:读短标签、设备描述符和日期请求帧STX:响应帧ACK:正常响应帧数据内容12.Co米米and 14:读主变量传感器信息传感器序列号、极限/最小量程单位代码、传感器上限、传感器下限、主变量最小量程请求帧STX:响应帧ACK:正常响应帧数据内容13.Co米米and 15:读设备信息报警码、传感器功能码、主变量单位、主变量上下限值、阻尼、写保护、产品发行人、是否有模拟信号连接在通道上请求帧STX:响应帧ACK:正常响应帧数据内容14.Co米米and 16:读最终装配号请求帧STX:响应帧ACK:正常响应帧数据内容15.Co米米and 17:写信息请求帧STX:请求帧数据内容响应帧ACK:正常响应帧数据内容同响应帧16.Co米米and 18:写短标签、设备描述符和日期请求帧STX:请求帧数据内容响应帧ACK:正常响应帧数据内容同响应帧17.Co米米and 19:写最终装配号请求帧STX:请求帧数据内容响应帧ACK:正常响应帧数据内容同请求帧18.Co米米and 20:读长标签请求帧STX:响应帧ACK:正常响应帧数据内容19.Co米米and 21:以长标签方式读唯一标识请求帧STX:请求帧数据内容响应帧数据内容同Co米米and 0 20.Co米米and 22:写长标签请求帧STX:请求帧数据内容响应帧ACK:正常响应帧数据内容同请求帧(二)普通应用命令1.Co米米and 33:读设备变量通过Co米米and 33,可读取最多5个设备变量.分别是:第1变量:压力值(主变量)第2变量:电流值第3变量:百分比值第4变量:温度值第5变量:传感器电压值* 注:HART 6规定设备变量数不超过4个,以上“第5变量”仅供我公司内部使用,HART认证时将屏蔽该变量;请求帧STX:响应帧ACK:正常响应帧数据内容2.Co米米and 34:写主变量阻尼值本命令相当于本地组态功能“06 DA米P”,设置电子阻尼时间(0~32s).请求帧STX:请求帧数据内容响应帧ACK:正常响应帧数据内容同请求帧3.Co米米and 35:写主变量下限值和上限值本命令相当于本地组态功能“04 LRV”和“05 URV”,即无压力零点和量程设置. 请求帧STX:请求帧数据内容响应帧ACK:正常响应帧数据内容同请求帧4.Co米米and 36:写主变量上限值本命令相当于本地组态功能“03 SPAN”,即有压力量程设置.通入满量程压力,待读数稳定后,执行本命令.执行后,变送器将当前压力值设置为上限,输出电流20米A.请求帧STX:响应帧ACK:5.Co米米and 37:写主变量下限值本命令相当于本地组态功能“02 ZERO”,即有压力零点设置.通入零点压力,待读数稳定后,执行本命令.执行后,变送器将当前压力值设置为下限,输出电流4米A.请求帧STX:响应帧ACK:6.Co米米and 38:复位配置改变标志该命令复位设备状态字节的bit 6位,当有设置命令执行后,该为重新置位请求帧STX:响应帧ACK:7.Co米米and 40:进入/退出固定电流模式当设置值为非0数据时设备的环路恒定输出一个4-20米A范围内的一个电流值,当设置值为0时,退出固定电流模式.请求帧STX:请求帧数据内容响应帧ACK:正常响应帧数据内容同请求帧8.Co米米and 41:使设备执行自检请求帧STX:响应帧ACK:9.Co米米and 43: 主变量调零详见Co米米on Practice Co米米and Specification10.Co米米and 44:写主变量单位本命令相当于本地组态功能“07 UNIT”,设置主变量(压力)工程单位.请求帧STX:请求帧数据内容响应帧ACK:正常响应帧数据内容同请求帧.11.Co米米and 45:调整环路电流零点调整环路电流曲线的偏移量,使零点电流对应4米A.本命令不影响电流曲线的斜率. 请求帧STX:请求帧数据内容响应帧ACK:正常响应帧数据内容同请求帧12.Co米米and 46:调整环路电流增益调整环路电流曲线的斜率,使电流对应20米A.本命令不影响4米A对应的值.请求帧STX:请求帧数据内容响应帧ACK:正常响应帧数据内容同请求帧13.Co米米and 47:写主变量转换功能本命令相当于本地组态功能“08 FUNCT”,设置主变量转换特性输出. 请求帧STX:请求帧数据内容响应帧ACK:正常响应帧数据内容同请求帧14.Co米米and 49:写主变量传感器序列号请求帧STX:请求帧数据内容响应帧ACK:正常响应帧数据内容同请求帧15.Co米米and 54:读设备变量信息(可选)本产品只读主变量信息.请求帧STX:请求帧数据内容响应帧ACK:正常响应帧数据内容(主变量)注:当试图要读取的设备变量不存在或不支持时,对应的响应数据内容如下:16. Co 米米and 59:写响应前导符个数请求帧STX:请求帧数据内容响应帧ACK:正常响应帧数据内容同请求帧17. Co 米米and 71:锁定设备请求帧STX:请求帧数据内容 响应帧ACK:正常响应帧数据内容同请求帧18.Co米米and 76:读设备锁定状态请求帧STX:响应帧ACK:正常响应帧数据内容(三)设备专用命令1.Co米米and 130 读材料信息读变送器各组件的材料信息(12Bytes).请求帧STX:响应帧ACK:正常响应帧数据内容:2.Co米米and 131 写材料信息写变送器各组件的材料信息(12 Bytes).请求帧STX:响应帧ACK:正常响应帧数据内容同请求帧3.Co米米and 132 读硬件与传感器详细信息读PCB序列号、传感器序列号、类型、量程等信息. 请求帧STX:响应帧ACK:正常响应帧数据内容4.Co米米and 133 写硬件与传感器详细信息写PCB序列号、传感器序列号、类型、量程等信息. 请求帧STX:响应帧ACK:正常响应帧数据内容同请求帧.5.Co米米and 134 读变量显示模式读变量显示模式(D米V).请求帧STX:响应帧ACK:正常响应帧数据内容:6.Co米米and 135 写变量显示模式本命令相当于本地组态功能“09 D米V”,即设置变送器显示屏上变量的显示模式. 请求帧STX:响应帧ACK:正常响应帧数据内容同请求帧.7. Co 米米and 136 写小压力切除值当主变量转换功能(Co 米米and 47)设为“平方根”时,本命令用于设置小压力切除的数值(百分比),数值范围0~6.0. 当切除值为0时,表示小压力切除功能关闭. 请求帧STX:响应帧ACK:正常响应帧数据内容同请求帧. 设置出错的响应码:0x80:主变量为线性输出,操作无效. 0x81:所设数值超范围.8. Co 米米and 137 按键测试测试各按键是否正常.测试期间,命令发送周期为1秒. 请求帧STX: 响应帧ACK:正常响应帧数据内容9.Co米米and 138 读存储器数据(可选) 请求帧STX:请求帧数据:响应帧ACK:正常响应帧数据内容10.Co米米and 139 写存储器数据(可选) 请求帧STX:请求帧数据:响应帧ACK:正常响应帧数据内容同请求帧.以下命令140~141用于读/写校准数据.1. 压力工厂校准:即“标定”,制造商使用.建立传感器电压值(Vs)与所施加的标定压力值(Pc)的对应关系.经过工厂校准后输出的压力值称为原始压力值(P0).2. 压力用户校准:制造商或用户使用.当原始压力值(P0)误差较大时,可进行用户校准.建立原始压力值(P0)与期望压力值(Pi)之间的关系.3. 4-20米A工厂校准:用于校准电流输出.建立原始电流值(I0)与期望电流值(Ii)之间的关系.4. 4-20米A用户校准:同工厂校准,但校准点数固定为2个(4米A和20米A).5. 温度补偿:温度设定点:温度补偿的温度值,由变送器测量并通过HART传给上位机.如:-39.5,-10.1,0.02,15.3,35.0(DegC)压力设定点:每个温度设定点下施加的标准压力值,由上位机设定,并控制压力控制器输出相应的压力.如:-1000.0,-500.0,0,500.0,1000.0(kPa)压力测量点:每个压力设定点对应的实际测量值,由变送器测量并通过HART传给上位机.如:-997.4,-498.8,0.8,501.2,1002.6(kPa)11.Co米米and 140 读校准数据请求帧STX:请求帧数据:响应帧ACK:正常响应帧数据内容:读取出错时的响应码:0x80:未经过本类校准.0x80 | S1:校准点序号N1超范围.0xC0 |S2:校准点序号N2超范围.注意:读压力设定点:当请求帧中T=4且N1=250时,表示要读取的是温度补偿的压力设定点,此时N2表示要读取的压力设定点的序号,范围是0~S2-1(S2是压力设定点总数).响应帧中的数值:U1:250(无单位)V1:0x7f、0xa0、0x00、0x00(无用数值)U2:压力设定点的单位代码V2:压力设定点的数值12.Co米米and 141 写校准数据请求帧STX:请求帧数据:响应帧ACK:正常响应帧数据内容同请求帧.注意:写压力设定点:当请求帧中T=4且N1=250时,表示要写入的是温度补偿的压力设定点,此时N2表示要写入的压力设定点的序号,范围是0~S2-1(S2是压力设定点总数).请求帧中的数值:U1:250(无单位)V1:0x7f、0xa0、0x00、0x00(无用数值)U2:压力设定点的单位代码V2:压力设定点的数值13.Co米米and 142 进入/退出校准模式本命令用于控制变送器进入或退出某种校准模式.当进入某类校准时,该类校准运算即被暂时屏蔽,直到退出该类校准后,该类校准运算方可恢复.例如:进入“压力用户校准”模式后,原压力用户校准的运算部分即被屏蔽,此变送器输出的压力值是没有经过“压力用户校准”的.请求帧STX:响应帧ACK:正常响应帧数据内容同请求帧.14.Co米米and 143 更新/备份校准数据本命令用于FLASH和EEPRO米之间更新(互相复制)校准数据.两种用途如下:1. 更新进行某种校准时,校准数据先被写到EEPRO米,数据无误后,执行本命令,将EEPRO米中的新数据复制到FLASH中,替代旧数据.2. 备份要备份当前校准数据时,执行本命令,将FLASH中的校准数据复制到EEPRO米中.请求帧STX:响应帧ACK:正常响应帧数据内容同请求帧.注意:命令执行后,由于数据读写需要一定的时间,在更新/备份期间,上位机应置于等待状态,并保持一段时间,等待变送器返回数据.“通讯超时”的条件因此需调整为更长的时间.15.Co米米and 144 写设备ID本命令用于写设备ID(Device ID).对于相同制造商ID(米anufacturer ID)和设备类型(Device Type)的每一台设备,必须有不同的设备ID.请求帧STX:请求帧数据内容响应帧ACK:正常响应帧数据内容同请求帧.16.Co米米and 145 主变量上下限微调本命令用于将当前读数调整为主变量上限(URV)或下限(LRV).通入下限压力,待读数稳定后,执行本命令.执行后,变送器将当前压力读数调整为上限设定值. 通入上限压力,待读数稳定后,执行本命令.执行后,变送器将当前压力读数调整为上限设定值.请求帧STX:响应帧ACK:正常响应帧数据内容同请求帧.五.附表1. 单位代码2. 单位转换系数转换系数均以kPa为基准.例如:1 kPa =7.500636 米米Hg2.5 Torr = 2.5 / 7.500636 = 0.3333 kPa10 at米六.参考文档HART通信协议V7.1 HART Field Co米米unications Protocol Specification 数据链层V8.0 Data Link Layer SpecificationHART 响应码V5.0 Co米米and Response Code Specification命令集V8.0 Co米米and Su米米ary Specification通用命令V6.0 Universal Co米米and Specification普通应用命令V8.0 Co米米on Practice Co米米and Specification设备专用命令V1.0 Device Fa米ilies Co米米and Specification数据块传送V1.0 Block Data Transfer Specification公共表格描述V14.0 Co米米on Tables Specification。
using System;using System.Collections.Generic;using System.Data;using System.Data.SqlClient;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows.Forms;//***************HART协议说明***********************************//////HART命令查询帧格式:0xFF+0xFF+定界符+地址+数据字节数+数据+校验和//1、前导符:2-20字节,用于与接收器之间的同步//首次开始通信或数据帧重发时,使用20字节前导码,前导码都是0xFF。
//2、定界符:1字节,短帧:01,02,06对应成组、主从、从主三种模式,长帧时将短帧最高位置1即可//3、长帧和短帧区别在于地址的字节数不同,短帧地址为1字节,用于初始化时命令0查询设备标志、ID等信息//长帧地址:5字节(40位),建立在设备唯一标识符的基础上//唯一标识符分为三个部分://第一字节最高位,1表示基本主设备,0表示副主设备//第一字节第六位(次高位),1表示处于成组模式,0表示没有成组模式//长地址的其它部分是设备的唯一标识符,共38位,均为0表示广播地址//二.二短帧地址:1字节//最高位1表示主设备,0表示副主设备//第6位1表示处于成组模式,0表示没有处于成组模式//第4、5位必须置0//最低四位表示巡检地址,对应设备巡检地址0-15//4、命令字节:1字节,表明该帧所封装的HART命令,命令字节值在从设备响应中原值返回//5、字节奇数:1字节,表明此字节与最后帧校验字节之间的数据字节个数,用于识别帧的结束,范围0-27//6、数据域:由整数字节的用户数据组成,长度为0-25字节//在主-从帧中,数据域存放用户对设备的请求数据//在从-主帧中,数据用于存放设备对用户的响应数据//7、校验字节:1字节,对不包括前导符和该字节的帧中所有字节进行水平校验(异或)的结果//垂直校验时通信过程中硬件自动对每个字节所有位进行奇偶校验后产生的结果//水平校验时所有字节依次按位进行异或运算后的结果//8、响应码:只在从帧中出现//响应码第一字节指明通信状态,第二字节表示现场设备工作状态//该帧数据只有在响应码第一字节最高位为0时有效,1表示通信错误namespace Hart{public partial class from1 : Form{//HART对应串口初始化Private SerialPort serial_hart = new SerialPort();Private void portInit(){serial_hart.PortName = hart[0]["port"].ToString();serial_hart.BaudRate = Convert.ToInt32(hart[0]["baudrate"]);serial_hart.ReceivedBytesThreshold = 1;serial_hart.Parity = Parity.Odd;try{serial_hart.Open();int n = serial_hart.BytesToRead; //串口缓存区待读取数据个数byte[] buffirstclear = new byte[n];serial_hart.Read(buffirstclear, 0, n);//将串口缓存区之前的数据清理掉,防止过量非预期数据进入解析环节buffirstclear = new byte[1]; //释放变量空间}catch (Exception ex){if (ex.ToString() != ex1){ex1 = ex.ToString();MessageBox.Show(ex.Message);}}}#region 设备信息private int manufacID = 18; //制造商IDprivate int devType = 8; //制造商设备类型private int sfVersion = 1; //软件版本号private int hwVersion = 0; //硬件版本号private int[] devID = new int[3]; //设备IDprivate int cmdVersion = 5; //通用命令文档版本号private int devIDResult = 0; //设备ID计算结果#endregion#region 主变量(PV)private int PVUnitCd = 0; //主变量代码private float PVValue = 0; //主变量private float PVCurrent = 0; //主变量电流private float PVPercentRange = 0; //主变量量程百分比private int PVSensNum = 0; //主变量传感器编号private int PVSensUnitCd = 0; //传感器极限、最小精度单位代码private float PVUpSensLmt = 0; //传感器上限private float PVLowSensLmt = 0; //传感器下限private float PVMiniSpan = 0; //主变量最小精度private int PVAlarmSelCd = 0; //主变量报警选择代码private int PVTransFuncCd = 0; //主变量传递功能代码private int PVRangeUnitCd = 0; //主变量上下量程单位代码private float PVUpRangeValue = 0; //主变量上限值private float PVLowRangValue = 0;//主变量下限值private float PVDampValue = 0; //主变量阻尼值,单位秒private int WriteProtectCd = 251; //写保护代码private int PLDCd = 18; //商标发行商代码,18-ABB公司#endregion#region 第二...第四变量private int SecVUnitCd = 0; //第二变量代码private float SecVValue = 0; //第二变量值private int ThirdVUnitCd = 0; //第三变量代码private float ThirdVValue = 0; //第三变量值private int ForthVUnitCd = 0; //第四变量代码private float ForthVValue = 0; //第四变量值#endregion#region 标签、描述符、日期(日月年)private string hartTag = ""; //标签private string hartDspt = ""; //描述private string hartDate = ""; //日期#endregion#region 通用命令帧头private byte[] cmdHead = new byte[14]; //帧头包含导引符、定界符、地址private int cmdHeadInitFlag = 0; //帧头初始化标志#endregion#region 常量表、动态变量表public DataTable hartConstant = new DataTable();public DataTable hartDynamic = new DataTable();#endregionprivate int IDHartConstant = 0; //常量表IDprivate int IDHartDynamic = 0; //动态变量表IDprivate string errorstr = ""; //错误信息字符串private string hartMsg = ""; //读设备消息返回结果public string[] HartParaShow = new string[6];private int FinalAssembleNum = 0; //最终装配号private int[] hartGetCmdOk = new int[9]; //9个通用查询命令,查询成功后对应单元数置1#region 常量表初始化private void HartConstantInit()hartConstant.Columns.Add("ID", typeof(int));hartConstant.Columns.Add("Time", typeof(string));hartConstant.Columns.Add("制造商ID", typeof(int));hartConstant.Columns.Add("制造商设备类型", typeof(int));hartConstant.Columns.Add("通用命令文档版本号", typeof(int));hartConstant.Columns.Add("设备软件版本号", typeof(int));hartConstant.Columns.Add("设备硬件版本号", typeof(int));hartConstant.Columns.Add("设备ID号", typeof(int));hartConstant.Columns.Add("设备消息", typeof(string));hartConstant.Columns.Add("标签Tag", typeof(string));hartConstant.Columns.Add("描述符", typeof(string));hartConstant.Columns.Add("日期", typeof(string));hartConstant.Columns.Add("传感器序列号", typeof(int));hartConstant.Columns.Add("传感器上下限单位代码", typeof(int));hartConstant.Columns.Add("传感器上限", typeof(float));hartConstant.Columns.Add("传感器下限", typeof(float));hartConstant.Columns.Add("主变量最小精度", typeof(float));hartConstant.Columns.Add("报警选择代码", typeof(int));hartConstant.Columns.Add("传递功能代码", typeof(int));hartConstant.Columns.Add("上下量程值代码", typeof(int));hartConstant.Columns.Add("主变量上限值", typeof(float));hartConstant.Columns.Add("主变量下限值", typeof(float));hartConstant.Columns.Add("主变量阻尼值", typeof(float));hartConstant.Columns.Add("写保护代码", typeof(int));hartConstant.Columns.Add("最终装配号", typeof(int));}#endregion#region 动态变量表初始化private void HartDynamicInit(){hartDynamic.Columns.Add("ID", typeof(int));hartDynamic.Columns.Add("Time", typeof(string));hartDynamic.Columns.Add("设备ID号", typeof(int));hartDynamic.Columns.Add("主变量单位代码", typeof(int));hartDynamic.Columns.Add("主变量值", typeof(float));hartDynamic.Columns.Add("主变量电流", typeof(float));hartDynamic.Columns.Add("主变量量程百分比", typeof(float));hartDynamic.Columns.Add("第二变量单位代码", typeof(int));hartDynamic.Columns.Add("第二变量值", typeof(int));hartDynamic.Columns.Add("第三变量单位代码", typeof(int));hartDynamic.Columns.Add("第三变量值", typeof(int));hartDynamic.Columns.Add("第四变量单位代码", typeof(int));hartDynamic.Columns.Add("第四变量值", typeof(int));#endregion#region 发送命令0(读标识码)查询请求private void hartSendCmd0() //发送命令0(读标识码)查询请求{if (hartopenflag == 1){byte[] cmd = new byte[13];for (int i = 0; i < 8; i++){cmd[i] = 0xff; //第一次通信前导符为20个0xFF}cmd[8] = 0x02; //短帧,主从模式cmd[9] = 0x80; //主设备cmd[10] = 0x00; //命令0cmd[11] = 0x00; //数据长度0cmd[12] = Convert.ToByte( cmd[8] ^ cmd[9] ^ cmd[10] ^ cmd[11]); //水平校验serial_hart.Write(cmd, 0, 13);}}#endregion#region 发送无请求数据的长帧通用方法private void hartLongFrameCmdsend(byte cmdn) //发送无请求数据的长帧通用方法{byte[] cmd = new byte[17];if (cmdHeadInitFlag == 1) //获取到设备信息后{for (int i = 0; i < 14; i++){cmd[i] = cmdHead[i]; //帧头赋值}cmd[14] = 1; //命令1cmd[15] = cmdn; //无请求数据cmd[16] = cmd[8];for (int i = 9; i < 16; i++){cmd[16] = Convert.ToByte(cmd[16] ^ cmd[i]); //校验和}serial_hart.Write(cmd, 0, 17); //发送请求}}#endregion#region 无请求数据的长地址查询private void hartSendCmd1() //发送通用命令1(读主变量)查询请求{hartLongFrameCmdsend(1);}private void hartSendCmd2() //发送通用命令2(读主变量电流值和百分比)查询请求{hartLongFrameCmdsend(2);}private void hartSendCmd3() //发送通用命令3(读动态变量和主变量电流)查询请求{hartLongFrameCmdsend(3);}private void hartSendCmd12() //发送通用命令12(读消息)查询请求{hartLongFrameCmdsend(12);}private void hartSendCmd13() //发送通用命令13(读标签Tag)查询请求{hartLongFrameCmdsend(13);}private void hartSendCmd14() //发送通用命令14(读主变量传感器信息)查询请求{hartLongFrameCmdsend(14);}private void hartSendCmd15() //发送通用命令15(读主变量输出信息)查询请求{hartLongFrameCmdsend(15);}private void hartSendCmd16() //发送通用命令16(读最终装配号)查询请求{hartLongFrameCmdsend(16);}private void hartSendCmd48() //发送常用命令48(读附加器件状态)查询请求{hartLongFrameCmdsend(48);}#endregion#region 命令0返回值解析(标识码)private void hartGetCmd0() //获取命令0设备返回的数据并解析{//预期接收到设备返回的数据为://FF FF FF FF FF FF FF FF 06 80 00 0E 00 00 FE 12 08 08 05 00 01 00或01 00 00 设备ID高字节设备ID低字节int n = serial_hart.BytesToRead;int begin = 0;byte[] buf;if (n >24){buf = new byte[n];serial_hart.Read(buf, 0, n);for (int i = 0; i < 9; i++){if ((buf[i] == 0x06) & (buf[i + 1] == 0x80) & (buf[i + 2] == 0x0) &(buf[i + 3] == 12)){begin = i;if ((buf[i + 4] == 0) & (buf[i + 5] == 0)){manufacID = buf[i + 7];devType = buf[i + 8];cmdVersion = buf[i + 10];sfVersion = buf[i + 12];hwVersion = buf[i + 13];devID[0] = buf[i + 15];devID[1] = buf[i + 16];devID[2] = buf[i + 17];devIDResult = devID[1] * 256 + devID[2];hartGetCmdOk[0] = 1; //返回值接收成功标志置1cmdHeadInit(); //初始化命令帧头}else{errorstr = "数据长度有误!";pictureBox_Normal.Image = Properties.Resources.异常;label_Right.Text = "异常";label_Hint.Text = "Hart通用命令0" + errorstr;hartSendCmd0(); //未能获取到设备信息则重新发送请求}}}}}#endregion#region 数据解析查找帧头相关变量private int headbegin = 0; //分界符位置private int headFindFlag = 0; //找到分解符标志#endregion#region 判断返回值是否正确,并找到帧头(分界符位置)//判断帧头(定界符)位置private void findFrameHead(byte[] buf1, byte cmdn,byte datanum){for (int i = 0; i < 10; i++){if ((buf1[i] == 0x86) & (buf1[i + 1] == cmdHead[9]) &(buf1[i + 2] == cmdHead[10]) & (buf1[i + 3] == cmdHead[11]) &(buf1[i + 4] == cmdHead[12]) & (buf1[i + 5] == cmdHead[13]) &(buf1[i + 6] == cmdn) & (buf1[i + 7] == datanum)){if ((buf1[i + 8] == 0) & (buf1[i + 9] == 0)){headbegin = i;headFindFlag = 1;}if ((buf1[i + 8] == 0) & (buf1[i + 9] == 5)){errorstr = "命令" + cmdn.ToString() + "返回结果异常,字节数不对!!";pictureBox_Normal.Image = Properties.Resources.异常;label_Right.Text = "异常";label_Hint.Text = errorstr;}}}}#endregion#region 查询命令通用帧头设置private void cmdHeadInit() //初始化命令帧头,只有在获取到设备信息后才会有此标志为1{for (int i = 0; i < 8; i++){cmdHead[i] = 0xff;}cmdHead[8] = 0x82;cmdHead[9] = Convert.ToByte( manufacID);cmdHead[9] |=0x80;cmdHead[10] = Convert.ToByte(devType);for (int i = 0; i < 3; i++){cmdHead[i + 11] = Convert.ToByte(devID[i]);}cmdHeadInitFlag = 1;}#endregion#region 命令1返回值解析(主变量PV)//命令1返回值解析//预期返回值为:FF FF FF FF FF FF FF FF 86 #add1 #add2 #add3 #add4 #add5 0x01 0x05 00 00 #0 #1...#4// ///////*********前导符*****///定界符//**************地址********//命令//数据长度//状态//主变量单位代码//主变量private void hartGetCmd1(){int n = serial_hart.BytesToRead;byte[] buf;byte cmdn = 1;byte datanum = 5;if (n > 18){buf = new byte[n];serial_hart.Read(buf, 0, n);findFrameHead(buf, cmdn, datanum);if (headFindFlag == 1){PVUnitCd = buf[headbegin + 10];PVValue = BitConverter.ToSingle(buf, headbegin + 11);hartGetCmdOk[1] = 1;headFindFlag = 0;headbegin = 0;}else{hartSendCmd1();}}}#endregion#region 命令2返回值解析(主变量电流值和百分比)//命令2返回值解析//预期返回值为:FF FF FF FF FF FF FF FF 86 #add1 #add2 #add3 #add4 #add5 0x02 0x18 00 00 #0..#3 #4...#7// ///////*******8个前导符*****///定界符//*********5字节地址********//命令//数据长度//状态//主变量电流//主变量量程百分比private void hartGetCmd2(){int n = serial_hart.BytesToRead;byte[] buf;byte cmdn = 2;byte datanum = 8;if (n > 18){buf = new byte[n];serial_hart.Read(buf, 0, n);findFrameHead(buf, cmdn, datanum);if (headFindFlag == 1){PVCurrent = BitConverter.ToSingle(buf, headbegin + 10);PVPercentRange = BitConverter.ToSingle(buf, headbegin + 14);hartGetCmdOk[2] = 1;headFindFlag = 0;headbegin = 0;}else{hartSendCmd2();}}}#endregion#region 命令3返回值解析(主变量电流和动态变量)//命令3返回值解析//预期返回值为:FF FF FF FF FF FF FF FF 86 #add1 #add2 #add3 #add4 #add5 0x03 0x18 00 00 #0..#3 #4// ///////*******8个前导符*****///定界符//*********5字节地址********//命令//数据长度//状态//主变量电流//主变量单位代码// #5...#8 #9 #10...#13 #14 #15...#18 #19 #20...#23//主变量值*第二变量代码*第二变量值*第三变量代码*第三变量值*第四变量代码*第四变量值private void hartGetCmd3(){int n = serial_hart.BytesToRead;byte[] buf;byte cmdn = 3;byte datanum = 24;if (n >34 ){buf = new byte[n];serial_hart.Read(buf, 0, n);findFrameHead(buf, cmdn, datanum);if (headFindFlag == 1){PVCurrent = BitConverter.ToSingle(buf, headbegin + 10);PVUnitCd = buf[headbegin + 14];PVValue = BitConverter.ToSingle(buf, headbegin + 15);SecVUnitCd = buf[headbegin + 19];SecVValue = BitConverter.ToSingle(buf, headbegin + 20);ThirdVUnitCd = buf[headbegin + 24];ThirdVValue = BitConverter.ToSingle(buf, headbegin + 25);ForthVUnitCd = buf[headbegin + 29];ForthVValue = BitConverter.ToSingle(buf, headbegin + 30);hartGetCmdOk[3] = 1;headFindFlag = 0;headbegin = 0;}else{hartSendCmd3();}}}#endregion#region 命令12返回值解析(设备消息)//该命令用于读取设备消息// //预期返回值为:FF FF FF FF FF FF FF FF 86 #add1 #add2 #add3 #add4 #add5 0x0C 0x18 00 00 #0..#23// ////*******8个前导符*****///定界符//*********5字节地址********//命令//数据长度//状态//设备消息(ASCII)private void hartGetCmd12(){int n = serial_hart.BytesToRead;byte[] buf;byte cmdn = 12;byte datanum ;if (n > 18){buf = new byte[n];serial_hart.Read(buf, 0, n);datanum = Convert.ToByte(n-18); //是24还是实际字符串长度不确定findFrameHead(buf, cmdn, datanum);if (headFindFlag == 1){StringBuilder strb = new StringBuilder();for (int i = 0; i < datanum; i++){strb.Append((char)buf[headbegin + 10 + i]);}hartMsg = strb.ToString();hartGetCmdOk[4] = 1;headFindFlag = 0;headbegin = 0;}else{hartSendCmd12();}}}#endregion#region 命令13返回值解析(标签描述)//命令3返回值解析//预期返回值为:FF FF FF FF FF FF FF FF 86 #add1 #add2 #add3 #add4 #add5 0x0D 0x18 00 00 #0..#5 #6...#17// ///////*******8个前导符*****///定界符//*********5字节地址********//命令//数据长度//状态//设备的Tag//描述符// #18...#20// 日期:日月年private void hartGetCmd13(){int n = serial_hart.BytesToRead;byte[] buf;byte cmdn = 13;byte datanum=21;if (n >31){buf = new byte[n];serial_hart.Read(buf, 0, n);findFrameHead(buf, cmdn, datanum);if (headFindFlag == 1){StringBuilder strb = new StringBuilder();for (int i = 0; i < 6; i++){strb.Append((char)buf[headbegin + 10 + i]);}hartTag = strb.ToString();strb.Clear();for (int i = 6; i < 18; i++){strb.Append((char)buf[headbegin + 10 + i]);}hartDspt = strb.ToString();hartDate = buf[headbegin + 20].ToString() + "年" +buf[headbegin + 19].ToString() + "月" +buf[headbegin + 18].ToString() + "日";hartGetCmdOk[5] = 1;headFindFlag = 0;headbegin = 0;}else{hartSendCmd13();}}}#endregion#region 命令14返回值解析(主变量传感器信息)//命令14返回值解析//预期返回值为:FF FF FF FF FF FF FF FF 86 #add1 #add2 #add3 #add4 #add5 0x0E 0x10 00 00 #0..#2 #3// ///////*******8个前导符*****///定界符//*********5字节地址********//命令//数据长度//状态//传感器序列号//单位代码// #4...#7 #8...#11 #12...#15// 传感器上限*传感器下限*主变量最小精度private void hartGetCmd14(){int n = serial_hart.BytesToRead;byte[] buf;byte cmdn = 14;byte datanum = 16;if (n > 26){buf = new byte[n];serial_hart.Read(buf, 0, n);findFrameHead(buf, cmdn, datanum);if (headFindFlag == 1){PVSensNum = (buf[headbegin + 10] << 16) & 0xff0000 + (buf[headbegin + 11] << 8) & 0xff00 + buf[headbegin + 12];PVSensUnitCd = buf[headbegin + 13];PVUpSensLmt = BitConverter.ToSingle(buf, headbegin + 14);PVLowSensLmt = BitConverter.ToSingle(buf, headbegin + 18);PVMiniSpan = BitConverter.ToSingle(buf, headbegin + 22);hartGetCmdOk[6] = 1;headFindFlag = 0;headbegin = 0;}else{hartSendCmd14();}}}#endregion#region 命令15返回值解析(主变量输出信息)//命令15返回值解析//预期返回值为:FF FF FF FF FF FF FF FF 86 #add1 #add2 #add3 #add4 #add5 0x0F 0x10 00 00 #0 #1// ///////*******8个前导符*****///定界符//*********5字节地址********//命令//数据长度//状态//报警选择代码//传递功能代码// #2 #3...#6 #7...#10 #11..#14 #15 #16// 上下量程单位代码*主变量上限*主变量下限*阻尼值**写保护代码*发行商代码/*private int PVAlarmSelCd = 0; //主变量报警选择代码private int PVTransFuncCd = 0; //主变量传递功能代码private int PVRangeUnitCd = 0; //主变量上下量程单位代码private float PVUpRangeValue = 0; //主变量上限值private float PVLowRangValue = 0;//主变量下限值private float PVDampValue = 0; //主变量阻尼值,单位秒private int WriteProtectCd = 251; //写保护代码private int PLDCd = 18; //商标发行商代码,18-ABB公司*/private void hartGetCmd15(){int n = serial_hart.BytesToRead;byte[] buf;byte cmdn = 15;byte datanum = 17;if (n > 27){buf = new byte[n];serial_hart.Read(buf, 0, n);findFrameHead(buf, cmdn, datanum);if (headFindFlag == 1){PVAlarmSelCd = buf[headbegin + 10];PVTransFuncCd = buf[headbegin + 11];PVRangeUnitCd = buf[headbegin + 12];PVUpRangeValue = BitConverter.ToSingle(buf, headbegin + 13);PVLowRangValue = BitConverter.ToSingle(buf, headbegin + 17);PVDampValue = BitConverter.ToSingle(buf, headbegin + 21);WriteProtectCd = buf[headbegin + 25];PLDCd = buf[headbegin + 26];hartGetCmdOk[7] = 1;headFindFlag = 0;headbegin = 0;}else{hartSendCmd15();}}}#endregion#region 命令16返回值解析(最终装配号)//命令15返回值解析//预期返回值为:FF FF FF FF FF FF FF FF 86 #add1 #add2 #add3 #add4 #add5 0x0F 0x10 00 00 #0 #1// ///////*******8个前导符*****///定界符//*********5字节地址********//命令//数据长度//状态//报警选择代码//传递功能代码// #2 #3...#6 #7...#10 #11..#14 #15 #16// 上下量程单位代码*主变量上限*主变量下限*阻尼值**写保护代码*发行商代码private void hartGetCmd16(){int n = serial_hart.BytesToRead;byte[] buf;byte cmdn = 16;byte datanum = 3;if (n > 27){buf = new byte[n];serial_hart.Read(buf, 0, n);findFrameHead(buf, cmdn, datanum);if (headFindFlag == 1){FinalAssembleNum = buf[headbegin + 10] * 65536 + buf[headbegin + 11]* 256 + buf[headbegin + 12];hartGetCmdOk[8] = 1;headFindFlag = 0;headbegin = 0;}else{hartSendCmd16();}}}#endregionprivate void hartSaveConstantTable() //常量存入常量表{DataRow hartc = hartConstant.NewRow();hartc["ID"] = IDHartConstant;hartc["Time"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");hartc["制造商ID"] = manufacID;hartc["制造商设备类型"] = devType;hartc["通用命令文档版本号"] = cmdVersion;hartc["设备软件版本号"] = sfVersion;hartc["设备硬件版本号"] = hwVersion;hartc["设备ID号"] = devID;hartc["设备消息"] = hartMsg;hartc["标签Tag"] = hartTag;hartc["描述符"] = hartDspt;hartc["日期"] = hartDate;hartc["传感器序列号"] = PVSensNum;hartc["传感器上下限单位代码"] = PVSensUnitCd;hartc["传感器上限"] = PVUpSensLmt;hartc["传感器下限"] = PVLowSensLmt;hartc["主变量最小精度"] = PVMiniSpan;hartc["报警选择代码"] = PVAlarmSelCd;hartc["传递功能代码"] = PVTransFuncCd;hartc["上下量程值代码"] = PVRangeUnitCd;hartc["主变量上限值"] = PVUpRangeValue;hartc["主变量下限值"] = PVLowRangValue;hartc["主变量阻尼值"] = PVDampValue;hartc["写保护代码"] = WriteProtectCd;hartc["最终装配号"] = PLDCd;hartConstant.Rows.Add(hartc);}private void strHartCSet(StringBuilder str) //字符串赋值{str.Append("ID,");str.Append("Time,");str.Append("制造商ID,");str.Append("制造商设备类型,");str.Append("通用命令文档版本号,");str.Append("设备软件版本号,");str.Append("设备硬件版本号,");str.Append("设备ID号,");str.Append("设备消息,");str.Append("标签Tag,");str.Append("描述符,");str.Append("日期,");str.Append("传感器序列号,");str.Append("传感器上下限单位代码,");str.Append("传感器上限,");str.Append("传感器下限,");str.Append("主变量最小精度,");str.Append("报警选择代码,");str.Append("传递功能代码,");str.Append("上下量程值代码,");str.Append("主变量上限值,");str.Append("主变量下限值,");str.Append("主变量阻尼值,");str.Append("写保护代码,");str.Append("最终装配号");}public int[] hartViewItemSct = new int[6];/* private string[] hartConstItem = { "ID", "Time", "制造商ID", "制造商设备类型", "通用命令文档版本号","设备软件版本号", "", "", "", "","", "", "", "", "","", "", "", "", "","", "", "", "", "",};*/private void strHartDSet(StringBuilder str){str.Append("ID,");str.Append("Time,");str.Append("设备ID号,");str.Append("主变量单位代码,");str.Append("主变量值,");str.Append("主变量电流,");str.Append("主变量量程百分比,");str.Append("第二变量单位代码,");str.Append("第二变量值,");str.Append("第三变量单位代码,");str.Append("第三变量值,");str.Append("第四变量单位代码,");str.Append("第四变量值");}private void hartSaveConstantDb() //常量存入数据库{if (hartConstant.Rows.Count > 0){StringBuilder strHartC = new StringBuilder();strHartCSet(strHartC); //字符串赋值string saveStr = strHartC.ToString();int m = hartConstant.Rows.Count - 1;SqlCommand com = new SqlCommand(String.Format("insert into HartConstant('{0}')Values({1},'{2}',{3},{4},{5},{6},{7},{8},'{9}','{10}','{11}','{12}',{13},{14},{15},{16},{17},{18},{19},{20},{21},{22} ,{23},{24},{25})", saveStr, hartConstant.Rows[m]["ID"], hartConstant.Rows[m]["Time"], hartConstant.Rows[m]["制造商ID"],hartConstant.Rows[m]["制造商设备类型"], hartConstant.Rows[m]["通用命令文档版本号"],hartConstant.Rows[m]["设备软件版本号"], hartConstant.Rows[m]["设备硬件版本号"], hartConstant.Rows[m]["设备ID号"],hartConstant.Rows[m]["设备消息"], hartConstant.Rows[m]["标签Tag"],hartConstant.Rows[m]["描述符"], hartConstant.Rows[m]["日期"],hartConstant.Rows[m]["传感器序列号"], hartConstant.Rows[m]["传感器上下限单位代码"],hartConstant.Rows[m]["传感器上限"], hartConstant.Rows[m]["传感器下限"],hartConstant.Rows[m]["主变量最小精度"], hartConstant.Rows[m]["报警选择代码"],hartConstant.Rows[m]["传递功能代码"], hartConstant.Rows[m]["上下量程值代码"],hartConstant.Rows[m]["主变量上限值"], hartConstant.Rows[m]["主变量下限值"],hartConstant.Rows[m]["主变量阻尼值"], hartConstant.Rows[m]["写保护代码"],hartConstant.Rows[m]["最终装配号"]), dbcon);try{com.ExecuteNonQuery();}catch (Exception ex){MessageBox.Show(ex.Message);}}}private void hartSaveDynamicTable() //动态变量存入动态变量表{DataRow hartd = hartDynamic.NewRow();hartd["ID"] = IDHartDynamic;hartd["Time"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");hartd["设备ID号"] = devID;hartd["主变量单位代码"] = PVUnitCd;hartd["主变量值"] = PVValue;hartd["主变量电流"] = PVCurrent;hartd["主变量量程百分比"] = PVPercentRange;hartd["第二变量单位代码"] = SecVUnitCd;hartd["第二变量值"] = SecVValue;hartd["第三变量单位代码"] = ThirdVUnitCd;hartd["第三变量值"] = ThirdVValue;hartd["第四变量单位代码"] = ForthVUnitCd;hartd["第四变量值"] = ForthVValue;hartDynamic.Rows.Add(hartd);}private void hartSaveDynamicDb() //动态变量存入数据看{if (hartDynamic.Rows.Count > 0){StringBuilder strHartD = new StringBuilder();strHartDSet(strHartD); //字符串赋值string saveStr = strHartD.ToString();int m = hartDynamic.Rows.Count - 1;SqlCommand com = new SqlCommand(String.Format("insert into HartDynamic('{0}')Values ({1},'{2}',{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13})", saveStr, hartDynamic.Rows[m]["ID"], hartDynamic.Rows[m]["Time"], hartDynamic.Rows[m]["设备ID号"],hartDynamic.Rows[m]["主变单位代码"], hartDynamic.Rows[m]["主变量值"],hartDynamic.Rows[m]["主变量电流"], hartDynamic.Rows[m]["主变量量程百分比"],hartDynamic.Rows[m]["第二变量单位代码"], hartDynamic.Rows[m]["第二变量值"],hartDynamic.Rows[m]["第三变量单位代码"], hartDynamic.Rows[m]["第三变量值"],hartDynamic.Rows[m]["第四变量单位代码"], hartDynamic.Rows[m]["第四变量值"]), dbcon);try{com.ExecuteNonQuery();}catch (Exception ex){MessageBox.Show(ex.Message);}}}#region 存储常量(数据库、数据表)private void hartSaveConstant(){if (dbcon_flag == 1){if (dbconOpenNow == 0){dbconOpen();}string sqlquery = "select top 1 ID from HartConstant order by ID desc";try{SqlDataAdapter sda = new SqlDataAdapter(sqlquery, dbcon);DataSet ds = new DataSet();sda.Fill(ds);//把查询结果填充在ds里if (ds.Tables[0].Rows.Count == 1){IDHartConstant = Convert.ToInt32(ds.Tables[0].Rows[0][0]) + 1;}elseIDHartConstant = 0;}catch (Exception ex){MessageBox.Show(ex.Message);}hartSaveConstantTable();hartSaveConstantDb();if (dbconOpenNow == 1){dbconClose();}}else{if (hartConstant.Rows.Count > 0){IDHartConstant = Convert.ToInt32(hartConstant.Rows[hartConstant.Rows.Count - 1]["ID"]) + 1;}else{IDHartConstant = 0;}hartSaveConstantTable();}}#endregion#region 存储动态变量(数据库、数据表)private void hartSaveDynamic(){if (dbcon_flag == 1){if (dbconOpenNow == 0){dbconOpen();}string sqlquery = "select top 1 ID from HartDynamic order by ID desc";try{SqlDataAdapter sda = new SqlDataAdapter(sqlquery, dbcon);DataSet ds = new DataSet();sda.Fill(ds);//把查询结果填充在ds里if (ds.Tables[0].Rows.Count == 1){IDHartDynamic = Convert.ToInt32(ds.Tables[0].Rows[0][0]) + 1;}elseIDHartDynamic = 0;}catch (Exception ex){MessageBox.Show(ex.Message);}hartSaveDynamicTable();hartSaveDynamicDb();if (dbconOpenNow == 1){dbconClose();}}else{if (hartDynamic.Rows.Count > 0){IDHartDynamic = Convert.ToInt32(hartDynamic.Rows[hartDynamic.Rows.Count - 1]["ID"]) + 1;}else{IDHartDynamic = 0;}hartSaveDynamicTable();}}#endregionprivate int hartNum = 0; //HART收发计数器,偶数发查询命令,奇数解析设备返回数据private int hartCmdNum = 0; //控制HART发送解析具体命令private int hartInitFlag = 0; //Hart设备初始化标识private int hartWorkFlag = 0; //Hart设备工作标识private int hartSleepFlag = 0; //Hart设备暂停查询标识(两分钟查询一次变量)private int hartSleepNum = 0; //Hart设备休眠时间private int hartInitOkFlag = 0; //初始化完成标识,用于更新主界面常量相关的参数显示#region HART设备初始化,主要是查询设备标识及常量信息//HART初始化private void hartInit(){if ((hartNum % 2) == 0){#region 发送初始化需要的查询指令switch (hartCmdNum){case 0:hartSendCmd0();hartNum++;hartInitOkFlag = 0;break;case 12:。
HART协议与软件开发遵照一套标准的协议来开发一套系统,其最大的好处是不论硬件设备提供商还是软件开发人员,都可以独立地按照标准的规范进行设计,分别提供标准的接口,从而大大提高工作效率,在最后的软件和硬件的联合调试中会带来最大的方便。
由于HART协议是本数据平台软件和整个控制系统软件设计的理论依据,只有充分理解和消化了HART协议各层规范后,才可以根据具体规范对协议的各层进行软件实现,从而达到上位机软件与基于HART协议的现场仪表之间进行通信,以完成数据交换的目的。
目前,国内对HART协议进行系统的翻译,总结和消化的文献还不多见,因此在本章,郑州波特电子有限公司将根据在研究工作中对HART协议的消化和理解,简要地对在软件设计中涉及到的HART协议规范进行总结。
1.HART协议概述:HART(Highway Addressable Remote Transducer)协议,是一项4-20mA信号与数字通信技术兼容的过渡性标准,现已有Rosemount,Smar ,ABB,Fuji,Moore,E+H,Honeywell,Fisher Controls,Arcom Control Systems Ltd.等70多家公司参加了HART协议基金HCF。
由于HART协议众多不容置疑的优点,使它成为全球应用最为广泛的现场通信协议,1994年,HART变送器占世界智能变送器市场的76%,已成为事实上的工业标准。
据业内人士估计,HART协议在国际上的使用寿命为15-20年,国内由于客观条件所限,这个时间还会更长些,因此,在今后很长一段时间内,HART产品仍有十分广泛的市场。
HART协议保留了4-20mA过程控制信号的工业标准,允许在同一个环路上同时存在模拟信号和数字通信信号而不相互影响。
这一点是通过采用Bell202的通信标准实现的,Bell202采用频移键控FSK(Frequency Shift Keying)技术。
Hart协议HART(Highway Addressable Remote Transducer),可寻址远程传感器高速通道的开放通信协议,是美国Rosement公司推出的一种用于现场智能仪表和控制室设备之间的通信协议。
HART装置提供具有相对低的带宽,适度响应时间的通信,经过多年的发展,HART技术在国内外已经十分成熟,并已成为全球智能仪表的工业标准。
HART协议采用基于Bell202标准的FSK频移键控信号,在低频的4~20mA模拟信号上叠加幅度为0.5mA的音频数字信号进行双向数字通讯,数据传输率为1.2Mbps。
由于FSK信号的平均值为0,不影响传送给控制系统模拟信号的大小,保证了与现有模拟系统的兼容性。
在HART协议通信中主要的变量和控制信息由4~20mA传送,在需要的情况下的测量、过程参数、设备组态、校准、诊断信息通过HART协议访问。
HART通信采用的是半双工的通信方式,其特点是在现有模拟信号传输线上实现数字信号通信,属于模拟系统向数字系统转变过程中过渡性产品,因而在当前的过渡时期具有较强的市场竞争能力,得到了较快发展。
HART 规定了一系列命令,按命令方式工作。
它有三类命令,第一类称为通用命令,这是所有设备都理解、都执行的命令;第二类称为一般行为命令,所提供的功能可以在许多现场设备(尽管不是全部)中实现,这类命令包括最常用的现场设备的功能库;第三类称为特殊设备命令,以便于工作在某些设备中实现特殊功能,这类命令既可以在基金会中开放使用,又可以为开发此命令的公司所独有。
在一个现场设备中通常可发现同时存在这三类命令。
HART采用统一的设备描述语言DDL。
现场设备开发商采用这种标准语言来描述设备特性,由HART基金会负责登记管理这些设备描述并把它们编为设备描述字典,主设备运用DDL 技术来理解这些设备的特性参数而不必为这些设备开发专用接口。
但由于这种模拟数字混合信号制,导致难以开发出一种能满足各公司要求的通信接口芯片。
hart协议帧
HART (HART Communication Foundation) 协议是一种用于工业自动化领域的通信协议。
它使用无线信号进行通信,并支持多种数据传输速率和通信距离。
HART协议帧是一种用于传输数据的帧格式,它包含了数据、控制信息和校验码等部分。
下面是一个简单的HART 协议帧的示例:
发送和接收数据的设备。
指令代码用于标识要执行的操作,例如读取或写入数据。
数据长度字段指示数据部分的长度。
数据部分包含要传输的实际数据。
校验码用
于检查数据在传输过程中是否发生了错误。
帧尾是一个固定的字节,用于标识帧的结束。
HART协议帧使用曼彻斯特编码方式进行传输,这意味着每个字节的起始位和结束位都是通过电平跳变来标识的。
此外,HART协议还支持多种通信速率和通信距离,这使得它适用于各种工业自动化应用场景。
HART通信协议一、引言HART通信协议是一种用于工业自动化领域的数字通信协议,它能够在4-20mA摹拟信号传输中叠加数字信号,实现双向通信。
本协议旨在规范HART通信的数据格式、通信流程以及相关参数的定义,以便确保不同设备之间的互操作性和兼容性。
二、术语定义在本协议中,以下术语定义适合于所有相关的协议规范和文档:1. HART主站(Host):指通过HART通信协议与HART从站进行通信的设备。
2. HART从站(Device):指通过HART通信协议与HART主站进行通信的设备。
3. 主站命令(Command):指由HART主站发送给HART从站的指令,用于请求数据或者执行操作。
4. 从站响应(Response):指HART从站对主站命令的回复,包含请求的数据或者执行结果。
5. HART通信速率(Communication Rate):指HART通信中数据传输的速率,通常以位/秒(bps)为单位表示。
6. HART通信帧(Communication Frame):指HART通信中的数据传输单位,包含起始字节、命令字节、数据字节和校验字节。
三、通信流程HART通信协议的通信流程如下:1. 主站发送请求:HART主站通过发送命令帧向HART从站发起请求,命令帧包含命令字节和相关参数。
2. 从站响应请求:HART从站接收到主站的命令帧后,根据命令字节执行相应的操作,并将响应数据封装在响应帧中发送给主站。
3. 主站接收响应:HART主站接收到从站的响应帧后,解析响应数据并进行相应的处理。
4. 通信结束:通信过程完成后,主站和从站可以继续进行其他操作或者关闭通信连接。
四、通信帧格式HART通信帧的格式如下:1. 起始字节:用于标识通信帧的开始,固定为0xFF。
2. 命令字节:用于标识主站发送的命令类型,包括请求数据、执行操作等。
3. 数据字节:用于传输命令相关的数据,长度可变。
4. 校验字节:用于校验通信帧的完整性和正确性。
前段时间做了一部分有线HART的解析,整理了一下基本的帧结构,在此做个笔记HART帧结构:[cpp]view plain copy1.|-------------------------------------------------------------------|2.| PREAMBLE[5..20] | START | ADDR | COM | BCNT | STATUS | DATA | CHK |3.|-------------------------------------------------------------------|4.5.6.FF FF FF FF FF 82 A6 06 B2 BF 01 0F 00 211. PREAMBLE引导码, 一般是5..20个0xFF, 他是一组同步传输的同步信号, 用以保证信息的同步.在开始通讯的时候,使用的是20个FF引导码, 从机应答0信号时将告之主机他“希望”接收几个字节的引导码, 另外主机也可以用59号命令告诉从机应答时应用几位引导码.2. START(1Byte)起始字节, 说明结构为“长”还是“短”, 消息源, 是否是“突发”模式消息.[cpp]view plain copy1.0x02: 主机到从机的短帧2.0x82: 主机到从机的长帧3.0x06:从机到主机的短帧4.0x86: 从机到主机的长帧5.0x01: 突发模式的短帧6.0x81: 突发模式的长帧一般设备进行通讯接收到2个FF字节后, 就表示数据位的接收已经同步, 就将侦听起始位.3. ADDR(1/5Bytes)地址字节, 他包含了主机地址和从机地址, 短结构中占1字节, 长结构中占5字节.不论长短帧结构, HART协议中允许2个主机存在, 所以我们用首字节的最高位来进行区分,值为1表示第一主机地址, 第二主机用0表示.“突发”模式是特例, 0,1值将交替出现, 也就是说, 在该模式下, 赋予2个主机的机会均等.次高位为1表示为“突发”模式, 短结构用首字节的0~4位表示值为0~15的从机地址, 第5,6位赋0.长结构用后6位表示从机的生产厂商的代码, 第2个字节表示从机设备型号代码,后3~5个字节表示从机的设备序列号, 构成“唯一”标志码.MA: 主机地址BM: 突发模式0 0SA 从SA机SA 地SA 址短帧地址结构另外,长结构的低38位如果都是0的话表示的是广播地址,即消息发送给所有的设备。
HART通信协议VER 1.6一.概述HART(Highway Addressable Remote Transducer)协议采用基于Bell202标准的FSK频移键控信号,在低频的4-20mA模拟信号上叠加幅度为0.5mA的音频数字信号进行双向数字通讯,数据传输率为1200bps。
由于FSK信号的平均值为0,不影响传送给控制系统模拟信号的大小,保证了与现有模拟系统的兼容性。
在HART协议通信中主要的变量和控制信息由4-20mA传送,在需要的情况下,另外的测量、过程参数、设备组态、校准、诊断信息通过HART协议访问。
在应用层,HART 规定了一系列命令,按命令方式工作。
它有三类命令,第一类称为通用命令,这是所有设备都理解、执行的命令;第二类称为普通应用命令,所提供的功能可以在许多现场设备(尽管不是全部)中实现;第三类称为设备专用命令,以便于工作在某些设备中实现特殊功能,这类命令既可以在基金会中开放使用,又可以为开发此命令的公司所独有。
二.含义1、主机(Master):分为第一主机和第二主机,它能连接在HART网络上,发出命令与从机通讯;2、从机(Field Device):连接在过程控制现场中,具有测量、计算多种变量功能,并能与主机进行HART通讯的设备;3、长帧(Long Frame):地址Address由5字节组成的HART数据帧,所有的HART命令都支持长帧数据帧。
长帧地址各字节含义如下图所示:制造商ID 号(Manufacturer ID )由HART 基金会分配;在生产中,同类型设备序列号唯一,并且永不重复。
广播地址使用长帧结构,低38 BIT 全为0。
厂商指定的设备类型(Decive Type )与制造商ID 满足下表要求;设备类型代码指定原则4、 短帧(Short Frame ):地址Address 由1字节组成的HART 数据帧,短帧只有通用命令Command 0能够使用,短帧字节含义如下:所有的HART 从机都支持长、短帧。
HART命令0:读标识码返回扩展的设备类型代码,版本和设备标识码。
请求:无响应:字节0:254字节1:制造商ID字节2:制造商设备类型字节3:请求的前导符数字节4:通用命令文档版本号字节5:变送器规范版本号字节6:设备软件版本号字节7:设备硬件版本号字节8:设备标志字节9-11:设备ID号HART命令1:读主变量(PV)以浮点类型返回主变量的值。
请求:无响应:字节0:主变量单位代码字节1-4:主变量HART命令2:读主变量电流值和百分比读主变量电流和百分比,主变量电流总是匹配设备的AO输出电流。
百分比没有限制在0-100%之间,如果超过了主变量的范围,会跟踪到传感器的上下限。
请求:无响应:字节0-3:主变量电流,单位毫安字节4-7:主变量量程百分比HART命令3:读动态变量和主变量电流读主变量电流和4个(最多)预先定义的动态变量,主变量电流总是匹配设备的AO输出电流。
每种设备类型都定义的第二、第三和第四变量,如第二变量是传感器温度等。
请求:无响应:字节0-3:主变量电流,单位毫安字节4:主变量单位代码字节5-8:主变量字节9:第二变量单位代码字节10-13:第二变量字节14:第三变量单位代码字节15-18:第三变量字节19:第四变量单位代码字节20-23:第四变量HART命令4:保留HART命令5:保留HART命令6:写POLLING地址这是数据链路层管理命令。
这个命令写Polling地址到设备,该地址用于控制主变量AO 输出和提供设备标识。
只有当设备的Polling地址被设成0时,设备的主变量AO才能输出,如果地址是1~15则AO处于不活动状态也不响应应用过程,此时AO被设成最小;并设置传输状态第三位——主变量模拟输出固定;上限/下限报警无效。
如果Polling地址被改回0,则主变量AO重新处于活动状态,也能够响应应用过程。
请求:字节0:设备的Polling地址响应:字节0:设备的Polling地址HART命令7:HART命令8:HART命令9:HART命令10:HART命令11:用设备的Tag读设备的标识读与工位号相关的唯一标示符这是一个数据链路层管理命令。
前段时间做了一部分有线HART的解析,整理了一下基本的帧结构,在此做个笔记HART帧结构:[cpp]view plain copy1.|-------------------------------------------------------------------|2.| PREAMBLE[5..20] | START | ADDR | COM | BCNT | STATUS | DATA | CHK |3.|-------------------------------------------------------------------|4.5.6.FF FF FF FF FF 82 A6 06 B2 BF 01 0F 00 211. PREAMBLE引导码, 一般是5..20个0xFF, 他是一组同步传输的同步信号, 用以保证信息的同步.在开始通讯的时候,使用的是20个FF引导码, 从机应答0信号时将告之主机他“希望”接收几个字节的引导码, 另外主机也可以用59号命令告诉从机应答时应用几位引导码.2. START(1Byte)起始字节, 说明结构为“长”还是“短”, 消息源, 是否是“突发”模式消息.[cpp]view plain copy1.0x02: 主机到从机的短帧2.0x82: 主机到从机的长帧3.0x06:从机到主机的短帧4.0x86: 从机到主机的长帧5.0x01: 突发模式的短帧6.0x81: 突发模式的长帧一般设备进行通讯接收到2个FF字节后, 就表示数据位的接收已经同步, 就将侦听起始位.3. ADDR(1/5Bytes)地址字节, 他包含了主机地址和从机地址, 短结构中占1字节, 长结构中占5字节.不论长短帧结构, HART协议中允许2个主机存在, 所以我们用首字节的最高位来进行区分,值为1表示第一主机地址, 第二主机用0表示.“突发”模式是特例, 0,1值将交替出现, 也就是说, 在该模式下, 赋予2个主机的机会均等.次高位为1表示为“突发”模式, 短结构用首字节的0~4位表示值为0~15的从机地址, 第5,6位赋0.长结构用后6位表示从机的生产厂商的代码, 第2个字节表示从机设备型号代码,后3~5个字节表示从机的设备序列号, 构成“唯一”标志码.MA: 主机地址BM: 突发模式0 0SA 从SA机SA 地SA 址短帧地址结构另外,长结构的低38位如果都是0的话表示的是广播地址,即消息发送给所有的设备。
4. COM(1Byte)命令字节, 范围为253个, 用HEX的0~FD表示. 31,127,254,255为预留值。
5. BCNT(1Byte)数据总长度,他的值表示的是BCNT下一个字节到最后(不包括校验字节)的字节数.接收设备用他可以鉴别出校验字节, 也可以知道消息的结束.因为规定数据最多为25字节, 所以他的值是从0~27。
6. STATUS(2Bytes)状态字节, 他也叫做“响应码”,顾名思义, 他只存在于从机响应主机消息的时候, 用2字节表示.他将报告通讯中的错误\接收命令的状态(如:设备忙、无法识别命令等)和从机的操作状态。
如果我们在通讯过程中发现了错误, 首字节的最高位(第7位)将置1,其余的7位将汇报出错误的细节,而第2个字节全为0.否则,当首字节的最高位为0时,表示通讯正常,其余的7位表示命令响应情况,第2个字节表示场设备状态的信息。
UART发现的通讯错误一般有:奇偶校验、溢出和结构错误等。
命令响应码可以有128个, 表示错误和警告, 他们可以是单一的意义, 也可以有多种意义,我们通过特殊命令进行定义、规定.现场设备状态信息用来表示故障和非正常操作模式。
7. DATA(BCNT-2)数据字节,首先我想说明的是并非所有的命令和响应都包含数据字节,他最多不超过25字节(随着通讯速度的提高,正在要求放宽这一标准)。
数据的形式可以是无符号的整数(可以是8,16,24,32 b),浮点数(用IEEE754单精浮点格式)或ASCII字符串,还有预先制定的单位数据列表。
具体的数据个数根据不同的命令而定。
8. CHK(1Byte)奇偶校验,方式是纵向奇偶校验,从起始字节开始到奇偶校验前一个字节为止。
另外,每一个字节都有1位的校验位,这两者的结合可以检测出3位的突发错误。
下面是几组实际通讯中抓包的示例:FF FF FF FF FF0280 // addr000082FF FF FF FF FF FF0680000E00 40FE 26 19 06 05 05 02 A0 00 91 F4 A5FE 26 19 06 05 05 02 A0 00 91 F4 A56D上面是主机到从机发送的一条消息。
前5个字节值都为FF,显然他是导言字节。
接着的82起始字节,表示主机到从机发出的长结构的消息。
后5个字节“A6,可见首字节A6的最高位为1表示主机,次高位为0表示非突发模式,后面的38 b表示设备的惟一标号:“100110”是生产厂家代码,值为38,是Rosemount公司的代码;后一字节06是设备型号代码,06代表的型号是3051C;后面的3个字节是设备识别号,本例中的值为12345678;再接下来的01是命令字节,表示1号命令,即读取PV值后面的00是表示数据的长度;本例中无数据,值为0;最HART协议的从机到主机的例子,表示的是从机到主机的一条消息。
本例大部分与例1相似,不同的是数据字节不再为0,其中的06表示单位PSI;后面的4个字节是用浮点数表示的值,为5.5。
并且由于本例是由从机到主机的应答消息,上面是突发模式HART协议发出的一条消息。
第1个字节81表示突发的长结构模式,与前例中相似的地方我们不再介绍。
注意到状态字节“00 60”后的字节“41 3FA000”,他表示的是当前的电流值,计算后是11.976 6;后面的27表示单位mA,像后面的39表示“%”一样。
数据字节中的“42 47 60 00”,“BF06 60 00”,“41 95 0000”分别表示“SV”,“TV”,“FV”表示方法与PV相同。
经过解释后的消息可以表示为:“LBTXS/RdAllPv/026/0060/11.9766/mA/11.9766/%/49.8438/psi/-0.524902/%/18.625/D4”。
-------------------------------华丽的分割线------------------------------------------FF FF FF FF FF FF 02 80 00 00 82以上就是一段单片机首先要跟设备发送的命令,接下来我们来解析一下FF FF FF FF FF 02 80 00 00 82先导码短指令地址0 命令0 0个数据异或校验位第一条指令是所有支持hart协议的设备都要懂得先导码:就是一段命令的开始,一般都是5个FF指令码:此处02表示的是告诉设备我现在发送过去的是一条短指令当然还有其他的指令结构:主机到从机为短结构时,起始位为02,长帧时为82。
从机到主机的短结构值为06,长结构值为86“突发”模式的短结构值为01,长结构为81• 1• 2• 3• 4 地址码:包含了主机地址和从机地址短指令时:地址码由一个字节表示(如80),其结构为一般我们不用到突发模式,这里的80表示的就是(主机1—-设备地址(0))长指令时:地址码由5个字节表示,其结构为命令码:如00表示的是命令0,这里的命令设备能够识别从而做出相应的响应数据个数码:表示的是再次之后的第一个数——最后一个(不包括校验位)的个数,这一位很重要,在编程的时候,我们经常用这个来调用数值响应吗:只存在于从机—主机的过程中(从机对主机的响应)校验码:此码主要是用来确保传输没有问题用的,采用的是异或校验(自己可以去百度一下异或校验的意思)(这一位都是根据前面的数值锁确定的)各种码都讲了一遍,现在我贴上几张自己试验获得的码,大家可以对着分析一下:FF FF FF FF FF 82 A6 06 63 56 BA 01 00 ACFF FF FF FF FF 82 A6 06 63 56 BA 02 00 AFHART协议1.1 简介现代工业生产中存在着多种不同的主机和现场设备,要想很好地使用他们,完善的通讯协议是必须的。
HART协议最初是由美国Rosemount公司开发,已应用了多年。
HART协议使用FSK技术,在4~20mA信号过程量上叠加一个频率信号,成功地把模拟信号和数字信号双向同时通讯,而不互相干扰。
HART协议参照了国际标准化组织的开放性互连模型,使用OSI标准的物理层、数据链路层、应用层。
HART协议规定了传输的物理形式、消息结构、数据格式和一系列操作命令,是一种主从协议。
当通讯模式为“问答式”的时候,一个现场设备只做出被要求的应答。
HART协议允许系统中存在2个主机(比如说,一个用于系统控制,另一个用于HART通信的手操仪),如果不需要模拟信号,多点系统中的一对电缆线上最多可以连接15个从设备。
1.2 物理层物理层规定了信号的传输方法、传输介质。
采用Bell202标准的FSK频移键控信号,在低频的4~20mA模拟信号上叠加一个频率数字信号进行双向数字通信。
数字信号的幅度为0.5mA,数据传输率为1200bps,1200Hz代表逻辑“1”,2200Hz代表逻辑“0”。
数字信号波形如下图所示:1.3 数据链路层数据链路层规定HART协议帧的格式,可寻址范围0~15,“0”时,处于4~20mA及数字信号点对点模式,现场仪表与两个数字通信主设备(也称作通信设备或主设备)之间采用特定的串行通信,主设备包括PC机或控制室系统和手持通信器。
单站操作中,主变量(过程变量)可以以模拟形式输出,也可以以数字通信方式读出,以数字方式读出时,轮询地址始终为0。
也就是说,单站模式时数字信号和4~20mA模拟信号同时有效。
“1~15”处于全数字通信状态,工作在点对多点模式,通信模式有“问答”式、“突发”式(点对点、自动连续地发送信息)。
按问答方式工作时的数据更新速率为2~3次/s,按突发方式工作时的数据更新速率为3~4次/s。
在本质安全要求下,只使用一个电源,至多能连接15台现场仪表,每个现场设备可有256个变量,每个信息最大可包含4个变量。
这就是所谓的多点(多站)操作模式。
这种工作方式尤其适用于远程监控,如管道系统和油罐储存场地。
采用多点模式,4~20mA的模拟输出信号不再有效(输出设在4mA使功耗最小,主要是为变送器供电,各个现场装置并联连接),系统以数字通信方式依次读取并联到一对传输线上的多台现场仪表的测量值(或其它数据)。