injectorCTL/protocol.c
2024-12-13 17:56:20 +08:00

1206 lines
34 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>
static DeviceStatus deviceStatus = {
.deviceStatus = 1,
.valves = {210, 120},
.pumps = {0, 0, 50, 50},
.bubbleStatus = 0,
.stopStatus = 0,
.errorCode = 0,
.initStatus = 1
};
DeviceDefaultParam dp = {{
{"pump1", 1, 100, 100, 100},
{"pump2", 2, 100, 100, 100},
{"valve1", 3, 100, 100, 100},
{"valve2", 4, 100, 100, 100}
}};
// 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校验值
*
* @param data 需要计算CRC的数据缓冲区
* @param length 数据长度
* @return 计算得到的CRC16校验值
*/
uint16_t CalculateCRC16(uint8_t *data, uint16_t length) {
uint16_t crc = 0xFFFF;
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(DeviceStatus_t status) {
deviceStatus.deviceStatus = status;
}
/**
* 更新三通阀状态
*
* @param index 阀门索引(1或2)
* @param angle 阀门角度值(0-360绝对角度)
*/
void updateValveStatus(uint8_t index, ValveAngle_t angle) {
if (index == 1) {
deviceStatus.valves.angle1 = angle;
} else if (index == 2) {
deviceStatus.valves.angle2 = angle;
}
}
/**
* 更新泵运行状态
*
* @param index 泵索引(1或2)
* @param status 泵状态值(停止/顺时针/逆时针)
*/
void updatePumpStatus(uint8_t index, PumpStatus_t status) {
if (index == 1) {
deviceStatus.pumps.status1 = status;
} else if (index == 2) {
deviceStatus.pumps.status2 = status;
}
}
/**
* 更新泵速度状态
*
* @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_t 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格式的命令
*
* @param txBuf 发送数据缓冲区
* @param txLen 发送数据长度
*/
void writeCMD(uint8_t *txBuf, uint16_t txLen) {
}
/**
* 发送数据到主机
*
* @param txBuf 发送数据缓冲区
* @param txLen 发送数据长度
* @return 0:成功 其他:失败
*/
void sendMsgToHost(uint8_t *txBuf, uint16_t txLen) {
// 发送数据
HAL_UART_Transmit_DMA(&huart2, 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 读寄存器
uint8_t ReadPump1Reg(uint8_t index, uint16_t reg) {
uint8_t data[8] = {0};
data[0] = index;
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 ReadPump2Reg(uint8_t index, uint16_t reg) {
uint8_t data[8] = {0};
data[0] = index;
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, 8);
}
/*
+----------+--------+------------+----------+------------+
| 从机地址 | 功能码 | 寄存器地址 | 寄存器值 | CRC校验值 |
+----------+--------+------------+----------+------------+
| 1字节 | 1字节 | 2字节 | 2字节 | 2字节 |
+----------+--------+------------+----------+------------+
*/
// 写泵1个寄存器
uint8_t WritePump1Reg(uint8_t index, uint16_t reg, uint16_t value) {
// 写一个寄存器不需要指定寄存器长度
uint8_t data[8] = {0};
data[0] = index;
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);
writeCMD(data, 8);
}
/*
+----------+--------+------------+------------+----------+----------+------------+
| 从机地址 | 功能码 | 寄存器地址 | 寄存器数量 | 数据长度 | 寄存器值 | CRC校验值 |
+----------+--------+------------+------------+----------+----------+------------+
| 1字节 | 1字节 | 2字节 | 2字节 | 1字节 | 4字节 | 2字节 |
+----------+--------+------------+------------+----------+----------+------------+
*/
// 写泵2个寄存器
uint8_t WritePump2Reg(uint8_t index, uint16_t reg, uint32_t value) {
// 写2个寄存器需要指定寄存器长度
uint8_t data[13] = {0};
data[0] = index;
data[1] = RTU_FUNC_WRITE_MULTI_REG;
FillBigEndian16(&data[2], reg);
FillBigEndian16(&data[4], 2);
data[6] = 8;
FillBigEndian32(&data[7], value);
uint16_t crc = CalculateCRC16(data, 11);
// 小端序填充
memcpy(&data[11], &crc, 2);
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) {
WritePump1Reg(index, RTU_PUMP_CMD_JA, acc);
}
/**
* 设置泵的步进减速度
*
* @param index 泵索引
* @param dec 减速度值
* @return 0:成功 其他:失败
*/
static uint8_t SetPumpJogDec(uint8_t index, uint16_t dec) {
WritePump1Reg(index, RTU_PUMP_CMD_JL, dec);
}
/**
* 设置泵的步进速度
*
* @param index 泵索引
* @param speed 速度值
* @return 0:成功 其他:失败
*/
static uint8_t SetPumpJogSpeed(uint8_t index, uint16_t speed) {
WritePump1Reg(index, RTU_PUMP_CMD_JS, speed);
}
// Jog=慢跑
// CJ=start jogging
// 写入命令操作码寄存器(40125)数据0x0096(CJ)即执行启动Jog控制
static uint8_t StartPumpJog(uint8_t index) {
WritePump1Reg(index, RTU_PUMP_CMD_CO, 0x0096);
}
// SJ=stop jogging
// 写入命令操作码寄存器(40125)数据0x00D8(SJ)即执行停止Jog控制
// CJ与SJ一一对应单次SJ无法停止所有全部CJ
// 直接停止泵需要使用SK命令
static uint8_t StopPumpJog(uint8_t index) {
WritePump1Reg(index, 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) {
WritePump1Reg(index, RTU_PUMP_CMD_AC, acc);
}
/**
* 设置泵的步进减速度
*
* @param index 泵索引
* @param dec 减速度值
* @return 0:成功 其他:失败
*/
static uint8_t SetPumpStepDec(uint8_t index, uint16_t dec) {
WritePump1Reg(index, RTU_PUMP_CMD_DE, dec);
}
/**
* 设置泵的步进速度
* 将百分比速度转换为实际速度值
*
* @param index 泵索引
* @param speed 速度百分比(0-100)
* @return 0:成功 其他:失败
*/
static uint8_t SetPumpStepSpeed(uint8_t index, uint16_t speed) {
// 目标速度转换为实际速度
speed = (uint16_t)(speed * dp.pump[index].maxSpeed / 100);
WritePump1Reg(index, RTU_PUMP_CMD_VE, speed);
}
/**
* 设置泵的步进目标位置
*
* @param index 泵索引
* @param target 目标位置值
* @return 0:成功 其他:失败
*/
static uint8_t SetPumpStepTarget(uint8_t index, uint32_t target) {
WritePump2Reg(index, RTU_PUMP_CMD_DI, target);
}
/**
* 执行相对位置移动,目前协议仅用到相对移动
* 写入FL(feed length)命令到操作码寄存器
*
* @param index 泵索引
* @return 0:成功 其他:失败
*/
static uint8_t StartPumpRelativeMove(uint8_t index) {
WritePump1Reg(index, RTU_PUMP_CMD_CO, 0x0066);
}
/**
* 执行绝对位置移动,目前无使用
* 写入FP(feed position)命令到操作码寄存器
*
* @param index 泵索引
* @return 0:成功 其他:失败
*/
static uint8_t StartPumpAbsoluteMove(uint8_t index) {
WritePump1Reg(index, RTU_PUMP_CMD_CO, 0x0067);
}
/**
* 停止泵运行
* 写入SK(Stop & Kill)命令到操作码寄存器
*
* @param index 泵索引
* @return 0:成功 其他:失败
*/
static uint8_t StopPump(uint8_t index) {
WritePump1Reg(index, RTU_PUMP_CMD_CO, 0x00E1);
}
/**
* 读取泵的硬件版本
*
* @param index 泵索引
* @return 0:成功 其他:失败
*/
uint8_t ReadPumpHWReg(uint8_t index) {
ReadPump1Reg(index, RTU_PUMP_CMD_HW);
}
/**
* 设置泵的通信波特率
*
* @param index 泵索引
* @param br 波特率值
* @return 0:成功 其他:失败
*/
uint8_t SetPumpBR(uint8_t index, uint16_t br) {
WritePump1Reg(index, RTU_PUMP_CMD_BR, br);
}
/**
* 设置泵的通信协议
*
* @param index 泵索引
* @param pr 协议类型值
* @return 0:成功 其他:失败
*/
uint8_t SetPumpPR(uint8_t index, uint16_t pr) {
WritePump1Reg(index, RTU_PUMP_CMD_PR, pr);
}
/**
* 读取泵的运行状态
*
* @param index 泵索引
*/
void ReadPumpStatus(uint8_t index) {
ReadPump1Reg(index, RTU_PUMP_CMD_SC);
}
/**
* 读取泵的告警信息
*
* @param index 泵索引
*/
void ReadPumpAlarm(uint8_t index) {
ReadPump1Reg(index, RTU_PUMP_CMD_AL);
}
/**
* 解码泵的告警信息
*
* @param reg4001 告警寄存器值
*/
void DecodePumpAlarmMsg(uint16_t reg4001) {
static AlarmCode_t alarmCode = {0};
// 与上次告警信息相同,则不更新,仅打印一次
if (alarmCode.all == reg4001)
{
return 0;
}
alarmCode.all = reg4001;
if(alarmCode.all == 0) {
//暂时屏蔽,避免刷屏
// printf("\r\n%s无报警信息\r\n", pumpName[index]);
return 0;
}
// 打印表格头部
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 1;
}
/**
* 解码泵的状态信息
*
* @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 UpdatePumpStatus() {
// 更新设备状态
ReadPumpStatus(0);
ReadPumpStatus(1);
ReadPumpAlarm(0);
ReadPumpAlarm(1);
}
/**
* 初始化泵参数
* 设置最大速度、加速度和减速度
*
* @return 0:成功 其他:失败
*/
uint8_t InitPump(void) {
// 初始化泵
log_e("InitPump");
SetPumpJogAcc(dp.pump[0].id, dp.pump[0].maxAccel);
SetPumpJogDec(dp.pump[0].id, dp.pump[0].maxDecel);
SetPumpJogSpeed(dp.pump[0].id, dp.pump[0].maxSpeed);
SetPumpStepAcc(dp.pump[0].id, dp.pump[0].maxAccel);
SetPumpStepDec(dp.pump[0].id, dp.pump[0].maxDecel);
SetPumpStepSpeed(dp.pump[0].id, dp.pump[0].maxSpeed);
SetPumpJogAcc(dp.pump[1].id, dp.pump[1].maxAccel);
SetPumpJogDec(dp.pump[1].id, dp.pump[1].maxDecel);
SetPumpJogSpeed(dp.pump[1].id, dp.pump[1].maxSpeed);
SetPumpStepAcc(dp.pump[1].id, dp.pump[1].maxAccel);
SetPumpStepDec(dp.pump[1].id, dp.pump[1].maxDecel);
SetPumpStepSpeed(dp.pump[1].id, dp.pump[1].maxSpeed);
}
// 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.使电机运行
(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通用
void (*writeValve1Reg)(uint8_t index, uint16_t reg, uint16_t value) = WritePump1Reg;
void (*writeValve2Reg)(uint8_t index, uint16_t reg, uint32_t value) = WritePump2Reg;
void (*readValve1Reg)(uint8_t index, uint16_t reg) = ReadPump1Reg;
void (*readValve2Reg)(uint8_t index, uint16_t reg) = ReadPump2Reg;
/**
* 设置阀门通信模式
*
* @param index 阀门索引
* @param mode 通信模式(如CIA402模式)
* @return 0:成功 其他:失败
*/
static uint8_t SetValveCOMMMode(uint8_t index, uint16_t mode) {
WritePump1Reg(index, RTU_VALVE_CMD_CTL_MODE, mode);
}
/**
* 设置阀门运行模式
*
* @param index 阀门索引
* @param mode 运行模式(如原点回归模式HM、轮廓位置模式PP)
* @return 0:成功 其他:失败
*/
static uint8_t SetValveRunMode(uint8_t index, uint16_t mode) {
WritePump1Reg(index, RTU_VALVE_CMD_RUN_MODE, mode);
}
// PP=轮廓位置模式
/**
* 设置阀门轮廓位置
*
* @param index 阀门索引
* @param pos 目标位置(用户单位)
* @return 0:成功 其他:失败
*/
static uint8_t SetValvePPPos(uint8_t index, uint32_t pos) {
writeValve2Reg(index, RTU_VALVE_CMD_PP_POS, pos);
}
/**
* 设置阀门轮廓速度
*
* @param index 阀门索引
* @param speed 运行速度(用户单位/s)
* @return 0:成功 其他:失败
*/
static uint8_t SetValvePPSpeed(uint8_t index, uint32_t speed) {
writeValve2Reg(index, RTU_VALVE_CMD_PP_SPEED, speed);
}
/**
* 设置阀门轮廓加速度
*
* @param index 阀门索引
* @param acc 加速度值(用户单位/s²)
* @return 0:成功 其他:失败
*/
static uint8_t SetValvePPAcc(uint8_t index, uint32_t acc) {
writeValve2Reg(index, RTU_VALVE_CMD_PP_ACCEL, acc);
}
/**
* 设置阀门轮廓减速度
*
* @param index 阀门索引
* @param dec 减速度值(用户单位/s²)
* @return 0:成功 其他:失败
*/
static uint8_t SetValvePPDec(uint8_t index, uint32_t dec) {
writeValve2Reg(index, RTU_VALVE_CMD_PP_DECEL, dec);
}
// HM=原点回归模式
/**
* 设置阀门原点检测模式
*
* @param index 阀门索引
* @param mode 检测模式(如负限位、正限位)
* @return 0:成功 其他:失败
*/
static uint8_t SetValveHomeDetectMode(uint8_t index, uint16_t mode) {
writeValve1Reg(index, RTU_VALVE_CMD_HOME_MODE, mode);
}
/**
* 设置阀门寻找限位开关速度
*
* @param index 阀门索引
* @param speed 寻找速度值
* @return 0:成功 其他:失败
*/
static uint8_t SetValveHomeSwtSpeed(uint8_t index, uint32_t speed) {
writeValve2Reg(index, RTU_VALVE_CMD_HOME_SWT_SPEED, speed);
}
/**
* 设置阀门寻找原点信号速度
*
* @param index 阀门索引
* @param speed 寻找速度值
* @return 0:成功 其他:失败
*/
static uint8_t SetValveHomeOriSpeed(uint8_t index, uint32_t speed) {
writeValve2Reg(index, RTU_VALVE_CMD_HOME_ORI_SPEED, speed);
}
/**
* 设置阀门回零加速度
*
* @param index 阀门索引
* @param acc 加速度值
* @return 0:成功 其他:失败
*/
static uint8_t SetValveHomeAcc(uint8_t index, uint32_t acc) {
writeValve2Reg(index, RTU_VALVE_CMD_HOME_ACCEL, acc);
}
/**
* 设置阀门功能控制字
*
* @param index 阀门索引
* @param func 功能控制字(如准备、使能、运行等)
* @return 0:成功 其他:失败
*/
static uint8_t SetValveFunc(uint8_t index, uint16_t func) {
writeValve1Reg(index, RTU_VALVE_CMD_FUNC, func);
}
/**
* 设置阀门原点回归堵转检测力矩
*
* @param index 阀门索引
* @param torque 力矩值
* @return 0:成功 其他:失败
*/
static uint8_t SetValveHomeTorque(uint8_t index, uint16_t torque) {
writeValve1Reg(index, RTU_VALVE_CMD_HOME_TORQUE, torque);
}
/**
* 设置阀门原点回归堵转检测时间
*
* @param index 阀门索引
* @param time 检测时间(ms)
* @return 0:成功 其他:失败
*/
static uint8_t SetValveHomeTime(uint8_t index, uint16_t time) {
writeValve1Reg(index, RTU_VALVE_CMD_HOME_TIME, time);
}
/**
* 阀门回归原点控制
* 包含设置原点回归方式、堵转检测、运行模式等配置
*
* @param index 阀门索引
* @return 0:成功 其他:失败
*/
uint8_t ValveBackToOrigin(uint8_t index) {
// 1.设置原点回归方式
// (0416h)=37;17=负限位18=正限位
SetValveHomeDetectMode(index, 37);
// 2.设置堵转检测力矩和堵转检测时间
// (0170h)=300(0172h)=50
SetValveHomeTorque(index, 300);//30%
SetValveHomeTime(index, 50);//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, 10000);
SetValveHomeOriSpeed(index, 1000);
// 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);
}
/**
* 阀门运行初始化
* 配置通信模式、运行模式、速度和加减速等参数
*
* @param index 阀门索引
* @return 0:成功 其他:失败
*/
uint8_t ValveRunInit(uint8_t index) {
// 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);
}
/**
* 控制阀门运行到指定角度
*
* @param index 阀门索引
* @param angle 目标角度0-360绝对角度
* @return 0:成功 其他:失败
*/
uint8_t ValveRunToAngle(uint8_t index, uint32_t angle) {
// 限制角度的逻辑不在这里,此处只执行控制逻辑
if(angle > 360) {
log_e("阀门角度设置错误");
return 1;
}
// 其它配置不变的情况下只需要写3个控制字
SetValvePPPos(index, (uint32_t)(angle*VALVE_PULSE_PER_ROUND/360));
// 电机以绝对位置,立即更新的方式运行
// (电机是以控制字 6040h(0380h)的 bit4 的上升沿接收新的位置命令,
// 所以每次执行完一次运行后需 要把此位清零。)
SetValveFunc(index, 0x2F);
SetValveFunc(index, 0x3F);
}
/**
* 初始化阀门参数
* 设置最大速度、加速度和减速度
*
* @return 0:成功 其他:失败
*/
uint8_t InitValve(void) {
SetValvePPSpeed(dp.valve[0].id, dp.valve[0].maxSpeed);
SetValvePPAcc(dp.valve[0].id, dp.valve[0].maxAccel);
SetValvePPDec(dp.valve[0].id, dp.valve[0].maxDecel);
SetValvePPSpeed(dp.valve[1].id, dp.valve[1].maxSpeed);
SetValvePPAcc(dp.valve[1].id, dp.valve[1].maxAccel);
SetValvePPDec(dp.valve[1].id, dp.valve[1].maxDecel);
}
/**
* 初始化设备状态
* 设置设备在线状态、阀门角度、泵运行状态等
*/
void InitDeviceStatus() {
// 初始化泵
// 更新设备状态
updateDeviceStatus(DEVICE_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);
}
// 初始化处理
static uint8_t HandleInit(void) {
// 实现初始化逻辑
InitDeviceStatus();
return 1;
}
// 状态查询处理
/**
* 处理状态查询命令
*
* @return 0:成功 其他:失败
*/
static uint8_t HandleStatusQuery(void) {
// 填充并返回数据
uint8_t txBuf[sizeof(DeviceStatus)] = {0};
memcpy(txBuf, &deviceStatus, sizeof(DeviceStatus));
sendMsgToHost(txBuf, sizeof(txBuf));
return 0;
}
// 三通阀控制处理
/**
* 处理三通阀控制命令
*
* @param Buff 接收到的数据缓冲区
* @param len 接收到的数据长度
* @return 0:成功 其他:失败
*/
static uint8_t HandleValveControl(uint8_t *Buff, uint8_t len) {
// 实现三通阀控制逻辑
if(len != 8) {
log_e("三通阀控制错误");
return 0;
}
uint8_t index = Buff[0];
uint8_t direction = Buff[1];//此状态位无效目前三通阀有硬件限位且指定角度必须为120或210
uint16_t angle = (Buff[2]<<8) | Buff[3];
if(angle > 360) {
log_e("三通阀控制错误");
return 1;
}
if (angle != VALVE_ANGLE_120 && angle != VALVE_ANGLE_210) {
log_e("三通阀控制错误");
return 1;
}
// 具体实现
ValveRunToAngle(index,angle);
// 更新三通阀状态
updateValveStatus(index, angle);
return 0;
}
// 泵时长控制处理
/**
* 处理泵时长控制命令
*
* @param Buff 接收到的数据缓冲区
* @param len 接收到的数据长度
* @return 0:成功 其他:失败
*/
static uint8_t HandlePumpTimeControl(uint8_t *Buff, uint8_t len) {
// 实现泵时长控制逻辑
// 使用步数方式更靠谱,通过时间和速度计算步数,结束时不用发送停止命令
return 1;
}
// 泵速度设置处理
/**
* 处理泵速度设置命令
*
* @param Buff 接收到的数据缓冲区
* @param len 接收到的数据长度
* @return 0:成功 其他:失败
*/
static uint8_t HandlePumpSpeedControl(uint8_t *Buff, uint8_t len) {
// 实现速度设置逻辑
if(len != 4) {
log_e("泵速度设置错误");
return 0;
}
uint8_t index = Buff[0];
uint16_t speed = Buff[1];
if (speed > 100) {
log_e("泵速度设置错误");
return 0;
}
SetPumpJogSpeed(index, speed);
SetPumpStepSpeed(index, speed);
updatePumpSpeedStatus(index, speed);
dp.pump[index].maxSpeed = speed;
index = Buff[2];
speed = Buff[3];
if (speed > 100) {
log_e("泵速度设置错误");
return 0;
}
SetPumpJogSpeed(index, speed);
SetPumpStepSpeed(index, speed);
updatePumpSpeedStatus(index, speed);
dp.pump[index].maxSpeed = speed;
return 1;
}
// 泵步进控制处理
/**
* 处理泵步进控制命令
*
* @param Buff 接收到的数据缓冲区
* @param len 接收到的数据长度
* @return 0:成功 其他:失败
*/
static uint8_t HandlePumpStepControl(uint8_t *Buff, uint8_t len) {
if(len != 10) {
log_e("泵步进设置错误");
return 0;
}
uint8_t index = Buff[0];
int32_t step = (Buff[1]<<24) | (Buff[2]<<16) | (Buff[3]<<8) | Buff[4];
SetPumpStepTarget(index, step);
StartPumpRelativeMove(index);
return 0;
}
/**
* 处理软急停命令
*
* @param rxBuf 接收到的数据缓冲区
* @param rxLen 接收到的数据长度
* @return 0:成功 1:失败
*/
static uint8_t HandleSoftStop(uint8_t *rxBuf, uint16_t rxLen) {
if(rxLen != 1) {
log_e("软急停设置错误");
return 1;
}
// 实现软急停功能逻辑
if(rxBuf[0] == 0) {
// 正常状态
updateEmergencyStop(ESTOP_NORMAL);
}
else {
// 急停状态
StopPump(0);
StopPump(1);
// StopPumpJog(0);
// StopPumpJog(1);
updateEmergencyStop(ESTOP_PRESSED);
}
return 0;
}
/**
* 处理电机相关消息
*
* @param rxBuf 接收到的数据缓冲区
* @param rxLen 接收到的数据长度
*/
void ProcessMotorMsg(uint8_t *rxBuf, uint16_t rxLen) {
// 实现电机消息处理逻辑
log_d("ProcessMotorMsg");
}
/**
* 将消息打包并发送给上位机
* 帧格式:帧头+功能码(2Byte)+数据长度(1Byte)+具体数据(NByte)+CRC16校验位+帧尾
*
* @param funcCode 功能码
* @param isOK 执行结果
*/
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+4, FRAME_HEADER);
FillBigEndian16(msgBuf+sizeof(FRAME_HEADER), funcCode);
if(funcCode == HOST_CMD_STATUS_QUERY) {
dlen = sizeof(DeviceStatus);
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);
}
/**
* 检查接收到的命令帧格式是否正确
*
* @param rxBuf 接收到的数据缓冲区
* @param rxLen 接收到的数据长度
* @return 命令帧错误码
*/
CmdFrameError_t checkHostCmd(uint8_t *rxBuf, uint16_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)
{
return CMD_FRAME_HEADER_ERROR;
}
if (memcmp(rxBuf + rxLen - sizeof(FRAME_TAIL), tail, sizeof(FRAME_TAIL)) != 0)
{
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)-6]<<8) | rxBuf[rxLen-sizeof(FRAME_TAIL)-5] != crc)
{
return CMD_FRAME_CHECK_ERROR;
}
return CMD_FRAME_OK;
}
/**
* 处理上位机发送的命令
* 采用自定义协议,非modbus协议
*
* @param rxBuf 接收到的数据缓冲区
* @param rxLen 接收到的数据长度
* @return 命令帧错误码
*/
CmdFrameError_t ProcessHostCommand(uint8_t *rxBuf, uint16_t rxLen) {
CmdFrameError_t error = checkHostCmd(rxBuf, rxLen);
if (error != CMD_FRAME_OK)
{
return error;
}
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:
error = HandleStatusQuery();
break;
case HOST_CMD_VALVE_CTRL:
error = HandleValveControl(data, dataLen);
break;
case HOST_CMD_PUMP_RUN_TIME:
error = HandlePumpTimeControl(data, dataLen);
break;
case HOST_CMD_PUMP_RUN_SPEED:
error = HandlePumpSpeedControl(data, dataLen);
break;
case HOST_CMD_SOFT_STOP:
error = HandleSoftStop(data, dataLen);
break;
case HOST_CMD_PUMP_RUN_STEP:
error = HandlePumpStepControl(data, dataLen);
break;
case HOST_CMD_SYSTEM_INIT:
error = HandleInit();
break;
default:
error = CMD_FRAME_CMD_ERROR;
break;
}
return error;
}