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

  1. 要知道连接PLC的通讯协议,波特率,奇偶校验,数据位等信息的获取,请自行查询。我使用的测试PLC协议为 9600 7 E 1
  2. 读数据命令格式

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();

}