/** * @file modbus_test.c * @brief Modbus RTU 从站私有功能码单元测试。 * @version 2.1 * @author zengbingjie * @date 2026-08-02 */ #include #include #include #include "modbus.h" #define TEST_SLAVE_ADDRESS (1U) #define TEST_EXTENDED_READ_FRAME_LENGTH (10U) #define TEST_EXTENDED_READ_RESPONSE_LENGTH (7U) #define TEST_EXTENDED_WRITE_FRAME_LENGTH (10U) #define TEST_ODD_READ_FRAME_LENGTH (8U) #define TEST_ODD_READ_RESPONSE_LENGTH (11U) #define TEST_ODD_READ_MAX_RESPONSE_LENGTH (251U) #define TEST_FC_READ_EXTENDED_REGISTER (0x41U) #define TEST_FC_WRITE_EXTENDED_REGISTER (0x42U) #define TEST_FC_READ_ODD_COUNT_REGS (0x43U) static uint16_t TestHoldingRegisters[MODBUS_DATA_POINT_COUNT]; static uint8_t TestCoilStorage[MODBUS_COIL_STORAGE_SIZE]; static uint16_t TestExtendedRegister; /** * @brief 向测试请求末尾追加 Modbus RTU CRC。 * @param[in,out] frame 测试请求帧。 * @param[in] payloadLength 未含 CRC 的帧长度。 * @return 无。 */ static void TestAppendCrc(uint8_t *frame, uint16_t payloadLength) { uint16_t crcValue; assert(frame != NULL); crcValue = ModbusCrc16(frame, payloadLength); frame[payloadLength] = (uint8_t)(crcValue & 0x00FFU); frame[payloadLength + 1U] = (uint8_t)(crcValue >> 8U); } /** * @brief 校验响应帧的 CRC 字节序和数值。 * @param[in] frame 响应帧。 * @param[in] frameLength 含 CRC 的帧长度。 * @return 无。 */ static void TestAssertFrameCrc(const uint8_t *frame, uint16_t frameLength) { uint16_t expectedCrc; uint16_t actualCrc; assert(frame != NULL); assert(frameLength >= MODBUS_RTU_ADU_MIN_LENGTH); expectedCrc = ModbusCrc16(frame, frameLength - MODBUS_RTU_CRC_LENGTH); actualCrc = (uint16_t)frame[frameLength - MODBUS_RTU_CRC_LENGTH] | ((uint16_t)frame[frameLength - 1U] << 8U); assert(actualCrc == expectedCrc); } /** * @brief 使用真实数据存储初始化一个从站测试实例。 * @param[out] slave 待初始化的从站。 * @return 无。 */ static void TestInitSlave(MODBUS_SLAVE *slave) { assert(slave != NULL); assert(ModbusSlaveInit(slave, TEST_SLAVE_ADDRESS, TestHoldingRegisters, TestCoilStorage) == MODBUS_STATUS_OK); assert(ModbusConfigureExtendedHoldingRegister(slave, &TestExtendedRegister) == MODBUS_STATUS_OK); } /** * @brief 回归验证 0x41 仍可读取固定扩展寄存器。 * @return 无。 */ static void TestReadExtendedRegister(void) { uint8_t request[TEST_EXTENDED_READ_FRAME_LENGTH] = { TEST_SLAVE_ADDRESS, TEST_FC_READ_EXTENDED_REGISTER, 0x00U, 0x01U, 0x00U, 0x00U, 0x00U, 0x01U, 0x00U, 0x00U }; uint8_t response[MODBUS_RTU_ADU_MAX_LENGTH]; uint16_t responseLength; MODBUS_SLAVE slave; TestInitSlave(&slave); TestExtendedRegister = 0x1000U; TestAppendCrc(request, 8U); assert(ModbusProcessFrame(&slave, request, sizeof(request), response, sizeof(response), &responseLength) == MODBUS_STATUS_OK); assert(responseLength == TEST_EXTENDED_READ_RESPONSE_LENGTH); assert(response[0U] == TEST_SLAVE_ADDRESS); assert(response[1U] == TEST_FC_READ_EXTENDED_REGISTER); assert(response[2U] == 2U); assert(response[3U] == 0x10U); assert(response[4U] == 0x00U); TestAssertFrameCrc(response, responseLength); } /** * @brief 验证 0x42 将指定值写入固定扩展寄存器并完整回显。 * @return 无。 */ static void TestWriteExtendedRegister(void) { uint8_t request[TEST_EXTENDED_WRITE_FRAME_LENGTH] = { TEST_SLAVE_ADDRESS, TEST_FC_WRITE_EXTENDED_REGISTER, 0x00U, 0x01U, 0x00U, 0x00U, 0xBEU, 0xEFU, 0x00U, 0x00U }; uint8_t response[MODBUS_RTU_ADU_MAX_LENGTH]; uint16_t responseLength; MODBUS_SLAVE slave; TestInitSlave(&slave); TestAppendCrc(request, 8U); assert(ModbusProcessFrame(&slave, request, sizeof(request), response, sizeof(response), &responseLength) == MODBUS_STATUS_OK); assert(responseLength == TEST_EXTENDED_WRITE_FRAME_LENGTH); assert(memcmp(response, request, 8U) == 0); assert(TestExtendedRegister == 0xBEEFU); TestAssertFrameCrc(response, responseLength); } /** * @brief 验证 0x42 拒绝非 0x00010000 的扩展地址。 * @return 无。 */ static void TestWriteExtendedRegisterRejectsInvalidAddress(void) { uint8_t request[TEST_EXTENDED_WRITE_FRAME_LENGTH] = { TEST_SLAVE_ADDRESS, TEST_FC_WRITE_EXTENDED_REGISTER, 0x00U, 0x01U, 0x00U, 0x01U, 0x12U, 0x34U, 0x00U, 0x00U }; uint8_t response[MODBUS_RTU_ADU_MAX_LENGTH]; uint16_t responseLength; MODBUS_SLAVE slave; TestInitSlave(&slave); TestAppendCrc(request, 8U); assert(ModbusProcessFrame(&slave, request, sizeof(request), response, sizeof(response), &responseLength) == MODBUS_STATUS_EXCEPTION); assert(responseLength == MODBUS_RTU_EXCEPTION_LENGTH); assert(response[0U] == TEST_SLAVE_ADDRESS); assert(response[1U] == (TEST_FC_WRITE_EXTENDED_REGISTER | 0x80U)); assert(response[2U] == MODBUS_EXCEPTION_ILLEGAL_DATA_ADDR); TestAssertFrameCrc(response, responseLength); } /** * @brief 验证 0x43 从起始基准地址后的奇数地址读取保持寄存器。 * @return 无。 */ static void TestReadOddAddressRegisters(void) { uint8_t request[TEST_ODD_READ_FRAME_LENGTH] = { TEST_SLAVE_ADDRESS, TEST_FC_READ_ODD_COUNT_REGS, 0x00U, 0x00U, 0x00U, 0x03U, 0x00U, 0x00U }; uint8_t response[MODBUS_RTU_ADU_MAX_LENGTH]; uint16_t responseLength; MODBUS_SLAVE slave; TestInitSlave(&slave); TestHoldingRegisters[0U] = 0xDEADU; TestHoldingRegisters[1U] = 0x1234U; TestHoldingRegisters[2U] = 0xBEEFU; TestHoldingRegisters[3U] = 0xABCDU; TestHoldingRegisters[4U] = 0xFEEDU; TestHoldingRegisters[5U] = 0x0001U; TestAppendCrc(request, 6U); assert(ModbusProcessFrame(&slave, request, sizeof(request), response, sizeof(response), &responseLength) == MODBUS_STATUS_OK); assert(responseLength == TEST_ODD_READ_RESPONSE_LENGTH); assert(response[0U] == TEST_SLAVE_ADDRESS); assert(response[1U] == TEST_FC_READ_ODD_COUNT_REGS); assert(response[2U] == 6U); assert(response[3U] == 0x12U); assert(response[4U] == 0x34U); assert(response[5U] == 0xABU); assert(response[6U] == 0xCDU); assert(response[7U] == 0x00U); assert(response[8U] == 0x01U); TestAssertFrameCrc(response, responseLength); } /** * @brief 验证 0x43 从偶数基准地址 10 开始读取 D11、D13 和 D15。 * @return 无。 */ static void TestReadOddAddressRegistersFromAddressTen(void) { uint8_t request[TEST_ODD_READ_FRAME_LENGTH] = { TEST_SLAVE_ADDRESS, TEST_FC_READ_ODD_COUNT_REGS, 0x00U, 0x0AU, 0x00U, 0x03U, 0x00U, 0x00U }; uint8_t response[MODBUS_RTU_ADU_MAX_LENGTH]; uint16_t responseLength; MODBUS_SLAVE slave; TestInitSlave(&slave); TestHoldingRegisters[10U] = 0xDEADU; TestHoldingRegisters[11U] = 0x1111U; TestHoldingRegisters[12U] = 0xBEEFU; TestHoldingRegisters[13U] = 0x1313U; TestHoldingRegisters[14U] = 0xFEEDU; TestHoldingRegisters[15U] = 0x1515U; TestAppendCrc(request, 6U); assert(ModbusProcessFrame(&slave, request, sizeof(request), response, sizeof(response), &responseLength) == MODBUS_STATUS_OK); assert(responseLength == TEST_ODD_READ_RESPONSE_LENGTH); assert(response[2U] == 6U); assert(response[3U] == 0x11U); assert(response[4U] == 0x11U); assert(response[5U] == 0x13U); assert(response[6U] == 0x13U); assert(response[7U] == 0x15U); assert(response[8U] == 0x15U); TestAssertFrameCrc(response, responseLength); } /** * @brief 验证 0x43 支持最大 123 个奇数地址寄存器。 * @return 无。 */ static void TestReadOddCountRegistersMaximum(void) { uint8_t request[TEST_ODD_READ_FRAME_LENGTH] = { TEST_SLAVE_ADDRESS, TEST_FC_READ_ODD_COUNT_REGS, 0x00U, 0x00U, 0x00U, 0x7BU, 0x00U, 0x00U }; uint8_t response[MODBUS_RTU_ADU_MAX_LENGTH]; uint16_t responseLength; MODBUS_SLAVE slave; TestInitSlave(&slave); TestHoldingRegisters[245U] = 0x7E57U; TestAppendCrc(request, 6U); assert(ModbusProcessFrame(&slave, request, sizeof(request), response, sizeof(response), &responseLength) == MODBUS_STATUS_OK); assert(responseLength == TEST_ODD_READ_MAX_RESPONSE_LENGTH); assert(response[0U] == TEST_SLAVE_ADDRESS); assert(response[1U] == TEST_FC_READ_ODD_COUNT_REGS); assert(response[2U] == 246U); assert(response[247U] == 0x7EU); assert(response[248U] == 0x57U); TestAssertFrameCrc(response, responseLength); } /** * @brief 验证 0x43 拒绝偶数数量。 * @return 无。 */ static void TestReadOddCountRegistersRejectsEvenCount(void) { uint8_t request[TEST_ODD_READ_FRAME_LENGTH] = { TEST_SLAVE_ADDRESS, TEST_FC_READ_ODD_COUNT_REGS, 0x00U, 0x00U, 0x00U, 0x02U, 0x00U, 0x00U }; uint8_t response[MODBUS_RTU_ADU_MAX_LENGTH]; uint16_t responseLength; MODBUS_SLAVE slave; TestInitSlave(&slave); TestAppendCrc(request, 6U); assert(ModbusProcessFrame(&slave, request, sizeof(request), response, sizeof(response), &responseLength) == MODBUS_STATUS_EXCEPTION); assert(responseLength == MODBUS_RTU_EXCEPTION_LENGTH); assert(response[0U] == TEST_SLAVE_ADDRESS); assert(response[1U] == (TEST_FC_READ_ODD_COUNT_REGS | 0x80U)); assert(response[2U] == MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE); TestAssertFrameCrc(response, responseLength); } /** * @brief 执行 Modbus RTU 私有功能码单元测试。 * @return 0 表示全部测试通过。 */ int main(void) { TestReadExtendedRegister(); TestWriteExtendedRegister(); TestWriteExtendedRegisterRejectsInvalidAddress(); TestReadOddAddressRegisters(); TestReadOddAddressRegistersFromAddressTen(); TestReadOddCountRegistersMaximum(); TestReadOddCountRegistersRejectsEvenCount(); return 0; }