#include "protocol.h" #include #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.使电机��行 (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); }