在項目中有一台FX2N系列的PLC需要通過C#以232協議的方式讀寫數據,查找了很多資料,都沒有具體的C#的讀寫方法,因此我記錄一下詳細的操作步驟。
- 要知道連接PLC的通訊協議,波特率,奇偶校驗,數據位等信息的獲取,請自行查詢。我使用的測試PLC協議為 9600 7 E 1
- 讀數據命令格式
02H 30H 起始地址1 讀取長度 03H 校驗1 校驗2
說明:
02H代表命令開始
30H代表讀操作
03H代表命令結束
起始地址計算方法:
ex: 讀D123,D124兩個點的數據
D123對應地址計算方法為:
123*2=246
246對應十六進制為F6H
Address=F6H+1000H=10F6H
故實際地址未 31H 30H 46H 36H
加入需要讀取D123,D124兩個地址,因為D的長度為2,所以讀取長度應該為2*2=4
故長度為 30H 34H
校驗計算方法為:
02H后道03H為止(包含03H)相加,然后取最后兩位得到校驗數據
如02H 30H 31H 30H 46H 36H 30H 34H 03H 校驗1 校驗2
30H+31H+30H+46H+36H+30H+34H+03H=174H
故校驗1=37H 校驗2=34H
所以,完整的命令如下
02H 30H 31H 30H 46H 36H 30H 34H 03H 37H 34H
示例,下面是獲取D120-D125 六個點的數據
命令為:
120*2=240 對應十六進制為F0
F0+1000=10F0
長度為6*2=12所以為C對應命令為30H 43H
校驗位為30H+31H+30H+46H+30H+30H+43H+03H=17D 故校驗位為37H 44H
02H 30H 31H 30H 46H 30H 30H 43H 03H 37H 44H
返回數據格式為:
02H 數據1.... 03H 校驗1 校驗2
執行上面的示例命令得到下面返回
02 32 30 30 30 43 38 30 31
34 43 30 30 32 32 30 30 34 31 30 30 35 36 30 30 03 43 43
解析方式:
每個數據由4個字節構成
例如上面返回總共6個數據,所以占24個字節
前四個字節32 30 30 30 構成第一個數據
數據構成規格為L1L2H1H2所以實際數據應該是30 30 32 30所以實際數據的十六進制為20對應十進制為32
同理第二個數據為43 38 30 31實際數據應為01C8,十六進制1C8對應十進制456
下面是C#讀取數據的源代碼:
232協議配置
SerialPort sp=new SerialPort();
sp.DataReceived += Sp_DataReceived;
sp.PortName = "COM5";
sp.DataBits = 7;
sp.BaudRate = 9600;
sp.Parity = Parity.Even;
sp.StopBits = StopBits.One;
sp.Open();
命令解析代碼
byte[] wD = new byte[11];
wD.Initialize();
wD[0]= 0x02;//起始位
wD[1] = 0x30;//30讀,31寫
if (txtStartAddress.Text.StartsWith("D"))
{
int addSou = Convert.ToInt32(txtStartAddress.Text.Substring(1));
if (addSou > 9999 || addSou < 0)
{
MessageBox.Show("地址范圍為0-9999");
return;
}
addSou *= 2;
int addBase = 4096;//1000H對應10進制為4096
int startAdd = addBase + addSou;
string strAdd = startAdd.ToString("x8").TrimStart('0').ToUpper().PadLeft(4, '0');
wD[2] = Convert.ToByte(strAdd[0]);
wD[3] = Convert.ToByte(strAdd[1]);
wD[4] = Convert.ToByte(strAdd[2]);
wD[5] = Convert.ToByte(strAdd[3]);
}
int length = Convert.ToInt32(txtLength.Text);
length *= 2;
string strLen = length.ToString("x8").TrimStart('0').ToUpper().PadLeft(2, '0');
wD[6] = Convert.ToByte(strLen[0]);
wD[7] = Convert.ToByte(strLen[1]);
wD[8] = 0x03;//結束位
int total = 0;
for (int i = 1; i < 9; i++)
{
total += Convert.ToInt32(wD[i].ToString());
}
string cal = total.ToString("x8").TrimStart('0').ToUpper();
cal = cal.Substring(cal.Length - 2).PadLeft(2, '0');
wD[9] = Convert.ToByte(cal[0]);
wD[10] = Convert.ToByte(cal[1]);
sp.Write(wD, 0, 11);
數據解析代碼
byte[] buffer = new byte[sp.BytesToRead];
sp.Read(buffer, 0, buffer.Length);
for (int i = 0; i< length; i++)
{
//返回數據的格式為L1L2H1H2,所以下面需要轉換順序
string d1 = Encoding.ASCII.GetString(buffer, i * 4 + 3, 1);
string d2 = Encoding.ASCII.GetString(buffer, i * 4 + 4, 1);
string d3 = Encoding.ASCII.GetString(buffer, i * 4 + 1, 1);
string d4 = Encoding.ASCII.GetString(buffer, i * 4 + 2, 1);
string temp = string.Format("{0}{1}{2}{3}", d1, d2, d3, d4);
string result = int.Parse(temp, NumberStyles.HexNumber).ToString();
}