前言
本文以Panasonic松下PLC位列,介绍C#通过上位机串口访问松下PLC,可以使用串口助手协助调试。
一、松下PLC简介?
PLC (Programmable Logic Controller),可编程逻辑控制器,一种数字运算操作的电子系统,专为在工业环境应用而设计的。它采用一类可编程存储器,用于其内部 存储程序,执行 逻辑运算,顺序控制,定时,计数与算术操作等面向用户的指令,并通过数字或模拟式输入/输出控制各种类型的机械或生产过程,是工业控制的核心部分。松下公司的 PLC产品中,FPO为微型机,FP1为整体式 小型机,FP3为中型机,FP5/FP10、FP10S(FP10的改进型)、FP20为 大型机,其中FP20是最新产品。松下公司近几年PLC产品的主要特点是:指令系统功能强。
二、使用步骤
1.引入库
using System.IO.Ports;
2.设置串口参数
/// <summary>
/// 设置串口参数
/// </summary>
/// <param name="SP_Panasonic">Serialport类的名字</param>
/// <param name="portname">串口号</param>
/// <param name="baudrate">波特率</param>
/// <param name="stopbits">停止位</param>
/// <param name="parity">校验位</param>
/// <param name="databits">数据位</param>
public static void SetSerialPort(SerialPort SP_Panasonic, string portname, int baudrate, StopBits stopbits, Parity parity, int databits)
{
SP_Panasonic.PortName = portname;
SP_Panasonic.BaudRate = baudrate;
SP_Panasonic.StopBits = stopbits;
SP_Panasonic.Parity = parity;
SP_Panasonic.DataBits = databits;
if (SP_Panasonic.IsOpen)
{
SP_Panasonic.Close();
SP_Panasonic.Open();
}
else
{
SP_Panasonic.Open();
}
}
3. 读取松下PLC寄存器数据,支持X、Y、R、D
/// <summary>
/// 3. 3
/// </summary>
/// <param name="SP_Panasonic">Serialport类的名字</param>
/// <param name="Station">站号</param>
/// <param name="Memory">寄存器类型</param>
/// <param name="Adress">起始地址</param>
/// <param name="Count">地址位数</param>
/// <returns></returns>
public static string Read_Pan(SerialPort SP_Panasonic, int Station, string Memory, int Adress, int Count)
{
string ReadData = "";
switch (Memory)
{
case "X":
ReadData = ReadXYR(SP_Panasonic, Station, Memory, Adress, Count);//暂不支持读取多位
break;
case "Y":
ReadData = ReadXYR(SP_Panasonic, Station, Memory, Adress, Count);
break;
case "R":
ReadData = ReadXYR(SP_Panasonic, Station, Memory, Adress, Count);
break;
case "D":
ReadData = ReadD(SP_Panasonic, Station, Memory, Adress, Count);//支持读取多位
break;
}
if (ReadData == "读取失败")
{
String LogMSg = "串口通讯超时";
cPublic.saveDataLog(LogMSg);
cPublic.UpdateGUI((int)cPublic.UpdateItem.label_error, "label_error", "串口通讯超时,请检查!");
}
else
{
cPublic.UpdateGUI((int)cPublic.UpdateItem.label_error, "label_error", "");
}
return ReadData;
}
4. 写入松下PLC寄存器数据,支持X、Y、R、D
/// <summary>
/// 写入松下PLC寄存器数据,支持X、Y、R、D
/// </summary>
/// <param name="SP_Panasonic">Serialport类的名字</param>
/// <param name="Station">站号</param>
/// <param name="Memory">寄存器类型</param>
/// <param name="Adress">起始地址</param>
/// <param name="Count">地址位数</param>
/// <param name="Data">写入的字符串</param>
/// <returns></returns>
public static bool Write_Pan(SerialPort SP_Panasonic, int Station, string Memory, int Adress, int Count, string Data)
{
bool WriteData = false;
switch (Memory)
{
case "Y":
WriteData = WriteXYR(SP_Panasonic, Station, Memory, Adress, Count, Data);//暂不支持读取多位
break;
case "R":
WriteData = WriteXYR(SP_Panasonic, Station, Memory, Adress, Count, Data);
break;
case "D":
WriteData = WriteD(SP_Panasonic, Station, Memory, Adress, Count, Data);//支持读取多位
break;
}
return WriteData;
}
5. 读取DT寄存器数据
/// <summary>
/// 读取DT寄存器数据
/// </summary>
/// <param name="SP_Panasonic">Serialport类的名字</param>
/// <param name="station">站号</param>
/// <param name="memory">寄存器类型</param>
/// <param name="address">起始地址</param>
/// <param name="addressCount">地址位数</param>
/// <returns></returns>
private static string ReadD(SerialPort SP_Panasonic, int station, string memory, int address, int addressCount)
{
lock(Lock_Serial)
{
send_count = 0;
string strSendData;
string strReceiveData = "";
string[] arrstrReceiveData = new string[addressCount * 2];
byte[] arrbyReceiveData = new byte[addressCount * 2];
string strStartAddress = address.ToString("d5");
string strEndAddress = (address + addressCount - 1).ToString("d5");
// 生成发送数据字符串
strSendData = "%0" + station.ToString() + "#RD" + memory.ToUpper() + strStartAddress + strEndAddress;
// 字符添加校验码和回车结束符
strSendData += GetCheckCode(strSendData) + "\r";
send_count += strSendData.Length;
if (SendData(SP_Panasonic, strSendData))
strReceiveData = ReceiveData(SP_Panasonic, 300);
receive_count += strReceiveData.Length;
if (strReceiveData != "" && strReceiveData[3] == '$')
{
// 解析串口接收的数据
for (int i = 0; i < addressCount * 2; i++)
{
arrstrReceiveData[i] = strReceiveData.Substring(6 + 2 * i, 2);
arrbyReceiveData[i] = Convert.ToByte(arrstrReceiveData[i], 16);
}
String s = BitConverter.ToString(arrbyReceiveData).Replace("-", "");
return s;
}
else
{
return "读取失败";
}
}
}
6. 读取X、Y、R寄存器数据
/// <summary>
/// 读取X、Y、R寄存器数据
/// </summary>
/// <param name="SP_Panasonic">Serialport类的名字</param>
/// <param name="station">站号</param>
/// <param name="memory">寄存器类型</param>
/// <param name="address">起始地址</param>
/// <param name="addressCount">地址位数</param>
/// <returns></returns>
private static string ReadXYR(SerialPort SP_Panasonic, int station, string memory, int address, int addressCount)
{
lock (Lock_Serial)
{
send_count = 0;
string strSendData;
string strReceiveData = "";
string[] arrstrReceiveData = new string[addressCount];
byte[] arrbyReceiveData = new byte[addressCount];
string strStartAddress = address.ToString("d4");
string strEndAddress = (address + addressCount - 1).ToString("d4");
// 生成发送数据字符串
strSendData = "%0" + station.ToString() + "#RCS" + memory.ToUpper() + strStartAddress;
// 字符添加校验码和回车结束符
strSendData += GetCheckCode(strSendData) + "\r";
send_count += strSendData.Length;
if (SendData(SP_Panasonic, strSendData))
strReceiveData = ReceiveData(SP_Panasonic, 100);
cPublic.strReceiveData_public = strReceiveData;
receive_count += strReceiveData.Length;
if (strReceiveData != "" && strReceiveData[3] == '$')
{
// 解析串口接收的数据
for (int i = 0; i < addressCount; i++)
{
arrstrReceiveData[i] = strReceiveData.Substring(6, 1);
arrbyReceiveData[i] = Convert.ToByte(arrstrReceiveData[i], 16);
}
String s = BitConverter.ToString(arrbyReceiveData).Replace("-", "");
return s;
}
else
{
return "读取失败";
}
}
}
7. 写入DT寄存器
/// <summary>
/// 写入DT寄存器
/// </summary>
/// <param name="SP_Panasonic">Serialport类的名字</param>
/// <param name="station">站号</param>
/// <param name="memory">寄存器类型</param>
/// <param name="address">起始地址</param>
/// <param name="addressCount">地址位数</param>
/// <param name="data">写入的字符串</param>
/// <returns></returns>
public static bool WriteD(SerialPort SP_Panasonic, int station, string memory, int address, int addressCount, string data)
{
lock (Lock_Serial)
{
send_count = 0;
string strSendData;
string strReceiveData = "";
string[] arrstrReceiveData = new string[addressCount * 2];
byte[] arrbyReceiveData = new byte[addressCount * 2];
string strStartAddress = address.ToString("d5");
string strEndAddress = (address + addressCount - 1).ToString("d5");
// 生成发送数据字符串
strSendData = "%0" + station.ToString() + "#WD" + memory.ToUpper() + strStartAddress + strEndAddress + data;
// 字符添加校验码和回车结束符
strSendData += GetCheckCode(strSendData) + "\r";
send_count += strSendData.Length;
if (SendData(SP_Panasonic, strSendData))
{
strReceiveData = ReceiveData(SP_Panasonic, 300);
receive_count += strReceiveData.Length;
if (strReceiveData != "" && strReceiveData[3] == '$')
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
}
8. 写入X、Y、R寄存器数据
/// <summary>
/// 写入X、Y、R寄存器数据
/// </summary>
/// <param name="SP_Panasonic">Serialport类的名字</param>
/// <param name="station">站号</param>
/// <param name="memory">寄存器类型</param>
/// <param name="address">起始地址</param>
/// <param name="addressCount">地址位数</param>
/// <param name="data">写入的字符串</param>
/// <returns></returns>
public static bool WriteXYR(SerialPort SP_Panasonic, int station, string memory, int address, int addressCount, string data)
{
lock (Lock_Serial)
{
send_count = 0;
string strSendData;
string strReceiveData = "";
string[] arrstrReceiveData = new string[addressCount * 2];
byte[] arrbyReceiveData = new byte[addressCount * 2];
string strStartAddress = address.ToString("d4");
string strEndAddress = (address + addressCount - 1).ToString("d4");
// 生成发送数据字符串
strSendData = "%0" + station.ToString() + "#WCS" + memory.ToUpper() + strStartAddress + data;
//strSendData = "%0" + station.ToString() + "#WCS" + memory.ToUpper() + strStartAddress + strEndAddress + data;
// 字符添加校验码和回车结束符
strSendData += GetCheckCode(strSendData) + "\r";
send_count += strSendData.Length;
if (SendData(SP_Panasonic, strSendData))
{
strReceiveData = ReceiveData(SP_Panasonic, 300);
receive_count += strReceiveData.Length;
//if (strReceiveData != "" && strReceiveData[3] == '$')
if (strReceiveData != "" && strReceiveData[3] == '$')
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
}
9. 校验码计算
/// <summary>
///
/// </summary>
/// <param name="sendData"></param>
/// <returns></returns>
private static string GetCheckCode(string sendData)
{
//将字符串转换成字节数组
byte[] arrSendData = new byte[sendData.Length];
arrSendData = Encoding.Default.GetBytes(sendData);
byte sum = 0;
// 进行校验码计算,异或求和后生成
for (int i = 0; i < arrSendData.Length; i++)
{ sum = (byte)(sum ^ arrSendData[i]); }
// 将计算结果转换成字符串
string strCheck = Convert.ToString(sum, 16);
return strCheck.ToUpper();
}
10. 串口发送字符串
/// <summary>
/// 往串口发送字符串
/// </summary>
/// <param name="SP_Panasonic">Serialport类的名字</param>
/// <param name="sendData">发送的字符串</param>
/// <returns></returns>
private static bool SendData(SerialPort SP_Panasonic, string sendData)
{
// 将字符串转换成字节数组
byte[] arrbySendBuffer = new byte[sendData.Length];
arrbySendBuffer = Encoding.Default.GetBytes(sendData);
// 通过串口发送数据
try
{
//发送数据
SP_Panasonic.Write(arrbySendBuffer, 0, arrbySendBuffer.Length);
return true;
}
catch (Exception ex)
{
//MessageBox.Show(ex.Message);
return false;
}
}
11. 串口接收字符串
/// <summary>
/// 从串口接收字符串
/// </summary>
/// <param name="SP_Panasonic">Serialport类的名字</param>
/// <param name="time">延时时间</param>
/// <returns>返回接收的字符串</returns>
private static string ReceiveData(SerialPort SP_Panasonic, int time)
{
int i = 0;
Thread.Sleep(time);
while (SP_Panasonic.BytesToRead == 0)
{
i++;
Thread.Sleep(20);
if (i == 50) return "";
}
byte[] arrReceiveData = new byte[SP_Panasonic.BytesToRead];
SP_Panasonic.Read(arrReceiveData, 0, SP_Panasonic.BytesToRead);
return Encoding.ASCII.GetString(arrReceiveData);
}
总结
例如:以上就是今天要讲的内容,本文仅仅简单介绍以松下PLC为列,使用C#上位机通过串口访问PLC所有寄存器,提供了大量能使我们快速便捷地处理数据的函数和方法。