injectorCTL/protocol.c
2024-12-26 22:27:14 +08:00

1641 lines
46 KiB
C
Raw 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.

#include "protocol.h"
#include <string.h>
#ifndef USE_ELOG
void elog_hexdump(const char *name, uint8_t width, const void *buf, uint16_t size) {
printf("%s: ", name);
// 按width的宽度打印
for(uint16_t i = 0; i < size; i++) {
printf("%02X ", ((uint8_t*)buf)[i]);
if((i+1)%width == 0) {
printf("\r\n");
}
}
printf("\r\n");
}
#endif
DeviceStatus_t deviceStatus = {
.sensorStatus = 1,
.valves = {{210, 120}},
.pumps = {{0, 0},{50, 50}},
.bubbleStatus = 0,
.activityMeter = 0,
.estopStatus = 0,
.errorCode = 0,
.initStatus = 1
};
SystemStatus_t systemStatus = {
.valvesSpeed = {0},
.valvesPos = {0},
.valvesSpeedPercent = {0},
.pumpsSpeed = {0},
.pumpsPos = {0},
.pumpsSpeedPercent = {0},
.rst = 0
};
uint8_t isValveMovingBackToOrigin[2] = {0,0};
DeviceParam_t dp = {
.pump = {
{"pump1", 4, 60, 100, 100,40000,0},
{"pump2", 3, 10, 100, 100,40000,0}
},
.valve = {
{"valve1", 1, 10, 100, 100,47620,0},
{"valve2", 2, 10, 100, 100,47620,0}
}
};
// CRC16 查表法实现
static const uint16_t crcTable[] = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
};
/**
* 计算数据的CRC16校验值,CRC16校验多项式为:X16+X15+X2+1
*
* @param data 需要计算CRC的数据缓冲区
* @param length 数据长度
* @return 计算得到的CRC16校验值
*/
uint16_t CalculateCRC16(uint8_t *data, uint16_t length) {
uint16_t crc = 0xFFFF;
if (!data) return 0;
for (uint16_t i = 0; i < length; i++) {
uint8_t index = (crc ^ data[i]) & 0xFF;
crc = (crc >> 8) ^ crcTable[index];
}
return crc;
}
/**
* 更新下挂设备状态
*
* @param status 设备状态值(在线/离线)
*/
// void updateDeviceStatus(SensorStatus status) {
// deviceStatus.sensorStatus = status;
// }
/**
* 更新三通阀状态
*
* @param index 阀门索引(1或2)
* @param angle 阀门角度值(0-360绝对角度)
*/
// void updateValveStatus(uint8_t index, ValveAngle angle) {
// if (index == 1) {
// deviceStatus.valves.angle1 = angle;
// } else if (index == 2) {
// deviceStatus.valves.angle2 = angle;
// }
// }
/**
* 更新泵速度状态
*
* @param index 泵索引(1或2)
* @param speed 泵速度值(0-100%)
*/
// void updatePumpSpeedStatus(uint8_t index, uint8_t speed) {
// if (index == 1) {
// deviceStatus.pumps.speed1 = speed;
// } else if (index == 2) {
// deviceStatus.pumps.speed2 = speed;
// }
// }
/**
* 更新气泡传感器状态
*
* @param value 气泡传感器状态值(有/无气泡)
*/
// void updateBubbleSensor(BubbleStatus value) {
// deviceStatus.bubbleStatus = value;
// }
/**
* 更新急停状态
*
* @param status 急停状态值(正常/按下)
*/
// void updateEmergencyStop(EstopStatus_t status) {
// deviceStatus.stopStatus = status;
// }
/**
* 更新错误码
*
* @param errorCode 错误码值
*/
// void updateErrorCode(ErrorCode_t errorCode) {
// deviceStatus.errorCode = errorCode;
// }
/**
* 更新初始化状态
*
* @param status 初始化状态值(进行中/成功/失败)
*/
// void updateInitStatus(InitStatus_t status) {
// deviceStatus.initStatus = status;
// }
/**
* ModBUS RTU写命令
* 通过串口发送ModBUS RTU格式的命令
*
* 发送完成后回读反馈结果累加到systemStatus.rst
*
* @param txBuf 发送数据缓冲区
* @param txLen 发送数据长度
* @return 0:成功 其他:失败
*/
static uint8_t writeCMD(uint8_t *txBuf, uint16_t txLen) {
elog_hexdump("writeCMD", 16, txBuf, txLen);
transDataToMotorValve(txBuf, txLen);
uint8_t rxBuf[30] = {0};
uint16_t rxLen = txLen;
readDataFromMotorValve(rxBuf, rxLen, READ_ACK_TIMEOUT);
if(memcmp(rxBuf, txBuf, 2) != 0) {//正常情况下返回的前2个字节应与发送的相同
log_e("writeCMD error!");
elog_hexdump("writeCMD error", 16, rxBuf, rxLen);
systemStatus.rst += 1;//结果计数
return 1;
}
else {
log_i("writeCMD success!");
systemStatus.rst += 0;//结果计数
return 0;
}
}
/**
* 发送数据到主机
*
* @param txBuf 发送数据缓冲区
* @param txLen 发送数据长度
*/
void sendMsgToHost(uint8_t *txBuf, uint16_t txLen) {
// 发送数据
transDataToHost(txBuf, txLen);
}
// 判断系统大端序还是小端序
static uint8_t IsBigEndian() {
uint32_t num = 0x12345678;
return ((*(uint8_t*)&num) == 0x12);
}
// 将数据按大端序填充
static void FillBigEndian32(uint8_t *data, uint32_t value) {
if(!IsBigEndian()) {
for(uint16_t i = 0; i < 4; i++) {
data[i] = (value >> ((4 - i - 1) * 8)) & 0xFF;
}
}
else {
for(uint16_t i = 0; i < 4; i++) {
data[i] = (value >> (i * 8)) & 0xFF;
}
}
}
static void FillBigEndian16(uint8_t *data, uint16_t value) {
if(!IsBigEndian()) {
for(uint16_t i = 0; i < 2; i++) {
data[i] = (value >> ((2 - i - 1) * 8)) & 0xFF;
}
}
else {
for(uint16_t i = 0; i < 2; i++) {
data[i] = (value >> (i * 8)) & 0xFF;
}
}
}
/*
+----------+--------+------------+------------+------------+
| 从机地址 | 功能码 | 寄存器地址 | 寄存器数量 | CRC校验值 |
+----------+--------+------------+------------+------------+
| 1字节 | 1字节 | 2字节 | 2字节 | 2字节 |
+----------+--------+------------+------------+------------+
*/
// pump 读寄存器
/**
* 以0x03指定读取1个寄存器
* @param id 设备id
* @param reg 寄存器地址
* @param return 寄存器值
*/
uint16_t ReadPump1Reg(uint8_t id, uint16_t reg) {
uint8_t data[8] = {0};
data[0] = id;
data[1] = RTU_FUNC_READ_HOLD_REG;
FillBigEndian16(&data[2], reg);
FillBigEndian16(&data[4], 1);
uint16_t crc = CalculateCRC16(data, 6);
// 小端序填充
memcpy(&data[6], &crc, 2);
writeCMD(data, 8);
uint8_t rxBuf[30] = {0};
uint16_t rxLen = 8;
readDataFromMotorValve(rxBuf, rxLen, READ_ACK_TIMEOUT);
return rxBuf[3]<<8|rxBuf[4];
}
/**
* 以0x03指定读取2个寄存器
* @param id 设备id
* @param reg 寄存器起始地址
* @param return 寄存器值
*/
uint32_t ReadPump2Reg(uint8_t id, uint16_t reg) {
uint8_t data[12] = {0};
data[0] = id;
data[1] = RTU_FUNC_READ_HOLD_REG;
FillBigEndian16(&data[2], reg);
FillBigEndian16(&data[4], 2);
uint16_t crc = CalculateCRC16(data, 6);
// 小端序填充
memcpy(&data[6], &crc, 2);
writeCMD(data, 12);
uint8_t rxBuf[30] = {0};
uint16_t rxLen = 12;
readDataFromMotorValve(rxBuf, rxLen, READ_ACK_TIMEOUT*2);
// 逻辑需进一步完善
return rxBuf[6]<<24|rxBuf[7]<<16|rxBuf[8]<<8|rxBuf[9];
}
/*
+----------+--------+------------+----------+------------+
| 从机地址 | 功能码 | 寄存器地址 | 寄存器值 | CRC校验值 |
+----------+--------+------------+----------+------------+
| 1字节 | 1字节 | 2字节 | 2字节 | 2字节 |
+----------+--------+------------+----------+------------+
*/
// 写泵1个寄存器
uint8_t WritePump1Reg(uint8_t id, uint16_t reg, int16_t value) {
// 写一个寄存器不需要指定寄存器长度
uint8_t data[8] = {0};
data[0] = id;
data[1] = RTU_FUNC_WRITE_REG;
FillBigEndian16(&data[2], reg);
FillBigEndian16(&data[4], value);
uint16_t crc = CalculateCRC16(data, 6);
// 小端序填充
memcpy(&data[6], &crc, 2);
return writeCMD(data, 8);
}
/*
+----------+--------+------------+------------+----------+----------+------------+
| 从机地址 | 功能码 | 寄存器地址 | 寄存器数量 | 数据长度 | 寄存器值 | CRC校验值 |
+----------+--------+------------+------------+----------+----------+------------+
| 1字节 | 1字节 | 2字节 | 2字节 | 1字节 | 4字节 | 2字节 |
+----------+--------+------------+------------+----------+----------+------------+
*/
// 写泵2个寄存器
uint8_t WritePump2Reg(uint8_t id , uint16_t reg, int32_t value) {
// 写2个寄存器需要指定寄存器长度
uint8_t data[13] = {0};
data[0] = id;
data[1] = RTU_FUNC_WRITE_MULTI_REG;
FillBigEndian16(&data[2], reg);
FillBigEndian16(&data[4], 2);
data[6] = 4;
FillBigEndian32(&data[7], value);
uint16_t crc = CalculateCRC16(data, 11);
// 小端序填充
memcpy(&data[11], &crc, 2);
return writeCMD(data, 13);
}
/*
+--------------+--------------------------------+
| jogging设置顺序 |
+--------------+--------------------------------+
| 1. | 设置加速度JA、减速度JL、速度JS |
+--------------+--------------------------------+
| 2. | 启动Jog-CJ |
+--------------+--------------------------------+
| 3. | 停止Jog-SJ |
+--------------+--------------------------------+
*/
/**
* 设置泵的步进加速度
*
* @param index 泵索引
* @param acc 加速度值
* @return 0:成功 其他:失败
*/
static uint8_t SetPumpJogAcc(uint8_t index, uint16_t acc) {
return WritePump1Reg(dp.pump[index].id, RTU_PUMP_CMD_JA, acc*PUMP_ACCEL_RPS);
}
/**
* 设置泵的步进减速度
*
* @param index 泵索引
* @param dec 减速度值
* @return 0:成功 其他:失败
*/
static uint8_t SetPumpJogDec(uint8_t index, uint16_t dec) {
return WritePump1Reg(dp.pump[index].id, RTU_PUMP_CMD_JL, dec*PUMP_DECEL_RPS);
}
/**
* 设置泵的步进速度
*
* @param index 泵索引
* @param speed 速度值
* @return 0:成功 其他:失败
*/
static uint8_t SetPumpJogSpeed(uint8_t index, uint16_t speed) {
return WritePump1Reg(dp.pump[index].id, RTU_PUMP_CMD_JS, speed*PUMP_SPEED_RPS);
}
// Jog=慢跑
// CJ=start jogging
// 写入命令操作码寄存器(40125)数据0x0096(CJ)即执行启动Jog控制
static uint8_t StartPumpJog(uint8_t index) {
return WritePump1Reg(dp.pump[index].id, RTU_PUMP_CMD_CO, 0x0096);
}
// SJ=stop jogging
// 写入命令操作码寄存器(40125)数据0x00D8(SJ)即执行停止Jog控制
// CJ与SJ一一对应单次SJ无法停止所有全部CJ
// 直接停止泵需要使用SK命令
static uint8_t StopPumpJog(uint8_t index) {
return WritePump1Reg(dp.pump[index].id, RTU_PUMP_CMD_CO, 0x00D8);
}
/*
+--------------+--------------------------------+
| step设置顺序 |
+--------------+--------------------------------+
| 1. | 设置加速度AC、减速度DE、速度VE |
+--------------+--------------------------------+
| 2. | 设置步进DI |
+--------------+--------------------------------+
| 3. | 动相对位置FL或绝对位置FP |
+--------------+--------------------------------+
| 4. | 停止泵SK |
+--------------+--------------------------------+
*/
/**
* 设置泵的步进加速度
*
* @param index 泵索引
* @param acc 加速度值
* @return 0:成功 其他:失败
*/
static uint8_t SetPumpStepAcc(uint8_t index, uint16_t acc) {
return WritePump1Reg(dp.pump[index].id, RTU_PUMP_CMD_AC, acc*PUMP_ACCEL_RPS);
}
/**
* 设置泵的步进减速度
*
* @param index 泵索引
* @param dec 减速度值
* @return 0:成功 其他:失败
*/
static uint8_t SetPumpStepDec(uint8_t index, uint16_t dec) {
return WritePump1Reg(dp.pump[index].id, RTU_PUMP_CMD_DE, dec*PUMP_DECEL_RPS);
}
/**
* 将百分比速度转换为实际速度值
*
* @param index 泵索引
* @param speedPercent 百分比速度
* @return 实际速度值
*/
uint16_t transSpeedPercentToSpeed(uint8_t index, uint8_t speedPercent) {
return speedPercent * dp.pump[index].speed / 100;
}
/**
* 将实际速度转换为百分比速度
*
* @param index 泵索引
* @param speed 实际速度
* @return 百分比速度
*/
uint8_t transSpeedToSpeedPercent(uint8_t index, uint32_t speed) {
return speed * 100 / dp.pump[index].speed;
}
/**
* 设置泵的步进速度
* 将百分比速度转换为实际速度值
*
* @param index 泵索引
* @param speed 用户速度
* @return 0:成功 其他:失败
*/
static uint8_t SetPumpStepSpeed(uint8_t index, uint16_t speed) {
// 速度类寄存器参数设定值单位为 1/240 rps
speed = speed * PUMP_SPEED_RPS;
return WritePump1Reg(dp.pump[index].id, RTU_PUMP_CMD_VE, speed);
}
/**
* 设置泵的步进目标位置
*
* @param index 泵索引
* @param target 目标位置值
* @return 0:成功 其他:失败
*/
static uint8_t SetPumpStepTarget(uint8_t index, int32_t target) {
return WritePump2Reg(dp.pump[index].id, RTU_PUMP_CMD_DI, target);
}
/**
* 执行相对位置移动,目前协议仅用到相对移动
* 写入FL(feed length)命令到操作码寄存器
*
* @param index 泵索引
* @return 0:成功 其他:失败
*/
static uint8_t StartPumpRelativeMove(uint8_t index) {
return WritePump1Reg(dp.pump[index].id, RTU_PUMP_CMD_CO, 0x0066);
}
/**
* 执行绝对位置移动,目前无使用
* 写入FP(feed position)命令到操作码寄存器
*
* @param index 泵索引
* @return 0:成功 其他:失败
*/
static uint8_t StartPumpAbsoluteMove(uint8_t index) {
return WritePump1Reg(dp.pump[index].id, RTU_PUMP_CMD_CO, 0x0067);
}
/**
* 停止泵运行
* 写入SK(Stop & Kill)命令到操作码寄存器
*
* @param index 泵索引
* @return 0:成功 其他:失败
*/
static uint8_t StopPump(uint8_t index) {
return WritePump1Reg(dp.pump[index].id, RTU_PUMP_CMD_CO, 0x00E1);
}
/**
* 读取泵的硬件版本
*
* @param index 泵索引
* @return 0:成功 其他:失败
*/
uint8_t ReadPumpHWReg(uint8_t index) {
return ReadPump1Reg(dp.pump[index].id, RTU_PUMP_CMD_HW);
}
/**
* 设置泵的通信波特率
*
* @param index 泵索引
* @param br 波特率值
* @return 0:成功 其他:失败
*/
uint8_t SetPumpBR(uint8_t index, uint16_t br) {
return WritePump1Reg(dp.pump[index].id, RTU_PUMP_CMD_BR, br);
}
/**
* 设置泵的通信协议
*
* @param index 泵索引
* @param pr 协议类型值
* @return 0:成功 其他:失败
*/
uint8_t SetPumpPR(uint8_t index, uint16_t pr) {
return WritePump1Reg(dp.pump[index].id, RTU_PUMP_CMD_PR, pr);
}
/**
* 读取泵的运行状态
*
* @param index 泵索引
*/
uint16_t ReadPumpStatus(uint8_t index) {
return ReadPump1Reg(dp.pump[index].id, RTU_PUMP_CMD_SC);
}
/**
* 读取泵的告警信息
*
* @param index 泵索引
*/
uint16_t ReadPumpAlarm(uint8_t index) {
return ReadPump1Reg(dp.pump[index].id, RTU_PUMP_CMD_AL);
}
/**
* 解码泵的告警信息
*
* @param reg4001 告警寄存器值
*/
void DecodePumpAlarmMsg(uint16_t reg4001) {
printf("reg4001: %x\r\n", reg4001);
static AlarmCode_t alarmCode = {0};
// 与上次告警信息相同,则不更新,仅打印一次
if (alarmCode.all == reg4001)
{
return;
}
alarmCode.all = reg4001;
if(alarmCode.all == 0) {
//暂时屏蔽,避免刷屏
// printf("\r\n%s无报警信息\r\n", pumpName[index]);
return;
}
// 打印表格头部
printf("\r\n+--------+------------------+\r\n");
printf("| 告警位 | 告警信息 |\r\n");
printf("+--------+------------------+\r\n");
for(uint16_t i = 0; i < 16; i++) {
if(alarmCode.all & (1 << i)) {
printf("| %6d | %-14s |\r\n", i, alarmInfo[i]);
printf("+--------+------------------+\r\n");
}
}
return;
}
/**
* 解码泵的状态信息
*
* @param reg4002 状态寄存器值
*/
void DecodePumpStatusMsg(uint16_t reg4002) {
static StatusCode_t statusCode = {0};
// 与上次状态信息相同,则不更新,仅打印一次
if (statusCode.all == reg4002)
{
return 1;
}
statusCode.all = reg4002;
printf("\r\n+--------+------------------+\r\n");
printf("| 状态位 | 状态信息 |\r\n");
printf("+--------+------------------+\r\n");
for(uint16_t i = 0; i < 16; i++) {
if(statusCode.all & (1 << i)) {
printf("| %6d | %-14s |\r\n", i, statusInfo[i]);
printf("+--------+------------------+\r\n");
}
}
return 0;
}
/**
* 读取泵的实时速度和位置
*
*/
void ReadPumpSpeedPos(void)
{
for(uint8_t index = 0; index < 2; index++) {
uint32_t pos = ReadPump2Reg(dp.pump[index].id,RTU_PUMP_CMD_POS);
systemStatus.pumpsPos[index] = pos;
log_d("%s pos = %d",dp.pump[index].name,pos);
uint16_t speed = ReadPump2Reg(dp.pump[index].id,RTU_PUMP_CMD_SPEED);
systemStatus.pumpsSpeed[index] = speed;
systemStatus.pumpsSpeedPercent[index] = (uint8_t)(speed * 100 / dp.pump[index].speed);
log_d("%s speed = %d",dp.pump[index].name,speed);
//判断正转、反转
if(speed > 0) {
if(ReadPump2Reg(dp.pump[index].id,RTU_PUMP_CMD_POS) > pos)
systemStatus.ds.pumps.status[index] = PUMP_STATUS_CLOCKWISE;
else
systemStatus.ds.pumps.status[index] = PUMP_STATUS_ANTICLOCKWISE;
} else {
systemStatus.ds.pumps.status[index] = PUMP_STATUS_STOP;
}
}
}
/**
* 更新泵的故障状态和运动状态,在轮询中调用
*
*/
void updatePumpStatus(void) {
// 更新设备状态
for(uint8_t index = 0; index < 2; index++) {
uint16_t reg4001 = ReadPumpAlarm(index);
DecodePumpAlarmMsg(reg4001);
uint16_t reg4002 = ReadPumpStatus(index);
DecodePumpStatusMsg(reg4002);
}
}
/**
* 初始化泵参数
* 设置最大速度、加速度和减速度
*
* @return 0:成功 其他:失败
*/
uint8_t InitPump(void) {
// 初始化泵
log_i("InitPump");
for(uint8_t index = 0; index < 2; index++) {
uint8_t rst = systemStatus.rst;
SetPumpJogAcc(index, dp.pump[index].accel);
SetPumpJogDec(index, dp.pump[index].decel);
SetPumpJogSpeed(index, dp.pump[index].speed*dp.pump[index].speedPercent/100);
SetPumpStepAcc(index, dp.pump[index].accel);
SetPumpStepDec(index, dp.pump[index].decel);
SetPumpStepSpeed(index, dp.pump[index].speed*dp.pump[index].speedPercent/100);
if (rst != systemStatus.rst)
{
log_e("InitPump %s failed!", dp.pump[index].name);
systemStatus.ds.initStatus = INIT_FAILED;
return 1;
}
}
return 0;
}
// valve
/*
# 轮廓位置模式,配置流程
1.配置模式:
00B1h=0、运行模式 03C2h=0x01使设备工作在轮廓位置模式
1.1(设置为 CIA402 模式)
1.2(设置为轮廓位置模式)
2.参数配置:
2.1写目标位置 (03E7h)(用户单位)
2.2写当前段位移指令匀速运行速度 (03F8h) (用户单位/s)
2.3设置位移的加速度 (03FCh)(用户单位/s2)
2.4设置位移的减速度 (03FEh)(用户单位/s2)
3.写控制字使电机使能
(0380h)= 0x06→0x07→ 0x0F电机使能
4.使电机<E794B5><E69CBA>
(0380h)= 0x2F→0x3F电机运行
5.监控参数:
实际位置反馈:(03C8h) (用户单位)
# 堵转找寻原点方式,配置流程
1.设置原点回归方式
(0416h)=37;17=负限位18=正限位
2.设置堵转检测力矩和堵转检测时间
(0170h)=300(0172h)=50
3.设置模式
写 (00B1h)=0、运行模式 (03C2h)=0x06使其工作在原点回归模式
4.写寻找限位开关速度和寻找原点信号速度
(0417h)= 10000 (0419h)=1000;
5.设置回零加速度
(041Bh)=200000
6.写控制字
(0380h)= 0x06→0x07→0x0F→0x1F电机运行
*/
// 与pump通用
uint8_t (*writeValve1Reg)(uint8_t index, uint16_t reg, uint16_t value) = WritePump1Reg;
uint8_t (*writeValve2Reg)(uint8_t index, uint16_t reg, uint32_t value) = WritePump2Reg;
uint8_t (*readValve1Reg)(uint8_t index, uint16_t reg) = ReadPump1Reg;
uint8_t (*readValve2Reg)(uint8_t index, uint16_t reg) = ReadPump2Reg;
/**
* 读取阀门1个输入寄存器
*
* @param id 阀门id
* @param reg 寄存器地址
* @return 寄存器值
*/
uint16_t ReadValve1InputReg(uint8_t id, uint16_t reg)
{
uint8_t data[8] = {0};
data[0] = id;
data[1] = RTU_FUNC_READ_INPUT_REG;
FillBigEndian16(&data[2], reg);
FillBigEndian16(&data[4], 1);
uint16_t crc = CalculateCRC16(data, 6);
// 小端序填充
memcpy(&data[6], &crc, 2);
writeCMD(data, 8);
uint8_t rxBuf[30] = {0};
uint16_t rxLen = 8;
uint8_t rst = HAL_UART_Receive(&huart2, rxBuf, rxLen, READ_ACK_TIMEOUT);
return rxBuf[3]<<8|rxBuf[4];
}
/**
* 读取阀门2个输入寄存器
*
* @param id 阀门id
* @param reg 寄存器地址
* @return 寄存器值
*/
uint32_t ReadValve2InputReg(uint8_t id, uint16_t reg)
{
uint8_t data[8] = {0};
data[0] = id;
data[1] = RTU_FUNC_READ_INPUT_REG;
FillBigEndian16(&data[2], reg);
FillBigEndian16(&data[4], 2);
uint16_t crc = CalculateCRC16(data, 6);
// 小端序填充
memcpy(&data[6], &crc, 2);
writeCMD(data, 8);
uint8_t rxBuf[30] = {0};
uint16_t rxLen = 9;
uint8_t rst = HAL_UART_Receive(&huart2, rxBuf, rxLen, READ_ACK_TIMEOUT);
return rxBuf[3]<<24|rxBuf[4]<<16|rxBuf[5]<<8|rxBuf[6];
}
/**
* 设置阀门通信模式
*
* @param index 阀门索引
* @param mode 通信模式(如CIA402模式)
* @return 0:成功 其他:失败
*/
static uint8_t SetValveCOMMMode(uint8_t index, uint16_t mode) {
return writeValve1Reg(dp.valve[index].id, RTU_VALVE_CMD_CTL_MODE, mode);
}
/**
* 设置阀门运行模式
*
* @param index 阀门索引
* @param mode 运行模式(如原点回归模式HM、轮廓位置模式PP)
* @return 0:成功 其他:失败
*/
static uint8_t SetValveRunMode(uint8_t index, uint16_t mode) {
return writeValve1Reg(dp.valve[index].id, RTU_VALVE_CMD_RUN_MODE, mode);
}
// PP=轮廓位置模式
/**
* 设置阀门轮廓位置
*
* @param index 阀门索引
* @param pos 目标位置(用户单位)
* @return 0:成功 其他:失败
*/
static uint8_t SetValvePPPos(uint8_t index, uint32_t pos) {
return writeValve2Reg(dp.valve[index].id, RTU_VALVE_CMD_PP_POS, pos);
}
/**
* 设置阀门轮廓速度
*
* @param index 阀门索引
* @param speed 运行速度(用户单位/s)
* @return 0:成功 其他:失败
*/
static uint8_t SetValvePPSpeed(uint8_t index, uint32_t speed) {
return writeValve2Reg(dp.valve[index].id, RTU_VALVE_CMD_PP_SPEED, speed);
}
/**
* 设置阀门轮廓加速度
*
* @param index 阀门索引
* @param acc 加速度值(用户单位/s²)
* @return 0:成功 其他:失败
*/
static uint8_t SetValvePPAcc(uint8_t index, uint32_t acc) {
return writeValve2Reg(dp.valve[index].id, RTU_VALVE_CMD_PP_ACCEL, acc);
}
/**
* 设置阀门轮廓减速度
*
* @param index 阀门索引
* @param dec 减速度值(用户单位/s²)
* @return 0:成功 其他:失败
*/
static uint8_t SetValvePPDec(uint8_t index, uint32_t dec) {
return writeValve2Reg(dp.valve[index].id, RTU_VALVE_CMD_PP_DECEL, dec);
}
// HM=原点回归模式
/**
* 设置阀门原点检测模式
*
* @param index 阀门索引
* @param mode 检测模式(如负限位、正限位)
* @return 0:成功 其他:失败
*/
static uint8_t SetValveHomeDetectMode(uint8_t index, uint16_t mode) {
return writeValve1Reg(dp.valve[index].id, RTU_VALVE_CMD_HOME_MODE, mode);
}
/**
* 设置阀门寻找限位开关速度
*
* @param index 阀门索引
* @param speed 寻找速度值
* @return 0:成功 其他:失败
*/
static uint8_t SetValveHomeSwtSpeed(uint8_t index, uint32_t speed) {
return writeValve2Reg(dp.valve[index].id, RTU_VALVE_CMD_HOME_SWT_SPEED, speed);
}
/**
* 设置阀门寻找原点信号速度
*
* @param index 阀门索引
* @param speed 寻找速度值
* @return 0:成功 其他:失败
*/
static uint8_t SetValveHomeOriSpeed(uint8_t index, uint32_t speed) {
return writeValve2Reg(dp.valve[index].id, RTU_VALVE_CMD_HOME_ORI_SPEED, speed);
}
/**
* 设置阀门回零加速度
*
* @param index 阀门索引
* @param acc 加速度值
* @return 0:成功 其他:失败
*/
static uint8_t SetValveHomeAcc(uint8_t index, uint32_t acc) {
return writeValve2Reg(dp.valve[index].id, RTU_VALVE_CMD_HOME_ACCEL, acc);
}
/**
* 设置阀门功能控制字
*
* @param index 阀门索引
* @param func 功能控制字(如准备、使能、运行等)
* @return 0:成功 其他:失败
*/
static uint8_t SetValveFunc(uint8_t index, uint16_t func) {
return writeValve1Reg(dp.valve[index].id, RTU_VALVE_CMD_FUNC, func);
}
/**
* 设置阀门原点回归堵转检测力矩
*
* @param index 阀门索引
* @param torque 力矩值百分比太低容易误判默认为30即30%
* @return 0:成功 其他:失败
*/
static uint8_t SetValveHomeTorque(uint8_t index, uint16_t torque) {
return writeValve1Reg(dp.valve[index].id, RTU_VALVE_CMD_HOME_TORQUE, torque*10);
}
/**
* 设置阀门原点回归堵转检测时间
*
* @param index 阀门索引
* @param time 检测时间(ms)太短容易误判默认为5即5ms
* @return 0:成功 其他:失败
*/
static uint8_t SetValveHomeTime(uint8_t index, uint16_t time) {
return writeValve1Reg(dp.valve[index].id, RTU_VALVE_CMD_HOME_TIME, time*10);
}
/**
* 读取阀门位置
*
* @param index 阀门索引
* @return 位置值(用户单位)
*/
static uint32_t ReadValvePos(uint8_t index) {
return ReadValve2InputReg(dp.valve[index].id, RTU_VALVE_CMD_POS);
}
/**
* 读取阀门速度
*
* @param index 阀门索引
* @return 速度值(用户单位/s)
*/
static uint32_t ReadValveSpeed(uint8_t index) {
return ReadValve2InputReg(dp.valve[index].id, RTU_VALVE_CMD_SPEED);
}
/**
* 阀门回归原点控制
* 包含设置原点回归方式、堵转检测、运行模式等配置
*
* 回归需要时间,回归结果在轮询中检查
*
* @param index 阀门索引
* @param direction 方向,正数正方向堵转,负数反方向堵转
*/
void ValveBackToOrigin(uint8_t index,int8_t direction) {
uint8_t rst = systemStatus.rst;
// 1.设置原点回归方式
// (0416h)=37;17=负限位18=正限位
if(direction > 0) {
log_i("back to Origin, +\r\n");
SetValveHomeDetectMode(index, 37);//正方向堵转
}
else {
log_i("back to Origin, -\r\n");
SetValveHomeDetectMode(index, 38);//反方向堵转
}
// 2.设置堵转检测力矩和堵转检测时间
// (0170h)=300(0172h)=50
SetValveHomeTorque(index, 30);//30%
SetValveHomeTime(index, 5);//50ms
// 3.写 (00B1h)=0、运行模式 (03C2h)=0x06使其工作在原点回归模式
SetValveCOMMMode(index, RTU_VALVE_CFG_COMM_CIA402);
SetValveRunMode(index, RTU_VALVE_CFG_MODE_HM);
// 4.写寻找限位开关速度和寻找原点信号速度(0417h)= 10000 (0419h)=1000;
// SetValveHomeSwtSpeed(index, 20000);
SetValveHomeOriSpeed(index, 20000);
// 5.设置回零加速度
SetValveHomeAcc(index, 200000);
// 6.写控制字
// (0380h)= 0x06→0x07→0x0F→0x1F电机运行
SetValveFunc(index, RTU_VALVE_CFG_PREPARE);
SetValveFunc(index, RTU_VALVE_CFG_DISABLE);
SetValveFunc(index, RTU_VALVE_CFG_ENABLE);
SetValveFunc(index, RTU_VALVE_CFG_RUN_ORIGIN);
if(rst != systemStatus.rst) {
log_e("ValveBackToOrigin[%d] failed!",index);
systemStatus.ds.initStatus = INIT_FAILED;
}
// 2,3,4,5设置过后可不再设置
// 1,6为必须
// 堵转点即为原点读位置应为0或小于200
// 不同在于负堵转时目标位置10000为正0x2710正堵转时目标位置为负0xffffd8f0
}
/**
* 检查原点回归结果
* @param index
* @return 0:成功 1:运动中 2:失败
*/
static void valveCheckBTOResult(uint8_t index)
{
static uint8_t retryCnt = 0;
uint8_t isSuccess = 0;
uint16_t rst = ReadValve1InputReg(dp.valve[index].id,RTU_VALVE_CMD_SC);
// 如果原点回归完成状态字第12位会从0变为1
// 如果原点回归失败状态字第13位会从0变为1。
// 此外也可以附加判断电机当前位置是否在0附近的200个脉冲以内。
if(rst & 0x0001<<12) {
// 成功
uint32_t pos = ReadValvePos(index);
if(pos > 200 || pos < (VALVE_PULSE_PER_ROUND-200)) {
// 位置超出范围
isSuccess = 0;
}
else {
isSuccess = 1;
}
}
if(rst & 0x0001<<13) {
// 失败
isSuccess = 0;
}
if (isSuccess)
{
retryCnt = 0;
systemStatus.ds.initStatus = INIT_SUCCESS;
return;
}
else {
retryCnt++;
if(retryCnt > 2) {//执行两次回归,都失败则认为初始化失败
systemStatus.ds.initStatus = INIT_FAILED;
retryCnt = 0;
return;
}
ValveBackToOrigin(index, -1);
}
}
/**
* 阀门运行初始化
* 配置通信模式、运行模式、速度和加减速等参数
*
* @param index 阀门索引
* @return 0:成功 其他:失败
*/
uint8_t ValvePPInit(uint8_t index) {
uint8_t rst = systemStatus.rst;
log_i("set mode to PP\r\n");
// 1.配置模式:
// 00B1h=0、运行模式 03C2h=0x01使设备工作在轮廓位置模式
SetValveCOMMMode(index, RTU_VALVE_CFG_COMM_CIA402);
SetValveRunMode(index, RTU_VALVE_CFG_MODE_PP);
// 2.2写当前段位移指令匀速运行速度 (03F8h) (用户单位/s)
SetValvePPSpeed(index, 10000);
// 2.3设置位移的加速度 (03FCh)(用户单位/s2)
SetValvePPAcc(index, 40000);
// 2.4设置位移的减速度 (03FEh)(用户单位/s2)
SetValvePPDec(index, 40000);
// 3.写控制字使电机使能
// (0380h)= 0x06→0x07→ 0x0F 电机使能:
SetValveFunc(index, RTU_VALVE_CFG_PREPARE);
SetValveFunc(index, RTU_VALVE_CFG_DISABLE);
SetValveFunc(index, RTU_VALVE_CFG_ENABLE);
if (rst != systemStatus.rst)
{
log_e("ValvePPInit[%d] failed!",index);
systemStatus.ds.initStatus = INIT_FAILED;
}
}
/**
* 控制阀门运行到指定角度
*
* @param index 阀门索引
* @param angle 目标角度0-360绝对角度
* @return 0:成功 其他:失败
*/
uint8_t ValveRunToAngle(uint8_t index, uint32_t angle) {
uint8_t rst = systemStatus.rst;
log_i("set angel to %d\r\n",angle);
// 限制角度的逻辑不在这里,此处只执行控制逻辑
if(angle > 360) {
log_e("阀门角度设置错误");
return 1;
}
// 其它配置不变的情况下只需要写3个控制字
SetValvePPPos(index, (uint32_t)(angle*dp.valve[index].fullCount/360+dp.valve[index].offsetPos));
// 电机以绝对位置,立即更新的方式运行
// (电机是以控制字 6040h(0380h)的 bit4 的上升沿接收新的位置命令,
// 所以每次执行完一次运行后需 要把此位清零。)
SetValveFunc(index, 0x2F);
SetValveFunc(index, 0x3F);
if(rst != systemStatus.rst) {
log_e("ValveRunToAngle[%d] failed!",index);
systemStatus.ds.initStatus = INIT_FAILED;
}
}
/**
* 初始化阀门参数
* 设置最大速度、加速度和减速度默认模式为轮廓位置模式PP
*
* @return 0:成功 其他:失败
*/
uint8_t InitValve(void) {
printf("InitValve\n");
ValvePPInit(0);
// ValvePPInit(1);
}
void ReadValveSpeedPos(void)
{
for(uint8_t index = 0; index < 2; index++) {
systemStatus.valvesSpeed[index] = ReadValveSpeed(index);
// systemStatus.valvesSpeedPercent[index] = transSpeedToSpeedPercent(index, systemStatus.valvesSpeed[index]);
systemStatus.valvesPos[index] = ReadValvePos(index);
systemStatus.ds.valves.angle[index] = systemStatus.valvesPos[index]*360/dp.valve[index].fullCount;
}
}
/**
* 更新阀门状态,包括回归状态、运行状态和告警,在轮询中调用
*/
void updateValveStatus(void)
{
valveCheckBTOResult(0);
valveCheckBTOResult(1);
//alarm
}
/**
* 停止阀门
*
* @param index 阀门索引
*/
void stopValve(uint8_t index) {
SetValveFunc(index, RTU_VALVE_CFG_DISABLE);
SetValveFunc(index, RTU_VALVE_CFG_ENABLE);
}
/**
* 初始化设备状态
* 设置设备在线状态、阀门角度、泵运行状态等
*/
// void InitDeviceStatus() {
// // 初始化泵
// // 更新设备状态
// updateDeviceStatus(SENSOR_ONLINE);
// updateValveStatus(1, 120);
// updateValveStatus(2, 210);
// updatePumpStatus(1, PUMP_CLOCKWISE);
// updatePumpStatus(2, PUMP_ANTICLOCKWISE);
// updatePumpSpeedStatus(1, 100);
// updatePumpSpeedStatus(2, 100);
// updateBubbleSensor(BUBBLE_DETECTED);
// updateEmergencyStop(ESTOP_NORMAL);
// updateInitStatus(INIT_SUCCESS);
// }
/**
* 更新valve和pump的信息如位置、角度、速度等
* 在轮询中调用
*/
void updateVPInfo(void)
{
// 获取回归状态
// 获取泵实时速度、位置
ReadPumpSpeedPos();
// 获取阀门实时速度、位置
ReadValveSpeedPos();
}
//在主循环中调用
/**
* 更新系统的所有状态数据
*/
void updateSystemStatus(void)
{
updateVPInfo();
updatePumpStatus();
updateValveStatus();
}
/**
* 初始化控制系统,初始化阀门和泵的默认参数,
* 有别于 HOST_CMD_SYSTEM_INIT=0x0007 指令对应的初始化功能
*/
void initCTLSystem(void)
{
systemStatus.ds = deviceStatus;
systemStatus.ds.initStatus = INIT_IN_PROGRESS;
systemStatus.rst = 0;
InitValve();
InitPump();
}
/**
* 将消息打包并发送给上位机
* 帧格式:帧头+功能码(2Byte)+数据长度(1Byte)+具体数据(NByte)+CRC16校验位+帧尾
*
* @param funcCode 功能码
*/
static void packMsgToHost(uint16_t funcCode, uint8_t isOK) {
// 实现打包消息到上位机逻辑
// 帧头+功能码(2Byte)+数据长度(1Byte)+ 具体数据(NByte)+CRC16校验位+帧尾
uint8_t msgBuf[64];//最大为4+2+1+15+2+4=28
uint8_t len = 0;
uint8_t dlen = 0;
uint8_t index = 0;
FillBigEndian32(msgBuf, FRAME_HEADER);
FillBigEndian16(msgBuf+sizeof(FRAME_HEADER), funcCode);
if(funcCode == HOST_CMD_STATUS_QUERY) {
dlen = sizeof(DeviceStatus_t);
index = sizeof(FRAME_HEADER)+2;
msgBuf[index] = dlen;
index += 1;
memcpy(msgBuf+index, &deviceStatus, dlen);
index += dlen;
uint16_t crc = CalculateCRC16(msgBuf+4, index-4);//不包含帧头
FillBigEndian16(msgBuf+index, crc);
index += 2;
FillBigEndian32(msgBuf+index, FRAME_TAIL);
len = index+4;
}
else {
dlen = 1;
index = sizeof(FRAME_HEADER)+2;
msgBuf[index] = dlen;
index += 1;
msgBuf[index] = isOK;
index += 1;
uint16_t crc = CalculateCRC16(msgBuf+4, index-4);//不包含帧头
FillBigEndian16(msgBuf+index, crc);
index += 2;
FillBigEndian32(msgBuf+index, FRAME_TAIL);
len = index+4;
}
// 发送数据
sendMsgToHost(msgBuf, len);
}
// 初始化处理
static uint8_t HandleInit(void) {
// 实现初始化逻辑
// 1.更新状态为“初始化中”
// 2.执行默认的初始化内容,此步骤系统上电后会自动执行
// 3.执行协议初始化流程
// 4.检查初始化结果,更新状态“成功”或“失败”
// 3.协议要求内容为2个三通阀步进电机堵转找原点重复至少2次然后各自转至120°。
systemStatus.ds.initStatus = INIT_IN_PROGRESS;
systemStatus.rst = 0;
initCTLSystem();
ValveBackToOrigin(0,-1);
ValveBackToOrigin(1,-1);
if(systemStatus.rst != 0) {
log_e("系统初始化失败");
systemStatus.ds.initStatus = INIT_FAILED;
return ACK_FAILED;
}
return ACK_OK;
}
// 状态查询处理
/**
* 处理状态查询命令
*
* @return 0:成功 其他:失败
*/
static void HandleStatusQuery(void) {
packMsgToHost(HOST_CMD_STATUS_QUERY, ACK_OK);
}
// 三通阀控制处理
/**
* 处理三通阀控制命令,正反转、目标角度
*
* @param Buff 接收到的数据缓冲区
* @param len 接收到的数据长度
* @return 0:成功 其他:失败
*/
static uint8_t HandleValveControl(uint8_t *Buff, uint8_t len) {
// 实现三通阀控制逻辑
if(len != 8) {
log_e("三通阀控制错误");
return 1;
}
// for
uint8_t index = Buff[0];
uint8_t direction = Buff[1];//此状态位无效目前三通阀有硬件限位且指定角度必须为120或210
uint16_t angle = (Buff[2]<<8) | Buff[3];
if (angle > 360 || angle != VALVE_ANGLE_120 || angle != VALVE_ANGLE_210) {
log_e("三通阀控制错误");
return 1;
}
// 具体实现
ValveRunToAngle(index,angle);
return 0;
}
// 泵时长控制处理
/**
* 处理泵时长控制命令
*
* @param Buff 接收到的数据缓冲区
* @param len 接收到的数据长度
* @return 0:成功 其他:失败
*/
static uint8_t HandlePumpTimeControl(uint8_t *Buff, uint8_t len) {
// 实现泵时长控制逻辑方向1字节时长2字节。全FF跳过
// 1表示启动泵顺时针转动2表示启动泵的逆时针转动0表示停止泵
// 时间为0则表示一直转
uint8_t rst = systemStatus.rst;
for(uint8_t index = 0; index < len; index++) {
if(memcmp(Buff+index*3, "\xFF\xFF\xFF", 3) == 0) {
continue;
}
int8_t direction = Buff[index*3];
if(direction == 0) {
StopPump(index);
continue;
}
if(direction == 2) {
direction = -1;
}
uint16_t time = (Buff[index*3+1]<<8) | Buff[index*3+2];
if(time == 0) {
// 方向控制办法待确定
StartPumpJog(index);
continue;
}
// 使用步数方式更靠谱,通过时间和速度计算步数,结束时不用发送停止命令
int32_t step = direction*time*dp.pump[index].speed*dp.pump[index].speedPercent/100;
SetPumpStepTarget(index, step);
StartPumpRelativeMove(index);
if(rst != systemStatus.rst) {
log_e("泵时长控制错误");
return ACK_FAILED;
}
}
return ACK_OK;
}
// 泵速度设置处理
/**
* 处理泵速度设置命令
*
* @param Buff 接收到的数据缓冲区
* @param len 接收到的数据长度
* @return 0:成功 其他:失败
*/
static uint8_t HandlePumpSpeedControl(uint8_t *Buff, uint8_t len) {
// 1个字节为速度百分比全FF跳过
for (size_t index = 0; index < len; index++)
{
uint8_t rst = systemStatus.rst;
uint8_t speedPercent = Buff[index];
if (speedPercent == 0xFF)
{
continue;
}
if (speedPercent > 100) {
log_e("泵速度设置错误");
return ACK_FAILED;
}
//更新参数
systemStatus.ds.pumps.speed[index] = speedPercent;
// 写入指令
uint16_t speed = transSpeedPercentToSpeed(index, speedPercent);
SetPumpJogSpeed(index, speed);
SetPumpStepSpeed(index, speed);
if(rst != systemStatus.rst) {
log_e("泵速度设置错误");
return ACK_FAILED;
}
}
return ACK_OK;
}
// 泵步进控制处理
/**
* 处理泵步进控制命令
*
* @param Buff 接收到的数据缓冲区
* @param len 接收到的数据长度
* @return 0:成功 其他:失败
*/
static uint8_t HandlePumpStepControl(uint8_t *Buff, uint8_t len) {
// 4字节步进全FF跳过
for (size_t index = 0; index < len; index++)
{
if(memcmp(Buff+index*4, "\xFF\xFF\xFF\xFF", 4) == 0) {
continue;
}
uint8_t rst = systemStatus.rst;
uint8_t index = Buff[index*4];
int32_t step = (Buff[index*4+1]<<24) | (Buff[index*4+2]<<16) | (Buff[index*4+3]<<8) | Buff[index*4+4];
SetPumpStepTarget(index, step);
StartPumpRelativeMove(index);
if(rst != systemStatus.rst) {
log_e("泵步进设置错误");
return ACK_FAILED;
}
}
return ACK_OK;
}
/**
* 处理软急停命令
*
* @param rxBuf 接收到的数据缓冲区
* @param rxLen 接收到的数据长度
* @return 0:成功 1:失败
*/
static uint8_t HandleSoftStop(uint8_t *rxBuf, uint16_t rxLen) {
if(rxLen != 1) {
log_e("软急停设置错误");
return ACK_FAILED;
}
// 实现软急停功能逻辑
if(rxBuf[0] == 0) {
// 正常状态
systemStatus.ds.estopStatus = ESTOP_NORMAL;
}
else {
// 急停状态
uint8_t rst = systemStatus.rst;
StopPump(0);
StopPump(1);
// StopPumpJog(0);
// StopPumpJog(1);
stopValve(0);
stopValve(1);
if(rst != systemStatus.rst) {
log_e("软急停错误");
return ACK_FAILED;
}
systemStatus.ds.estopStatus = ESTOP_PRESSED;
}
return ACK_OK;
}
/**
* 检查接收到的命令帧格式是否正确
*
* @param rxBuf 接收到的数据缓冲区
* @param rxLen 接收到的数据长度
* @return 命令帧错误码
*/
CmdFrameError_t checkHostCmd(uint8_t *rxBuf, uint8_t rxLen) {
// 检查命令是否正确
// FRAME_HEADER是按小端序存储的而rxBuf是按大端序存的
uint8_t header[sizeof(FRAME_HEADER)];
FillBigEndian32(header, FRAME_HEADER);
uint8_t tail[sizeof(FRAME_TAIL)];
FillBigEndian32(tail, FRAME_TAIL);
if(memcmp(rxBuf, header, sizeof(FRAME_HEADER)) != 0)
{
log_e("CMD_FRAME_HEADER_ERROR\r\n");
return CMD_FRAME_HEADER_ERROR;
}
if (memcmp(rxBuf + rxLen - sizeof(FRAME_TAIL), tail, sizeof(FRAME_TAIL)) != 0)
{
log_e("CMD_FRAME_TAIL_ERROR\r\n");
return CMD_FRAME_TAIL_ERROR;
}
uint16_t crc = CalculateCRC16(rxBuf+sizeof(FRAME_HEADER), rxLen - sizeof(FRAME_HEADER)-sizeof(FRAME_TAIL)-2); // 计算crc不包含帧头和帧尾和crc自身
if (((rxBuf[rxLen-sizeof(FRAME_TAIL)-2]<<8) | rxBuf[rxLen-sizeof(FRAME_TAIL)-1]) != crc)
{
log_e("CMD_FRAME_CHECK_ERROR\r\n");
return CMD_FRAME_CHECK_ERROR;
}
return CMD_FRAME_OK;
}
/**
* 处理上位机发送的命令
* 采用自定义协议,非modbus协议
*
* @param rxBuf 接收到的数据缓冲区
* @param rxLen 接收到的数据长度
* @return 命令帧错误码
*/
void ProcessHostCommand(uint8_t *rxBuf, uint8_t rxLen) {
if (checkHostCmd(rxBuf, rxLen) != CMD_FRAME_OK)
{
log_e("命令错误");
return;
}
uint8_t error = 0;
uint16_t cmdCode = (rxBuf[sizeof(FRAME_HEADER)] << 8) | rxBuf[sizeof(FRAME_HEADER)+1];//提取命令码
uint8_t dataLen = rxBuf[sizeof(FRAME_HEADER)+2];//提取数据长度
uint8_t *data = &rxBuf[sizeof(FRAME_HEADER)+3];//提取数据
switch(cmdCode) {
case HOST_CMD_STATUS_QUERY:
HandleStatusQuery();
break;
case HOST_CMD_VALVE_CTRL:
error = HandleValveControl(data, dataLen);
packMsgToHost(HOST_CMD_VALVE_CTRL, error);
break;
case HOST_CMD_PUMP_RUN_TIME:
error = HandlePumpTimeControl(data, dataLen);
packMsgToHost(HOST_CMD_PUMP_RUN_TIME, error);
break;
case HOST_CMD_PUMP_RUN_SPEED:
error = HandlePumpSpeedControl(data, dataLen);
packMsgToHost(HOST_CMD_PUMP_RUN_SPEED, error);
break;
case HOST_CMD_SOFT_STOP:
error = HandleSoftStop(data, dataLen);
packMsgToHost(HOST_CMD_SOFT_STOP, error);
break;
case HOST_CMD_PUMP_RUN_STEP:
error = HandlePumpStepControl(data, dataLen);
packMsgToHost(HOST_CMD_PUMP_RUN_STEP, error);
break;
case HOST_CMD_SYSTEM_INIT:
error = HandleInit();
packMsgToHost(HOST_CMD_SYSTEM_INIT, error);
break;
default:
error = CMD_FRAME_CMD_ERROR;
break;
}
return error;
}
void runPumpDemo(void) {
printf("runPumpDemo\r\n");
// printf("InitPump\n");
InitPump();
// 泵1正转100步
printf("SetPumpStepTarget(0, 100)\n");
SetPumpStepTarget(0, 200000);
printf("StartPumpRelativeMove(0)\n");
StartPumpRelativeMove(0);
// HAL_Delay(1000);
// 泵1反转100步
printf("SetPumpStepTarget(0, -100)\n");
SetPumpStepTarget(1, -300000);
printf("StartPumpRelativeMove(0)\n");
StartPumpRelativeMove(1);
// HAL_Delay(1000);
// printf("StopPump(0)\n");
// StopPump(0);
// HAL_Delay(1000);
}
void runValveDemo(void) {
printf("runValveDemo\r\n");
ValveBackToOrigin(0,-1);
HAL_Delay(5000);
// 阀门1正转120度
printf("ValveRunToAngle(0, 120)\n");
ValvePPInit(0);
ValveRunToAngle(0, 120);
}