红着眼 2024-01-26 17:15 采纳率: 27.8%
浏览 32
已结题

C# 操作485通讯字节转换错误的问题。

C# 485通讯读取和写入设备遇到的问题。
1,调用读取方法时,如果设备里面的值超过了256时,读取出来的内容是错误的。
2,调用写入方法时,当写入的值超过256时,写进设备的值也是错误的。

代码是我从CSDN上东拼西凑过来的,所以不介意修改这里面的代码,只要能满足需求。在此预祝大家新年快乐工作顺利。
源代码如下:
操作界面代码↓

using System;
using System.Linq;
using System.IO.Ports;
using System.Windows.Forms;
using System.Collections.Generic;

namespace PLCTest
{
    public partial class Form1 : Form
    {
        private ModbusRTU modbusRTU;
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            modbusRTU = new ModbusRTU("COM5", 9600, Parity.None, 8, StopBits.One);
        }

        /// <summary>
        /// 打开端口
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnOpen_Click(object sender, EventArgs e)
        {
            bool isSuccess = modbusRTU.Open();
            if (isSuccess)
            {
                MessageBox.Show("端口打开成功");
            }
            else
            {
                MessageBox.Show("端口打开失败");
            }
        }
        /// <summary>
        /// 关闭端口
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnClose_Click(object sender, EventArgs e)
        {
            bool isSuccess = modbusRTU.Close();
            if (isSuccess)
            {
                MessageBox.Show("端口关闭成功");
            }
            else
            {
                MessageBox.Show("端口关闭失败");
            }
        }
        /// <summary>
        /// 读取数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnRead_Click(object sender, EventArgs e)
        {
            //读取参数说明:2=站点,1002=起始位置,1=读取长度
            modbusRTU.ReadMethod("2", 1002, 1);
            ModbusRTU.Result result = modbusRTU.GetReceivedData();
            if (result.IsSuccess)
            {
                MessageBox.Show("读取成功,值:" + result.RefMsg);
            }
            else
            {
                MessageBox.Show("读取失败");
            }
        }
        /// <summary>
        /// 写入数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnWrite_Click(object sender, EventArgs e)
        {
            //写入参数说明:2=站点,1005=起始位置,0=写入的数据
            modbusRTU.WriteMethod("2", 1005, 0);
        }
    }
}

ModbusRTU.cs代码↓

using System;
using System.Linq;
using System.IO.Ports;
using System.Threading;
using System.Collections.Generic;

namespace PLCTest
{
    internal class ModbusRTU
    {
        private Result refResult;
        private SerialPort serialPort;
        int p; int m = 0;
        byte[] TxData2 = new byte[] { };
        bool ReadResult = true;
        //读取结果封装内部类
        public class Result
        {
            public bool IsSuccess;
            public string RefMsg;
        }

        /// <summary>
        /// 构建对象
        /// </summary>
        /// <param name="PortName">串口</param>
        /// <param name="baudRate">波特率</param>
        /// <param name="parity">奇偶验证位</param>
        /// <param name="dataBits">数据位</param>
        /// <param name="stopBits">停止位</param>
        public ModbusRTU(string PortName, int baudRate, Parity parity, int dataBits, StopBits stopBits)
        {
            this.serialPort = new SerialPort() { PortName = PortName, BaudRate = baudRate, DataBits = dataBits, Parity = parity, StopBits = stopBits };
            //订阅SerialDataReceivedEventHandler事件
            this.serialPort.DataReceived += new SerialDataReceivedEventHandler(this.DataReceived);
        }

        /// <summary>
        /// 打开串口
        /// </summary>
        /// <returns></returns>
        public bool Open()
        {
            try { if (!this.serialPort.IsOpen) this.serialPort.Open(); }
            catch { return false; }
            return true;
        }

        /// <summary>
        /// 关闭串口
        /// </summary>
        /// <returns></returns>
        public bool Close()
        {
            try { if (this.serialPort.IsOpen) this.serialPort.Close(); }
            catch { return false; }
            return true;
        }

        /// <summary>
        /// 读取数据的方法
        /// </summary>
        /// <param name="deviceAddress">站点</param>
        /// <param name="startAddress">起始位置</param>
        /// <param name="length">读取长度</param>
        public void ReadMethod(string deviceAddress, int startAddress, int length)
        {
            refResult = new Result();
            ReadResult = false;
            int n = 6;
            byte[] buf = new byte[n];
            buf[0] = Convert.ToByte(deviceAddress);
            buf[1] = Convert.ToByte(3);
            if (startAddress <= 255)
            {
                buf[2] = Convert.ToByte(0);
                buf[3] = Convert.ToByte(startAddress);
            }
            else
            {
                if (Convert.ToString(startAddress, 16).Length < 4)
                {
                    int b = Convert.ToInt32(Convert.ToString(startAddress, 16).Substring(0, 1), 16);
                    buf[2] = Convert.ToByte(b);
                    int c = Convert.ToInt32(Convert.ToString(startAddress, 16).Substring(1, 2), 16);
                    buf[3] = Convert.ToByte(c);
                }
                else
                {
                    int b = Convert.ToInt32(Convert.ToString(startAddress, 16).Substring(0, 2), 16);
                    buf[2] = Convert.ToByte(b);
                    int c = Convert.ToInt32(Convert.ToString(startAddress, 16).Substring(2, 2), 16);
                    buf[3] = Convert.ToByte(c);
                }
            }
            if (length <= 255)
            {
                buf[4] = Convert.ToByte(0);
                buf[5] = Convert.ToByte(length);
            }
            else
            {
                if (Convert.ToString(length, 16).Length < 4)
                {
                    int d = Convert.ToInt32(Convert.ToString(length, 16).Substring(0, 1), 16);
                    buf[4] = Convert.ToByte(d);
                    int f = Convert.ToInt32(Convert.ToString(length, 16).Substring(1, 2), 16);
                    buf[5] = Convert.ToByte(f);
                }
                else
                {
                    int d = Convert.ToInt32(Convert.ToString(length, 16).Substring(0, 2), 16);
                    buf[4] = Convert.ToByte(d);
                    int f = Convert.ToInt32(Convert.ToString(length, 16).Substring(2, 2), 16);
                    buf[5] = Convert.ToByte(f);
                }
            }
            p = length * 2 + 5;
            byte[] buf1 = new byte[n + 2];
            byte[] ReturnData = new byte[2];
            ReturnData = CRC16_C(buf);
            for (int i = 0; i < n; i++)
            {
                buf1[i] = buf[i];
            }
            buf1[n] = ReturnData[1];
            buf1[n + 1] = ReturnData[0];
            //buf1是要写入的数据, 0是写入数据的起始位置,n + 2是要写入的数据长度。
            serialPort.Write(buf1.ToArray(), 0, n + 2);
        }

        /// <summary>
        /// 写入数据的方法
        /// </summary>
        /// <param name="deviceAddress">站点</param>
        /// <param name="startAddress">起始位置</param>
        /// <param name="value">写入值</param>
        /// <returns></returns>
        public bool WriteMethod(string deviceAddress, int startAddress, int value)
        {
            int n = 6;
            byte[] buf = new byte[n];
            buf[0] = Convert.ToByte(deviceAddress);
            buf[1] = Convert.ToByte(6); // 写入命令的功能码为6
            if (startAddress <= 255)
            {
                buf[2] = Convert.ToByte(0);
                buf[3] = Convert.ToByte(startAddress);
            }
            else
            {
                if (Convert.ToString(startAddress, 16).Length < 4)
                {
                    int b = Convert.ToInt32(Convert.ToString(startAddress, 16).Substring(0, 1), 16);
                    buf[2] = Convert.ToByte(b);
                    int c = Convert.ToInt32(Convert.ToString(startAddress, 16).Substring(1, 2), 16);
                    buf[3] = Convert.ToByte(c);
                }
                else
                {
                    int b = Convert.ToInt32(Convert.ToString(startAddress, 16).Substring(0, 2), 16);
                    buf[2] = Convert.ToByte(b);
                    int c = Convert.ToInt32(Convert.ToString(startAddress, 16).Substring(2, 2), 16);
                    buf[3] = Convert.ToByte(c);
                }
            }
            if (value <= 255)
            {
                buf[4] = Convert.ToByte(0);
                buf[5] = Convert.ToByte(value);
            }
            else
            {
                if (Convert.ToString(value, 16).Length < 4)
                {
                    int d = Convert.ToInt32(Convert.ToString(value, 16).Substring(0, 1), 16);
                    buf[4] = Convert.ToByte(d);
                    int f = Convert.ToInt32(Convert.ToString(value, 16).Substring(1, 2), 16);
                    buf[5] = Convert.ToByte(f);
                }
                else
                {
                    int d = Convert.ToInt32(Convert.ToString(value, 16).Substring(0, 2), 16);
                    buf[4] = Convert.ToByte(d);
                    int f = Convert.ToInt32(Convert.ToString(value, 16).Substring(2, 2), 16);
                    buf[5] = Convert.ToByte(f);
                }
            }
            byte[] buf1 = new byte[n + 2];
            byte[] ReturnData = new byte[2];
            ReturnData = CRC16_C(buf);
            for (int i = 0; i < n; i++)
            {
                buf1[i] = buf[i];
            }
            buf1[n] = ReturnData[1];
            buf1[n + 1] = ReturnData[0];
            serialPort.Write(buf1, 0, n + 2);
            return true;
        }

        /// <summary>
        /// 获取数据接收事件反馈的结果
        /// </summary>
        /// <returns></returns>
        public Result GetReceivedData()
        {
            while (!ReadResult)
            {
                Thread.Sleep(100);
            }
            return refResult;
        }

        /// <summary>
        /// 数据接收事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            refResult = new Result();
            int n = serialPort.BytesToRead;
            byte[] TxData = new byte[n];//声明一个临时数组存储当前来的串口数据
            serialPort.Read(TxData, 0, n); //读取缓冲数据 
            m = m + n;
            Array.Resize(ref  TxData2, m);//改写数组大小
            TxData.CopyTo(TxData2, m - TxData.Length);
            if (m == p)
            {
                byte[] data = new byte[p - 2];
                for (int i = 0; i < TxData2.Length - 2; i++)
                {
                    data[i] = TxData2[i];
                }
                byte[] data2 = new byte[2];
                data2[0] = TxData2[TxData2.Length - 2];
                data2[1] = TxData2[TxData2.Length - 1];
                byte[] data1 = new byte[2];
                data1 = CRC16_C(data);
                if (data1[0] == data2[1] && data1[1] == data2[0])
                {
                    string result = string.Empty;
                    for (int j = 3; j <= m - 4; j = j + 2)
                    {
                        string s1 = TxData2[j].ToString();
                        string s2 = TxData2[j + 1].ToString();
                        string s = s1 + s2;
                        int g = Convert.ToInt32(s, 10);
                        string h = g.ToString();
                        result += h;
                    }
                    refResult.RefMsg = result;
                    refResult.IsSuccess = true;
                }
                else
                {
                    refResult.IsSuccess = false;
                }
                m = 0;
            }
            ReadResult = true;
        }

        /// <summary>
        /// 转换
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public byte[] CRC16_C(byte[] data)
        {
            byte CRC16Lo;
            byte CRC16Hi;
            byte CL; byte CH;
            byte SaveHi; byte SaveLo;
            byte[] tmpData;
            int Flag;
            CRC16Lo = 0xFF;
            CRC16Hi = 0xFF;
            CL = 0x01;
            CH = 0xA0;
            tmpData = data;
            for (int i = 0; i < tmpData.Length; i++)
            {
                CRC16Lo = (byte)(CRC16Lo ^ tmpData[i]);
                for (Flag = 0; Flag <= 7; Flag++)
                {
                    SaveHi = CRC16Hi;
                    SaveLo = CRC16Lo;
                    CRC16Hi = (byte)(CRC16Hi >> 1);
                    CRC16Lo = (byte)(CRC16Lo >> 1);
                    if ((SaveHi & 0x01) == 0x01)
                    {
                        CRC16Lo = (byte)(CRC16Lo | 0x80);
                    }

                    if ((SaveLo & 0x01) == 0x01)
                    {
                        CRC16Hi = (byte)(CRC16Hi ^ CH);
                        CRC16Lo = (byte)(CRC16Lo ^ CL);
                    }
                }
            }
            byte[] ReturnData = new byte[2];
            ReturnData[0] = CRC16Hi;
            ReturnData[1] = CRC16Lo;
            return ReturnData;
        }
    }
}

  • 写回答

23条回答 默认 最新

  • bug菌¹ Java领域优质创作者 2024-01-26 18:18
    关注
    获得2.00元问题酬金

    该回答引用ChatGPT,望对题主有所帮助/启发;若有帮助,还望采纳。


    根据你的描述,你遇到的问题可能是由于字节转换错误导致的。特别是当设备的值超过256时,读取和写入的内容都变得错误。

    在代码中,读取方法中的字节转换部分可能存在问题:

    int b = Convert.ToInt32(Convert.ToString(startAddress, 16).Substring(0, 1), 16);
    buf[2] = Convert.ToByte(b);
    int c = Convert.ToInt32(Convert.ToString(startAddress, 16).Substring(1, 2), 16);
    buf[3] = Convert.ToByte(c);
    

    同样,写入方法中也存在字节转换的问题:

    int b = Convert.ToInt32(Convert.ToString(startAddress, 16).Substring(0, 1), 16);
    buf[2] = Convert.ToByte(b);
    int c = Convert.ToInt32(Convert.ToString(startAddress, 16).Substring(1, 2), 16);
    buf[3] = Convert.ToByte(c);
    

    这些代码会将地址转换为16进制字符串,然后再进行转换。由于转换方式不正确,可能会导致转换结果不符合预期。

    解决这个问题的一种方法是使用BitConverter.GetBytes方法来进行字节转换。你可以尝试使用以下代码替换相应的代码段:

    byte[] startAddressBytes = BitConverter.GetBytes(startAddress);
    buf[2] = startAddressBytes[1]; // 高位
    buf[3] = startAddressBytes[0]; // 低位
    

    这样你就可以正确地将地址转换为字节数组,并将相应的字节赋值给buf数组。

    另外,为了避免写入内容错误,你也可以对写入值进行类似的处理:

    byte[] valueBytes = BitConverter.GetBytes(value);
    buf[4] = valueBytes[1]; // 高位
    buf[5] = valueBytes[0]; // 低位
    

    通过这些修改,你的代码应该能够正确地处理超过256的读取和写入值了。希望对你有帮助!祝你新年快乐,工作顺利!

    评论

报告相同问题?

问题事件

  • 系统已结题 2月3日
  • 创建了问题 1月26日

悬赏问题

  • ¥50 AI大模型精调(百度千帆、飞浆)
  • ¥15 关于#c语言#的问题:我在vscode和codeblocks中编写c语言时出现打不开源文件该怎么办
  • ¥15 非科班怎么跑代码?如何导数据和调参
  • ¥15 福州市的全人群死因监测点死亡原因报表
  • ¥15 Altair EDEM中生成一个颗粒,并且各个方向没有初始速度
  • ¥15 系统2008r2 装机配置推荐一下
  • ¥500 服务器搭建cisco AnyConnect vpn
  • ¥15 悬赏Python-playwright部署在centos7上
  • ¥15 psoc creator软件有没有人能远程安装啊
  • ¥15 快速扫描算法求解Eikonal方程咨询