您的位置:首页 > 编程语言 > C#

C# SerialPort 读写三菱FX系列PLC

2016-06-07 22:53 519 查看
1:串口初始化  

com = new SerialPort("COM3", 9600, Parity.Even, 7, StopBits.One);
2:打开关闭串口
if (com.IsOpen)

 {com.Close();}
com.Open();
 
if (com.IsOpen)

{ com.Close();}
3:C# ASCII转字符及字符转ASCII
public static string Chr(int asciiCode)

        {

            if (asciiCode >= 0 && asciiCode <= 255)

            {

                System.Text.ASCIIEncoding asciiEncoding = new System.Text.ASCIIEncoding();

                byte[] byteArray = new byte[] { (byte)asciiCode };

                string strCharacter = asciiEncoding.GetString(byteArray);

                return (strCharacter);

            }

            else

            {

                throw new Exception("ASCII Code is not valid.");

            }

        }
 
public static int Asc(string character)

        {

            if (character.Length == 1)

            {

                System.Text.ASCIIEncoding asciiEncoding = new System.Text.ASCIIEncoding();

                int intAsciiCode = (int)asciiEncoding.GetBytes(character)[0];

                return (intAsciiCode);

            }

            else

            {

                throw new Exception("Character is not valid.");

            }

        }
 
4:写入串口的命令字符串的和校验
 
/// <summary>

        /// 和校验

        /// </summary>

        /// <param name="data"></param>

        /// <returns></returns>

        public string SumCheck(string data)

        {

            int sum = 0;

            for (int i = 0; i < data.Length; i++)

            {

                sum += Asc(data.Substring(i, 1));

            }

            string res = sum.ToString("X");

            res = res.Substring(res.Length - 2, 2);

            return res;

        }
 
 5:写入PLC
private void btnWrite_Click(object sender, EventArgs e)

        { 

            string[] write = new string[] { "2","2"}; //将准备写入PLC的值

            //将要写入的值转换成16进制数,补齐两个字节,注意高低字节需要交换

            string sWriteData = "";

            for (int i = 0; i < write.Length; i++)

            {

                int val = Convert.ToInt32(write[i].Length>0?write[i]:"0");

                string s = val.ToString("X");

                while (s.Length<4)

                {

                    s = "0" + s;

                }

                sWriteData += s.Substring(2,2)+s.Substring(0,2);

            }

            MessageBox.Show(sWriteData);

            //写入命令,1表示写入,1194表示D202这个地址的16进制,04表示D202,D203为4个BYTE,1194=(202*2)+4096的16进制数,至于用它表示D202的起始位置,三菱故意要这么麻烦了.

            sWriteData = "1119404" + sWriteData + Chr(3);

            //chr(2)和chr(3)是构成命令的标志字符,然后加上校验和,命令组织完成

            sWriteData = Chr(2) + sWriteData + SumCheck(sWriteData);

            MessageBox.Show(sWriteData);

            //写入串口

            com.Write(sWriteData);

            //byte[] data = Encoding.ASCII.GetBytes(sWriteData); 

            //com.Write(data,0,data.Length);

        }
 
6:读PLC
 private void btnRead_Click(object sender, EventArgs e)

        {

            

            this.txtRead0.Clear();

            string sReadData = "";

            //在读PLC中的数据之前,需要先发个指令给它,让它将数据发送到串口,下面的字符串中,chr(2),chr(3)为PLC命令的格式标志,0119404中,0表示读,1194表示D202的起始地址,04表示读D202,D203两个字,共4个字节,66为0119404和chr(3)的校验和,向串口写入"读"命令,其实和向plc地址中写入数据是一样的,只是没有数据,用0表示读

            string sReadCmd = Chr(2) + "0119404" + Chr(3) + "66";

            com.Write(sReadCmd);

            //等待1秒钟

            System.Threading.Thread.Sleep(1000);

            // 从串口读数据

            byte[] data = new byte[1024];

            com.Read(data, 0, 1024);

           //如果首位为2,则表示数据有效.这里有个问题,在第二次读,第2位才为'2',第三次又是首位为2,需要再测试

            if (data[0]==2)

            {

                string sReceiveData = System.Text.Encoding.ASCII.GetString(data);

                //MessageBox.Show(sReceiveData);

               //解析命令,将读到的字符解析成数字,注意高低位的转换

                for (int i = 1; i < 8; i += 4)

                {

                    string sLow = sReceiveData.Substring(i,2);

                    string sHigh = sReceiveData.Substring(i + 2, 2);

                    //int res = Convert.ToInt32(sHigh)+ Convert.ToInt32(sLow);

                    int res = Convert.ToInt32(sHigh,16) + Convert.ToInt32(sLow,16);

                    this.txtRead0.Text += res.ToString() + ",";

                }               

                

            }

 
 

分类: C#

好文要顶 关注我 收藏该文 

 






白沙河
关注 - 0
粉丝 - 3

+加关注

2

0

(请您对文章做出评价)

« 上一篇:无法加载
DLL“sqlceme30.dll”: 找不到指定的模块。解决
» 下一篇:MSComm控件使用详解

posted on 2008-12-29 09:20 白沙河 阅读(3929) 评论(8) 编辑 收藏

FeedBack:

#1楼

2009-02-25 11:42 | Ryan.net

博主用的哪一款PLC? 

我也在用这东西FX2N(RS232),他给的手册好像不是这样说的。。。。 

手册示例: 

在第5站从X040向X077读32个点的数据(消息等待时间设为100毫秒) 

应该向PLC发送: 

ENQ 站号 PC号 读写指令 响应时间 起始地址 读取位数 和校验码 

ENQ 0 5 F F W R 0 X 0 0 4 0 0 2 4 8 

05H 30H 35H 46H 46H 57H 52H 30H 58H 30H 30H 34H 30H 30H 32H 34H 38H 

我发送 

m_ComPort.COM.Write(ASCIIEncoding.ASCII.GetString(byteTmp) + "FFWR0X004002" + SumChk("FFWR0X004002")); 

可是获取不到正确的数据
支持(0)反对(0)
  

#2楼[楼主]

2009-02-25 12:01 | 白沙河  

Public Const PLC_X_Group_Base_AddRess = 128

X地址是不是要先加一个128?
支持(0)反对(0)
  

#3楼

2009-02-25 14:23 | Ryan.net

先谢谢博主回复 

不过没太看明白 

格式如下(中间用“|”人为分割): 

在第5站从X040向X077读32个点的数据(消息等待时间设为100毫秒) 

ENQ|站号|PC号|读写指令|响应时间|起始地址|读取位数|和校验码 

ENQ|0 5 |F F|W R|0 |X 0 0 4 0| 0 2| 4 8 

05H|30H 35H |46H 46H |57H 52H |30H |58H 30H 30H 34H 30H |30H 32H|34H 38H 

我写的代码: 

m_ComPort.COM.Write(Chr(5) + "FFWR0X004002" + SumChk("FFWR0X004002")); 

X地址加一个128? 

是不是X0040转换成ASC之后加128 

支持(0)反对(0)
  

#4楼[楼主]

2009-02-25 15:18 | 白沙河  

以下资料是从网上找的,没有找到关于X的字或位的说明.但SumChk("FFWR0X004002"))应该不是从0x0040开始吧

//

由于没有寄存器类型信息,所以地址的计算十分关键,如D100和M100分别对应哪个地址呢?下面就是三菱Fx系列PLC地址对应表:

Public Const PLC_D_Base_AddRess = 4096

Public Const PLC_D_Special_Base_AddRess = 3584

Public Const PLC_Y_Group_Base_AddRess = 160

Public Const PLC_PY_Group_Base_AddRess = 672

Public Const PLC_T_Group_Base_AddRess = 192

Public Const PLC_OT_Group_Base_AddRess = 704

Public Const PLC_RT_Group_Base_AddRess = 1216

Public Const PLC_M_SINGLE_Base_AddRess = 2048(命令为7或8时)

Public Const PLC_M_Group_Base_AddRess = 256

Public Const PLC_PM_Group_Base_AddRess = 768

Public Const PLC_S_Group_Base_AddRess = 0

Public Const PLC_X_Group_Base_AddRess = 128

Public Const PLC_C_Group_Base_AddRess = 448

Public Const PLC_OC_Group_Base_AddRess = 960

Public Const PLC_RC_Group_Base_AddRess = 1472

Public Const PLC_TV_Group_Base_AddRess = 2048

Public Const PLC_CV16_Group_Base_AddRess = 2560

Public Const PLC_CV32_Group_Base_AddRess = 3072 

当我们用DEVICE READ命令时,D100地址=100*2+4096;M100地址=100+256;不同的是D类型寄存器存放的是字,M寄存器存放的是位,同样是读两个字节,D100返回的就是PLC中D100地址的值,M类型寄存器返回的是M100到M116的值。所以当我们用FORCE ON 命令时,M100寄存器地址=100+2048;

这也没有什么复杂的,不是吗?可是三菱公司好像不甘于如此,FORCE ON/Off命令中地址排列与DEVICE READ/WRITE不同,是低位在前高位在后。如Y20,地址是0510H,代码中4个字节地址表示为:1005。(注意:Y寄存器为八进制,如Y20 地址=16+1280=0510H) 

其实一点技术含量都没有,就是拐了几个弯,偏偏很多时候又不都告诉你,让人浪费不少时间。
支持(0)反对(0)
  

#5楼

2009-02-25 18:02 | Ryan.net

谢了,兄弟! 

和厂商通过另外一种模式解决了 

因为时间实在太急,对于这些东西懂得也不多,就没能继续研究
支持(0)反对(0)
  

#6楼

2009-06-24 10:28 | 明月伴我行  

你好,请教如何读D1024后面的值?我现在一直返回零,郁闷。
支持(0)反对(0)
  

#7楼[楼主]

2009-06-26 09:56 | 白沙河  

@明月伴我行

假设读2个地址,那是不是应该以"0180004"开始,手里没有PLC,也没有试过从1024开始,所以也就不确定了.
支持(0)反对(0)
  

#8楼

2009-07-14 17:29 | 明月伴我行  

应该不是
支持(0)反对(0)
  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: