本文根据项目需要仅编写了部分继电器控制的代码,输入读取功能待后续完善,使用是可直接调用该类中的函数,实现继电器模块继电器的开启和关闭。
- 通信方式:RS485,使用USB转485通信线
- 编程语言:C#
- 框架:.NET Framework 4.5.2
1 串口设置代码
1.1 串口初始化
cs
// 初始化串口参数
private void InitializeSerialPortPra(int BaudRate)
{
// 默认参数设置
_serialPort.BaudRate = BaudRate; // 波特率
_serialPort.DataBits = 8; // 数据位
_serialPort.StopBits = StopBits.One; // 停止位
_serialPort.Parity = Parity.None; // 校验位
_serialPort.ReadTimeout = 500; // 读取超时
_serialPort.WriteTimeout = 500; // 写入超时
_serialPort.ReadBufferSize = 1024;
// 绑定数据接收事件
_serialPort.DataReceived += SerialPort_DataReceived;
}
1.2 串口开启、关闭代码
cs
/// <summary>
/// 打开/关闭串口函数
/// </summary>
/// <param name="COMPort_Name"></param>
private void OpenClose_SerialPort(String COMPort_Name,bool isOpen)
{
// 加载可用串口列表
string[] portNames = SerialPort.GetPortNames();
if (portNames.Length == 0) // 说明无串口可用
{
MessageBox.Show("可用端口数为0!");
return;
}
if (isOpen)
{
try
{
// 设置端口名称
_serialPort.PortName = COMPort_Name;
// 打开串口
_serialPort.Open();
}
catch (Exception ex)
{
MessageBox.Show("打开串口失败:" + ex.Message);
}
}
else
{
// 关闭串口
_serialPort.Close();
}
}
// 发送数据按钮
public void SerialPort_SendByte(Byte[] SendData)
{
if (!_serialPort.IsOpen)
{
MessageBox.Show("请先打开串口");
return;
}
try
{
if (SendData.Length>0)
{
// 发送数据,字节数组
_serialPort.Write(SendData,0, SendData.Length);
}
}
catch (Exception ex)
{
MessageBox.Show("发送数据失败:" + ex.Message);
}
}
1.3 发送及接收数据代码
cs
// 发送数据函数
public void SerialPort_SendByte(Byte[] SendData)
{
if (!_serialPort.IsOpen)
{
MessageBox.Show("请先打开串口");
return;
}
try
{
if (SendData.Length>0)
{
// 发送数据,字节数组
_serialPort.Write(SendData,0, SendData.Length);
}
}
catch (Exception ex)
{
MessageBox.Show("发送数据失败:" + ex.Message);
}
}
byte[] RevData = new byte[1024]; // 串口接收数据缓存
bool RevDataFlag = false; //串口接收数据标志位
int RevedBytesNum = 0; //接收到的字节数
// 数据接收事件处理
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
// 读取接收到的数据
//string receivedData = _serialPort.ReadLine();
RevedBytesNum = _serialPort.Read(RevData,0, _serialPort.ReadBufferSize); // 读取接收到的字节数组,读取缓冲中所有数据
if (RevedBytesNum>0)
{
RevDataFlag = true;
}
// 跨线程更新UI
//this.Invoke(new Action(() =>
//{
// txtLog.AppendText("接收:" + receivedData + Environment.NewLine);
//}));
}
catch (Exception ex)
{
//this.Invoke(new Action(() =>
//{
// txtLog.AppendText("接收数据错误:" + ex.Message + Environment.NewLine);
//}));
}
}
2 继电器控制函数
cs
/// <summary>
/// 计算并添加CRC16校验码(Modbus标准)
/// </summary>
/// <param name="data">原始字节数组</param>
/// <returns>添加CRC16校验码后的完整数组</returns>
public static byte[] AppendCrc16(byte[] data)
{
if (data == null || data.Length == 0)
return new byte[0];
// 计算CRC16校验码
ushort crc = 0xFFFF; // 初始值
for (int i = 0; i < data.Length; i++)
{
crc ^= data[i]; // 与当前字节异或
for (int j = 0; j < 8; j++) // 处理每个bit
{
bool lsb = (crc & 1) == 1; // 检查最低位
crc >>= 1;
if (lsb) crc ^= 0xA001; // 如果最低位为1,异或多项式
}
}
// 创建新数组(原始数据 + 2字节CRC)
byte[] result = new byte[data.Length + 2];
Array.Copy(data, 0, result, 0, data.Length);
// 添加CRC(低字节在前)
result[data.Length] = (byte)(crc & 0xFF); // 低字节
result[data.Length + 1] = (byte)((crc >> 8) & 0xFF); // 高字节
return result;
}
/// <summary>
/// 适用于256路及以内的继电器模块
/// </summary>
/// <param name="Relay">需要控制的继电器</param>
/// <param name="isOpen">打开还是关闭</param>
/// <returns></returns>
private byte[] RelayCtrlFrameGer( RelayNo Relay,bool isOpen,byte mySiteNo)
{
byte[] RelayCtrlBaseFrame = { 0X01, 0X06, 0X00, 0X00, 0X00, 0X00};
RelayCtrlBaseFrame[0] = mySiteNo; //控制的站点
SiteNo = mySiteNo; //目前控制的站点
RelayCtrlBaseFrame[3] = (byte) Relay;
RelayCtrlBaseFrame[5] = (byte)(isOpen ? 0X01 : 0X00);
// 创建新数组(原始数据 + 2字节CRC)
byte[] RelayCtrlFrame = new byte[RelayCtrlBaseFrame.Length + 2];
RelayCtrlFrame = AppendCrc16(RelayCtrlBaseFrame);
return RelayCtrlFrame;
}
/// <summary>
/// 继电器控制函数
/// </summary>
/// <param name="Relay">继电器编号</param>
/// <param name="isOpen">开启或关闭</param>
/// <returns>返回枚举类型可判断是否控制成功</returns>
public RelayCtrlStatus RelayCtrlFunc(RelayNo Relay, bool isOpen, byte mySiteNo)
{
// 清除缓存
RevData = new byte[1024]; // 串口接收数据缓存
RevDataFlag = false; //串口接收数据标志
RelayCtrlStatus ReturnStus = RelayCtrlStatus.Unknow;
SerialPort_SendByte(RelayCtrlFrameGer( Relay,isOpen, mySiteNo));
Thread.Sleep(50);
if (RevDataFlag)
{
if (RevData[0] == SiteNo && RevData[1] == 0X06) // 站点及功能码验证
{
if ((RevData[5] == 0X01 && isOpen) || (RevData[5] == 0X00 && !isOpen))
{
ReturnStus = RelayCtrlStatus.Success;
}
else
{
ReturnStus = RelayCtrlStatus.Fail;
}
}
}
return ReturnStus;
}
3. 上位机界面
界面较为简易,控制成功后相应指示灯变为绿色,默认深灰色。
