C#上位机生成特定下行报文的方法

引: 上位机需要与单片机通讯,一次性写入全部数据。这里简单介绍如何根据有数据地址、偏移量、长度、数值等信息生成下行报文的方法。寄存器长度为两个字节,储存模式为大端模式。

下行报文格式参见博客https://blog.csdn.net/Serendipitor/article/details/129745301?spm=1001.2014.3001.5501

简单模式:数据长度16位(Int16或UInt16)

此时,配置信息为Int16数据与寄存器地址。将Int16的数据值转换为相邻的两个字节,重复若干次,即可完成整个寄存器的报文。

private byte[] IntToBytes(int a)
{
    int high  = a / 256;
    int low = a % 256;
    string highstring = Convert.ToString((short)high,2).PadLeft(8,"0");
    string lowstring = Convert.ToString((short)low,2).PadLeft(8,"0");
    byte[] b = new byte[2];
    b[0] = Convert.ToInt16(highstring,2);
    b[1] = Convert.ToInt16(lowstring,2);
    return b;
}

复杂模式:Addr的结构

Addr结构如下,有地址、偏移量、长度三个成员变量。

而待写入的数据就是一个Addr与int的字典Dictionary<Addr,int>。WriteReg()方法将这个字典解析为字节数组。

    internal class Addr
    {
        public int _addr;
        public int _offset;
        public int _length;
        public Addr(int addr, int offset, int length)
        {
            _addr = addr;
            _offset = offset;
            _length = length;
        }
    }
        private byte[] WriteReg(Dictionary<Addr, int> Data,int len,int stpos)
        {
            byte[] b = new byte[len * 2];
            foreach(Addr a in Data.Keys)
            {
                int addr = a._addr;
                int length = a._length;
                int offset   = a._offset;   
                int value = Data[a];
                string boolstring;
                if (length == 16)
                {
                    //一个寄存器16位
                    if (value >= 0)
                    {
                        boolstring = Convert.ToString(value, 2).PadLeft(16, '0');
                    }
                    else
                    {
                        boolstring = Convert.ToString((Int16)value, 2).PadLeft(16, '0');
                    }
                    b[(addr - stpos) * 2 + 0] = Convert.ToByte(boolstring.Substring(0, 8), 2);
                    b[(addr - stpos) * 2 + 1] = Convert.ToByte(boolstring.Substring(8, 8), 2);
                }
               else if (length == 8)
                {
                    if (offset == 0)
                    {
                        b[(addr - stpos) * 2 + 1] = (byte)value;
                    }
                    else
                    //否则偏移量为8
                    {
                        b[(addr - stpos) * 2 + 0] = (byte)value;
                    }
                }
                else if (length == 1)
                {
                    if (offset >= 0 && offset <= 7)
                    {
                        int temp = b[(addr - stpos) * 2 + 1];
                        temp |= value << offset;
                        b[(addr - stpos) * 2 + 1] = (byte)temp;
                    }
                    else
                    {
                        int temp = b[(addr - stpos) * 2 + 0];
                        temp |= value << offset;
                        b[(addr - stpos) * 2 + 0] = (byte)temp;
                    }
                }
            }
          
            return b;
        }

最后,把下行报文依照:从机号-指令-开始地址-寄存器数量-字节数-内容-CRC校验位的格式封起来

		public byte[] Mod(int ID=0X81, char func='w', int addr=31000,int len=20, params byte[]a)
		{
			byte[] b = default;
			//读指令
			if (func == 'r')
			{
				//读指令固定长度为8字节
				b = Enumerable.Repeat((byte)0,8).ToArray();
				b[0] = (byte)ID;
				b[1] = 0x03;
				b[2] = b[2] = Convert.ToByte(addr/ 256);//开始地址高字节
				b[3] = Convert.ToByte(addr % 256);//开始地址低字节
				b[4] = Convert.ToByte(len / 256);//寄存器数量高字节
				b[5] = Convert.ToByte(len % 256);//寄存器数量低字节
				byte[] crc = ModbusCrcCalc(b, 6);
				b[6] = crc[0];
				b[7] = crc[1];
				
			}
			else
			{
				b = Enumerable.Repeat((byte)0, 9+len*2).ToArray();
				b[0] = (byte)ID;
				b[1] = 0x10;
				b[2] = b[2] = Convert.ToByte(addr / 256);//开始地址高字节
				b[3] = Convert.ToByte(addr % 256);//开始地址低字节
				b[4] = Convert.ToByte(len / 256);//寄存器数量高字节
				b[5] = Convert.ToByte(len % 256);//寄存器数量低字节
				b[6] = Convert.ToByte(len *2);//寄存器数量低字节
				Array.Copy(a, 0, b, 7, len * 2);
				byte[] crc = ModbusCrcCalc(b, len * 2 + 7);
				b[len*2+7] = crc[0];
				b[len*2+8] = crc[1];
			}
			return b;
		}
        //计算校验位
		private byte[] ModbusCrcCalc(Byte[] data, int lenth = 0)//计算CRC16校验码
		{
			if (lenth == 0)
				lenth = data.Length;

			// crc计算赋初始值
			int crc = 0xffff;
			for (int i = 0; i < lenth; i++)
			{
				crc = crc ^ data[i];
				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;
				}
			}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值