三菱PLC 编程口串口通讯
工作中写的三菱PLC串口通讯,封装成了一个类,可以方便随时调用;
数据传送分为 循环 和 一次性 两种方式;
为了避免冲突,数据的收发使用了一个线程来排队完成。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.IO.Ports;
namespace 三菱PLC读写
{
class mitsubishi
{
Thread mitsubishiTread; //mitsubishi
SerialPort m_sp;
int m_sendAddr, m_sendLen, m_recieveAddr, m_recieveLen;
bool[] m_Q = new bool[5]; //是否已请求
bool[] m_R = new bool[5]; //是否已回复
char[] m_T = new char[5]; //类型
int[] m_A = new int[5]; //地址
char[] m_C = new char[5]; //命令
int[] m_D = new int[5]; //发出数据
bool[] m_R_Bool = new bool[5]; //返回值
int[] m_R_Int = new int[5]; //返回值
public byte[] m_wData, m_rData; //循环写和读数据
char[] m_WData; //循环写数据转换格式
bool circle = false; //是否循环读写,判断不同字节长度
#region mitsubishi类构造函数
public mitsubishi(string portName, int sendA, int sendL, int revA, int revL) //(串口号、写入起始地址、写入字节长度、读取起始地址、读取字节长度)
{
m_sp = new SerialPort();
m_sp.PortName = portName;
m_sp.BaudRate = 9600;
m_sp.DataBits = 7;
m_sp.StopBits = (StopBits)1;
m_sp.Parity = Parity.Even;
m_sp.ReadTimeout = 500;
//收发数据起始地址和字节长度
m_sendAddr = sendA;
m_sendLen = sendL;
m_recieveAddr = revA;
m_recieveLen = revL;
m_wData = new byte[sendL];
m_rData = new byte[revL];
m_WData = new char[sendL*2];
if (!m_sp.IsOpen)
{
m_sp.Open();
Thread.Sleep(500); //串口打开,等待0.5S后开始启动线程
mitsubishiTread = new Thread(new ThreadStart(start));
mitsubishiTread.Start();
}
else
{
MessageBox.Show("串口已被打开,请检查端口!");
m_sp.Close();
}
}
#endregion
#region 单线程排队发送
public void start()
{
while (true)
{
//这里用排序发送
if (!m_sp.IsOpen)
{
m_sp.Open();
Thread.Sleep(500); //串口打开,等待0.5S后开始发送
}
//有排队先完成
m_First();
//循环写部分
circle = true;
for (int i = 0; i < m_sendLen; i++)
{
m_WData[i * 2] = Tran2(m_wData[i] / 0x10);
m_WData[i * 2 + 1] = Tran2(m_wData[i] % 0x10);
}
char[] m_write = SendData('d', m_sendAddr, 'w', m_WData);
if(m_write[0] - 6 != 0)
{
MessageBox.Show("循环写入数据出错!");
}
//有排队先完成
m_First();
//循环读部分
circle = true;
char[] m_read = SendData('d', m_recieveAddr, 'r', m_WData);
if(m_read.Length/2 == m_recieveLen)
{
for (int i = 0; i < m_read.Length; i = i + 2)
{
m_rData[i/2] = (byte)(Tran1(m_read[i])*0x10 + Tran1(m_read[i+1]));
}
}
else
{
MessageBox.Show("循环读出数据出错!");
}
}
}
#endregion
#region 优先处理
private void m_First()
{
circle = false;
for (int i = 0; i < 5; i++)
{
if (m_Q[i] && !m_R[i]) //有请求但无回复
{
int m_Data_Len;
if (m_C[i] == 'D' || m_C[i] == 'S')
{
m_Data_Len = 8;
}
else
{
m_Data_Len = 4;
}
char[] m_Data = HTC(m_D[i], m_Data_Len);
char[] m_return = SendData(m_T[i], m_A[i], m_C[i], m_Data);
int n = m_return.Length;
if (n > 1)
{
m_R_Int[i] = 0;
for (int j = n - 1; j > 0; j = j - 2)
{
m_R_Int[i] *= 0x100;
m_R_Int[i] += (Tran1(m_return[j - 1]) * 0x10 + Tran1(m_return[j]));
}
}
else if (n == 1 && m_return[0] - 6 == 0)
{
m_R_Bool[i] = true;
}
m_R[i] = true;
}
}
}
#endregion
#region 编码转换
private int Tran1(Char InChar) //ASCII码转数字,接收时使用
{
int OutInt;
if (InChar < 0x40)
{
OutInt = InChar - 0x30;
}
else
{
OutInt = InChar - 0x37;
}
return OutInt;
}
private Char Tran2(int InChar) //数字转ASCII码,发送时使用
{
Char OutChar;
if (InChar < 10)
{
OutChar = (Char)(InChar + 0x30);
}
else
{
OutChar = (Char)(InChar + 0x37);
}
return OutChar;
}
#endregion
#region 发送或接收数据,返回Char数组
private Char[] SendData(Char type, int startAdd, Char cmd, char[] inChar) //(S、D、M、X、Y、T、C) (E00/1000/100/80/A0/C0/1C0) (0、1、w/W、r/R)
{
Char[] outChar = null;
//报文长度
int n = 0;
if (cmd == '0' || cmd == '1') // 0、1复位置位
{
n = 9;
}
else if (cmd == 'R' || cmd == 'r') // R/r读单双字
{
n = 11;
}
else if (cmd == 'w' || cmd == 'W') // w写单字
{
n = 11 + inChar.Length;
}
char[] subuffer = new char[n];
//报文首
subuffer[0] = (char)2;
//命令代码
if (cmd == '0')
{
subuffer[1] = (char)0x38;
}
else if (cmd == '1')
{
subuffer[1] = (char)0x37;
}
else if (n == 11)
{
subuffer[1] = (char)0x30;
}
else
{
subuffer[1] = (char)0x31;
}
//首地址
int Addr = 0;
if (type == 'S' || type == 's') // S、s
{
Addr = 0xE00;
}
else if (type == 'D' || type == 'd') // D、d
{
Addr = 0x1000;
}
else if (type == 'M' || type == 'm') // M、m
{
Addr = 0x100;
}
else if (type == 'X' || type == 'x') // X、x
{
Addr = 0x80;
}
else if (type == 'Y' || type == 'y') // Y、y
{
Addr = 0xA0;
}
else if (type == 'T' || type == 't') // T、t
{
Addr = 0xC0;
}
else if (type == 'C' || type == 'c') // C、c
{
Addr = 0x1C0;
}
//计算首地址
Char[] Addrs = new Char[4];
if (n == 9)
{
Addr = Addr * 8 + startAdd;
Addrs = HTC(Addr, 4);
}
else
{
Addr += (startAdd * 2);
Addrs = StartData(Addr);
}
Addrs.CopyTo(subuffer, 2);
//字节长度
if (circle)
{
if (cmd == 'R' || cmd == 'W') // R/W 读写双字
{
subuffer[7] = Tran2(m_sendLen % 0x10);
subuffer[6] = Tran2 (m_sendLen / 0x10);
}
else if (cmd == 'r' || cmd == 'w') // r/w 读写单字
{
subuffer[7] = Tran2(m_recieveLen % 0x10);
subuffer[6] = Tran2 (m_recieveLen / 0x10);
}
}
else
{
subuffer[6] = (char)0x30;
if (cmd == 'R' || cmd == 'W') // R/W 读写双字
{
subuffer[7] = (char)0x34;
}
else if (cmd == 'r' || cmd == 'w') // r/w 读写单字
{
subuffer[7] = (char)0x32;
}
}
//数据内容
if (cmd == 'w' || cmd == 'W')
{
inChar.CopyTo(subuffer, 8);
}
//报文尾
subuffer[n - 3] = (char)3;
//校验码
Char[] T = new Char[2];
T = CCD(subuffer, n - 3);
T.CopyTo(subuffer, n - 2);
if (m_sp.IsOpen)
{
m_sp.Write(subuffer, 0, n);
//MessageBox.Show("已发送成功!");
for (int i = 0; i < 5; i++)
{
Thread.Sleep(100);
int Len = m_sp.BytesToRead;
if (Len > 0)
{
i = 5; //跳出循环
char[] RecieveBuf = new char[Len];
m_sp.Read(RecieveBuf, 0, Len);
if (Len == 1) //非写入或出错状态
{
outChar = RecieveBuf;
}
else //写入状态,读取返回数据
{
T = CCD(RecieveBuf, Len - 3); //验证接收信息校验码
if (T[0] == RecieveBuf[Len - 2] && T[1] == RecieveBuf[Len - 1])
{
Char[] outChar2 = new Char[Len - 4];
Array.Copy(RecieveBuf, 1,outChar2, 0, Len - 4);
outChar = outChar2;
}
}
}
}
}
return outChar;
}
#endregion
#region 和校验
private char[] CCD(char[] InChar, int len) //和校验
{
int S = 3;
for (int i = 1; i < len; i++)
{
S += InChar[i];
}
char[] OutChar = HTC(S % 0x100, 2);
return OutChar;
}
#endregion
#region 字元件起始地址转化
private char[] StartData(int inData) //字元件起始地址转化
{
int t = inData;
int[] tt = new int[4];
char[] H = new char[4];
for (int i = 3; i >= 0; i--)
{
tt[i] = t % 0x10;
t = t / 0x10;
H[i] = Tran2(tt[i]);
}
return H;
}
#endregion
#region 位元件起始地址、发送数据转化
public char[] HTC(int inData, int inBit) //位元件起始地址、发送数据转化
{
byte[] TranByte = System.BitConverter.GetBytes(inData);
int len = TranByte.Length;
if (2 * len > inBit)
{
len = inBit / 2;
}
char[] OutChar = new char[inBit];
for (int i = 0; i < len; i++)
{
int HL = TranByte[i];
int H = HL / 0x10;
OutChar[2 * i] = Tran2(H);
int L = HL % 0x10;
OutChar[2 * i + 1] = Tran2(L);
}
return OutChar;
}
#endregion
#region 读SD/D
public int read(string s)
{
int outInt = 0;
if (System.Text.RegularExpressions.Regex.IsMatch(s, @"^[dDsS]\d{1,4}$+"))
{
Char[] inChar = s.ToCharArray(0, 1);
Char type = inChar[0],cmd;
if (type == 'd' || type == 's')
{
cmd = 'r';
}
else
{
cmd = 'R';
}
int addr = Convert.ToInt32(s.Substring(1));
int n = 5;
for (int i = 0; i < 5; i++)
{
if (!m_Q[i])
{
n = i;
i = 5;
m_Q[n] = true;
m_T[n] = type;
m_A[n] = addr;
m_C[n] = cmd;
m_R[n] = false;
m_R_Bool[n] = false;
}
}
for (int i = 0; i <= 5; i++)
{
Thread.Sleep(100);
if (m_R[n])
{
m_Q[n] = false;
m_R[n] = false;
outInt = m_R_Int[n];
i = 5;
}
}
}
else
{
MessageBox.Show("指令格式错误!");
}
return outInt;
}
#endregion
#region 写SD/D
public bool write(string s,int d)
{
bool outBool = false;
if (System.Text.RegularExpressions.Regex.IsMatch(s, @"^[dDsS]\d{1,4}$+"))
{
Char type = s.ToCharArray(0, 1)[0], cmd;
if (type == 'd' || type == 's')
{
cmd = 'w';
}
else
{
cmd = 'W';
}
int addr = Convert.ToInt32(s.Substring(1));
int n = 5;
for (int i = 0; i < 5; i++)
{
if (!m_Q[i])
{
n = i;
i = 5;
m_Q[n] = true;
m_T[n] = type;
m_A[n] = addr;
m_C[n] = cmd;
m_D[n] = d;
m_R[n] = false;
m_R_Bool[n] = false;
}
}
for (int i = 0; i <= 5; i++)
{
Thread.Sleep(100);
if (m_R[n])
{
m_Q[n] = false;
m_R[n] = false;
outBool = m_R_Bool[n];
i = 5;
}
}
}
else
{
MessageBox.Show("指令格式错误!");
}
return outBool;
}
#endregion
#region 置位X/Y/M/T/C
public bool set(string s)
{
bool outBool = false;
if (System.Text.RegularExpressions.Regex.IsMatch(s, @"^[xXyYmMtTcC]\d{1,4}$+"))
{
Char[] inChar = s.ToCharArray(0, 1);
Char type = inChar[0];
int addr = Convert.ToInt32(s.Substring(1));
int n = 5;
for (int i = 0; i < 5; i++)
{
if (!m_Q[i])
{
n = i;
i = 5;
m_Q[n] = true;
m_T[n] = type;
m_A[n] = addr;
m_C[n] = '1';
m_R[n] = false;
m_R_Bool[n] = false;
}
}
for (int i = 0; i <= 5; i++)
{
Thread.Sleep(100);
if (m_R[n])
{
m_Q[n] = false;
m_R[n] = false;
outBool = m_R_Bool[n];
i = 5;
}
}
}
else
{
MessageBox.Show("指令格式错误!");
}
return outBool;
}
#endregion
#region 复位X/Y/M/T/C
public bool rst(string s)
{
bool outBool = false;
if (System.Text.RegularExpressions.Regex.IsMatch(s, @"^[xXyYmMtTcC]\d{1,4}$+"))
{
Char[] inChar = s.ToCharArray(0, 1);
Char type = inChar[0];
int addr = Convert.ToInt32(s.Substring(1));
int n = 5;
for (int i = 0; i < 5; i++)
{
if (!m_Q[i])
{
n = i;
i = 5;
m_Q[n] = true;
m_T[n] = type;
m_A[n] = addr;
m_C[n] = '0';
m_R[n] = false;
m_R_Bool[n] = false;
}
}
for (int i = 0; i <= 5; i++)
{
Thread.Sleep(100);
if (m_R[n])
{
m_Q[n] = false;
m_R[n] = false;
outBool = m_R_Bool[n];
i = 5;
}
}
}
else
{
MessageBox.Show("指令格式错误!");
}
return outBool;
}
#endregion
}
}
在程序中调用封装的类:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.IO.Ports;
namespace 三菱PLC读写
{
public partial class Form1 : Form
{
mitsubishi mit = new mitsubishi("COM6",0,10,10,10);
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
//读
private void button1_Click(object sender, EventArgs e)
{
int result1 = mit.read("D100"); //读D100双字
int result2 = mit.read("d100"); //读D100单字
}
//将100写入到D8340双字
private void button2_Click_1(object sender, EventArgs e)
{
bool result = mit.write("S340", 100);
}
//置位
private void button3_Click_1(object sender, EventArgs e)
{
bool result = mit.set("M0");
}
//复位
private void button4_Click(object sender, EventArgs e)
{
bool result = mit.rst("Y0");
}
//循环写入D1,D1位置在2、3两个字节
private void button5_Click(object sender, EventArgs e)
{
int sendData = 5678;
mit.m_wData[2] = (byte)(sendData % 0x100);
mit.m_wData[3] = (byte)(sendData / 0x100);
}
//循环读取D12,D12位置在4、5两个字节
private void button6_Click(object sender, EventArgs e)
{
int revData = mit.m_rData[4] + mit.m_rData[5] * 0x100;
}
}
}

浙公网安备 33010602011771号