1. 协议概述
跳转到“1. 协议概述”1.1 物理层参数
跳转到“1.1 物理层参数”| 参数 | 值 |
|---|---|
| 物理接口 | RS232 |
| 波特率 | 38400 bps |
| 数据位 | 8 |
| 停止位 | 2 |
| 校验位 | 无 |
| 流控制 | 无 |
1.2 协议特性
跳转到“1.2 协议特性”- 字节序:大端序(Big-Endian,高字节在前)
- 校验方式:CRC-16/MODBUS
- 超时时间:1000ms(上位机等待控制板响应)
- 重试次数:3次
- 序列号范围:0x01~0xFF(循环使用,0x00保留)
2. 帧格式定义
跳转到“2. 帧格式定义”2.1 通用帧结构
跳转到“2.1 通用帧结构”┌──────┬──────┬──────┬──────┬────────┬──────┬──────┐│ 帧头 │ 长度 │ 序号 │ 命令 │ 数据 │ CRC │ 帧尾 ││ 2B │ 1B │ 1B │ 1B │ N字节 │ 2B │ 1B │└──────┴──────┴──────┴──────┴────────┴──────┴──────┘ 0xAA55 Len SeqNo CMD Data CRC16 0xEE2.2 字段说明
跳转到“2.2 字段说明”| 字段 | 长度 | 说明 |
|---|---|---|
| 帧头 | 2B | 固定为 0xAA55,用于帧同步 |
| 长度 | 1B | 数据区字节数(不含帧头、长度、CRC、帧尾) |
| 序号 | 1B | 流水号,范围 0x01~0xFF,用于匹配请求与响应 |
| 命令 | 1B | 命令码,见命令码表 |
| 数据 | N字节 | 命令相关数据,长度由”长度”字段指定 |
| CRC | 2B | CRC-16/MODBUS校验,计算范围:序号~数据区末尾 |
| 帧尾 | 1B | 固定为 0xEE,用于帧结束标识 |
2.3 命令码定义
跳转到“2.3 命令码定义”| 命令码 | 命令名称 | 方向 | 说明 |
|---|---|---|---|
| 0x01 | 启动命令 | 上位机→控制板 | 启动电机运行 |
| 0x81 | 启动响应 | 控制板→上位机 | 启动命令执行结果 |
| 0x02 | 停止命令 | 上位机→控制板 | 停止电机运行 |
| 0x82 | 停止响应 | 控制板→上位机 | 停止命令执行结果 |
| 0x03 | 查找脉冲命令 | 上位机→控制板 | 伺服标定 |
| 0x83 | 查找脉冲响应 | 控制板→上位机 | 标定执行结果 |
| 0x04 | 速度设置命令 | 上位机→控制板 | 设置加减速度 |
| 0x84 | 速度设置响应 | 控制板→上位机 | 设置执行结果 |
| 0x05 | 速度查询命令 | 上位机→控制板 | 查询当前速度 |
| 0x85 | 速度查询响应 | 控制板→上位机 | 返回当前速度 |
| 0x10 | 状态查询命令 | 上位机→控制板 | 查询控制板状态 |
| 0x90 | 状态查询响应 | 控制板→上位机 | 返回状态信息 |
规则:响应命令码 = 请求命令码 | 0x80
3. 数据类型定义
跳转到“3. 数据类型定义”3.1 基本类型
跳转到“3.1 基本类型”| 类型 | 字节数 | 范围 | 说明 |
|---|---|---|---|
| UINT8 | 1 | 0~255 | 无符号8位整数 |
| UINT16 | 2 | 0~65535 | 无符号16位整数(大端序) |
| UINT32 | 4 | 0~4294967295 | 无符号32位整数(大端序) |
3.2 业务参数定义
跳转到“3.2 业务参数定义”| 参数名称 | 类型 | 范围 | 单位 | 说明 |
|---|---|---|---|---|
| 转速 | UINT16 | 0~10000 | RPM | 电机转速 |
| 角度 | UINT16 | 0~3600 | 0.1° | 停止角度,精度0.1度 |
| 加减速度 | UINT16 | 100~5000 | RPM/s | 加减速度,过小或过大会被限制 |
3.3 状态码定义
跳转到“3.3 状态码定义”| 状态码 | 名称 | 说明 |
|---|---|---|
| 0x00 | SUCCESS | 执行成功 |
| 0x01 | ERR_CYLINDER_UP | 气缸上升失败 |
| 0x02 | ERR_CYLINDER_DOWN | 气缸下降失败 |
| 0x03 | ERR_SERVO_TIMEOUT | 伺服响应超时 |
| 0x04 | ERR_SERVO_POSITION | 伺服位置偏差过大 |
| 0x05 | ERR_PARAM_RANGE | 参数超出范围 |
| 0x06 | ERR_INVALID_CMD | 无效命令 |
| 0x07 | ERR_CRC_ERROR | CRC校验错误 |
| 0x08 | ERR_BUSY | 设备忙,拒绝执行 |
| 0x09 | ERR_NOT_READY | 设备未就绪 |
| 0xFF | ERR_UNKNOWN | 未知错误 |
4. 命令详细说明
跳转到“4. 命令详细说明”4.1 启动命令 (0x01/0x81)
跳转到“4.1 启动命令 (0x01/0x81)”功能:启动电机,设置目标转速
上位机 → 控制板
| 字段 | 值/类型 | 说明 |
|---|---|---|
| 帧头 | 0xAA55 | 固定 |
| 长度 | 0x03 | 数据区3字节 |
| 序号 | UINT8 | 流水号 |
| 命令 | 0x01 | 启动命令 |
| 数据[0-1] | UINT16 | 目标转速(0~10000 RPM) |
| 数据[2] | UINT8 | 启动模式(0x01=正常启动) |
| CRC | UINT16 | CRC校验值 |
| 帧尾 | 0xEE | 固定 |
控制板 → 上位机
| 字段 | 值/类型 | 说明 |
|---|---|---|
| 帧头 | 0xAA55 | 固定 |
| 长度 | 0x04 | 数据区4字节 |
| 序号 | UINT8 | 与请求相同 |
| 命令 | 0x81 | 启动响应 |
| 数据[0] | UINT8 | 状态码 |
| 数据[1-2] | UINT16 | 实际转速(反馈值) |
| 数据[3] | UINT8 | 运行状态(0x00=停止,0x01=运行) |
| CRC | UINT16 | CRC校验值 |
| 帧尾 | 0xEE | 固定 |
示例
请求:AA55 03 12 01 09C4 01 [CRC] EE 帧头 长度 序号 命令 2500RPM 模式 校验 帧尾
响应:AA55 04 12 81 00 09C4 01 [CRC] EE 帧头 长度 序号 命令 成功 2500RPM 运行 校验 帧尾4.2 停止命令 (0x02/0x82)
跳转到“4.2 停止命令 (0x02/0x82)”功能:停止电机,可选定位停止功能
上位机 → 控制板
| 字段 | 值/类型 | 说明 |
|---|---|---|
| 帧头 | 0xAA55 | 固定 |
| 长度 | 0x04 | 数据区4字节 |
| 序号 | UINT8 | 流水号 |
| 命令 | 0x02 | 停止命令 |
| 数据[0] | UINT8 | 停止模式 0x00=立即停止 0x01=定位停止 |
| 数据[1-2] | UINT16 | 停止角度(0~3600,单位0.1°) 仅定位停止时有效 |
| 数据[3] | UINT8 | 保留,填0x00 |
| CRC | UINT16 | CRC校验值 |
| 帧尾 | 0xEE | 固定 |
控制板 → 上位机
| 字段 | 值/类型 | 说明 |
|---|---|---|
| 帧头 | 0xAA55 | 固定 |
| 长度 | 0x04 | 数据区4字节 |
| 序号 | UINT8 | 与请求相同 |
| 命令 | 0x82 | 停止响应 |
| 数据[0] | UINT8 | 状态码 |
| 数据[1-2] | UINT16 | 实际停止角度(反馈值) |
| 数据[3] | UINT8 | 运行状态(0x00=已停止) |
| CRC | UINT16 | CRC校验值 |
| 帧尾 | 0xEE | 固定 |
示例
立即停止:请求:AA55 04 13 02 00 0000 00 [CRC] EE响应:AA55 04 13 82 00 0000 00 [CRC] EE
定位停止到180度:请求:AA55 04 14 02 01 0708 00 [CRC] EE (0x0708 = 1800 = 180.0度)响应:AA55 04 14 82 00 0708 00 [CRC] EE4.3 查找脉冲命令 (0x03/0x83)
跳转到“4.3 查找脉冲命令 (0x03/0x83)”功能:执行伺服电机Z相脉冲查找,用于系统标定
上位机 → 控制板
| 字段 | 值/类型 | 说明 |
|---|---|---|
| 帧头 | 0xAA55 | 固定 |
| 长度 | 0x01 | 数据区1字节 |
| 序号 | UINT8 | 流水号 |
| 命令 | 0x03 | 查找脉冲命令 |
| 数据[0] | UINT8 | 查找模式(0x01=标准模式) |
| CRC | UINT16 | CRC校验值 |
| 帧尾 | 0xEE | 固定 |
控制板 → 上位机
| 字段 | 值/类型 | 说明 |
|---|---|---|
| 帧头 | 0xAA55 | 固定 |
| 长度 | 0x05 | 数据区5字节 |
| 序号 | UINT8 | 与请求相同 |
| 命令 | 0x83 | 查找脉冲响应 |
| 数据[0] | UINT8 | 状态码 |
| 数据[1-4] | UINT32 | 脉冲位置(编码器计数值) |
| CRC | UINT16 | CRC校验值 |
| 帧尾 | 0xEE | 固定 |
示例
请求:AA55 01 15 03 01 [CRC] EE
响应:AA55 05 15 83 00 00001234 [CRC] EE 状态成功,脉冲位置=0x000012344.4 速度设置命令 (0x04/0x84)
跳转到“4.4 速度设置命令 (0x04/0x84)”功能:设置加减速度参数
上位机 → 控制板
| 字段 | 值/类型 | 说明 |
|---|---|---|
| 帧头 | 0xAA55 | 固定 |
| 长度 | 0x03 | 数据区3字节 |
| 序号 | UINT8 | 流水号 |
| 命令 | 0x04 | 速度设置命令 |
| 数据[0-1] | UINT16 | 加减速度(100~5000 RPM/s) |
| 数据[2] | UINT8 | 保留,填0x00 |
| CRC | UINT16 | CRC校验值 |
| 帧尾 | 0xEE | 固定 |
控制板 → 上位机
| 字段 | 值/类型 | 说明 |
|---|---|---|
| 帧头 | 0xAA55 | 固定 |
| 长度 | 0x03 | 数据区3字节 |
| 序号 | UINT8 | 与请求相同 |
| 命令 | 0x84 | 速度设置响应 |
| 数据[0] | UINT8 | 状态码 |
| 数据[1-2] | UINT16 | 实际设置的速度值 |
| CRC | UINT16 | CRC校验值 |
| 帧尾 | 0xEE | 固定 |
示例
设置为1000 RPM/s:请求:AA55 03 16 04 03E8 00 [CRC] EE
响应:AA55 03 16 84 00 03E8 [CRC] EE 状态成功,速度=10004.5 速度查询命令 (0x05/0x85)
跳转到“4.5 速度查询命令 (0x05/0x85)”功能:查询当前加减速度设置
上位机 → 控制板
| 字段 | 值/类型 | 说明 |
|---|---|---|
| 帧头 | 0xAA55 | 固定 |
| 长度 | 0x01 | 数据区1字节 |
| 序号 | UINT8 | 流水号 |
| 命令 | 0x05 | 速度查询命令 |
| 数据[0] | UINT8 | 保留,填0x00 |
| CRC | UINT16 | CRC校验值 |
| 帧尾 | 0xEE | 固定 |
控制板 → 上位机
| 字段 | 值/类型 | 说明 |
|---|---|---|
| 帧头 | 0xAA55 | 固定 |
| 长度 | 0x03 | 数据区3字节 |
| 序号 | UINT8 | 与请求相同 |
| 命令 | 0x85 | 速度查询响应 |
| 数据[0] | UINT8 | 状态码 |
| 数据[1-2] | UINT16 | 当前速度值 |
| CRC | UINT16 | CRC校验值 |
| 帧尾 | 0xEE | 固定 |
示例
请求:AA55 01 17 05 00 [CRC] EE
响应:AA55 03 17 85 00 03E8 [CRC] EE 当前速度=1000 RPM/s4.6 状态查询命令 (0x10/0x90)
跳转到“4.6 状态查询命令 (0x10/0x90)”功能:查询控制板当前状态(可选扩展功能)
上位机 → 控制板
| 字段 | 值/类型 | 说明 |
|---|---|---|
| 帧头 | 0xAA55 | 固定 |
| 长度 | 0x01 | 数据区1字节 |
| 序号 | UINT8 | 流水号 |
| 命令 | 0x10 | 状态查询命令 |
| 数据[0] | UINT8 | 保留,填0x00 |
| CRC | UINT16 | CRC校验值 |
| 帧尾 | 0xEE | 固定 |
控制板 → 上位机
| 字段 | 值/类型 | 说明 |
|---|---|---|
| 帧头 | 0xAA55 | 固定 |
| 长度 | 0x08 | 数据区8字节 |
| 序号 | UINT8 | 与请求相同 |
| 命令 | 0x90 | 状态查询响应 |
| 数据[0] | UINT8 | 运行状态 0x00=停止 0x01=运行 0x02=故障 |
| 数据[1-2] | UINT16 | 当前转速 |
| 数据[3-4] | UINT16 | 当前角度 |
| 数据[5] | UINT8 | 气缸状态 0x00=下 0x01=上 0xFF=异常 |
| 数据[6] | UINT8 | 伺服状态 0x00=离线 0x01=就绪 0xFF=故障 |
| 数据[7] | UINT8 | 保留 |
| CRC | UINT16 | CRC校验值 |
| 帧尾 | 0xEE | 固定 |
5. CRC-16/MODBUS 校验算法
跳转到“5. CRC-16/MODBUS 校验算法”5.1 计算范围
跳转到“5.1 计算范围”从 序号 字段开始,到 数据区末尾 结束(不含帧头、长度、CRC、帧尾)
5.2 算法参数
跳转到“5.2 算法参数”- 多项式: 0xA001(反向多项式)
- 初始值: 0xFFFF
- 结果异或值: 0x0000
- 字节序: 低字节在前
5.3 C语言参考代码
跳转到“5.3 C语言参考代码”/** * @brief 计算CRC-16/MODBUS校验值 * @param data: 数据指针 * @param length: 数据长度 * @return CRC校验值(16位) */uint16_t crc16_modbus(const uint8_t *data, uint16_t length){ uint16_t crc = 0xFFFF;
for (uint16_t i = 0; i < length; i++) { crc ^= data[i]; for (uint8_t j = 0; j < 8; j++) { if (crc & 0x0001) { crc = (crc >> 1) ^ 0xA001; } else { crc = crc >> 1; } } }
return crc;}
/** * @brief 校验接收到的数据帧 * @param frame: 完整帧数据(含帧头到帧尾) * @param frame_len: 帧总长度 * @return 1=校验通过, 0=校验失败 */uint8_t verify_frame(const uint8_t *frame, uint16_t frame_len){ if (frame_len < 8) return 0; // 最小帧长度
// 检查帧头帧尾 if (frame[0] != 0xAA || frame[1] != 0x55) return 0; if (frame[frame_len - 1] != 0xEE) return 0;
// 计算CRC(从序号到数据区末尾) uint8_t data_len = frame[2]; // 长度字段 uint16_t calc_crc = crc16_modbus(&frame[3], data_len + 2);
// 提取帧中的CRC(低字节在前) uint16_t frame_crc = frame[frame_len - 3] | (frame[frame_len - 2] << 8);
return (calc_crc == frame_crc) ? 1 : 0;}5.4 Python参考代码
跳转到“5.4 Python参考代码”def crc16_modbus(data: bytes) -> int: """计算CRC-16/MODBUS""" crc = 0xFFFF for byte in data: crc ^= byte for _ in range(8): if crc & 0x0001: crc = (crc >> 1) ^ 0xA001 else: crc >>= 1 return crc
def build_frame(seq_no: int, cmd: int, data: bytes) -> bytes: """构建完整帧""" length = len(data) + 2 # 序号+命令+数据长度 frame = bytearray([0xAA, 0x55, length, seq_no, cmd]) frame.extend(data)
# 计算CRC(从序号开始) crc = crc16_modbus(frame[3:]) frame.append(crc & 0xFF) # CRC低字节 frame.append((crc >> 8) & 0xFF) # CRC高字节 frame.append(0xEE) # 帧尾
return bytes(frame)
# 示例:构建启动命令seq = 0x12cmd = 0x01data = bytes([0x09, 0xC4, 0x01]) # 2500RPM, 模式1frame = build_frame(seq, cmd, data)print(frame.hex().upper())6. 通讯流程
跳转到“6. 通讯流程”6.1 正常流程
跳转到“6.1 正常流程”上位机 控制板 | | |------- 启动命令(序号=0x12) ------->| | | 执行命令 |<------ 启动响应(序号=0x12) --------| | | | (等待操作完成) | | | |------- 停止命令(序号=0x13) ------->| | | |<------ 停止响应(序号=0x13) --------| | |6.2 超时重传流程
跳转到“6.2 超时重传流程”上位机 控制板 | | |------- 命令(序号=0x12) ---------->| (帧丢失) | | |----- 等待1000ms超时 -------------| | | |------- 命令(序号=0x12) ---------->| | | 执行成功 |<------ 响应(序号=0x12) -----------| | |6.3 CRC错误处理
跳转到“6.3 CRC错误处理”上位机 控制板 | | |------- 命令(序号=0x12) ---------->| CRC校验失败 | | |<------ 响应(状态=0x07) -----------| (返回CRC错误) | | |------- 重发命令(序号=0x12) ------>| | | |<------ 响应(状态=0x00) -----------| (成功) | |7. 错误处理规则
跳转到“7. 错误处理规则”7.1 上位机处理规则
跳转到“7.1 上位机处理规则”| 情况 | 处理方式 |
|---|---|
| 超时无响应 | 重发命令,最多3次,仍失败则报警 |
| CRC校验失败 | 丢弃该帧,等待超时后重发 |
| 序号不匹配 | 丢弃该帧,等待正确响应 |
| 收到错误状态码 | 根据状态码进行相应处理,记录日志 |
7.2 控制板处理规则
跳转到“7.2 控制板处理规则”| 情况 | 处理方式 |
|---|---|
| 帧头/帧尾错误 | 丢弃该帧,不做响应 |
| CRC校验失败 | 返回状态码0x07(CRC错误) |
| 命令码无效 | 返回状态码0x06(无效命令) |
| 参数超出范围 | 返回状态码0x05(参数错误) |
| 设备忙 | 返回状态码0x08(设备忙) |
8. 完整示例
跳转到“8. 完整示例”8.1 启动电机到2500RPM
跳转到“8.1 启动电机到2500RPM”请求帧构造:
帧头: AA 55长度: 03 (序号1+命令1+数据3 = 5字节 - 2 = 3)序号: 12命令: 01数据: 09 C4 01 (转速=0x09C4=2500, 模式=0x01)CRC: [计算] = 对 [12 01 09 C4 01] 计算 CRC帧尾: EE
完整帧(假设CRC=0x1234):AA 55 03 12 01 09 C4 01 34 12 EE响应帧:
AA 55 04 12 81 00 09 C4 01 [CRC] EE ↑ ↑ ↑ ↑ 命令 成功 2500RPM 运行中8.2 定位停止到180度
跳转到“8.2 定位停止到180度”请求帧:
AA 55 04 13 02 01 07 08 00 [CRC] EE ↑ ↑ 定位模式 1800(0.1度)=180度响应帧:
AA 55 04 13 82 00 07 08 00 [CRC] EE ↑ 实际停止位置9. 常见问题 FAQ
跳转到“9. 常见问题 FAQ”Q1: 为什么要用双字节帧头?
跳转到“Q1: 为什么要用双字节帧头?”A: 单字节帧头容易与数据混淆。双字节 0xAA55 出现在随机数据中的概率极低(1/65536),大幅提高帧同步可靠性。
Q2: 序列号为什么不能为0?
跳转到“Q2: 序列号为什么不能为0?”A: 0x00保留用于特殊用途(如广播或无需响应的命令)。实际通讯使用0x01~0xFF循环。
Q3: CRC计算包含哪些字段?
跳转到“Q3: CRC计算包含哪些字段?”A: 从”序号”开始到”数据区末尾”,不包含帧头、长度、CRC本身、帧尾。
Q4: 大端序是什么意思?
跳转到“Q4: 大端序是什么意思?”A: 高字节在前。例如转速2500(0x09C4),发送顺序为: 09 C4(不是 C4 09)
Q5: 如何处理连续发送?
跳转到“Q5: 如何处理连续发送?”A: 每次发送需要更新序列号,等待响应或超时后才发送下一条。不建议在未收到响应时发送新命令。
Q6: 控制板重启后序列号怎么办?
跳转到“Q6: 控制板重启后序列号怎么办?”A: 上位机序列号独立维护,无需与控制板同步。控制板只需将收到的序列号原样返回即可。
Q7: 转速为0是否允许?
跳转到“Q7: 转速为0是否允许?”A: 允许。转速=0表示目标转速为0,电机将减速停止。
Q8: 如何兼容旧版本协议?
跳转到“Q8: 如何兼容旧版本协议?”A: 建议通过版本协商机制。可在启动时发送版本查询命令(自定义),根据响应决定使用V1.x或V2.0协议。
10. 测试用例
跳转到“10. 测试用例”| 测试项 | 输入 | 期望输出 | 测试目的 |
|---|---|---|---|
| 正常启动 | 转速=1000 | 状态=成功 | 基本功能 |
| 超速启动 | 转速=20000 | 状态=参数错误 | 参数校验 |
| CRC错误帧 | 故意篡改CRC | 无响应或错误码 | 校验机制 |
| 帧头错误 | 0xAA56开头 | 无响应 | 帧同步 |
| 序号匹配 | 发送序号0x20 | 响应序号0x20 | 序号机制 |
| 超时重传 | 模拟丢包 | 重发3次 | 可靠性 |
| 连续命令 | 快速发送5条 | 依次正确响应 | 并发处理 |
11. 版本升级说明
跳转到“11. 版本升级说明”从V1.2升级到V2.0
跳转到“从V1.2升级到V2.0”| 项目 | V1.2 | V2.0 | 迁移建议 |
|---|---|---|---|
| 帧头 | 0xc1~c4 | 0xAA55 | 修改帧头识别逻辑 |
| CRC | 无 | CRC-16 | 添加CRC计算函数 |
| 序列号 | 无 | 必需 | 添加序号管理模块 |
| 错误码 | 3种 | 11种 | 扩展错误处理 |
| 字节序 | 未定义 | 大端序 | 确认并统一字节序 |
建议升级路径:
- 先在测试环境验证V2.0协议
- 确保CRC计算正确性
- 逐步替换生产环境设备
- 保留V1.2兼容代码至少3个月
12. 附录
跳转到“12. 附录”附录A: 命令码快速查询表
跳转到“附录A: 命令码快速查询表”| 0x01 启动 | 0x02 停止 | 0x03 脉冲 | 0x04 设速 | 0x05 查速 | 0x10 状态 |
|---|---|---|---|---|---|
| 0x81 响应 | 0x82 响应 | 0x83 响应 | 0x84 响应 | 0x85 响应 | 0x90 响应 |
附录B: 状态码快速查询表
跳转到“附录B: 状态码快速查询表”| 0x00 成功 | 0x01 气缸↑ | 0x02 气缸↓ | 0x03 伺服超时 | 0x04 伺服偏差 |
|---|---|---|---|---|
| 0x05 参数 | 0x06 命令 | 0x07 CRC | 0x08 忙 | 0x09 未就绪 |
附录C: 调试技巧
跳转到“附录C: 调试技巧”- 使用串口调试工具: 推荐 SSCOM、Serial Port Utility
- 开启日志: 记录所有收发数据的HEX格式
- 验证CRC: 使用在线CRC计算器验证: http://www.ip33.com/crc.html (选择CRC-16/MODBUS)
- 波形分析: 使用逻辑分析仪抓取RS232波形
文档结束
更新记录
跳转到“更新记录”- 2025.10.9: 发布V2.0标准化协议,完全重构帧格式
来源:CSDN 原文,首次发布于 2025-10-09。