电器模C#汇控电子继块驱动(Modbus协议)

本文根据项目需要仅编写了部分继电器控制的代码,输入读取功能待后续完善,使用是可直接调用该类中的函数,实现继电器模块继电器的开启和关闭。
  • 通信方式: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. 上位机界面

界面较为简易,控制成功后相应指示灯变为绿色,默认深灰色。

相关推荐
咩图4 小时前
C#创建AI项目
开发语言·人工智能·c#
2501_941147115 小时前
智能物流与实时调度:Python与Go构建高效运输管理系统
visual studio
周杰伦fans5 小时前
C# - Task 是什么?想象一下你在餐厅点餐
服务器·开发语言·c#
一只小小汤圆7 小时前
简化点集合 道格拉斯-普克算法(Douglas-Peucker Algorithm)
c#·occ
scixing7 小时前
函数式编程 第八讲 循环者,递归也
开发语言·c#
屠夫8 小时前
SqlSugar的简单使用
c#
dotent·1 天前
C#基于WPF UI框架的通用基础上位机测试WPF框架
ui·c#·wpf
合作小小程序员小小店1 天前
桌面开发,超市管理系统开发,基于C#,winform,sql server数据库
开发语言·数据库·sql·microsoft·sqlserver·c#