|
- /**
- * @file modbus_test.c
- * @brief Modbus RTU 从站协议层单元测试。
- * @version 2.0
- * @author zengbingjie
- * @date 2026-07-30
- * @copyright 版权所有 (c) 2026 Xinje Electric。
- * @note 修改记录:2.0 改为直接构造主站请求帧。
- */
-
- #include <assert.h>
- #include <stdint.h>
-
- #include "modbus.h"
-
- #define TEST_SLAVE_ADDRESS (1U)
- #define TEST_COIL_COUNT (8U)
- #define TEST_COIL_WRITE_BYTE_COUNT (1U)
- #define TEST_COIL_WRITE_PAYLOAD_LENGTH (9U)
- #define TEST_COIL_WRITE_FRAME_LENGTH (11U)
- #define TEST_COIL_READ_PAYLOAD_LENGTH (6U)
- #define TEST_COIL_READ_FRAME_LENGTH (8U)
- #define TEST_COIL_READ_RESPONSE_LENGTH (6U)
- #define TEST_COIL_WRITE_VALUE (0x4DU)
-
- #define TEST_REGISTER_START_ADDRESS (0x0100U)
- #define TEST_REGISTER_COUNT (3U)
- #define TEST_REGISTER_WRITE_BYTE_COUNT (6U)
- #define TEST_REGISTER_WRITE_PAYLOAD_LENGTH (13U)
- #define TEST_REGISTER_WRITE_FRAME_LENGTH (15U)
- #define TEST_REGISTER_READ_PAYLOAD_LENGTH (6U)
- #define TEST_REGISTER_READ_FRAME_LENGTH (8U)
- #define TEST_REGISTER_READ_RESPONSE_LENGTH (11U)
- #define TEST_REGISTER_VALUE_0 (0x1234U)
- #define TEST_REGISTER_VALUE_1 (0xABCDU)
- #define TEST_REGISTER_VALUE_2 (0x0001U)
-
- #define TEST_BROADCAST_WRITE_PAYLOAD_LENGTH (9U)
- #define TEST_BROADCAST_WRITE_FRAME_LENGTH (11U)
- #define TEST_BROADCAST_WRITE_VALUE (0xBEEFU)
-
- /**
- * @brief 向测试帧末尾追加 Modbus RTU CRC。
- * @param[in,out] frame 测试帧缓冲区。
- * @param[in] payloadLength 未含 CRC 的帧长度。
- */
- 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 校验 Modbus RTU 响应帧的 CRC。
- * @param[in] frame 响应帧缓冲区。
- * @param[in] frameLength 含 CRC 的完整响应帧长度。
- */
- 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 验证标准 Modbus RTU CRC 字节序。
- */
- static void TestCrcByteOrder(void)
- {
- static const uint8_t requestPayload[] = {
- TEST_SLAVE_ADDRESS, MODBUS_FC_READ_HOLDING_REGS, 0x00U, 0x00U,
- 0x00U, 0x01U
- };
-
- assert(ModbusCrc16(requestPayload, sizeof(requestPayload)) == 0x0A84U);
- }
-
- /**
- * @brief 验证从站处理 0x0F 写线圈和 0x01 读线圈请求。
- */
- static void TestCoilReadWrite(void)
- {
- static MODBUS_SLAVE slave;
- uint8_t writeRequest[TEST_COIL_WRITE_FRAME_LENGTH] = {
- TEST_SLAVE_ADDRESS, MODBUS_FC_WRITE_MULTIPLE_COILS,
- 0x00U, 0x00U, 0x00U, TEST_COIL_COUNT,
- TEST_COIL_WRITE_BYTE_COUNT, TEST_COIL_WRITE_VALUE,
- 0x00U, 0x00U
- };
- uint8_t readRequest[TEST_COIL_READ_FRAME_LENGTH] = {
- TEST_SLAVE_ADDRESS, MODBUS_FC_READ_COILS,
- 0x00U, 0x00U, 0x00U, TEST_COIL_COUNT,
- 0x00U, 0x00U
- };
- uint8_t responseFrame[MODBUS_RTU_ADU_MAX_LENGTH];
- uint16_t responseLength;
-
- assert(ModbusSlaveInit(&slave, TEST_SLAVE_ADDRESS)
- == MODBUS_STATUS_OK);
- TestAppendCrc(writeRequest, TEST_COIL_WRITE_PAYLOAD_LENGTH);
- assert(ModbusProcessFrame(&slave, writeRequest, sizeof(writeRequest),
- responseFrame, sizeof(responseFrame),
- &responseLength) == MODBUS_STATUS_OK);
- assert(responseLength == MODBUS_RTU_WRITE_RESPONSE_LENGTH);
- assert(responseFrame[0U] == TEST_SLAVE_ADDRESS);
- assert(responseFrame[1U] == MODBUS_FC_WRITE_MULTIPLE_COILS);
- assert(responseFrame[2U] == 0x00U);
- assert(responseFrame[3U] == 0x00U);
- assert(responseFrame[4U] == 0x00U);
- assert(responseFrame[5U] == TEST_COIL_COUNT);
- TestAssertFrameCrc(responseFrame, responseLength);
-
- TestAppendCrc(readRequest, TEST_COIL_READ_PAYLOAD_LENGTH);
- assert(ModbusProcessFrame(&slave, readRequest, sizeof(readRequest),
- responseFrame, sizeof(responseFrame),
- &responseLength) == MODBUS_STATUS_OK);
- assert(responseLength == TEST_COIL_READ_RESPONSE_LENGTH);
- assert(responseFrame[0U] == TEST_SLAVE_ADDRESS);
- assert(responseFrame[1U] == MODBUS_FC_READ_COILS);
- assert(responseFrame[2U] == TEST_COIL_WRITE_BYTE_COUNT);
- assert(responseFrame[3U] == TEST_COIL_WRITE_VALUE);
- TestAssertFrameCrc(responseFrame, responseLength);
- }
-
- /**
- * @brief 验证从站处理 0x10 写寄存器和 0x03 读寄存器请求。
- */
- static void TestHoldingRegisterReadWrite(void)
- {
- static MODBUS_SLAVE slave;
- uint8_t writeRequest[TEST_REGISTER_WRITE_FRAME_LENGTH] = {
- TEST_SLAVE_ADDRESS, MODBUS_FC_WRITE_MULTIPLE_REGS,
- 0x01U, 0x00U, 0x00U, TEST_REGISTER_COUNT,
- TEST_REGISTER_WRITE_BYTE_COUNT,
- 0x12U, 0x34U, 0xABU, 0xCDU, 0x00U, 0x01U,
- 0x00U, 0x00U
- };
- uint8_t readRequest[TEST_REGISTER_READ_FRAME_LENGTH] = {
- TEST_SLAVE_ADDRESS, MODBUS_FC_READ_HOLDING_REGS,
- 0x01U, 0x00U, 0x00U, TEST_REGISTER_COUNT,
- 0x00U, 0x00U
- };
- uint8_t responseFrame[MODBUS_RTU_ADU_MAX_LENGTH];
- uint16_t responseLength;
-
- assert(ModbusSlaveInit(&slave, TEST_SLAVE_ADDRESS)
- == MODBUS_STATUS_OK);
- TestAppendCrc(writeRequest, TEST_REGISTER_WRITE_PAYLOAD_LENGTH);
- assert(ModbusProcessFrame(&slave, writeRequest, sizeof(writeRequest),
- responseFrame, sizeof(responseFrame),
- &responseLength) == MODBUS_STATUS_OK);
- assert(responseLength == MODBUS_RTU_WRITE_RESPONSE_LENGTH);
- assert(responseFrame[0U] == TEST_SLAVE_ADDRESS);
- assert(responseFrame[1U] == MODBUS_FC_WRITE_MULTIPLE_REGS);
- assert(responseFrame[2U] == 0x01U);
- assert(responseFrame[3U] == 0x00U);
- assert(responseFrame[4U] == 0x00U);
- assert(responseFrame[5U] == TEST_REGISTER_COUNT);
- TestAssertFrameCrc(responseFrame, responseLength);
-
- TestAppendCrc(readRequest, TEST_REGISTER_READ_PAYLOAD_LENGTH);
- assert(ModbusProcessFrame(&slave, readRequest, sizeof(readRequest),
- responseFrame, sizeof(responseFrame),
- &responseLength) == MODBUS_STATUS_OK);
- assert(responseLength == TEST_REGISTER_READ_RESPONSE_LENGTH);
- assert(responseFrame[0U] == TEST_SLAVE_ADDRESS);
- assert(responseFrame[1U] == MODBUS_FC_READ_HOLDING_REGS);
- assert(responseFrame[2U] == TEST_REGISTER_WRITE_BYTE_COUNT);
- assert(responseFrame[3U] == 0x12U);
- assert(responseFrame[4U] == 0x34U);
- assert(responseFrame[5U] == 0xABU);
- assert(responseFrame[6U] == 0xCDU);
- assert(responseFrame[7U] == 0x00U);
- assert(responseFrame[8U] == 0x01U);
- TestAssertFrameCrc(responseFrame, responseLength);
- }
-
- /**
- * @brief 验证从站对非法数据地址返回 0x02 异常响应。
- */
- static void TestIllegalAddressException(void)
- {
- static MODBUS_SLAVE slave;
- uint8_t requestFrame[MODBUS_RTU_READ_REQUEST_LENGTH] = {
- TEST_SLAVE_ADDRESS, MODBUS_FC_READ_HOLDING_REGS,
- 0x27U, 0x0FU, 0x00U, 0x02U,
- 0x00U, 0x00U
- };
- uint8_t responseFrame[MODBUS_RTU_ADU_MAX_LENGTH];
- uint16_t responseLength;
-
- assert(ModbusSlaveInit(&slave, TEST_SLAVE_ADDRESS)
- == MODBUS_STATUS_OK);
- TestAppendCrc(requestFrame, TEST_COIL_READ_PAYLOAD_LENGTH);
- assert(ModbusProcessFrame(&slave, requestFrame, sizeof(requestFrame),
- responseFrame, sizeof(responseFrame),
- &responseLength) == MODBUS_STATUS_EXCEPTION);
- assert(responseLength == MODBUS_RTU_EXCEPTION_LENGTH);
- assert(responseFrame[0U] == TEST_SLAVE_ADDRESS);
- assert(responseFrame[1U]
- == (MODBUS_FC_READ_HOLDING_REGS | 0x80U));
- assert(responseFrame[2U] == MODBUS_EXCEPTION_ILLEGAL_DATA_ADDR);
- TestAssertFrameCrc(responseFrame, responseLength);
- }
-
- /**
- * @brief 验证广播写寄存器生效且从站不返回响应。
- */
- static void TestBroadcastWrite(void)
- {
- static MODBUS_SLAVE slave;
- uint8_t requestFrame[TEST_BROADCAST_WRITE_FRAME_LENGTH] = {
- MODBUS_BROADCAST_ADDRESS, MODBUS_FC_WRITE_MULTIPLE_REGS,
- 0x01U, 0x00U, 0x00U, 0x01U, 0x02U,
- 0xBEU, 0xEFU, 0x00U, 0x00U
- };
- uint8_t responseFrame[MODBUS_RTU_ADU_MAX_LENGTH];
- uint16_t responseLength;
- uint16_t registerValue;
-
- assert(ModbusSlaveInit(&slave, TEST_SLAVE_ADDRESS)
- == MODBUS_STATUS_OK);
- TestAppendCrc(requestFrame, TEST_BROADCAST_WRITE_PAYLOAD_LENGTH);
- assert(ModbusProcessFrame(&slave, requestFrame, sizeof(requestFrame),
- responseFrame, sizeof(responseFrame),
- &responseLength) == MODBUS_STATUS_BROADCAST);
- assert(responseLength == 0U);
- assert(ModbusGetHoldingRegister(&slave, TEST_REGISTER_START_ADDRESS,
- ®isterValue) == MODBUS_STATUS_OK);
- assert(registerValue == TEST_BROADCAST_WRITE_VALUE);
- }
-
- /**
- * @brief 执行 Modbus RTU 从站协议单元测试。
- * @return 0 表示全部测试通过。
- */
- int main(void)
- {
- TestCrcByteOrder();
- TestCoilReadWrite();
- TestHoldingRegisterReadWrite();
- TestIllegalAddressException();
- TestBroadcastWrite();
-
- return 0;
- }
|