/* * modbus.c * * Created on: Jul 3, 2025 * Author: shiyanlei */ #include "stm32g0xx_hal.h" #include #include #include "main.h" #include "modbus.h" #include "usart.h" //uint16_t holdingRegisters[REGISTER_COUNT] = {0}; /*长度为10*/ int16_t holdingRegisters[REGISTER_COUNT] = {0}; /*长度为10*/ /* 寄存器可写标志位(1:可写,0:只读) */ uint8_t registerWritable[REGISTER_COUNT] = { registerWritable_0, registerWritable_1, registerWritable_2, registerWritable_3, registerWritable_4, registerWritable_5, registerWritable_6, registerWritable_7, registerWritable_8, registerWritable_9 }; /* 全局变量 */ uint8_t modbusBuffer[MODBUS_BUFFER_SIZE]; //int8_t modbusBuffer[MODBUS_BUFFER_SIZE]; uint16_t modbusBufferIndex = 0; uint32_t lastByteTime = 0; bool messageComplete = false; uint8_t response[256]; /* 函数声明 */ void Modbus_Init(void); void Modbus_Process(void); void Modbus_SendResponse(uint8_t *data, uint8_t length); void Modbus_HandleFunction03(uint16_t startAddress, uint16_t registerCount); void Modbus_HandleFunction06(uint16_t registerAddress, uint16_t value); void Modbus_HandleFunction10(uint16_t startAddress, uint16_t registerCount, uint8_t *data); void Modbus_SendExceptionResponse(uint8_t exceptionCode); uint16_t CalculateCRC(uint8_t *data, uint8_t length); /* 初始化函数 */ void Modbus_Init(void) { /* RS485 方向控制引脚置为接收状态 */ HAL_GPIO_WritePin(DIR_GPIO_Port, DIR_Pin, GPIO_PIN_RESET); /* 启动串口中断接收 */ modbusBufferIndex = 0; HAL_UART_Receive_IT(&huart2, (uint8_t *)&modbusBuffer[modbusBufferIndex], 1); } /* Modbus处理函数 - 应在主循环中调用 */ void Modbus_Process(void) { uint16_t startAddress = 0; uint16_t registerCount = 0; uint16_t value = 0; uint8_t byteCount = 0; uint16_t crcReceived; uint16_t crcCalculated; if (modbusBufferIndex > 0 && (HAL_GetTick() - lastByteTime) > MODBUS_TIMEOUT_MS) { /* 超时,标记消息完成 */ messageComplete = true; } if (!messageComplete) { return; } /* 一个有效的 RTU 帧至少包含:地址 + 功能码 + 2 字节 CRC */ if (modbusBufferIndex < 4) { modbusBufferIndex = 0; messageComplete = false; return; } crcReceived = (uint16_t)((modbusBuffer[modbusBufferIndex - 1] << 8) | modbusBuffer[modbusBufferIndex - 2]); crcCalculated = CalculateCRC(modbusBuffer, (uint8_t)(modbusBufferIndex - 2)); if (MODBUS_CRC_OFF == 1 || crcReceived == crcCalculated) { if (modbusBuffer[0] == MODBUS_SLAVE_ADDRESS) /*ID 0x42*/ { switch (modbusBuffer[1]) { case MODBUS_FUNCTION_CODE_03: if (modbusBufferIndex >= 8) { startAddress = (uint16_t)((modbusBuffer[2] << 8) | modbusBuffer[3]); registerCount = (uint16_t)((modbusBuffer[4] << 8) | modbusBuffer[5]); Modbus_HandleFunction03(startAddress, registerCount); } break; case MODBUS_FUNCTION_CODE_06: if (modbusBufferIndex >= 8) { startAddress = (uint16_t)((modbusBuffer[2] << 8) | modbusBuffer[3]); value = (uint16_t)((modbusBuffer[4] << 8) | modbusBuffer[5]); Modbus_HandleFunction06(startAddress, value); } break; case MODBUS_FUNCTION_CODE_10: if (modbusBufferIndex >= 9) { startAddress = (uint16_t)((modbusBuffer[2] << 8) | modbusBuffer[3]); registerCount = (uint16_t)((modbusBuffer[4] << 8) | modbusBuffer[5]); byteCount = modbusBuffer[6]; /* 检查数据长度是否正确 */ if (byteCount == (uint8_t)(registerCount * 2U) && (modbusBufferIndex - 2U) >= (uint16_t)(7U + byteCount)) { Modbus_HandleFunction10(startAddress, registerCount, &modbusBuffer[7]); } else { Modbus_SendExceptionResponse(0x03); } } break; default: Modbus_SendExceptionResponse(0x01); break; } } } /* 重置缓冲区 */ modbusBufferIndex = 0; messageComplete = false; } /* 处理功能码03 */ void Modbus_HandleFunction03(uint16_t startAddress, uint16_t registerCount) { /* 检查地址和数量是否合法 */ if ((startAddress + registerCount) > (REGISTER_START_ADDRESS + REGISTER_COUNT) || registerCount > 0x7D) { Modbus_SendExceptionResponse(0x02); return; } uint8_t byteCount = (uint8_t)(registerCount * 2U); uint8_t index = 0; response[index++] = MODBUS_SLAVE_ADDRESS; response[index++] = MODBUS_FUNCTION_CODE_03; response[index++] = byteCount; for (uint16_t i = 0; i < registerCount; i++) { uint16_t regValue = holdingRegisters[(startAddress - REGISTER_START_ADDRESS) + i]; response[index++] = (uint8_t)((regValue >> 8) & 0xFF); response[index++] = (uint8_t)(regValue & 0xFF); } uint16_t crc = CalculateCRC(response, index); response[index++] = (uint8_t)(crc & 0xFF); response[index++] = (uint8_t)((crc >> 8) & 0xFF); Modbus_SendResponse(response, index); } /* 处理功能码06 */ void Modbus_HandleFunction06(uint16_t registerAddress, uint16_t value) { /* 检查地址是否合法 */ if (registerAddress >= (REGISTER_START_ADDRESS + REGISTER_COUNT)) { Modbus_SendExceptionResponse(0x02); return; } uint16_t regIndex = registerAddress - REGISTER_START_ADDRESS; uint16_t responseValue = holdingRegisters[regIndex]; /* 只有可写寄存器才更新值 */ if (registerWritable[regIndex] == 1) { holdingRegisters[regIndex] = value; responseValue = value; } /* 准备响应帧 - 功能码06的响应是原请求帧的原样返回 */ uint8_t index = 0; response[index++] = MODBUS_SLAVE_ADDRESS; response[index++] = MODBUS_FUNCTION_CODE_06; response[index++] = (uint8_t)((registerAddress >> 8) & 0xFF); response[index++] = (uint8_t)(registerAddress & 0xFF); response[index++] = (uint8_t)((responseValue >> 8) & 0xFF); response[index++] = (uint8_t)(responseValue & 0xFF); /* 计算CRC并添加到响应帧 */ uint16_t crc = CalculateCRC(response, index); response[index++] = (uint8_t)(crc & 0xFF); response[index++] = (uint8_t)((crc >> 8) & 0xFF); /* 发送响应 */ Modbus_SendResponse(response, index); } /* 处理功能码0x10 */ void Modbus_HandleFunction10(uint16_t startAddress, uint16_t registerCount, uint8_t *data) { /* 检查地址和数量是否合法 */ if ((startAddress + registerCount) > (REGISTER_START_ADDRESS + REGISTER_COUNT) || registerCount == 0 || registerCount > 0x7B) { Modbus_SendExceptionResponse(0x02); return; } /* 处理每个寄存器 */ for (uint16_t i = 0; i < registerCount; i++) { uint16_t regIndex = (startAddress - REGISTER_START_ADDRESS) + i; uint16_t value = (uint16_t)((data[i * 2] << 8) | data[i * 2 + 1]); /* 只有可写寄存器才更新值 */ if (registerWritable[regIndex] == 1) { holdingRegisters[regIndex] = value; } } /* 准备响应帧 - 功能码0x10的响应包含起始地址和写入数量 */ uint8_t index = 0; response[index++] = MODBUS_SLAVE_ADDRESS; response[index++] = MODBUS_FUNCTION_CODE_10; response[index++] = (uint8_t)((startAddress >> 8) & 0xFF); response[index++] = (uint8_t)(startAddress & 0xFF); response[index++] = (uint8_t)((registerCount >> 8) & 0xFF); response[index++] = (uint8_t)(registerCount & 0xFF); /* 计算CRC并添加到响应帧 */ uint16_t crc = CalculateCRC(response, index); response[index++] = (uint8_t)(crc & 0xFF); response[index++] = (uint8_t)((crc >> 8) & 0xFF); /* 发送响应 */ Modbus_SendResponse(response, index); } /* 发送异常响应 */ void Modbus_SendExceptionResponse(uint8_t exceptionCode) { response[0] = MODBUS_SLAVE_ADDRESS; response[1] = modbusBuffer[1] | 0x80; response[2] = exceptionCode; uint16_t crc = CalculateCRC(response, 3); response[3] = (uint8_t)(crc & 0xFF); response[4] = (uint8_t)((crc >> 8) & 0xFF); Modbus_SendResponse(response, 5); } /* 发送响应 */ void Modbus_SendResponse(uint8_t *data, uint8_t length) { /* 使能RS485发送 */ HAL_GPIO_WritePin(DIR_GPIO_Port, DIR_Pin, GPIO_PIN_SET); /* DMA 发送数据,发送完成后在 HAL_UART_TxCpltCallback 中切回接收 */ HAL_UART_Transmit_DMA(&huart2, data, length); } /* CRC计算 */ uint16_t CalculateCRC(uint8_t *data, uint8_t length) { uint16_t crc = 0xFFFF; for (uint8_t i = 0; i < length; i++) { crc ^= (uint16_t)data[i]; for (uint8_t j = 0; j < 8; j++) { if ((crc & 0x0001) != 0) { crc >>= 1; crc ^= 0xA001; } else { crc >>= 1; } } } return crc; } /* UART接收中断回调函数 */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart != &huart2) { return; } lastByteTime = HAL_GetTick(); if (modbusBufferIndex < MODBUS_BUFFER_SIZE) { /* HAL 已经将接收到的字节写入 modbusBuffer[modbusBufferIndex], 此处只需向后移动索引。 */ modbusBufferIndex++; } if (modbusBufferIndex < MODBUS_BUFFER_SIZE) { HAL_UART_Receive_IT(&huart2, (uint8_t *)&modbusBuffer[modbusBufferIndex], 1); } else { /* 缓冲区满,丢弃并等待下一帧 */ modbusBufferIndex = 0; HAL_UART_Receive_IT(&huart2, (uint8_t *)&modbusBuffer[modbusBufferIndex], 1); } } /* UART发送完成回调:切回RS485接收方向 */ void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart2) { HAL_GPIO_WritePin(DIR_GPIO_Port, DIR_Pin, GPIO_PIN_RESET); } } /* UART错误回调:重新同步接收 */ void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) { if (huart == &huart2) { modbusBufferIndex = 0; HAL_UART_Receive_IT(&huart2, (uint8_t *)&modbusBuffer[modbusBufferIndex], 1); } }