AngleReader.cs
7.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
using System.Collections;
using System.Collections.Generic;
using System.IO.Ports;
using UnityEngine;
using System;
public class AngleReader : MonoBehaviour
{
// 串口号,根据实际情况修改
public string portName = "COM3";
// 波特率,根据传感器设置修改
public int baudRate = 9600;
public int dataBits = 8;
public Parity parity = Parity.None;
public StopBits stopBits = StopBits.One;
private SerialPort serialPort;
public float angle;
public string readAngleCommand = "01 03 00 01 00 02 95 CB";
void Start()
{
// 创建串口对象并设置参数
serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
try
{
// 打开串口
serialPort.Open();
Debug.Log("串口已成功打开");
// 启动协程定时发送命令
StartCoroutine(SendCommandPeriodically());
}
catch (System.Exception e)
{
Debug.LogError("无法打开串口: " + e.Message);
}
}
void Update()
{
if (serialPort != null && serialPort.IsOpen)
{
try
{
if (serialPort.BytesToRead > dataBits)
{
Debug.Log("BytesToRead: " + serialPort.BytesToRead);
byte[] buffer = new byte[serialPort.BytesToRead];
int bytesRead = serialPort.Read(buffer, 0, buffer.Length);
ParseModbusData(buffer, bytesRead);
serialPort.DiscardInBuffer();
}
}
catch (System.Exception e)
{
Debug.LogError("读取串口数据时出错: " + e.Message);
serialPort.DiscardInBuffer();
}
}
}
void OnDestroy()
{
if (serialPort != null && serialPort.IsOpen)
{
serialPort.Close();
Debug.Log("串口已关闭");
}
}
public float GetAngle()
{
return angle;
}
System.Collections.IEnumerator SendCommandPeriodically()
{
while (true)
{
if (serialPort != null && serialPort.IsOpen)
{
try
{
// 去除十六进制命令中的空格
string cleanHexCommand = readAngleCommand.Replace(" ", "");
// 将十六进制字符串转换为字节数组
byte[] commandBytes = HexStringToByteArray(cleanHexCommand);
// byte[] commandBytes = { 0x01, 0x03, 0x00, 0x01, 0x00, 0x02, 0x95, 0xCB };
// 向串口发送字节数组
serialPort.Write(commandBytes, 0, commandBytes.Length);
// foreach (byte b in commandBytes) {
// Debug.Log("已发送数据: " + b);
// }
Debug.Log("已发送命令: " + readAngleCommand);
}
catch (System.Exception e)
{
Debug.LogError("发送数据时出错: " + e.Message);
}
}
// 等待指定的时间间隔
yield return new WaitForSecondsRealtime(0.1f);
}
}
// 将十六进制字符串转换为字节数组的方法
private byte[] HexStringToByteArray(string hex)
{
int length = hex.Length;
byte[] bytes = new byte[length / 2];
for (int i = 0; i < length; i += 2)
{
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
return bytes;
}
// 将字节数组转换为十六进制字符串的方法
private string BytesToHexString(byte[] bytes, int length)
{
System.Text.StringBuilder hex = new System.Text.StringBuilder(length * 2);
for (int i = 0; i < length; i++)
{
hex.AppendFormat("{0:X2} ", bytes[i]);
}
return hex.ToString().Trim();
}
// 解析Modbus协议数据
void ParseModbusData(byte[] data, int length)
{
if (length < 5 || !VerifyCRC(data, length))
{
Debug.LogError("数据帧长度不足或 CRC 校验失败");
return;
}
byte slaveAddress = data[0];
byte functionCode = data[1];
switch (functionCode)
{
case 0x03: // 读取保持寄存器
byte byteCount = data[2];
if (length != 5 + byteCount)
{
Debug.LogError("数据帧长度与字节计数不匹配");
return;
}
ushort[] registers = new ushort[byteCount / 2];
for (int i = 0; i < byteCount / 2; i++)
{
registers[i] = (ushort)((data[3 + i * 2] & 0xFF) << 8 | (data[4 + i * 2] & 0xFF));
}
Debug.Log($"从站地址: {slaveAddress}, 功能码: {functionCode}, 寄存器数量: {registers.Length}");
// 本传感器共有2个寄存器,数据拼起来就是角度的16进制数据
string hexString = "";
for (int i = 0; i < registers.Length; i++)
{
hexString += registers[i].ToString("X");
Debug.Log($"寄存器 {i}: {registers[i]}");
}
// 偶现某种情况下寄存器数据会丢失,此处确保两个寄存器数据都正常,否则本次数据忽略
if (hexString.Length != 8)
{
break;
}
float floatAngle = HexStringToFloatIEEE754(hexString);
angle = floatAngle;
Debug.Log($"计算角度为: {floatAngle}");
break;
default:
Debug.LogError($"不支持的功能码: {functionCode}");
break;
}
}
private bool VerifyCRC(byte[] data, int length)
{
ushort receivedCRC = (ushort)((data[length - 2] & 0xFF) | ((data[length - 1] & 0xFF) << 8));
ushort calculatedCRC = CalculateCRC16(data, length - 2);
return receivedCRC == calculatedCRC;
}
private ushort CalculateCRC16(byte[] data, int length)
{
ushort crc = 0xFFFF;
for (int i = 0; i < length; i++)
{
crc ^= (ushort)data[i];
for (int j = 0; j < 8; j++)
{
if ((crc & 0x0001) == 0x0001)
{
crc >>= 1;
crc ^= 0xA001;
}
else
{
crc >>= 1;
}
}
}
return crc;
}
private float HexStringToFloatIEEE754(string hexString)
{
Debug.Log($"hexString: {hexString}");
// 将十六进制字符串转换为 uint 类型
if (uint.TryParse(hexString, System.Globalization.NumberStyles.HexNumber, null, out uint hexValue))
{
return HexToFloatIEEE754(hexValue);
}
else
{
// 处理转换失败的情况
Console.WriteLine("十六进制字符串转换为 uint 失败,请检查输入。");
return 0;
}
}
private float HexToFloatIEEE754(uint hexValue)
{
Debug.Log($"hexValue: {hexValue}");
// 分离符号位、指数位和尾数位
int sign = (int)(hexValue >> 31) & 1;
int exponent = ((int)(hexValue >> 23) & 0xFF) - 127;
uint mantissa = hexValue & 0x7FFFFF;
// 计算尾数的实际值
double mantissaValue = 1.0 + (double)mantissa / Math.Pow(2, 23);
// 计算浮点数的值
double result = Math.Pow(-1, sign) * mantissaValue * Math.Pow(2, exponent);
return (float)result;
}
}