Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 
 

257 rader
9.6 KiB

  1. /**
  2. * @file modbus_test.c
  3. * @brief Modbus RTU 从站协议层单元测试。
  4. * @version 2.0
  5. * @author zengbingjie
  6. * @date 2026-07-30
  7. * @copyright 版权所有 (c) 2026 Xinje Electric。
  8. * @note 修改记录:2.0 改为直接构造主站请求帧。
  9. */
  10. #include <assert.h>
  11. #include <stdint.h>
  12. #include "modbus.h"
  13. #define TEST_SLAVE_ADDRESS (1U)
  14. #define TEST_COIL_COUNT (8U)
  15. #define TEST_COIL_WRITE_BYTE_COUNT (1U)
  16. #define TEST_COIL_WRITE_PAYLOAD_LENGTH (9U)
  17. #define TEST_COIL_WRITE_FRAME_LENGTH (11U)
  18. #define TEST_COIL_READ_PAYLOAD_LENGTH (6U)
  19. #define TEST_COIL_READ_FRAME_LENGTH (8U)
  20. #define TEST_COIL_READ_RESPONSE_LENGTH (6U)
  21. #define TEST_COIL_WRITE_VALUE (0x4DU)
  22. #define TEST_REGISTER_START_ADDRESS (0x0100U)
  23. #define TEST_REGISTER_COUNT (3U)
  24. #define TEST_REGISTER_WRITE_BYTE_COUNT (6U)
  25. #define TEST_REGISTER_WRITE_PAYLOAD_LENGTH (13U)
  26. #define TEST_REGISTER_WRITE_FRAME_LENGTH (15U)
  27. #define TEST_REGISTER_READ_PAYLOAD_LENGTH (6U)
  28. #define TEST_REGISTER_READ_FRAME_LENGTH (8U)
  29. #define TEST_REGISTER_READ_RESPONSE_LENGTH (11U)
  30. #define TEST_REGISTER_VALUE_0 (0x1234U)
  31. #define TEST_REGISTER_VALUE_1 (0xABCDU)
  32. #define TEST_REGISTER_VALUE_2 (0x0001U)
  33. #define TEST_BROADCAST_WRITE_PAYLOAD_LENGTH (9U)
  34. #define TEST_BROADCAST_WRITE_FRAME_LENGTH (11U)
  35. #define TEST_BROADCAST_WRITE_VALUE (0xBEEFU)
  36. /**
  37. * @brief 向测试帧末尾追加 Modbus RTU CRC。
  38. * @param[in,out] frame 测试帧缓冲区。
  39. * @param[in] payloadLength 未含 CRC 的帧长度。
  40. */
  41. static void TestAppendCrc(uint8_t *frame, uint16_t payloadLength)
  42. {
  43. uint16_t crcValue;
  44. assert(frame != NULL);
  45. crcValue = ModbusCrc16(frame, payloadLength);
  46. frame[payloadLength] = (uint8_t)(crcValue & 0x00FFU);
  47. frame[payloadLength + 1U] = (uint8_t)(crcValue >> 8U);
  48. }
  49. /**
  50. * @brief 校验 Modbus RTU 响应帧的 CRC。
  51. * @param[in] frame 响应帧缓冲区。
  52. * @param[in] frameLength 含 CRC 的完整响应帧长度。
  53. */
  54. static void TestAssertFrameCrc(const uint8_t *frame, uint16_t frameLength)
  55. {
  56. uint16_t expectedCrc;
  57. uint16_t actualCrc;
  58. assert(frame != NULL);
  59. assert(frameLength >= MODBUS_RTU_ADU_MIN_LENGTH);
  60. expectedCrc = ModbusCrc16(frame, frameLength - MODBUS_RTU_CRC_LENGTH);
  61. actualCrc = (uint16_t)frame[frameLength - MODBUS_RTU_CRC_LENGTH]
  62. | ((uint16_t)frame[frameLength - 1U] << 8U);
  63. assert(actualCrc == expectedCrc);
  64. }
  65. /**
  66. * @brief 验证标准 Modbus RTU CRC 字节序。
  67. */
  68. static void TestCrcByteOrder(void)
  69. {
  70. static const uint8_t requestPayload[] = {
  71. TEST_SLAVE_ADDRESS, MODBUS_FC_READ_HOLDING_REGS, 0x00U, 0x00U,
  72. 0x00U, 0x01U
  73. };
  74. assert(ModbusCrc16(requestPayload, sizeof(requestPayload)) == 0x0A84U);
  75. }
  76. /**
  77. * @brief 验证从站处理 0x0F 写线圈和 0x01 读线圈请求。
  78. */
  79. static void TestCoilReadWrite(void)
  80. {
  81. static MODBUS_SLAVE slave;
  82. uint8_t writeRequest[TEST_COIL_WRITE_FRAME_LENGTH] = {
  83. TEST_SLAVE_ADDRESS, MODBUS_FC_WRITE_MULTIPLE_COILS,
  84. 0x00U, 0x00U, 0x00U, TEST_COIL_COUNT,
  85. TEST_COIL_WRITE_BYTE_COUNT, TEST_COIL_WRITE_VALUE,
  86. 0x00U, 0x00U
  87. };
  88. uint8_t readRequest[TEST_COIL_READ_FRAME_LENGTH] = {
  89. TEST_SLAVE_ADDRESS, MODBUS_FC_READ_COILS,
  90. 0x00U, 0x00U, 0x00U, TEST_COIL_COUNT,
  91. 0x00U, 0x00U
  92. };
  93. uint8_t responseFrame[MODBUS_RTU_ADU_MAX_LENGTH];
  94. uint16_t responseLength;
  95. assert(ModbusSlaveInit(&slave, TEST_SLAVE_ADDRESS)
  96. == MODBUS_STATUS_OK);
  97. TestAppendCrc(writeRequest, TEST_COIL_WRITE_PAYLOAD_LENGTH);
  98. assert(ModbusProcessFrame(&slave, writeRequest, sizeof(writeRequest),
  99. responseFrame, sizeof(responseFrame),
  100. &responseLength) == MODBUS_STATUS_OK);
  101. assert(responseLength == MODBUS_RTU_WRITE_RESPONSE_LENGTH);
  102. assert(responseFrame[0U] == TEST_SLAVE_ADDRESS);
  103. assert(responseFrame[1U] == MODBUS_FC_WRITE_MULTIPLE_COILS);
  104. assert(responseFrame[2U] == 0x00U);
  105. assert(responseFrame[3U] == 0x00U);
  106. assert(responseFrame[4U] == 0x00U);
  107. assert(responseFrame[5U] == TEST_COIL_COUNT);
  108. TestAssertFrameCrc(responseFrame, responseLength);
  109. TestAppendCrc(readRequest, TEST_COIL_READ_PAYLOAD_LENGTH);
  110. assert(ModbusProcessFrame(&slave, readRequest, sizeof(readRequest),
  111. responseFrame, sizeof(responseFrame),
  112. &responseLength) == MODBUS_STATUS_OK);
  113. assert(responseLength == TEST_COIL_READ_RESPONSE_LENGTH);
  114. assert(responseFrame[0U] == TEST_SLAVE_ADDRESS);
  115. assert(responseFrame[1U] == MODBUS_FC_READ_COILS);
  116. assert(responseFrame[2U] == TEST_COIL_WRITE_BYTE_COUNT);
  117. assert(responseFrame[3U] == TEST_COIL_WRITE_VALUE);
  118. TestAssertFrameCrc(responseFrame, responseLength);
  119. }
  120. /**
  121. * @brief 验证从站处理 0x10 写寄存器和 0x03 读寄存器请求。
  122. */
  123. static void TestHoldingRegisterReadWrite(void)
  124. {
  125. static MODBUS_SLAVE slave;
  126. uint8_t writeRequest[TEST_REGISTER_WRITE_FRAME_LENGTH] = {
  127. TEST_SLAVE_ADDRESS, MODBUS_FC_WRITE_MULTIPLE_REGS,
  128. 0x01U, 0x00U, 0x00U, TEST_REGISTER_COUNT,
  129. TEST_REGISTER_WRITE_BYTE_COUNT,
  130. 0x12U, 0x34U, 0xABU, 0xCDU, 0x00U, 0x01U,
  131. 0x00U, 0x00U
  132. };
  133. uint8_t readRequest[TEST_REGISTER_READ_FRAME_LENGTH] = {
  134. TEST_SLAVE_ADDRESS, MODBUS_FC_READ_HOLDING_REGS,
  135. 0x01U, 0x00U, 0x00U, TEST_REGISTER_COUNT,
  136. 0x00U, 0x00U
  137. };
  138. uint8_t responseFrame[MODBUS_RTU_ADU_MAX_LENGTH];
  139. uint16_t responseLength;
  140. assert(ModbusSlaveInit(&slave, TEST_SLAVE_ADDRESS)
  141. == MODBUS_STATUS_OK);
  142. TestAppendCrc(writeRequest, TEST_REGISTER_WRITE_PAYLOAD_LENGTH);
  143. assert(ModbusProcessFrame(&slave, writeRequest, sizeof(writeRequest),
  144. responseFrame, sizeof(responseFrame),
  145. &responseLength) == MODBUS_STATUS_OK);
  146. assert(responseLength == MODBUS_RTU_WRITE_RESPONSE_LENGTH);
  147. assert(responseFrame[0U] == TEST_SLAVE_ADDRESS);
  148. assert(responseFrame[1U] == MODBUS_FC_WRITE_MULTIPLE_REGS);
  149. assert(responseFrame[2U] == 0x01U);
  150. assert(responseFrame[3U] == 0x00U);
  151. assert(responseFrame[4U] == 0x00U);
  152. assert(responseFrame[5U] == TEST_REGISTER_COUNT);
  153. TestAssertFrameCrc(responseFrame, responseLength);
  154. TestAppendCrc(readRequest, TEST_REGISTER_READ_PAYLOAD_LENGTH);
  155. assert(ModbusProcessFrame(&slave, readRequest, sizeof(readRequest),
  156. responseFrame, sizeof(responseFrame),
  157. &responseLength) == MODBUS_STATUS_OK);
  158. assert(responseLength == TEST_REGISTER_READ_RESPONSE_LENGTH);
  159. assert(responseFrame[0U] == TEST_SLAVE_ADDRESS);
  160. assert(responseFrame[1U] == MODBUS_FC_READ_HOLDING_REGS);
  161. assert(responseFrame[2U] == TEST_REGISTER_WRITE_BYTE_COUNT);
  162. assert(responseFrame[3U] == 0x12U);
  163. assert(responseFrame[4U] == 0x34U);
  164. assert(responseFrame[5U] == 0xABU);
  165. assert(responseFrame[6U] == 0xCDU);
  166. assert(responseFrame[7U] == 0x00U);
  167. assert(responseFrame[8U] == 0x01U);
  168. TestAssertFrameCrc(responseFrame, responseLength);
  169. }
  170. /**
  171. * @brief 验证从站对非法数据地址返回 0x02 异常响应。
  172. */
  173. static void TestIllegalAddressException(void)
  174. {
  175. static MODBUS_SLAVE slave;
  176. uint8_t requestFrame[MODBUS_RTU_READ_REQUEST_LENGTH] = {
  177. TEST_SLAVE_ADDRESS, MODBUS_FC_READ_HOLDING_REGS,
  178. 0x27U, 0x0FU, 0x00U, 0x02U,
  179. 0x00U, 0x00U
  180. };
  181. uint8_t responseFrame[MODBUS_RTU_ADU_MAX_LENGTH];
  182. uint16_t responseLength;
  183. assert(ModbusSlaveInit(&slave, TEST_SLAVE_ADDRESS)
  184. == MODBUS_STATUS_OK);
  185. TestAppendCrc(requestFrame, TEST_COIL_READ_PAYLOAD_LENGTH);
  186. assert(ModbusProcessFrame(&slave, requestFrame, sizeof(requestFrame),
  187. responseFrame, sizeof(responseFrame),
  188. &responseLength) == MODBUS_STATUS_EXCEPTION);
  189. assert(responseLength == MODBUS_RTU_EXCEPTION_LENGTH);
  190. assert(responseFrame[0U] == TEST_SLAVE_ADDRESS);
  191. assert(responseFrame[1U]
  192. == (MODBUS_FC_READ_HOLDING_REGS | 0x80U));
  193. assert(responseFrame[2U] == MODBUS_EXCEPTION_ILLEGAL_DATA_ADDR);
  194. TestAssertFrameCrc(responseFrame, responseLength);
  195. }
  196. /**
  197. * @brief 验证广播写寄存器生效且从站不返回响应。
  198. */
  199. static void TestBroadcastWrite(void)
  200. {
  201. static MODBUS_SLAVE slave;
  202. uint8_t requestFrame[TEST_BROADCAST_WRITE_FRAME_LENGTH] = {
  203. MODBUS_BROADCAST_ADDRESS, MODBUS_FC_WRITE_MULTIPLE_REGS,
  204. 0x01U, 0x00U, 0x00U, 0x01U, 0x02U,
  205. 0xBEU, 0xEFU, 0x00U, 0x00U
  206. };
  207. uint8_t responseFrame[MODBUS_RTU_ADU_MAX_LENGTH];
  208. uint16_t responseLength;
  209. uint16_t registerValue;
  210. assert(ModbusSlaveInit(&slave, TEST_SLAVE_ADDRESS)
  211. == MODBUS_STATUS_OK);
  212. TestAppendCrc(requestFrame, TEST_BROADCAST_WRITE_PAYLOAD_LENGTH);
  213. assert(ModbusProcessFrame(&slave, requestFrame, sizeof(requestFrame),
  214. responseFrame, sizeof(responseFrame),
  215. &responseLength) == MODBUS_STATUS_BROADCAST);
  216. assert(responseLength == 0U);
  217. assert(ModbusGetHoldingRegister(&slave, TEST_REGISTER_START_ADDRESS,
  218. &registerValue) == MODBUS_STATUS_OK);
  219. assert(registerValue == TEST_BROADCAST_WRITE_VALUE);
  220. }
  221. /**
  222. * @brief 执行 Modbus RTU 从站协议单元测试。
  223. * @return 0 表示全部测试通过。
  224. */
  225. int main(void)
  226. {
  227. TestCrcByteOrder();
  228. TestCoilReadWrite();
  229. TestHoldingRegisterReadWrite();
  230. TestIllegalAddressException();
  231. TestBroadcastWrite();
  232. return 0;
  233. }