C00225155-02/C00225155/MegaRobo.C00225155/MegaRobo.C00225155.ControlD.../TemperatureControl/HighTemperatureUartHCM.cs

443 lines
19 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.ControlDevices
{
public class HighTemperatureUartHCM
{
#region
private const byte ST = 0x5E; // 帧头
private const byte HELLO = 0x01; // 握手指令
private const byte RDTEMP = 0x08; // 读取温度
private const byte RDADC = 0x09; // 读取传感器AD
private const byte RDEE = 0x11; // 读取EEPROM
private const byte WREE = 0x12; // 写入EEPROM
private const byte Calb = 0x15; // 校准温度
private const byte SETHOTTEMP = 0x16; // 加热温度控制
private const byte RESPONSE_FLAG = 0x80; // 上传指令标志
#endregion
#region 6n-18
/// <summary>
/// 计算帧的校验和核心前length字节之和的低8位
/// </summary>
private static byte CalculateChecksum(byte[] data, int length)
{
if (data == null || length <= 0 || length > data.Length)
throw new ArgumentException("校验和计算参数无效");
int sum = 0;
for (int i = 0; i < length; i++)
sum += data[i];
return (byte)(sum & 0xFF); // 取低8位确保1字节
}
#endregion
#region 1.1g>2msThread.Sleep确保阻塞
/// <summary>
/// 帧间隔时间预留3ms余量确保符合协议要求
/// </summary>
private static void AddFrameInterval()
{
Task.Delay(3);
}
#endregion
#region PC
/// <summary>
/// 握手指令(系统开机必发)
/// </summary>
public static byte[] SendHello(byte deviceId = 0)
{
byte[] frame = new byte[5]; // 协议固定帧长5字节
// 帧结构ST → HELLO(0x01) → 设备ID → LTH=5 → 校验和
frame[0] = ST;
frame[1] = HELLO; // 移除序号或运算CMD为固定指令码协议3.1
frame[2] = deviceId;
frame[3] = 5; // LTH固定为5
frame[4] = CalculateChecksum(frame, 4); // 前4字节求和
AddFrameInterval();
return frame;
}
/// <summary>
/// 读取温度指令
/// </summary>
public static byte[] SendReadTempRequest(byte deviceId = 0)
{
byte[] frame = new byte[5]; // 协议固定帧长5字节
// 帧结构ST → RDTEMP(0x08) → 设备ID → LTH=5 → 校验和
frame[0] = ST;
frame[1] = RDTEMP;
frame[2] = deviceId;
frame[3] = 5;
frame[4] = CalculateChecksum(frame, 4);
AddFrameInterval();
return frame;
}
/// <summary>
/// 读取传感器AD指令
/// </summary>
public static byte[] SendReadAdcRequest(byte deviceId = 0)
{
byte[] frame = new byte[5]; // 协议固定帧长5字节
// 帧结构ST → RDADC(0x09) → 设备ID → LTH=5 → 校验和
frame[0] = ST;
frame[1] = RDADC;
frame[2] = deviceId;
frame[3] = 5;
frame[4] = CalculateChecksum(frame, 4);
AddFrameInterval();
return frame;
}
/// <summary>
/// 读取EEPROM指令
/// </summary>
public static byte[] SendReadEepromRequest(byte addrHigh, byte addrMid, byte addrLow, int readLength, byte deviceId = 0)
{
if (readLength < 1)
throw new ArgumentOutOfRangeException(nameof(readLength), "读取长度必须≥1");
int frameLength = 9 + readLength; // 帧长=9固定段+ 读取长度协议3.4
byte[] frame = new byte[frameLength];
// 帧结构ST → RDEE(0x11) → 设备ID → LTH=N → 地址高→中→低 → 读取长度 → 校验和
frame[0] = ST;
frame[1] = RDEE;
frame[2] = deviceId;
frame[3] = (byte)frameLength;
frame[4] = addrHigh;
frame[5] = addrMid;
frame[6] = addrLow;
frame[7] = (byte)readLength;
frame[frameLength - 1] = CalculateChecksum(frame, frameLength - 1);
AddFrameInterval();
return frame;
}
/// <summary>
/// 写入EEPROM指令
/// </summary>
public static byte[] SendWriteEepromRequest(byte addrHigh, byte addrMid, byte addrLow, byte[] writeData, byte deviceId = 0)
{
if (writeData == null || writeData.Length == 0)
throw new ArgumentException("写入数据不能为空", nameof(writeData));
int frameLength = 9 + writeData.Length; // 帧长=9固定段+ 数据长度协议3.5
byte[] frame = new byte[frameLength];
// 帧结构ST → WREE(0x12) → 设备ID → LTH=N → 地址高→中→低 → 写入数据 → 校验和
frame[0] = ST;
frame[1] = WREE;
frame[2] = deviceId;
frame[3] = (byte)frameLength;
frame[4] = addrHigh;
frame[5] = addrMid;
frame[6] = addrLow;
Array.Copy(writeData, 0, frame, 7, writeData.Length);
frame[frameLength - 1] = CalculateChecksum(frame, frameLength - 1);
AddFrameInterval();
return frame;
}
/// <summary>
/// 校准温度指令设置B值=(实际温度-目标温度)×100
/// </summary>
public static byte[] SendCalibrateTempRequest(int calibBValue, byte deviceId = 0)
{
if (calibBValue < short.MinValue || calibBValue > short.MaxValue)
throw new ArgumentOutOfRangeException(nameof(calibBValue), "校准B值必须在-32768~32767之间");
byte[] frame = new byte[7]; // 协议固定帧长7字节
short bParam = (short)calibBValue; // 16位有符号数支持负值
// 帧结构ST → Calb(0x15) → 设备ID → LTH=7 → 参数1(高8) → 参数2(低8) → 校验和
frame[0] = ST;
frame[1] = Calb;
frame[2] = deviceId;
frame[3] = 7;
frame[4] = (byte)(bParam >> 8); // 高位在前协议3.6
frame[5] = (byte)(bParam & 0xFF);
frame[6] = CalculateChecksum(frame, 6);
AddFrameInterval();
return frame;
}
/// <summary>
/// 加热温度控制(设置目标温度或释放控制)
/// </summary>
public static byte[] SendSetHotTemp(double targetTemp, byte deviceId = 0, bool releaseControl = false)
{
byte[] frame = new byte[7]; // 协议固定帧长7字节
ushort tempParam;
if (releaseControl)
{
tempParam = 0xFFFF; // 协议约定:释放控制=0xFFFF关断功率
}
else
{
// 协议范围:室温~100℃对应室温×100~10000室温暂以20℃为参考
if (targetTemp < 20.0 || targetTemp > 100.0)
throw new ArgumentOutOfRangeException(nameof(targetTemp), "温度必须在[20.0, 100.0]℃之间(符合协议室温要求)");
tempParam = (ushort)(targetTemp * 100); // 放大100倍协议3.7
}
// 帧结构ST → SETHOTTEMP(0x16) → 设备ID → LTH=7 → 参数1(高8) → 参数2(低8) → 校验和
frame[0] = ST;
frame[1] = SETHOTTEMP;
frame[2] = deviceId;
frame[3] = 7;
frame[4] = (byte)(tempParam >> 8); // 高位在前协议3.7
frame[5] = (byte)(tempParam & 0xFF);
frame[6] = CalculateChecksum(frame, 6);
AddFrameInterval();
return frame;
}
#endregion
#region
/// <summary>
/// 解析主控板返回的帧数据(入口方法)
/// </summary>
public static ParseResult ParseReceivedFrame(byte[] receivedFrame)
{
// 基础校验帧非空、最短帧≥6字节上传指令最短为握手响应6字节
if (receivedFrame == null || receivedFrame.Length < 6)
return new ParseResult(false, "无效帧:数据为空或长度不足", null);
// 1. 校验帧头
if (receivedFrame[0] != ST)
return new ParseResult(false, $"帧头错误实际0x{receivedFrame[0]:X2}期望0x{ST:X2}", null);
// 2. 提取核心字段
byte cmdWithFlag = receivedFrame[1];
byte deviceId = receivedFrame[2];
byte frameLength = receivedFrame[3];
byte checksum = receivedFrame[frameLength - 1];
// 3. 校验帧长(实际长度=帧长字段)
if (receivedFrame.Length != frameLength)
return new ParseResult(false, $"帧长不匹配:实际{receivedFrame.Length}字节,期望{frameLength}字节", null);
// 4. 校验和验证
byte calculatedChecksum = CalculateChecksum(receivedFrame, frameLength - 1);
if (calculatedChecksum != checksum)
return new ParseResult(false, $"校验和错误实际0x{checksum:X2}计算0x{calculatedChecksum:X2}", null);
// 5. 去除响应标志,获取原始指令码
byte originalCmd = (byte)(cmdWithFlag & ~RESPONSE_FLAG);
// 6. 按指令类型分发解析
switch (originalCmd)
{
case HELLO:
return ParseHelloResponse(receivedFrame, deviceId);
case RDTEMP:
return ParseReadTempResponse(receivedFrame, deviceId);
case RDADC:
return ParseReadAdcResponse(receivedFrame, deviceId);
case RDEE:
return ParseReadEepromResponse(receivedFrame, deviceId);
case WREE:
return ParseWriteEepromResponse(receivedFrame, deviceId);
case Calb:
return ParseCalibrateTempResponse(receivedFrame, deviceId);
case SETHOTTEMP:
return ParseSetHotTempResponse(receivedFrame, deviceId);
default:
return new ParseResult(false, $"未知指令0x{originalCmd:X2}", null);
}
}
#region
/// <summary>
/// 解析握手响应
/// </summary>
private static ParseResult ParseHelloResponse(byte[] frame, byte deviceId)
{
if (frame.Length != 6) // 协议固定响应帧长6字节
return new ParseResult(false, "握手响应帧长错误需6字节", null);
byte status = frame[4]; // 状态位协议3.1
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "握手成功" : $"握手失败(错误码:{status}" }
};
return new ParseResult(true, "握手响应解析成功", data);
}
/// <summary>
/// 解析读取温度响应
/// </summary>
private static ParseResult ParseReadTempResponse(byte[] frame, byte deviceId)
{
if (frame.Length != 0x0D) // 协议固定响应帧长13字节0x0D
return new ParseResult(false, $"读取温度响应帧长错误需13字节实际{frame.Length}字节", null);
// 提取数据参数1(设置温度)、参数2(实际温度)、参数3(预留)、标志位、状态位协议3.2
ushort setTempParam = (ushort)((frame[4] << 8) | frame[5]); // 高位在前
ushort actualTempParam = (ushort)((frame[6] << 8) | frame[7]); // 高位在前
byte flag = frame[10]; // 预留标志位NC
byte status = frame[11];
// 转换为实际温度除以100精度0.1℃)
double setTemp = setTempParam / 100.0;
double actualTemp = actualTempParam / 100.0;
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "读取温度成功" : $"读取失败(错误码:{status}" },
{ "设置温度(℃)", setTemp },
{ "实际温度(℃)", actualTemp },
{ "预留标志位", flag },
{ "原始设置温度参数", $"0x{setTempParam:X4}" },
{ "原始实际温度参数", $"0x{actualTempParam:X4}" }
};
return new ParseResult(true, "读取温度响应解析成功", data);
}
/// <summary>
/// 解析读取AD响应
/// </summary>
private static ParseResult ParseReadAdcResponse(byte[] frame, byte deviceId)
{
if (frame.Length != 0x0A) // 协议固定响应帧长10字节0x0A修正原13字节错误
return new ParseResult(false, $"读取AD响应帧长错误需10字节实际{frame.Length}字节", null);
// 提取数据AD值5-6字节、预留7-8字节、状态位9字节协议3.3
ushort adcValue = (ushort)((frame[4] << 8) | frame[5]); // 高位在前
byte status = frame[8];
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "读取AD成功" : $"读取失败(错误码:{status}" },
{ "AD值", adcValue },
{ "原始AD参数", $"0x{adcValue:X4}" }
};
return new ParseResult(true, "读取AD响应解析成功", data);
}
/// <summary>
/// 解析读取EEPROM响应
/// </summary>
private static ParseResult ParseReadEepromResponse(byte[] frame, byte deviceId)
{
if (frame.Length < 6) // 协议响应帧长≥6字节
return new ParseResult(false, "读取EEPROM响应帧长不足需≥6字节", null);
// 提取数据参数1~n5到N-2字节、状态位N-2字节协议3.4
int dataLength = frame.Length - 6; // 数据字节数=N-6
byte[] eepromData = new byte[dataLength];
Array.Copy(frame, 4, eepromData, 0, dataLength);
byte status = frame[frame.Length - 2];
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "读取EEPROM成功" : $"读取失败(错误码:{status}" },
{ "读取数据长度(字节)", dataLength },
{ "读取数据(十六进制)", BitConverter.ToString(eepromData).Replace("-", " ") },
{ "原始数据数组", eepromData }
};
return new ParseResult(true, "读取EEPROM响应解析成功", data);
}
/// <summary>
/// 解析写入EEPROM响应
/// </summary>
private static ParseResult ParseWriteEepromResponse(byte[] frame, byte deviceId)
{
if (frame.Length != 6) // 协议固定响应帧长6字节
return new ParseResult(false, "写入EEPROM响应帧长错误需6字节", null);
byte status = frame[4]; // 状态位协议3.5
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "写入EEPROM成功" : $"写入失败(错误码:{status}" }
};
return new ParseResult(true, "写入EEPROM响应解析成功", data);
}
/// <summary>
/// 解析校准温度响应
/// </summary>
private static ParseResult ParseCalibrateTempResponse(byte[] frame, byte deviceId)
{
if (frame.Length != 6) // 协议固定响应帧长6字节
return new ParseResult(false, "校准温度响应帧长错误需6字节", null);
byte status = frame[4]; // 状态位协议3.6
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "温度校准成功" : $"校准失败(错误码:{status}" }
};
return new ParseResult(true, "校准温度响应解析成功", data);
}
/// <summary>
/// 解析加热温度控制响应
/// </summary>
private static ParseResult ParseSetHotTempResponse(byte[] frame, byte deviceId)
{
if (frame.Length != 6) // 协议固定响应帧长6字节
return new ParseResult(false, "加热温度响应帧长错误需6字节", null);
byte status = frame[4]; // 状态位协议3.7
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "加热温度指令接收成功" : $"接收失败(错误码:{status}" }
};
return new ParseResult(true, "加热温度响应解析成功", data);
}
#endregion
#endregion
#region CRC16校验使
public static byte[] CRC16(byte[] data)
{
int len = data.Length;
if (len <= 0)
return new byte[] { 0, 0 };
ushort crc = 0xFFFF;
for (int i = 0; i < len; i++)
{
crc = (ushort)(crc ^ data[i]);
for (int j = 0; j < 8; j++)
{
crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ 0xA001) : (ushort)(crc >> 1);
}
}
byte hi = (byte)((crc & 0xFF00) >> 8);
byte lo = (byte)(crc & 0x00FF);
return BitConverter.IsLittleEndian ? new byte[] { lo, hi } : new byte[] { hi, lo };
}
#endregion
}
}