|
- /**
- * @file modbus_test.c
- * @brief Modbus RTU 从站私有功能码单元测试。
- * @version 2.1
- * @author zengbingjie
- * @date 2026-08-02
- */
-
- #include <assert.h>
- #include <stdint.h>
- #include <string.h>
-
- #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;
- }
|