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

本文根据项目需要仅编写了部分继电器控制的代码,输入读取功能待后续完善,使用是可直接调用该类中的函数,实现继电器模块继电器的开启和关闭。
  • 通信方式:RS485,使用USB转485通信线
  • 编程语言:C#
  • 框架:.NET Framework 4.5.2

1 串口设置代码

1.1 串口初始化
          // 初始化串口参数
            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 串口开启、关闭代码
/// <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 发送及接收数据代码

   // 发送数据函数
        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 继电器控制函数

 /// <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. 上位机界面

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Triumph++

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值