NET工控,上位机,Modbus485网口/串口通讯(鸣志步进电机,鸣志伺服电机,松下
先上两个通用Modbus帮助类,下面这个是多线程不安全版,在多线程多电机同一端口通信下,可能造成步进电机丢步或者输出口无响应等,还有个多线程安全版,只是基于这个不安全版加上了LOCK,THISusing Modbus.Device;
using Sunny.UI;
using System;
using System.IO.Ports;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;<br><br>namespace SunnyUI_NuclearForm
{
/// <summary>
/// Modbus通用帮助类
/// </summary>
public class ModbusHelper : IDisposable
{<br><br> IModbusMaster master;<br><br> //串口通信
SerialPort serialPort;<br><br> //网口-TCP
TcpClient tcpClient;<br><br> //网口-套接字(备用)
Socket socket;<br><br>
//private static readonly object threadObj = new object();//线程安全锁<br><br> //ManualResetEvent manualEvent = new ManualResetEvent(true);//true运行,false不运行<br><br>
#regionconnection<br><br> /// <summary>
/// 串口连接
/// </summary>
/// <returns></returns>
public bool ConnectForSerialPort(string com, int baudRate)
{
bool result = false;<br><br> if (!string.IsNullOrWhiteSpace(com) && baudRate > 0)
{
serialPort = new SerialPort();<br><br> serialPort.PortName = com;
serialPort.BaudRate = baudRate;<br><br> //下面三个参数看情况要不要开放
serialPort.DataBits = 8;
serialPort.StopBits = StopBits.One;
serialPort.Parity = Parity.None;<br><br> try
{
if (!serialPort.IsOpen)
{
serialPort.Open();
}<br><br>
master = ModbusSerialMaster.CreateRtu(serialPort);
master.Transport.ReadTimeout = 3 * 1000;//读 超时时间
master.Transport.WriteTimeout = 3 * 1000;//写 超时时间
master.Transport.Retries = 20;//重试次数
master.Transport.WaitToRetryMilliseconds = 3 * 1000;//重试间隔<br><br> result = true;
}
catch (Exception e)
{
//打开失败
LogHelper.Error($"串口无法连接!COM:{com},波特率:{baudRate}。以下为详细信息:\r\n{e.Message}");
result = false;
}
}
return result;
}<br><br> /// <summary>
/// 网口TCP连接
/// </summary>
/// <param name="ip"></param>
/// <returns></returns>
public bool ConnectForTCP(string ip, int port)
{
bool result = false;
if (!string.IsNullOrWhiteSpace(ip) && port > 0)
{
try
{
tcpClient = new TcpClient(ip, port);
if (tcpClient.Connected)
{
master = ModbusIpMaster.CreateIp(tcpClient);<br><br> master.Transport.ReadTimeout = 3 * 1000;//读 超时时间
master.Transport.WriteTimeout = 3 * 1000;//写 超时时间
master.Transport.Retries = 20;//重试次数
master.Transport.WaitToRetryMilliseconds = 3 * 1000;//重试间隔<br><br> result = true;
}
else
{
LogHelper.Error($"网口无法连接!IP:{ip},端口:{port}。\r\n");
result = false;
}
}
catch (Exception e)
{
LogHelper.Error($"网口无法连接!IP:{ip},端口:{port}。以下为详细信息:\r\n{e.Message}");
return false;
}<br><br> }
return result;<br><br> }<br><br> /// <summary>
///Socket连接(备用)
/// </summary>
/// <param name="ip"></param>
/// <param name="port"></param>
/// <returns></returns>
public bool ConnectForSocket(string ip, int port)
{
bool result = false;
if (!string.IsNullOrWhiteSpace(ip) && port > 0)
{
//部分硬件,用TCP发指令没响应,但是用Socket可以,不知道是没支持好TCP还是没支持好Modbus
//所以在此留下备用通信方法 <br><br> socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);<br><br> try
{
socket.Connect(IPAddress.Parse(ip), port);
result = true;<br><br> }
catch (Exception e)
{
//连接失败
LogHelper.Error($"Socket(备用)无法连接!IP:{ip},端口:{port}。以下为详细信息:\r\n{e.Message}");
result = false;
}<br><br> }
return result;
}<br><br> #endregion<br><br> #region send<br><br> /// <summary>
/// 向串口发送数据 Modbus报文(字节)
/// </summary>
public void SendSerialByte(byte[] arr)
{
//this.manualEvent.WaitOne();//等待
//lock (threadObj)
//{
//this.manualEvent.Reset();//红灯<br><br> if (serialPort != null && serialPort.IsOpen && arr != null && arr.Count() > 0)
{
serialPort.DiscardInBuffer();
serialPort.DiscardOutBuffer();<br><br> Thread.Sleep(50);<br><br> serialPort.Write(arr, 0, arr.Length);<br><br> Thread.Sleep(50);//写入读取命令后延时读取 或者跟下一条命令之间产生间隔<br><br> try
{
// 接收来自读取命令的数据 广播命令执行完毕不响应
//byte[] receiveData = new byte;
//int bytesRead = serialPort.Read(receiveData, 0, receiveData.Length);<br><br> if (serialPort.BytesToRead > 0)
{
var result = serialPort.ReadExisting();
}<br><br> }
catch (Exception e)
{<br><br> }<br><br> }<br><br> //this.manualEvent.Set();//绿灯
//}
}<br><br> /// <summary>
/// 向串口发送数据 Modbus报文(字符串)
/// </summary>
public string SendSerialString(string str)
{
string result = string.Empty;<br><br> //this.manualEvent.WaitOne();//等待<br><br> //lock (threadObj)
//{
//this.manualEvent.Reset();//红灯
if (serialPort != null && serialPort.IsOpen && !string.IsNullOrWhiteSpace(str))
{<br><br> serialPort.DiscardInBuffer();
serialPort.DiscardOutBuffer();<br><br> Thread.Sleep(50);<br><br> serialPort.Write(str);<br><br> Thread.Sleep(50);//写入读取命令后延时读取 或者跟下一条命令之间产生间隔<br><br> try
{
// 接收来自读取命令的数据 广播命令执行完毕不响应
if (serialPort.BytesToRead > 0)
{
result = serialPort.ReadExisting();
}
}
catch (Exception e)
{<br><br> }<br><br> }
//this.manualEvent.Set();//绿灯
//}
return result;
}<br><br> /// <summary>
/// 向网口发送数据 Modbus报文
/// </summary>
/// <param name="arr"></param>
public void SendTCPByte(byte[] arr)
{
if (arr != null && arr.Count() > 0 && tcpClient.Connected)
{
// 获取网络流
NetworkStream stream = tcpClient.GetStream();<br><br> //tcpClient.Client.Send(arr);
stream.Write(arr, 0, arr.Length);
//Thread.Sleep(50);//写入读取命令后延时读取 或者跟下一条命令之间产生间隔<br><br> try
{
// 接收来自读取命令的数据 广播命令执行完毕不响应
byte[] receiveData = new byte;
int bytesRead = stream.Read(receiveData, 0, receiveData.Length);
}
catch (Exception)
{<br><br> throw;
}<br><br> }<br><br>
}<br><br> /// <summary>
/// 向网口发送数据 Modbus报文(Socket备用)
/// </summary>
/// <param name="arr"></param>
public void SendSocketByte(byte[] arr)
{
if (arr != null && arr.Count() > 0 && socket.Connected)
{
socket.Send(arr);
//Thread.Sleep(50);//写入读取命令后延时读取 或者跟下一条命令之间产生间隔<br><br> try
{
// 接收来自读取命令的数据 广播命令执行完毕不响应
byte[] buffer = new byte;
int byteRead = socket.Receive(buffer);
}
catch (Exception)
{<br><br> throw;
}<br><br>
}
}<br><br> #endregion<br><br> #region read<br><br>
/// <summary>
/// 读取单个线圈 Modbus 01功能码
/// </summary>
/// <param name="slaveAddress">从站号</param>
/// <param name="StartAddress">开始读取的寄存器地址</param>
/// <param name="numberOfPoints">读取数据个数</param>
/// <returns></returns>
public bool[] ReadCoils_01(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
return master.ReadCoils(slaveAddress, startAddress, numberOfPoints);
}<br><br> /// <summary>
/// 读取离散/输入线圈Modbus 02功能码
/// </summary>
/// <param name="slaveAddress">从站号</param>
/// <param name="StartAddress">开始读取的寄存器地址</param>
/// <param name="numberOfPoints">读取数据个数</param>
/// <returns></returns>
public bool[] ReadInputs_02(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
return master.ReadInputs(slaveAddress, startAddress, numberOfPoints);
}<br><br> /// <summary>
/// 读取保持型寄存器Modbus 03功能码
/// </summary>
/// <param name="slaveAddress">从站号</param>
/// <param name="StartAddress">开始读取的寄存器地址</param>
/// <param name="numberOfPoints">读取数据个数</param>
/// <returns></returns>
public ushort[] ReadHoldingRegisters_03(byte slaveAddress, ushort StartAddress, ushort numberOfPoints)
{<br><br> try
{
ushort[] Uvalue = master.ReadHoldingRegisters(slaveAddress, StartAddress, numberOfPoints);<br><br> //ushort 转short
//short[] value = new short;
//for (int i = 0; i < Uvalue.Length; i++)
//{
// if (Uvalue > short.MaxValue)
// {
// value = (short)(Uvalue - 65536);
// }
// else
// {
// value = (short)Uvalue;
// }
//}<br><br> return Uvalue;
}
catch (Exception e)
{
return null;
}<br><br> }<br><br> /// <summary>
/// 读取输入寄存器 Modbus 04功能码
/// </summary>
/// <param name="slaveAddress">从站号</param>
/// <param name="StartAddress">开始读取的寄存器地址</param>
/// <param name="numberOfPoints">读取数据个数</param>
/// <returns></returns>
public ushort[] ReadInputRegisters_04(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{<br><br> try
{
ushort[] Uvalue = master.ReadInputRegisters(slaveAddress, startAddress, numberOfPoints);<br><br> //ushort 转short<br><br> //short[] value = new short;
//for (int i = 0; i < Uvalue.Length; i++)
//{
// if (Uvalue > short.MaxValue)
// {
// value = (short)(Uvalue - 65536);
// }
// else
// {
// value = (short)Uvalue;
// }<br><br>
//}<br><br> return Uvalue;
}
catch (Exception e)
{
return null;
}<br><br> }<br><br>
#endregion<br><br> #region write<br><br> /// <summary>
/// 写单个线圈 Modbus 05功能码
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="coilAddress"></param>
/// <param name="value"></param>
public void WriteSingleCoil_05(byte slaveAddress, ushort coilAddress, bool value)
{<br><br> master.WriteSingleCoil(slaveAddress, coilAddress, value);<br><br> }<br><br> /// <summary>
/// 写单个保持寄存器 Modbus 06功能码
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="registerAddress"></param>
/// <param name="value"></param>
public void WriteSingleRegister_06(byte slaveAddress, ushort registerAddress, ushort value)
{<br><br> try
{
//short 转 ushort<br><br> //ushort Uvalue;
//if (value < 0)
//{
// Uvalue = (ushort)(ushort.MaxValue + value + 1);
//}
//else
//{
// Uvalue = (ushort)value;
//}
master.WriteSingleRegister(slaveAddress, registerAddress, value);
}
catch (Exception e)
{
return;
}<br><br>
}<br><br><br><br> /// <summary>
/// 写多个寄存器Modbus 10功能码
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
public void WriteMultipleRegisters_10(byte slaveAddress, ushort startAddress, ushort[] data)
{
try
{
master.WriteMultipleRegisters(slaveAddress, startAddress, data);
//master.WriteMultipleRegistersAsync(slaveAddress, startAddress, data);
}
catch (Exception e)
{<br><br> }<br><br><br><br>
}<br><br> #endregion<br><br> #region鸣志伺服-多线程不安全模式<br><br> // 设置加减速,减速度,速度 数值1-10k/分钟
public void SetAccelerationSpeedDeceleration(byte slaveAddr, ushort acceleration, ushort deceleration, ushort speed)
{
ushort parametersAddress = 344;<br><br> ushort[] parameters = new ushort;
parameters = 0;
parameters = acceleration;
parameters = 0;
parameters = deceleration;
parameters = 0;
parameters = speed;<br><br> WriteMultipleRegisters_10(slaveAddr, parametersAddress, parameters);
}<br><br> /// <summary>
/// 读取伺服电机加速度减速度速度
/// </summary>
/// <param name="slaveAddr"></param>
/// <returns></returns>
public ushort[] ReadAccelerationSpeedDeceleration(byte slaveAddr)
{
ushort[] result = new ushort;
ushort[] speedArr = ReadHoldingRegisters_03(slaveAddr, 344, 6);
if (speedArr != null && speedArr.Length >= 6)
{
//AC加速度344+1
//DC减速度346+1
//VE速度348+1
result = speedArr;
result = speedArr;
result = speedArr;
}
return result;
}<br><br> // 设置距离 数值1-100/圈
public void Distance(byte slaveAddr, long distance)
{<br><br> ushort parametersAddress = 350;
ushort[] parameters = new ushort;
parameters = (ushort)distance;<br><br> WriteMultipleRegisters_10(slaveAddr, parametersAddress, parameters);<br><br> }<br><br> // 设置距离 脉冲数
public void DistanceForPulse(byte slaveAddr, int pulse)
{
string convert = pulse.ToString("X").PadLeft(8, '0'); //转十六进制然后八位补零<br><br> var sendByte = new byte[] {
slaveAddr,
0X10, 0x01, 0x5E, 0x00, 0x02 ,0x04 ,
Convert.ToByte(convert.Substring(0, 2), 16),
Convert.ToByte(convert.Substring(2, 2), 16),
Convert.ToByte(convert.Substring(4, 2), 16),
Convert.ToByte(convert.Substring(6, 2), 16)};<br><br> var crc = CRC16.CRC16Calc(sendByte);
var newSend = sendByte.Concat(crc).ToArray();<br><br> SendSerialByte(newSend);<br><br>
}<br><br>
//Modbus 寄存器表中寄存器40125被定义为操作码寄存器,向40125寄存器写入相应的操作码,即执行相应操作码的动作
//FL 0x66
//SK 0xE1
//SO 0x8B<br><br> /// <summary>
/// 电机旋转运行到指定距离 (执行相对移动命令,移动距离和方向来自最后一个DI命令 按给定长度进至距离)
/// </summary>
/// <param name="slaveAddr"></param>
public void Opcode_FL(byte slaveAddr)
{
ushort operationCodeAddress = 124;
byte opCode = 0x66;<br><br> WriteSingleRegister_06(slaveAddr, operationCodeAddress, opCode);
}<br><br> /// <summary>
/// 强制停止
/// </summary>
/// <param name="slaveAddr"></param>
public void Opcode_SK(byte slaveAddr)
{
ushort operationCodeAddress = 124;
byte opCode = 0xE1;
WriteSingleRegister_06(slaveAddr, operationCodeAddress, opCode);
}<br><br> ///// <summary>
///// 强制停止-带缓冲
///// </summary>
///// <param name="slaveAddr"></param>
//publicvoid Opcode_SKD(byte slaveAddr)
//{
// ushort operationCodeAddress = 124;
// byte opCode = 0xE2;
// master.WriteSingleRegister(slaveAddr, operationCodeAddress, opCode);
//}<br><br> /// <summary>
/// 报警清除
/// </summary>
/// <param name="slaveAddr"></param>
public void Opcode_AX(byte slaveAddr)
{
ushort operationCodeAddress = 124;
byte opCode = 0xBA;
WriteSingleRegister_06(slaveAddr, operationCodeAddress, opCode);
}<br><br>
//1 0x31 数字量输入/输出端口1
//2 0x32 数字量输入/输出端口2
//3 0x33 数字量输入/输出端口3
//4 0x34 数字量输入/输出端口4
//5 0x35 数字量输入/输出端口5
//6 0x36 数字量输入/输出端口6
//7 0x37 数字量输出端口7
//8 0x38 数字量输出端口8
//9 0x39 数字量输出端口9
//L 0x4C 低电平(光耦导通)
//H 0x48 高电平(光耦断开)<br><br> /// <summary>
/// Y口输出L 0x4C H 0x48
/// </summary>
/// <param name="slaveAddr"></param>
public void Opcode_SO(byte slaveAddr, byte ioPoint, byte condition)
{
ushort operationCodeAddress = 124;
byte operationCode = 0x8B;<br><br> ushort[] operationCodeParameters = new ushort;
operationCodeParameters = operationCode;
operationCodeParameters = ioPoint;
operationCodeParameters = condition;<br><br> WriteMultipleRegisters_10(slaveAddr, operationCodeAddress, operationCodeParameters);<br><br>
}<br><br> /// <summary>
/// X口输入 ,0触碰1断开
/// </summary>
/// <param name="slaveAddr"></param>
public string Opcode_IS(byte slaveAddr)
{
string result = string.Empty;
try
{
ushort inputPortAddress = 5;
ushort numberRegisters = 1;<br><br> ushort[] data = ReadHoldingRegisters_03(slaveAddr, inputPortAddress, numberRegisters);
if (data != null && data.Count() > 0)
{
result = Convert.ToString(data, 2).PadLeft(12, '0');//转2进制,然后12位补零;
}
}
catch (Exception)
{<br><br> throw;
}
return result;
}<br><br> /// <summary>
/// 伺服使能
/// </summary>
/// <param name="slaveAddr"></param>
/// <returns></returns>
public void Opcoded_ME(byte slaveAddr)
{
ushort operationCodeAddress = 124;
byte opCode = 0x9F;
WriteSingleRegister_06(slaveAddr, operationCodeAddress, opCode);
}<br><br> /// <summary>
/// 伺服使能关闭
/// </summary>
/// <param name="slaveAddr"></param>
/// <returns></returns>
public void Opcoded_MD(byte slaveAddr)
{
ushort operationCodeAddress = 124;
byte opCode = 0x9E;
WriteSingleRegister_06(slaveAddr, operationCodeAddress, opCode);
}<br><br> //以下单独设置参数,在不知道其他参数,不改动其他参数的时候使用(备用)<br><br> // 设置加速度
public void Acceleration(byte slaveAddr, ushort acceleration)
{
ushort parametersAddress = 344;<br><br> ushort[] parameters = new ushort;
parameters = 0;
parameters = acceleration;<br><br>
WriteMultipleRegisters_10(slaveAddr, parametersAddress, parameters);
}<br><br> // 设置减速度
public void Deceleration(byte slaveAddr, ushort deceleration)
{
ushort parametersAddress = 346;<br><br> ushort[] parameters = new ushort;
parameters = 0;
parameters = deceleration;<br><br>
WriteMultipleRegisters_10(slaveAddr, parametersAddress, parameters);
}<br><br> // 设置速度
public void Speed(byte slaveAddr, ushort speed)
{
ushort parametersAddress = 348;<br><br> ushort[] parameters = new ushort;
parameters = 0;
parameters = speed;<br><br>
WriteMultipleRegisters_10(slaveAddr, parametersAddress, parameters);
}<br><br> #endregion<br><br> #region 鸣志步进-多线程不安全模式 <br><br> //鸣志步进写入,读取命令延时在 ModbusHelper.SendSerialString 统一设置<br><br> //步进电机10以及以上从站地址以符号表示 <br><br> //AC25 - 将加 设置为25转/秒/秒 344
//DE25 - 将减 设置为25转/秒/秒 346
//VE1.5 - 将速度设置为1.5转/秒 348
//FL20000 - 执行20000步的按长度给进移动到距离 350<br><br> /// <summary>
///步进电机设置加速,减速,速度 支持整数和小数 数值1-10转/秒
/// </summary>
public void Stepping_SetAccelerationSpeedDeceleration(object slaveAddr, string acceleration, string deceleration, string speed)
{
string strAC = $"{slaveAddr}AC{acceleration}\r";
SendSerialString(strAC);
//加速度<br><br>
string strDE = $"{slaveAddr}DE{deceleration}\r";
SendSerialString(strDE);
//减速度<br><br>
string strVE = $"{slaveAddr}VE{speed}\r";
SendSerialString(strVE);
//速度
}<br><br> /// <summary>
/// 步进电机读取当前电机的加速度减速度速度
/// </summary>
/// <param name="slaveAddr"></param>
/// <returns></returns>
public int[] Stepping_Read_AC_DE_VE(object slaveAddr)
{
int[] result = new int;<br><br>
//加速度
string strAC = $"{slaveAddr}AC\r";
var speed = SendSerialString(strAC);
speed = speed.Replace($"{slaveAddr}AC=", string.Empty);
speed = speed.Replace("\r", string.Empty);
if (!string.IsNullOrWhiteSpace(speed))
{
result = Convert.ToInt32(speed);
}<br><br> //减速度
string strDE = $"{slaveAddr}DE\r";
speed = SendSerialString(strDE);
speed = speed.Replace($"{slaveAddr}DE=", string.Empty);
speed = speed.Replace("\r", string.Empty);
if (!string.IsNullOrWhiteSpace(speed))
{
result = Convert.ToInt32(speed);
}<br><br> //速度
string strVE = $"{slaveAddr}VE\r";
speed = SendSerialString(strVE);
speed = speed.Replace($"{slaveAddr}VE=", string.Empty);
speed = speed.Replace("\r", string.Empty);
if (!string.IsNullOrWhiteSpace(speed))
{
result = Convert.ToInt32(speed);
}<br><br> return result;
}<br><br>
/// <summary>
///设置步进电机移动距离 (支持正负数) 数值1w脉冲/圈
/// </summary>
/// <param name="slaveAddr"></param>
/// <param name="speed"></param>
public void Stepping_Distance(object slaveAddr, long distance)
{
string strDI = $"{slaveAddr}DI{distance}\r";
SendSerialString(strDI);
//移动距离
}<br><br> /// <summary>
///电机旋转运行到指定距离 (执行相对移动命令,移动距离和方向来自最后一个DI命令 按给定长度进至距离) 数值同DI
/// </summary>
public void Stepping_FL(object slaveAddr, string numberStr)
{
string str = $"{slaveAddr}FL{numberStr}\r";
SendSerialString(str);
}<br><br>
/// <summary>
///设置步进电机强制停止
/// </summary>
public void Stepping_SK(object slaveAddr)
{
string str = $"{slaveAddr}SK\r";
SendSerialString(str);
}<br><br> /// <summary>
///设置步进电机报警复位(部分电机是限位开关,不是限位传感器)
/// </summary>
public void Stepping_AR(object slaveAddr)
{
string str = $"{slaveAddr}AR\r";
SendSerialString(str);
}<br><br> /// <summary>
///设置步进电机Y口开关,L开,H关
/// </summary>
public void Stepping_SO(object slaveAddr, int number, string _switch)
{
string str = $"{slaveAddr}SO{number}{_switch}\r";
SendSerialString(str);
}<br><br> /// <summary>
///读取步进电机X口开关,0触碰1断开
/// </summary>
public string Stepping_IS(object slaveAddr)
{
string str = $"{slaveAddr}IS\r";
str = SendSerialString(str);
return str;<br><br> }<br><br> /// <summary>
///设置步进电机运转时同时输出 1 支持 0 不支持
/// </summary>
public void Stepping_MT(object slaveAddr, int number)
{
//2024年 鸣志研发确认鸣志伺服暂不支持MT多任务并行
string str = $"{slaveAddr}MT{number}\r";
SendSerialString(str);
}<br><br><br><br>
#endregion<br><br> #region close关闭串口、网口、Socket连接<br><br> /// <summary>
///关闭串口,Modbus485连接
/// </summary>
public void CloseSerialPort()
{
serialPort.Close();
master.Dispose();
}<br><br> /// <summary>
///关闭网口,Modbus485连接
/// </summary>
public void CloseTCP()
{
tcpClient.Close();
master.Dispose();
}<br><br> /// <summary>
/// 关闭socket,Modbus485连接
/// </summary>
public void CloseSocket()
{
socket.Close();
socket.Disconnect(true);<br><br> //master.Dispose();
}<br><br> /// <summary>
/// 垃圾回收,串口,网口,socket,Modbus485 全部关闭
/// 单个设备调试用using,多线程流程用手动连接并关闭
/// </summary>
public void Dispose()
{
try
{
//串口部分
if (serialPort != null)
{
serialPort.Close();
}<br><br> //网口部分
if (tcpClient != null)
{
tcpClient.Close();
}<br><br> //socket 备用部分
if (socket != null)
{
socket.Close();
}<br><br> //modbus-485
if (master != null)
{
master.Dispose();
}<br><br> }
catch (Exception e)
{
LogHelper.Error($"垃圾回收处理异常:" + e.Message);
}<br><br> }<br><br> #endregion<br><br> }<br><br> /// <summary>
///16位CRC校验
/// </summary>
public static class CRC16
{
/// <summary>
/// 输出CRC校验码
/// </summary>
/// <param name="data">输入字节数组</param>
/// <returns>字节0是高8位,字节1是低8位</returns>
public static byte[] CRC16Calc(byte[] data)
{
//crc计算赋初始值
int crc = 0xffff;
for (int i = 0; i < data.Length; i++)
{
crc = crc ^ data;
for (int j = 0; j < 8; j++)
{
int temp;
temp = crc & 1;
crc = crc >> 1;
crc = crc & 0x7fff;
if (temp == 1)
{
crc = crc ^ 0xa001;
}
crc = crc & 0xffff;
}
}
//CRC寄存器的高低位进行互换
byte[] crc16 = new byte;
//CRC寄存器的高8位变成低8位,
crc16 = (byte)((crc >> 8) & 0xff);
//CRC寄存器的低8位变成高8位
crc16 = (byte)(crc & 0xff);<br><br> return crc16;
}<br><br>
}<br><br>}
以下是多线程安全版,基于上方代码封装的版本
using System;
using System.Linq;
using System.Threading;<br><br>namespace SunnyUI_NuclearForm
{
/// <summary>
/// 线程安全版 鸣志步进/伺服,松下伺服,华庆军继电器模块通用帮助类
/// </summary>
public class ThreadModbusHelper
{<br><br> private static readonly object threadLock = new object();//线程安全锁<br><br> /// <summary>
///松下
/// </summary>
/// <param name="com"></param>
/// <param name="baudRate"></param>
/// <param name="arr"></param>
/// <returns></returns>
public void PannasonicSendByte(string com, int baudRate, byte[] arr)
{
lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> modbus.SendSerialByte(arr);<br><br> }
}
}<br><br> /// <summary>
/// 华庆军
/// </summary>
/// <param name="com"></param>
/// <param name="baudRate"></param>
/// <param name="arr"></param>
/// <returns></returns>
public void HuaqingjunSendByte(string com, int baudRate, byte[] arr)
{
lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> modbus.SendSerialByte(arr);<br><br> }
}
}<br><br>
/// <summary>
/// 称重模块获取数值/去皮归零
/// </summary>
public string GetWeight(string com, int baudRate, byte slaveAddr)
{
string result = string.Empty;<br><br> lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> //去皮
//slaveAddr = 18;
ushort operationCodeAddress = 240; //去皮地址
modbus.WriteSingleCoil_05(slaveAddr, operationCodeAddress, true);<br><br> Thread.Sleep(100);//去皮再称重延迟<br><br> ushort[] weightArr = modbus.ReadHoldingRegisters_03(slaveAddr, 1, 3);
if (weightArr != null && weightArr.Length > 0)
{
ushort weight = weightArr;
var weight1 = weight.ToString().Substring(0, weight.ToString().Length - 1);//正整数部分
var weight2 = weight.ToString().Substring(weight.ToString().Length - 1, 1);//小数部分<br><br> result = $"{weight1}.{weight2}";
}<br><br>
}
}<br><br> return result;<br><br> }<br><br>
#region Stepping_Moons<br><br> /// <summary>
/// 步进电机设置加速,减速,速度 支持整数和小数 数值1-10转/秒
/// </summary>
/// <param name="com"></param>
/// <param name="baudRate"></param>
/// <param name="arr"></param>
/// <returns></returns>
public void Stepping_SetAccelerationSpeedDeceleration(string com, int baudRate, object slaveAddr, string acceleration, string deceleration, string speed)
{
lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> string strAC = $"{slaveAddr}AC{acceleration}\r";
modbus.SendSerialString(strAC);
//加速度<br><br>
string strDE = $"{slaveAddr}DE{deceleration}\r";
modbus.SendSerialString(strDE);
//减速度<br><br>
string strVE = $"{slaveAddr}VE{speed}\r";
modbus.SendSerialString(strVE);
//速度<br><br> }
}
}<br><br>
/// <summary>
/// 设置步进电机移动距离 (支持正负数) 数值1w脉冲/圈
/// </summary>
/// <param name="com"></param>
/// <param name="baudRate"></param>
/// <param name="arr"></param>
/// <returns></returns>
public void Stepping_Distance(string com, int baudRate, object slaveAddr, long distance)
{
lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> string strDI = $"{slaveAddr}DI{distance}\r";
modbus.SendSerialString(strDI);
//移动距离<br><br> }
}
}<br><br>
/// <summary>
/// 电机旋转运行到指定距离 (执行相对移动命令,移动距离和方向来自最后一个DI命令 按给定长度进至距离) 数值同DI
/// </summary>
/// <param name="com"></param>
/// <param name="baudRate"></param>
/// <param name="arr"></param>
/// <returns></returns>
public void Stepping_FL(string com, int baudRate, object slaveAddr, string numberStr)
{
lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> string str = $"{slaveAddr}FL{numberStr}\r";
modbus.SendSerialString(str);<br><br> }
}
}<br><br> /// <summary>
/// 设置步进电机强制停止
/// </summary>
/// <param name="com"></param>
/// <param name="baudRate"></param>
/// <param name="arr"></param>
/// <returns></returns>
public void Stepping_SK(string com, int baudRate, object slaveAddr)
{
lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> string str = $"{slaveAddr}SK\r";
modbus.SendSerialString(str);<br><br> }
}
}<br><br> /// <summary>
///设置步进电机报警复位(部分电机是限位开关,不是限位传感器)
/// </summary>
/// <param name="com"></param>
/// <param name="baudRate"></param>
/// <param name="arr"></param>
/// <returns></returns>
public void Stepping_AR(string com, int baudRate, object slaveAddr)
{
lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> string str = $"{slaveAddr}AR\r";
modbus.SendSerialString(str);<br><br> }
}
}<br><br> /// <summary>
///设置步进电机Y口开关,L开,H关
/// </summary>
/// <param name="com"></param>
/// <param name="baudRate"></param>
/// <param name="arr"></param>
/// <returns></returns>
public void Stepping_SO(string com, int baudRate, object slaveAddr, int number, string _switch)
{
lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> string str = $"{slaveAddr}SO{number}{_switch}\r";
modbus.SendSerialString(str);<br><br> }
}
}<br><br> /// <summary>
///读取步进电机X口开关,0触碰1断开
/// </summary>
/// <param name="com"></param>
/// <param name="baudRate"></param>
/// <param name="arr"></param>
/// <returns></returns>
public string Stepping_IS(string com, int baudRate, object slaveAddr)
{
string str = string.Empty;
lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> str = $"{slaveAddr}IS\r";
str = modbus.SendSerialString(str);<br><br> }
}
return str;
}<br><br> /// <summary>
///设置步进电机运转时同时输出 1 支持 0 不支持
/// </summary>
public void Stepping_MT(string com, int baudRate, object slaveAddr, int number)
{
lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> modbus.Stepping_MT(slaveAddr, number);<br><br> }
}
}<br><br><br><br> #endregion<br><br> #region Servo_Moons<br><br> /// <summary>
/// 伺服电机设置加速,减速,速度 支持整数 数值1-10k/分钟
/// </summary>
/// <param name="com"></param>
/// <param name="baudRate"></param>
/// <param name="arr"></param>
/// <returns></returns>
public void Servo_SetAccelerationSpeedDeceleration(string com, int baudRate, byte slaveAddr, ushort acceleration, ushort deceleration, ushort speed)
{
lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> ushort parametersAddress = 344;<br><br> ushort[] parameters = new ushort;
parameters = 0;
parameters = acceleration;
parameters = 0;
parameters = deceleration;
parameters = 0;
parameters = speed;<br><br> modbus.WriteMultipleRegisters_10(slaveAddr, parametersAddress, parameters);<br><br> }
}
}<br><br> /// <summary>
/// 设置距离 数值1-100/圈
/// </summary>
/// <param name="com"></param>
/// <param name="baudRate"></param>
/// <param name="arr"></param>
/// <returns></returns>
public void Servo_Distance(string com, int baudRate, byte slaveAddr, long distance)
{
lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> ushort parametersAddress = 350;<br><br> ushort[] parameters = new ushort;
parameters = (ushort)distance;<br><br> modbus.WriteMultipleRegisters_10(slaveAddr, parametersAddress, parameters);<br><br> }
}
}<br><br> /// <summary>
/// 电机旋转运行到指定距离 (执行相对移动命令,移动距离和方向来自最后一个DI命令 按给定长度进至距离)
/// </summary>
/// <param name="com"></param>
/// <param name="baudRate"></param>
/// <param name="arr"></param>
/// <returns></returns>
public void Servo_FL(string com, int baudRate, byte slaveAddr)
{
lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> ushort operationCodeAddress = 124;
byte opCode = 0x66;<br><br> modbus.WriteSingleRegister_06(slaveAddr, operationCodeAddress, opCode);<br><br> }
}
}<br><br> /// <summary>
/// 强制停止
/// </summary>
/// <param name="com"></param>
/// <param name="baudRate"></param>
/// <param name="arr"></param>
/// <returns></returns>
public void Servo_SK(string com, int baudRate, byte slaveAddr)
{
lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> ushort operationCodeAddress = 124;
byte opCode = 0xE1;
modbus.WriteSingleRegister_06(slaveAddr, operationCodeAddress, opCode);<br><br> }
}
}<br><br> /// <summary>
/// 报警清除
/// </summary>
/// <param name="com"></param>
/// <param name="baudRate"></param>
/// <param name="arr"></param>
/// <returns></returns>
public void Servo_AX(string com, int baudRate, byte slaveAddr)
{
lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> ushort operationCodeAddress = 124;
byte opCode = 0xBA;
modbus.WriteSingleRegister_06(slaveAddr, operationCodeAddress, opCode);<br><br> }
}
}<br><br>
/// <summary>
/// Y口输出L 0x4C H 0x48
/// </summary>
/// <param name="com"></param>
/// <param name="baudRate"></param>
/// <param name="arr"></param>
/// <returns></returns>
public void Servo_SO(string com, int baudRate, byte slaveAddr, byte ioPoint, byte condition)
{
lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> ushort operationCodeAddress = 124;
byte operationCode = 0x8B;<br><br> ushort[] operationCodeParameters = new ushort;
operationCodeParameters = operationCode;
operationCodeParameters = ioPoint;
operationCodeParameters = condition;<br><br> modbus.WriteMultipleRegisters_10(slaveAddr, operationCodeAddress, operationCodeParameters);<br><br> }
}
}<br><br>
/// <summary>
/// X口输入
/// </summary>
/// <param name="com"></param>
/// <param name="baudRate"></param>
/// <param name="arr"></param>
/// <returns></returns>
public string Servo_IS(string com, int baudRate, byte slaveAddr)
{
string result = string.Empty;
lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> try
{
ushort inputPortAddress = 5;
ushort numberRegisters = 1;<br><br> ushort[] data = modbus.ReadHoldingRegisters_03(slaveAddr, inputPortAddress, numberRegisters);
if (data != null && data.Count() > 0)
{
result = Convert.ToString(data, 2).PadLeft(12, '0');//转2进制,然后12位补零;
}
}
catch (Exception)
{<br><br> }<br><br>
}
}
return result;
}<br><br> /// <summary>
/// 伺服使能关闭
/// </summary>
/// <param name="com"></param>
/// <param name="baudRate"></param>
/// <param name="arr"></param>
/// <returns></returns>
public void Servo_MD(string com, int baudRate, byte slaveAddr)
{
lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> modbus.Opcoded_MD(slaveAddr);<br><br> }
}
}<br><br> /// <summary>
/// 伺服使能
/// </summary>
/// <param name="com"></param>
/// <param name="baudRate"></param>
/// <param name="slaveAddr"></param>
public void Servo_ME(string com, int baudRate, byte slaveAddr)
{
lock (threadLock)
{
using (ModbusHelper modbus = new ModbusHelper())
{
modbus.ConnectForSerialPort(com, baudRate);<br><br> modbus.Opcoded_ME(slaveAddr);<br><br> }
}
}<br><br><br><br> <br><br><br><br>
#endregion<br><br> }
}支持串口通讯,也支持网口通讯。先讲讲Modbus 通讯是什么,软件部署在工控机上作上位机控制下位机运转,比如各种电机和继电器模块(将电脑的小电流信号转换为大电流信号发送到硬件上)
ModbusRTU的报文格式: 从站地址/设备Id(1个字节)+功能码(1个字节)+数据部分(N个字节)+校验、CRC检验(2个字节)
Modbus-RTU的功能码是用于指示Modbus协议进行何种数据操作的标识符。以下是常用的Modbus-RTU功能码及其含义:
01:读取线圈状态,用于读取开关量输入(DO)。
02:读取离散输入状态,用于读取开关量输入(DI)。
03:读取保持寄存器,用于读取模拟量输入(AI)。
04:读取输入寄存器,用于读取模拟量输入(AI)。
05:写单个线圈,用于控制开关量输出(DO)。
06:写单个保持寄存器,用于控制模拟量输出(AO)。
15:写多个线圈,用于控制多个开关量输出(DO)。
16:写多个保持寄存器,用于控制多个模拟量输出(AO)。
需要注意的是,不同设备支持的功能码可能不同,因此在使用Modbus-RTU通信时需要根据实际情况选择合适的功能码。一般常用的01,03,05,06,10
01是读线圈,03是读寄存器
05是写线圈,06是写寄存器
10是往多个寄存器里写数值(16)
15不怎么用
线圈是什么,软件层面可以简单的理解为开关阀门
寄存器是什么,可以简单的理解为一个存放数值的地址,每个地址有不同的作用,写入不同的数值发挥不同的效应
Modbus 报文如何编写?
举几个栗子:
01 功能码示例:
读取 00A0h 伺服准备状态 (S-RDY) ReadOnly 0:准备 OFF 1:准备 ON
01 01 00 A0 00 01 FD E8
01 01 01 01 90 48 请求报文: 01 ,从站地址 ,一般一个485占一个COM口能接入31轴地址
01 ,Modbus 功能码 读取线圈地址
00 a0 ,读取地址00a0
00 01 ,读取一个字节长度,modbus通讯里,基本上是两个字节为一个地址
FD E8 ,crc 16位校验,可以自行生成
应答报文:01, 从站地址
01, 响应功能码01
01,返回一位字节
01,这时候返回的响应字节,就得看每个厂家是如何解释的了,比如这里,00 就是 off ,01 就是 on
9048,crc 16位校验,可以自行生成
03 功能码示例:
读取电压
01 03 60 2C 00 02 1B C2
01 03 04 05 28 00 05 BA F4
00050528H = 329000 (D请求报文: 01 ,从站地址 ,一般一个485占一个COM口能接入31轴地址
03 ,Modbus 功能码 读取寄存器地址
602c ,读取地址602c
00 02 ,读取2字节长度,modbus通讯里,基本上是两个字节为一个地址
1B C2 ,crc 16位校验,可以自行生成
应答报文:01, 从站地址
03, 响应功能码03
04,返回4位字节
05280005,这时候返回的响应字节,就得看每个厂家是如何解释的了,比如这里,一般是高位在前低位在后,但是这个厂家,低位在前高位在后,所以两组字节要反过来转换十进制。
0005在前0528在后,所以最后得到的电压是 00050528HEX = 329000 DEC
05 功能码示例:
01 05 00 61 FF 00 DD E4 ,报警清除 0000h:输入 OFF、FF00h:输入 ON
01 05 00 61 00 00 9C 14请求报文: 01 ,从站地址 ,一般一个485占一个COM口能接入31轴地址
05 ,Modbus 功能码 写线圈
0061 ,写线圈0061
FF00,写入FF00,硬件的编码器看到FF00编译为NO,机器开,00000编译为0FF,机器关
DDE4 ,crc 16位校验,可以自行生成
应答报文:05,06的正确应答报文一般都是把请求报文原样输出回来表示已经执行
06 功能码示例:
加速度减速度速度
01 06 46 00 01f4 设置v0=500请求报文: 01 ,从站地址 ,一般一个485占一个COM口能接入31轴地址
06 ,Modbus 功能码 写寄存器
4600,写寄存器4600
01f4, 十六进制的500,modbus中全部都是十六进制字节请求和应答
这里还有两节CRC,因为速度经常改变,所以没有带CRC,每次请求报文的字节发生变化时CRC一定会变化。
应答报文:05,06的正确应答报文一般都是把请求报文原样输出回来表示已经执行
10 功能码示例:
0E 10 01 5E 00 02 04 00 1E 84 80//200w
0E10 015E 0002 04 FFE1 7B80 //-200w这里展示一个稍微复杂一点的报文
请求报文: 0E , 是十进制从站地址14 ,
10,Modbus 功能码 写多个寄存器
015E,写寄存器015E
0002, 从015e开始写两个寄存器地址
04,四组字节
00 1E 84 80 ,高位字节在前低位字节在后,两组字节为一个寄存器地址,两个寄存器地址合起来存放一个大数值。比如这里的200w, 00 1E 84 80 转换为十进制=200w
这里还有两节CRC,因为速度经常改变,所以没有带CRC,每次请求报文的字节发生变化时CRC一定会变化。
应答报文:10的正确应答报文一般都是把请求报文原样输出回来表示已经执行
下面一组报文其实同上,只是写入的数值是-200w,
根据计算器可以直观的看到十进制-200w=FFE1 7B80
接下来说说两个通用帮助类如何使用
1. 网口通讯
ModbusHelper modbusHelper = new ModbusHelper();<br><br>modbusHelper.ConnectForTCP("IP", "端口")<br><br>// 发送数据到硬件
//00 00 00 00 00 06 01 05 00 00 ff 00 开
byte[] arr = new byte;
arr = 0x00;
arr = 0x00;
arr = 0x00;
arr = 0x00;
arr = 0x00;
arr = 0x06;
arr = 0x01;
arr = 0x05;
arr = 0x00;
arr = 0x00;
arr = 0xff;
arr = 0x00;
modbusHelper.SendTCPByte(arr);<br>modbusHelper.CloseTCP();发送的字节数组是上面曾说过的06功能码,每个厂家都有自己的操作指令
2. 串口通讯
using (ModbusHelper com5_modbusHelper = new ModbusHelper())
{
com5_modbusHelper.ConnectForSerialPort("COM5", 9600);
byte slaveAddr = 2; //开盖抓手X
com5_modbusHelper.Stepping_AR(slaveAddr);//报警清除
//你操作厂家硬件的指令代码集
}我的Modbus 通用帮助类 ,你可以选择用Using连接Dispose自动垃圾回收,也可以自己控制连接和关闭,建议每次发完一组操作指令后就断开和硬件的连接
接下来说鸣志步进电机的指令和控制代码
在 region 鸣志步进-多线程不安全模式 endregion,这个折叠标签里
从上到下依次是常用的十几个命令如下:
步进电机设置加速,减速,速度 支持整数和小数 数值1-10转/秒
步进电机读取当前电机的加速度减速度速度
设置步进电机移动距离 (支持正负数) 数值1w脉冲/圈
电机旋转运行到指定距离 (执行相对移动命令,移动距离和方向来自最后一个DI命令 按给定长度进至距离) 数值同DI
设置步进电机强制停止
设置步进电机报警复位
设置步进电机Y口开关,L开,H关
读取步进电机X口开关,0触碰1断开
设置步进电机运转时同时输出调用方式如下:
using (ModbusHelper com6_modbusHelper = new ModbusHelper())
{
com6_modbusHelper.ConnectForSerialPort("COM6", 9600);<br><br> //从站地址
byte slaveAddr = 1;<br> com6_modbusHelper.Stepping_SetAccelerationSpeedDeceleration(slaveAddr, "8", "8", "6");//设置加速度减速度速度<br> int[] arr=com6_modbusHelper.Stepping_Read_AC_DE_VE(slaveAddr);//读取指定电机的加速度减速度速度//比如一个电机走一段路,从0米处运转到时速100码的过程是加速度,然后按照速度120码均速前进到终点,再终点前按照减速度逐渐停止到0码是减速度。<br> com6_modbusHelper.Stepping_Distance(slaveAddr, 10000);//1w是脉冲数,一般绝大部分电机都是1w脉冲一圈, -1w就是反转1圈<br> com6_modbusHelper.Stepping_FL(slaveAddr, "同DI脉冲数");//一般设置了DI后在执行FL,电机才会运行<br> com6_modbusHelper.Stepping_SK(slaveAddr);//强制停止电机当前的运转<br> com6_modbusHelper.Stepping_AR(slaveAddr);//报警清除,一般电机运行到限位处或者负载过大,驱动器会拒绝继续执行操作命令,需要先清除报警<br> com6_modbusHelper.Stepping_SO(slaveAddr, 1, "L"); //电机的Y_口开启<br> com6_modbusHelper.Stepping_SO(slaveAddr, 1, "H"); //电机的Y_口关闭<br> com6_modbusHelper.Stepping_IS(slaveAddr)//根据这个从站地址读取这个电机的全部输出口,0开1关<br> com6_modbusHelper.Stepping_MT(slaveAddr, number);//设置步进电机运转时同时输出 1 支持 0 不支持,鸣志步进电机支持此指令,伺服电机不支持<br>
}接下来说鸣志伺服电机的指令和控制代码
在 #region Servo_Moons endregion,这个折叠标签里
从上到下依次是常用的十几个命令如下:
设置加减速,减速度,速度
读取伺服电机加速度减速度速度
设置距离 数值1-100/圈
设置距离 脉冲数
电机旋转运行到指定距离 (执行相对移动命令,移动距离和方向来自最后一个DI命令 按给定长度进至距离)
强制停止
报警清除
Y口输出L 0x4C H 0x48
X口输入 ,0触碰1断开
伺服使能
伺服使能关闭伺服和步进的区别在于,伺服是闭环控制自带编码器,步进是开环控制没有数据反馈容易丢步。伺服更精准更贵。
在软件层面,步进电机通过写入简单明了的字符串指令轻易控制,但是同品牌的伺服电机要写地址操作起来更复杂
调用方式如下:
private static ThreadModbusHelper modbus_com5 = new ThreadModbusHelper();//在这里控制伺服全部使用多线程安全帮助类<br> modbus_com5.Servo_SetAccelerationSpeedDeceleration("com5", 9600, slaveAddr, 3000, 3000, 5000);//设置加速度减速度速度,这里的加减速度要/6,速度要/240,才是真正的RPM(每分钟X转)
ushort[] arr=modbus_com5.ReadAccelerationSpeedDeceleration("com5", 9600, slaveAddr);//读取加速度减速度速度,这个方法在多线程不安全版本里,没有集成进多线程安全版本 modbus_com5.Servo_Distance("com5", 9600, slaveAddr, 15);//通过圈数控制电机运转,支持正负数
modbus_com5.DistanceForPulse(slaveAddr, pulse);//通过脉冲控制电机运转,支持正负数
modbus_com5.Servo_FL("com5", 9600, slaveAddr);//让电机运转执行DI命令
modbus_com5.Servo_SK("com6", 9600, slaveAddr);//电机强制停止
modbus_com5.Servo_AX("com5", 9600, slaveAddr);//报警清除
modbus_com5.Servo_SO("com5", 9600, slaveAddr, 0x34, 0x4C);//Y口输出,这里展示的是Y4口,开启。0x34可以替换为 0x31-0x38 对应y1-y8地址,0x4c是开启,0x48是关闭
modbus_com5.Servo_IS("com5", 9600, slaveAddr)//读取X口,0触碰1断开,如果8个输入口都是触碰状态,可能返回的是0,需要强制补零
modbus_com5.Servo_MD("COM6", 9600, slaveAddr);//伺服使能关闭
modbus_com5.Servo_ME("COM6", 9600, slaveAddr);//伺服使能开启,在使用伺服电机需要先伺服使能才能使用其功能
<br><br>
接下来说华庆军继电器模块的指令和控制代码
示例如下:
// 0A05000AFF00AD43 Y11打开
modbus_com8.HuaqingjunSendByte("com8", 9600, new byte[] { 0x0A, 0X05, 0X00, 0X0A, 0XFF, 0X00, 0XAD, 0X43 });又是一串很熟悉的开关线圈字节数组指令,华庆军官网上下载调试软件QingJunTestV3.0.exe
左侧设置开关量型选择,比如我选择的是32路输入输出
左侧下方输入地址或者IP
右侧点击对应的Y1-32,蓝色的一条操作码就是我们需要的字节数组指令
上面是华庆军输出指令,输入指令如下
//十进制
var modbusData = modbusHelper.ReadHoldingRegisters_03(0x01, 0x04, 1);
//十进制转二进制并八位补零
var binary = modbusData != null && modbusData.Length > 0 ? Convert.ToString(modbusData, 2).PadLeft(8, '0') : "00000000";
//二进制转数组
var inArray = StringToArray(binary);
//IN1-IN8数据绑定 //0断开1闭合 接下来说松下伺服电机的指令和控制代码
松下modbus请求报文
00 05 00 60 FF 00 8D F5,00 为广播全部从站伺服开启00 05 00 60 00 00 CC 05,00 为广播全部从站伺服关闭01 05 00 60 FF 00 8C 24 , 指定从站,伺服使能开启0060线圈写入FF00 ON01 05 00 60 00 00 CD D4, 指定从站,伺服使能关闭0060线圈写入0000 off01 06 44 14 00 00 DD 3E,指定运行block编号,4414寄存器地址写入0000,指定运行block_0号动作这里解释下block 是什么,松下伺服电机需要先编辑好block动作,然后调用block,电机才会运转。block里包括常用的正转反转加速减速速度,回原点,紧急刹车等功能01 05 01 20 FF 00 8C 0C,0120线圈对应地址,STB开关, FF00开启01 05 01 20 00 00 CD FC ,0120线圈对应地址,STB开关, 0000关闭05线圈 01 05 00 61 FF 00 DD E4 ,报警清除 0000h:输入 OFF、FF00h:输入 ON
01 05 00 61 00 00 9C 1406 寄存器01 06 46 3a 00 00 原点有效化01 06 46 3a 00 01 原点无效化01 06 10 20 61 73 保存到 EEPROM加速度减速度速度
01 06 46 00 01f4 设置v0=50001 06 46 10 00 64 设置a0=10001 06 46 20 00 64 设置d0=100读取 00A0h 伺服准备状态 (S-RDY) ReadOnly 0:准备 OFF 1:准备 ON
01 01 00 A0 00 01 FD E8
01 01 01 01 90 48 读取电压
01 03 60 2C 00 02 1B C2
01 03 04 05 28 00 05 BA F4
00050528H = 329000 (D
以下为本人编辑好的Block动作一览
//松下电机 Block相关动作编号
//从站地址10 桶盖
//0 正10w
//1 负10w
//2 正1000
//3 负1000
//4 163w3k
//5 正3k
//6 负3k
//7 绝对定位 0 回原点
//8 减速停止/即刻停止
比如调用紧急刹车如下
这里就要用到CRC动态生成校验数组了
using (ModbusHelper bottle_modbusHelper = new ModbusHelper())
{
bottle_modbusHelper.ConnectForSerialPort("COM6", 9600);
//16 10
//10 06 44 14 00 08指定运行block编号
byteArr = new byte[] { 0x10, 0x06, 0x44, 0x14, 0x00, 0x08 };
crc = CRC16.CRC16Calc(byteArr);
newArr = byteArr.Concat(crc).ToArray();
bottle_modbusHelper.SendSerialByte(newArr);
Thread.Sleep(50);//松下命令间隔
//10 05 01 20 FF 00 ,STB开
byteArr = new byte[] { 0x10, 0x05, 0x01, 0x20, 0xFF, 0x00 };
crc = CRC16.CRC16Calc(byteArr);
newArr = byteArr.Concat(crc).ToArray();
bottle_modbusHelper.SendSerialByte(newArr);
}以下为工厂部分硬件实拍
以下为部分UI实拍
祝各位在工控/自动化的道路越走越舒坦<br><br>来源:https://www.cnblogs.com/Gao1234/p/18096175
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页:
[1]