該通信規約適用於本地系統中多功能表的費率裝置與手持單元(HHU)或其它數據終端設備進行點對點的或一主多從的數據交換方式,規定了它們之間的物理連接、通信鏈路及應用技術規范。該協議在做“電力監控”、“水監控”等“工控領域”,具有廣泛的應用。協議具體內容可以從網上Google,下面主要介紹下協議的解析方法。
DL645協議需要先發送“前導字節”,用來喚醒從站,當然有些設備是不需要這樣喚醒的。在發送幀信息之前,先發送1-4個字節FEH,以喚醒接收方("FEH",H表示16進制,是一個字節), “前導字節” + “取數據報文”=“整體報文”。
首先我們先整體了解一個發送報文“68 12 34 56 78 90 12 68 01 02 43 C3 cs 16”
在這之前呢,先看一下BCD碼。
BCD碼:二進制編碼的十進制代碼。
比如:
BCD碼都是由0~9,10個字符構成。
也就是說,BCD碼一個字節是兩位,一位是0~9,一個字節能夠表示00~99。
幀格式(DL/T645-1997通訊規約標准文檔中)
幀是傳送信息的基本單元。幀格式如圖所示。
說 明 |
代碼 |
幀起始符 |
68H |
地址域 |
A0 |
A1 |
|
A2 |
|
A3 |
|
A4 |
|
A5 |
|
幀起始符 |
68H |
控制碼 |
C |
數據長度域 |
L |
數據域 |
DATA |
校驗碼 |
CS |
結束符 |
16H |
4.2.1 幀起始符68H:標識一幀信息的開始,其值為68H=01101000B。
4.2.2地址域A0∽A5:地址域由6個字節構成,每字節2位BCD碼。地址長度為12位十進制數,可以為表號、資產號、用戶號、設備號等。具體使用可由用戶自行決定。當使用的地址碼長度不足6字節時,用十六進制AAH補足6字節。低地址位在先,高地址位在后。當地址為999999999999H時,為廣播地址。
4.2.3 控制碼C:控制碼的格式如下所示。
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
功能碼
后續幀標志
從站異常標志
傳送方向
D7=0:由主站發出的命令幀
D7=1:由從站發出的應答幀
D6=0:從站正確應答
D6=1:從站對異常信息的應答
D5=0:無后續數據幀
D5=1:有后續數據幀
D4∽D0:請求及應答功能碼
00000:保留
00001:讀數據
00010:讀后續數據
00011:重讀數據
00100:寫數據
01000:廣播校時
01010:寫設備地址
01100:更改通信速率
01111:修改密碼
10000:最大需量清零
4.2.4 數據長度L:L為數據域的字節數。讀數據時L≤200,寫數據時L≤50,L=0 表示無數據域。
4.2.5 數據域DATA:數據域包括數據標識和數據、密碼等,其結構隨控制碼的功能而改變。傳輸時發送方按字節進行加33H處理,接收方按字節進行減33H處理。
4.2.6 校驗碼CS:從幀起始符開始到校驗碼之前的所有各字節的模256的和, 即各字節二進制算術和,不計超過256的溢出值。
4.2.7結束符號16H:標識一幀信息的結束,其值為16H=00010110B。68 12 34 56 78 90 12 68 01 02 43 C3 cs 16
說明如下:
68(幀起始符)
12 34 56 78 90 12 (6字節,沒字節兩位,共12位地址,BCD碼表示。)
68(幀起始符)
01(控制碼“包含功能碼”= 01 = 讀取數據)
02 (數據域長度,指 “43,C3”一共是兩字節。)
“43 c3” (“43 C3”是數據域,表示讀取數據類型 = “10 90”, 發送報文中 數據域需要做“+0x33處理”)
cs(加校驗和,一個字節,根據前面的計算)
16(幀結束符)
下面詳細的解釋下:
幀起始符(645協議規定由68H開始,16進制。)
0x12(BCD數值碼) --- 12(string) 轉為字符串0x12.ToString("X2")
0x45(BCD數值碼) --- 45(string)
如果表地址為:000021010203(電表上看到的地址串),里面就是12位(字符串來看)
通訊時,使用 0x00, 0x00 , 0x21 ,0x01 , 0x02 , 0x03, 6個字節表示地址,但發送的時候要先發低字節,再發高字節。發送時也是“0x03,0x02,0x01, 0x21,0x00,0x00”
控制碼:
從電腦到表具:0(主從站方向)+0(設備沒問題)+0(有無后續幀)+00001(讀)=1=0x1
數據域長度:表示數據域有多長
串口傳輸方式:所有數據項均先傳送低位字節,后傳送高位字節
數據域內容,也就是“數據類型+數據項”(比如電表): 1001(電能量)+00(當前)+00(有功)+0001(正向電能)+0000(總點能)
02(數據域長度)
10
90(數據域內容,兩個字節) 拼接后的報文,要先發低字節(90) ,再發10. 發送報文要加上"33",最后數據域就是"10 + 33"和"90 + 33"最終就是“ 43 c3”。
02 10 90 == 02 (10 + 33) (90 + 33) = 02 43 c3
返回報文:
68 12 34 56 78 90 12 68 81 06 43 c3 33 44 55 66 cs 16
68(幀起始符)
12 34 56 78 90 12(地址域)
68(幀起始符)
81(控制碼,返回有功總電能“10000001”,讀取數據)
06(數據域長度)
43 C3(數據域)
33 44 55 66 (數據項,四字節)
數據項要減去“33H”,根據數據類型(附錄)點小數點。
33 44 55 66 (數據域數據計算) = (33 - 33) (44 - 33) (55 - 33) (66 - 33) = 00112233 = 1122.33 kWh
最后我們來看一個電表實例,取地址為“694561”的電表的“反相有功電能(發電量)”。
實例如下:
一、發送報文
1、電表地址:694561(地址,6位BCD碼,BCD碼1位是2進制的4位。按16進制表示,沒有ABCDEF。不足12位,需要在高位“補0”。)。
2、完善地址:000000694561(12位BCD碼)
3、前導符字節:0xFE
4、幀起始符:0x68
5、地址域:0x614569000000(第字節在前,高字節在后。DL645協議,先發低字節再發高字節。)
6、幀起始符:0x68
7、控制碼:0x01(讀數據1,讀后續數據2,其中還包含功能碼等。)
8、數據域長度:0x02(2字節)
9、待發送數據類型(數據類型放在數據域中):9(1001,電能量)+0(0000,00當前,00有功)+ 2(0010,反向電能)+0(0000,總電能)
10、數據域+33H:0x90+0x33=0xC3 0x20+0x33=0x53
11、發送數據域:0x53 0xC3
12、校驗碼:CS(1個字節,比如計算出來的是“0x01”)
13、結束符:0x16
14、發送字符串:0xFE 0x68 0x61 45 69 00 00 00 0x68 0x01 0x02 0x53 0xC3 0x01 0x16
15、Byte[] bufferlist=new Byte[] {0xFE,0x68,0x61,0x45,0x69,0x00,0x00,0x00,0x68,0x01,0x02,0x53,0xC3,0x01,0x16 }
DL645編碼過程:
增加“前導符字節”(可以沒有)
增加幀起始符
增加地址域
增加幀起始符
增加控制碼
增加數據域長度
增加數據域
計算CS校驗碼,並增加
增加結束符
二、返回報文
1、幀起始符:0x68
2、地址域:0x614569000000
3、幀起始符:0x68
4、控制碼:傳送方向(0—電腦到表具,1—表具到電腦),從站異常標志(0—表具無異常,1—表具有異常),后續幀標識(0—無后續數據,1—有后續數據)
功能碼(讀數據):00001
最后是10000001(讀反相有功電能。從站向主站發送,第一位是“1”。主站向從站,第一位是“0”。),也就是“0x81”
5、數據域長度:
返回數據長度,根據請求的數據類型來定的。比如,電能量是“6個字節”,包含“2的數據類型”和“4字節的數據項”。
0x06
6、數據域
數據類型:0x53(低字節先返回) 0xC3(高字節后返回)
數據:0x33 0x78 0x34 0x66(先返回低字節,再返回高字節。電表是發送方。)
7、數據減去“33H”
0x33-0x33=0x00
0x78-0x33=0x45
0x34-0x33-0x01
0x66-0x33=0x33
8、按BCD碼解析16進制數
single datas = single.parse(0x33.ToString("X2");+0x01.ToString("X2");+0x45.ToString("X2");+"."+0x00.ToString("X2");)
9、最后結果
datas=datas+" kWh";
結果就是:330145.00 kWh (電表總反相有功電能量,也就是“發電量”。耗電量是“正相有功電能量”)
DL645解碼過程:
--幀起始符
if 字節長度 = 1
{
if(字節!=0x68)
{
清除字節
程序等待
}
}
--地址
if 字節長度 = 7
{
循環判斷地址字段
{
if(地址不相等)
{
清除字節
程序等待
}
}
}
--幀起始符
if 字節長度 = 8
{
if(字節!=0x68)
{
清除字節
程序等待
}
}
--控制碼
if 字節長度 = 9
{
if(不等於0x81)
{
清除字節
程序等待
}
}
--計算報文總長度
if 字節長度 >= 10
{
報文總長度=1 幀起始符 + 6 地址+ 1 幀起始符+ 1 控制碼 + 1 數據域長度+ 1 校驗碼 + 1 結束符 + “數據域長度”
}
--校驗
if 報文總長度大於 >= 計算報文長度
{
計算校驗碼
if(校驗碼 != 被校驗報文 && 結束符 != 0x16)
{
清除字節
程序等待
}
}
處理數據域數據
寫入處理數據域數據