在项目中有一台FX2N系列的PLC需要通过C#以232协议的方式读写数据,查找了很多资料,都没有具体的C#的读写方法,因此我记录一下详细的操作步骤。
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();
}