qq_36044120 2023-12-11 10:41 采纳率: 0%
浏览 4
已结题

unity串口发送16进制问题,不知道是不是负数问题导致的

unity中想发送16进制给串口 这是16进制的内容: 01 03 00 00 00 01 84 0A 但是串口收到的变成01 03 00 00 00 01 04 0A


using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Text;
using System.Threading;
using UnityEngine;
using System.Linq;
using Microsoft.Win32;

namespace HSJFramework
{
    public enum ReceiveDataType
    {
        EventHandler,
        NewThread
    }
    public class SeriaPortManager : MonoBehaviour
    {
        #region 定义串口属性
        //定义基本信息
        public string portName ;//串口名
        public int baudRate = 9600;//波特率
        public int dataBits = 8;//数据位
        public Parity parity = Parity.None;//效验位
        public StopBits stopBits = StopBits.One;//停止位
        SerialPort sp = null;//声明的串口对象
        Thread dataReceiveThread;//监听串口消息的线程

        //接收消息方式
        public  ReceiveDataType receiveDataType = ReceiveDataType.NewThread;
        /// <summary>
        /// 缓存消息列表
        /// </summary>
        List<byte> bufferList = new List<byte>();
        /// <summary>
        /// 一条消息的长度(消息为16进制)
        /// </summary>
        int messageLen = 15;

        #endregion
        void Start()
        {
            ScanSeria();
            //本地配置数据加载
            if (File.Exists(Application.streamingAssetsPath + "/SeriaPortConfig.txt"))
            {
                Debug.Log("加载串口配置数据");
                string path = Application.streamingAssetsPath + "/SeriaPortConfig.txt";
                string[] strs = File.ReadAllLines(path);
                portName = strs[0];
            }

            OpenPort();
        }

        /// <summary>
        /// 创建打开串口
        /// </summary>
        public void OpenPort()
        {
            Debug.Log("打开串口"+portName);
            sp = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
            sp.ReadTimeout = 4000;
            sp.WriteTimeout = 4000;
            sp.Open();
            if (receiveDataType == ReceiveDataType.NewThread)
            {
                dataReceiveThread = new Thread(DataReceiveFunction);
                dataReceiveThread.Start();
            }
            //经测试无效
            else if (receiveDataType == ReceiveDataType.EventHandler)
            {
                sp.DataReceived += new SerialDataReceivedEventHandler(DataReceived);
            }
        }
        private void DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            ReceiveData();
        }
        /// <summary>
        /// 写入发送数据
        /// </summary>
        /// <param name="dataStr"></param>
        public void WriteData()
        {
            string dataStr= "01 03 00 00 00 01 84 0A";

            if (sp.IsOpen)
            {
                Debug.Log("发送指令:"+dataStr);
                //方式一
                var bd = StringToHexBytes(dataStr);//转16进制字节数组写入
                sp.Write(bd, 0, bd.Length);
                //方式二
                sp.Write(CheckModul(), 0, CheckModul().Length);//拼接16进制字节数组 
            }
        }
        //测试发送串口指令—拼接
        public  byte[] CheckModul()
        {
            return new byte[] { 0x01, 0x03, 0x00, 0x00, 0x00, 0x84, 0x0A };
        }

       
        /// <summary>
        /// 接收数据的线程方法,在.Net一般使用回调函数DataReceived处理,unity里面会有异常故使用开启新线程的方式
        /// </summary>
        void DataReceiveFunction()
        {
            while (sp != null && sp.IsOpen)
            {
                try
                {                
                    Thread.Sleep(100);
                    //单字节接收,只接收单帧数据的第一个字节,不考虑数据的异常情况(一般使用在测试或者协议简单,功能单一的场景)
                    ReceiveChar();
                    //接收单帧的所有数据,可能缺失也能粘包
                    ReceiveData();
                }
                catch (Exception ex)
                {
                    Debug.Log(ex.Message);
                }
            }
        }

        private void ReceiveChar()
        {
            List<byte> listReceive = new List<byte>();
            char[] strchar = new char[100];//接收的字符信息转换为字符数组信息
            byte addr = Convert.ToByte(sp.ReadByte());
            sp.DiscardInBuffer();
            listReceive.Add(addr);
            string str = "";
            for (int i = 0; i < listReceive.Count; i++)
            {
                strchar[i] = (char)(listReceive[i]);
                str = new string(strchar);
            }
            Debug.Log(str);
            listReceive.Clear();
        }

        /// <summary>
        /// 接收读取并处理消息
        /// </summary>
        private void ReceiveData()
        {     
            //待读字节个数
            int n = sp.BytesToRead;
            Debug.Log(n);

            //创建n个字节的缓存
            byte[] buf = new byte[n];
            //读到在数据存储到buf
            sp.Read(buf, 0, n);
            //1.缓存数据 不断地将接收到的数据加入到buffer链表中
            bufferList.AddRange(buf);
      
            //2.完整性判断 至少包含帧头(1字节)、类型(1字节)、功能位(22字节) 根据设计不同而不同
            while (bufferList.Count >= 2)
            {
                //2.1 查找数据头 根据帧头和类型
                if (bufferList[0] == 85 && bufferList[1] == 170)
                {
                    //如果小于则说明数据区尚未接收完整,
                    if (bufferList.Count < messageLen)
                    {
                        //跳出接收函数后之后继续接收数据
                        break;
                    }
                    Debug.Log(bufferList.Count);
                    //得到一帧完整的数据,进行处理,在此之前可以使用校验位保证此帧数据完整性
                    byte[] processingByteArray = new byte[messageLen];
                    //从缓存池中拷贝到处理数组
                    bufferList.CopyTo(0, processingByteArray, 0, messageLen);
                    //处理一帧数据
                    DataAnalysis(processingByteArray);
                    //从缓存池移除处理完的这帧
                    bufferList.RemoveRange(0, messageLen);
                }
                else
                {
                    //帧头不正确时,清除第一个字节,继续检测下一个。
                    bufferList.RemoveAt(0);
                }
            }
        }
        /// <summary>
        /// 数据解析处理  具体的处理解析方案跟下位机一同制定协商
        /// </summary>
        private void DataAnalysis(byte[] dataBytes)
        {
          List<byte>  allDB= dataBytes.ToList();

            byte[] b1 =new byte[2];
            allDB.CopyTo(0, b1, 0, 2);
            Debug.Log("消息头:" + byteToHexStr(b1));

            byte[] b2 = new byte[4];
            allDB.CopyTo(b1.Length, b2, 0, 4);
            Debug.Log("幅度:" + byteToHexStr(b2)+"/"+ + HexBufToFloat(b2, 0));
            byte[] b3 = new byte[4];
            allDB.CopyTo(b1.Length+b2.Length, b3, 0, 4);
            Debug.Log("载频:" + byteToHexStr(b3)+"/"+ HexBufToFloat(b3, 0));
            byte[] b4 = new byte[4];
            allDB.CopyTo(b1.Length + b2.Length+b3.Length, b4, 0, 4);
            Debug.Log("低频:" + byteToHexStr(b4)+"/" + HexBufToFloat(b4, 0));
            byte[] b5 = new byte[1];
            allDB.CopyTo(allDB.Count-1, b5, 0, 1);
            Debug.Log("消息尾:" + byteToHexStr(b5));
        }
        /// <summary>
        /// 字节数组转16进制字符串
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static string byteToHexStr(byte[] bytes)
        {
            string returnStr = "";
            if (bytes != null)
            {
                for (int i = 0; i < bytes.Length; i++)
                {
                    returnStr += bytes[i].ToString("X2");
                    returnStr += "-";
                }
                returnStr = returnStr.Remove(returnStr.Length - 1);
            }
            return returnStr;
        }

      
        /// <summary>
        /// 关闭串口
        /// </summary>
        public void ClosePort()
        {
            if (sp!=null)
            {
                sp.Close();
                if (dataReceiveThread!=null)
                {
                    dataReceiveThread.Abort();

                }
            }
        }

        private void OnDestroy()
        {
            ClosePort();
        }
        /// <summary>
        /// 字符串转16进制字符串
        /// </summary>
        /// <param name="input">要转格式的字符串</param>
        /// <returns>转化为16进制的字符串</returns>
        private string StringToHexStr(string input)
        {
            char[] values = input.ToCharArray();
            string end = string.Empty;
            foreach (char letter in values)
            {
                int value = Convert.ToInt32(letter);
                string hexoutput = string.Format("{0:X}", value); //0 表示占位符 x或X表示十六进制
                end += hexoutput + "_";
            }
            end = end.Remove(end.Length - 1);
            return end;
        }
        //字符串转16进制字节数组
        public byte[] StringToHexBytes(string hs)
        {
            string[] strArr = hs.Trim().Split(' ');
            byte[] b = new byte[strArr.Length];
            //逐个字符变为16进制字节数据
            for (int i = 0; i < strArr.Length; i++)
            {
                b[i] = Convert.ToByte(strArr[i], 16);
            }
            //按照指定编码将字节数组变为字符串
            return b;
        }
        public char[] StringToHexChar(string hs)
        {
            string[]strArr = hs.Trim().Split(' ');
            char[] b = new char[strArr.Length*2];
            //逐个字符变为16进制字节数据
            for (int i = 0; i < strArr.Length; i++)
            {
                b[i*2] = strArr[i].ToCharArray()[0];
                b[i * 2+1] = strArr[i].ToCharArray()[1];
            }
            //按照指定编码将字节数组变为字符串
            return b;
        }
        /// <summary>
        /// 16进制字符串转回正常字符串
        /// </summary>
        /// <param name="input">16进制</param>
        /// <returns>转回的字符串</returns>
        private string HexStrToStr(string input)
        {
            string[] hexvaluesplit = input.Split('-');
            string end = string.Empty;
            foreach (string hex in hexvaluesplit)
            {
                if (hex != "")
                {
                    int value = Convert.ToInt32(hex, 16);
                    string stringvalue = char.ConvertFromUtf32(value);
                    char charValue = (char)value;
                    end += charValue;
                }
            }
            return end;
        }
        //16进制字节数据转Float数据,offset默认为0根据消息协议设定设置
        private float HexBufToFloat(byte[] buf, int offset)
        {
            float f = 0;
            UInt32 num = HexBufToU32(buf, offset);
            byte[] bs = BitConverter.GetBytes(num);
            f = BitConverter.ToSingle(bs, 0);

            return f;
        }
        //16进制字节数据转Int数据

        private UInt32 HexBufToU32(byte[] buf, int offset)
        {
            UInt32 val = 0;
            if ((offset + 4) > buf.Length)
            {
                return 0;
            }

            val |= (UInt32)buf[offset + 0] << 24;
            val |= (UInt32)buf[offset + 1] << 16;
            val |= (UInt32)buf[offset + 2] << 8;
            val |= (UInt32)buf[offset + 3] << 0;

            return val;
        }
        //扫描本机所有端口
        private void ScanSeria()
        {
            string[] portList = SerialPort.GetPortNames();
            foreach (var item in portList)
            {
                Debug.Log(item);
            }
        }
        //使用注册表信息扫描
        private string[] ScanPorts_Regedit()
        {
            RegistryKey keyCom = Registry.LocalMachine.OpenSubKey("Hardware\\DeviceMap\\SerialComm");
            string[] SubKeys = keyCom.GetValueNames();
            string[] portList = new string[SubKeys.Length];
            for (int i = 0; i < SubKeys.Length; i++)
            {
                portList[i] = (string)keyCom.GetValue(SubKeys[i]);
            }
            return portList;
        }
        public byte[] hexStringToBytes(string hexString)
        {
            if ((hexString == null) || (hexString.Equals("")))
            {
                return null;
            }
            hexString = hexString.ToUpper();
            int length = hexString.Length / 2;
            char[] hexChars = hexString.ToCharArray();
            byte[] d = new byte[length];
            for (int i = 0; i < length; i++)
            {
                int pos = i * 2;
                d[i] = ((byte)(charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])));
            }
            return d;
        }

        private sbyte charToByte(char c)
        {
            return (byte)"0123456789ABCDEF".IndexOf(c);
        }
        public byte[] getObject(string obj)
        {
            string[] arr = obj.Split(' ');
            byte[] b = new byte[arr.Length];
            for (int i = 0; i < arr.Length; i++)
            {
                b[i] = hexStringToBytes(arr[i])[0];
            }

            return b;
        }
    }
}

不管用方法1还是方法2,

img

最后在串口助手里收到的都是不对

img


最后还使用了getObject的方法去转

img


得到的结果还是一样的。而getObject的方法是通过java同事的脚本改写过来的,java里面这样写是没有任何问题的。且发现出错的84在他们那边转换之后得到的值其实是-124,而我们这边转换的值132。后面把byte改用sbyte,虽然转换的值可以得到-124.但是write()方法之后在串口助手里得到的值仍然是04.
有人可以解惑吗?

  • 写回答

1条回答 默认 最新

  • qq_36044120 2023-12-11 14:12
    关注

    搞了半天原来是串口的数据位设错了,在串口助手里面把数据位设置成了6,改成8就正常了,被自己搞无语了

    评论

报告相同问题?

问题事件

  • 系统已结题 12月19日
  • 创建了问题 12月11日

悬赏问题

  • ¥30 关于#微信#的问题:微信实名不绑卡 可以实现吗 有没有专家 可以解决
  • ¥15 (标签-考研|关键词-set)
  • ¥15 求修改代码,图书管理系统
  • ¥15 请问有没求偏多标签数据集yeast,reference,recreation,scene,health数据集。
  • ¥15 传感网应用开发单片机实训
  • ¥15 Delphi 关于sAlphaImageList使用问题
  • ¥15 寻找将CAJ格式文档转txt文本的方案
  • ¥15 shein测试开发会问些啥我是写java的
  • ¥15 关于#单片机#的问题:我有个课程项目设计,我想在STM32F103veTX单片机,M3主控模块上设计一个程序,在Keil uVision5(C语言)上代码该怎么编译?(嫌钱少我可以加钱,急急急)
  • ¥15 opnet仿真网络协议遇到问题