Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 
 

1171 рядки
40 KiB

  1. /**
  2. * @file modbus.c
  3. * @brief Modbus RTU 从站协议实现。
  4. * @version 2.0
  5. * @author zengbingjie
  6. * @date 2026-07-29
  7. * @copyright 版权所有信捷科技股份有限公司
  8. * @note 修改记录:2.0 增加严格 RTU 校验和广播处理。
  9. */
  10. #include "modbus.h"
  11. /* RTU ADU 布局为:地址和功能码在前,后面是 PDU 和两字节 CRC。
  12. * 以下偏移量由所有请求和响应处理路径共用。 */
  13. #define MODBUS_RTU_ADDRESS_INDEX (0U)
  14. #define MODBUS_RTU_FUNCTION_INDEX (1U)
  15. #define MODBUS_RTU_DATA_INDEX (2U)
  16. #define MODBUS_RTU_BYTE_COUNT_INDEX (6U)
  17. #define MODBUS_RTU_WRITE_DATA_INDEX (7U)
  18. #define MODBUS_RTU_EXCEPTION_MASK (0x80U)
  19. #define MODBUS_RTU_CRC_INIT (0xFFFFU)
  20. #define MODBUS_RTU_CRC_POLYNOMIAL (0xA001U)
  21. #define MODBUS_BITS_PER_BYTE (8U)
  22. #define MODBUS_INITIAL_ON_COIL_COUNT (3U)
  23. typedef enum {
  24. MODBUS_REQUEST_OK = 0,
  25. MODBUS_REQUEST_ILLEGAL_ADDRESS = MODBUS_EXCEPTION_ILLEGAL_DATA_ADDR,
  26. MODBUS_REQUEST_ILLEGAL_VALUE = MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE,
  27. /* 0xFF 仅作为内部哨兵值,绝不会作为 Modbus 异常码发送。
  28. * 调用者会将上面的两类协议错误转换为异常响应 0x02 和 0x03。 */
  29. MODBUS_REQUEST_RESPONSE_ERROR = 0xFFU
  30. } MODBUS_REQUEST_RESULT;
  31. /**
  32. * @brief 判断地址是否为有效的单播从站地址。
  33. * @param[in] slaveAddress 待校验的从站地址。
  34. * @return 地址有效时返回 1,否则返回 0。
  35. */
  36. static uint8_t ModbusIsUnicastAddress(uint8_t slaveAddress)
  37. {
  38. if ((slaveAddress < MODBUS_SLAVE_ADDRESS_MIN)
  39. || (slaveAddress > MODBUS_SLAVE_ADDRESS_MAX)) {
  40. return 0U;
  41. }
  42. return 1U;
  43. }
  44. /**
  45. * @brief 判断地址范围是否位于 Modbus 数据区内。
  46. * @param[in] startAddress 范围内的首个地址。
  47. * @param[in] dataCount 连续数据点数量。
  48. * @return 地址范围有效时返回 1,否则返回 0。
  49. */
  50. static uint8_t ModbusIsDataRangeValid(uint16_t startAddress,
  51. uint16_t dataCount)
  52. {
  53. /* 使用减法而不是 startAddress + dataCount,避免 16 位溢出后错误地将越界
  54. * 请求判断为有效。 */
  55. if ((dataCount == 0U) || (startAddress > MODBUS_DATA_ADDRESS_MAX)) {
  56. return 0U;
  57. }
  58. if (dataCount > (MODBUS_DATA_POINT_COUNT - startAddress)) {
  59. return 0U;
  60. }
  61. return 1U;
  62. }
  63. /**
  64. * @brief 校验私有 32 位地址是否唯一指向扩展保持寄存器。
  65. * @param[in] slave 从站数据对象。
  66. * @param[in] address 私有功能码帧内的 32 位地址。
  67. * @return 地址为 0x00010000 且扩展存储已绑定时返回 1,否则返回 0。
  68. */
  69. static uint8_t ModbusIsExtendedRegisterAddressValid(
  70. const MODBUS_SLAVE *slave, uint32_t address)
  71. {
  72. if ((slave == NULL) || (slave->extendedHoldingRegister == NULL)
  73. || (address != MODBUS_EXTENDED_HOLDING_ADDRESS)) {
  74. return 0U;
  75. }
  76. return 1U;
  77. }
  78. /**
  79. * @brief 从 Modbus ADU 中读取一个大端序 16 位数值。
  80. * @param[in] buffer 数据缓冲区。
  81. * @return 解析得到的 16 位数值。
  82. */
  83. static uint16_t ModbusReadU16(const uint8_t *buffer)
  84. {
  85. uint16_t value;
  86. /* Modbus 字段采用大端序(高字节在前),但 CRC 在总线上按低字节在前发送。 */
  87. value = ((uint16_t)buffer[0] << 8U) | (uint16_t)buffer[1];
  88. return value;
  89. }
  90. /**
  91. * @brief 从私有功能码 PDU 读取大端序 32 位地址。
  92. * @param[in] buffer 至少包含 4 个字节的 PDU 字段。
  93. * @return 解析后的 32 位地址。
  94. */
  95. static uint32_t ModbusReadU32(const uint8_t *buffer)
  96. {
  97. uint32_t value;
  98. value = ((uint32_t)buffer[0] << 24U)
  99. | ((uint32_t)buffer[1] << 16U)
  100. | ((uint32_t)buffer[2] << 8U)
  101. | (uint32_t)buffer[3];
  102. return value;
  103. }
  104. /**
  105. * @brief 向私有功能码 PDU 写入大端序 32 位地址。
  106. * @param[out] buffer 至少可容纳 4 个字节的 PDU 字段。
  107. * @param[in] value 待写入的 32 位地址。
  108. */
  109. static void ModbusWriteU32(uint8_t *buffer, uint32_t value)
  110. {
  111. buffer[0] = (uint8_t)(value >> 24U);
  112. buffer[1] = (uint8_t)(value >> 16U);
  113. buffer[2] = (uint8_t)(value >> 8U);
  114. buffer[3] = (uint8_t)value;
  115. }
  116. /**
  117. * @brief 向 Modbus ADU 中写入一个大端序 16 位数值。
  118. * @param[out] buffer 数据缓冲区。
  119. * @param[in] value 待写入的 16 位数值。
  120. */
  121. static void ModbusWriteU16(uint8_t *buffer, uint16_t value)
  122. {
  123. buffer[0] = (uint8_t)(value >> 8U);
  124. buffer[1] = (uint8_t)(value & 0x00FFU);
  125. }
  126. /**
  127. * @brief 在 ADU 尾部追加 Modbus CRC 校验字段。
  128. * @param[in,out] buffer ADU 缓冲区。
  129. * @param[in] capacity ADU 缓冲区容量,单位为字节。
  130. * @param[in,out] length 追加 CRC 前的 ADU 有效载荷长度。
  131. * @return 成功返回 MODBUS_STATUS_OK,否则返回错误状态。
  132. */
  133. static MODBUS_STATUS ModbusAppendCrc(uint8_t *buffer, uint16_t capacity,
  134. uint16_t *length)
  135. {
  136. uint16_t crcValue;
  137. if ((buffer == NULL) || (length == NULL)) {
  138. return MODBUS_STATUS_INVALID_ARGUMENT;
  139. }
  140. if ((capacity < MODBUS_RTU_CRC_LENGTH)
  141. || (*length > (capacity - MODBUS_RTU_CRC_LENGTH))) {
  142. return MODBUS_STATUS_FRAME_ERROR;
  143. }
  144. /* 按 Modbus RTU 要求,以小端序追加 CRC 值。 */
  145. crcValue = ModbusCrc16(buffer, *length);
  146. buffer[*length] = (uint8_t)(crcValue & 0x00FFU);
  147. buffer[*length + 1U] = (uint8_t)(crcValue >> 8U);
  148. *length += MODBUS_RTU_CRC_LENGTH;
  149. return MODBUS_STATUS_OK;
  150. }
  151. /**
  152. * @brief 校验完整 Modbus RTU ADU 的 CRC 字段。
  153. * @param[in] buffer 完整的 ADU 缓冲区。
  154. * @param[in] length 完整 ADU 长度,单位为字节。
  155. * @return 校验成功返回 MODBUS_STATUS_OK,否则返回错误状态。
  156. */
  157. static MODBUS_STATUS ModbusValidateCrc(const uint8_t *buffer, uint16_t length)
  158. {
  159. uint16_t expectedCrc;
  160. uint16_t actualCrc;
  161. if (buffer == NULL) {
  162. return MODBUS_STATUS_INVALID_ARGUMENT;
  163. }
  164. if (length < MODBUS_RTU_ADU_MIN_LENGTH) {
  165. return MODBUS_STATUS_FRAME_ERROR;
  166. }
  167. /* 对去掉末尾两个字节的 ADU 重新计算 CRC,再与收到的低字节/高字节表示进行比较。 */
  168. expectedCrc = ModbusCrc16(buffer, length - MODBUS_RTU_CRC_LENGTH);
  169. actualCrc = (uint16_t)buffer[length - MODBUS_RTU_CRC_LENGTH]
  170. | ((uint16_t)buffer[length - 1U] << 8U);
  171. if (actualCrc != expectedCrc) {
  172. return MODBUS_STATUS_CRC_ERROR;
  173. }
  174. return MODBUS_STATUS_OK;
  175. }
  176. /**
  177. * @brief 组建 Modbus 异常响应 ADU。
  178. * @param[in] slaveAddress 响应使用的从站地址。
  179. * @param[in] functionCode 请求失败时的功能码。
  180. * @param[in] exceptionCode Modbus 异常码。
  181. * @param[out] txBuffer 响应 ADU 缓冲区。
  182. * @param[in] txCapacity 响应 ADU 缓冲区容量,单位为字节。
  183. * @param[out] txLength 生成的响应长度,单位为字节。
  184. * @return 成功返回 MODBUS_STATUS_EXCEPTION,否则返回错误状态。
  185. */
  186. static MODBUS_STATUS ModbusBuildException(uint8_t slaveAddress,
  187. uint8_t functionCode,
  188. uint8_t exceptionCode,
  189. uint8_t *txBuffer,
  190. uint16_t txCapacity,
  191. uint16_t *txLength)
  192. {
  193. MODBUS_STATUS status;
  194. if ((txBuffer == NULL) || (txLength == NULL)) {
  195. return MODBUS_STATUS_INVALID_ARGUMENT;
  196. }
  197. if ((functionCode & MODBUS_RTU_EXCEPTION_MASK) != 0U) {
  198. return MODBUS_STATUS_FRAME_ERROR;
  199. }
  200. if (txCapacity < MODBUS_RTU_EXCEPTION_LENGTH) {
  201. return MODBUS_STATUS_FRAME_ERROR;
  202. }
  203. /* 标准异常响应格式为:原功能码 | 0x80,后跟一个字节的异常码和重新计算的 CRC。 */
  204. txBuffer[MODBUS_RTU_ADDRESS_INDEX] = slaveAddress;
  205. txBuffer[MODBUS_RTU_FUNCTION_INDEX] = functionCode
  206. | MODBUS_RTU_EXCEPTION_MASK;
  207. txBuffer[MODBUS_RTU_DATA_INDEX] = exceptionCode;
  208. *txLength = MODBUS_RTU_DATA_INDEX + 1U;
  209. status = ModbusAppendCrc(txBuffer, txCapacity, txLength);
  210. if (status != MODBUS_STATUS_OK) {
  211. return status;
  212. }
  213. return MODBUS_STATUS_EXCEPTION;
  214. }
  215. /**
  216. * @brief 从压缩存储区读取一个线圈位。
  217. * @param[in] slave 从站数据对象。
  218. * @param[in] address 线圈地址。
  219. * @return 线圈值,0 表示关闭,1 表示开启。
  220. */
  221. static uint8_t ModbusReadCoilBit(const MODBUS_SLAVE *slave, uint16_t address)
  222. {
  223. uint16_t byteIndex;
  224. uint8_t bitIndex;
  225. uint8_t value;
  226. /* 线圈按低位在前压缩:线圈 0 是字节 0 的 bit 0,线圈 8 是字节 1 的 bit 0。
  227. * 构造 0x01/0x0F 帧时使用相同的映射关系。 */
  228. byteIndex = address / MODBUS_BITS_PER_BYTE;
  229. bitIndex = (uint8_t)(address % MODBUS_BITS_PER_BYTE);
  230. value = (uint8_t)((slave->coilStorage[byteIndex] >> bitIndex) & 0x01U);
  231. return value;
  232. }
  233. /**
  234. * @brief 向压缩存储区写入一个线圈位。
  235. * @param[in,out] slave 从站数据对象。
  236. * @param[in] address 线圈地址。
  237. * @param[in] value 线圈值,0 表示关闭,非 0 表示开启。
  238. */
  239. static void ModbusWriteCoilBit(MODBUS_SLAVE *slave, uint16_t address,
  240. uint8_t value)
  241. {
  242. uint16_t byteIndex;
  243. uint8_t bitIndex;
  244. uint8_t bitMask;
  245. /* 只更新选中的位,保持同一压缩字节中相邻线圈的值不变。 */
  246. byteIndex = address / MODBUS_BITS_PER_BYTE;
  247. bitIndex = (uint8_t)(address % MODBUS_BITS_PER_BYTE);
  248. bitMask = (uint8_t)(1U << bitIndex);
  249. if (value != 0U) {
  250. slave->coilStorage[byteIndex] |= bitMask;
  251. }
  252. else {
  253. slave->coilStorage[byteIndex] &= (uint8_t)(~bitMask);
  254. }
  255. }
  256. /**
  257. * @brief 组建读取线圈的响应帧。
  258. * @param[in] slave 从站数据对象。
  259. * @param[in] startAddress 首个线圈地址。
  260. * @param[in] coilCount 待读取的线圈数量。
  261. * @param[out] txBuffer 响应 ADU 缓冲区。
  262. * @param[in] txCapacity 响应 ADU 缓冲区容量,单位为字节。
  263. * @param[out] txLength 生成的响应长度,单位为字节。
  264. * @return 请求校验结果。
  265. */
  266. static MODBUS_REQUEST_RESULT ModbusBuildReadCoilsResponse(
  267. const MODBUS_SLAVE *slave, uint16_t startAddress, uint16_t coilCount,
  268. uint8_t *txBuffer, uint16_t txCapacity, uint16_t *txLength)
  269. {
  270. MODBUS_STATUS status;
  271. uint16_t byteCount;
  272. uint16_t coilOffset;
  273. uint16_t byteIndex;
  274. if ((coilCount == 0U) || (coilCount > MODBUS_MAX_READ_COIL_COUNT)) {
  275. return MODBUS_REQUEST_ILLEGAL_VALUE;
  276. }
  277. if (ModbusIsDataRangeValid(startAddress, coilCount) == 0U) {
  278. return MODBUS_REQUEST_ILLEGAL_ADDRESS;
  279. }
  280. byteCount = (coilCount + 7U) / MODBUS_BITS_PER_BYTE;
  281. if (txCapacity < (3U + byteCount + MODBUS_RTU_CRC_LENGTH)) {
  282. return MODBUS_REQUEST_RESPONSE_ERROR;
  283. }
  284. txBuffer[MODBUS_RTU_ADDRESS_INDEX] = slave->slaveAddress;
  285. txBuffer[MODBUS_RTU_FUNCTION_INDEX] = MODBUS_FC_READ_COILS;
  286. txBuffer[MODBUS_RTU_DATA_INDEX] = (uint8_t)byteCount;
  287. for (byteIndex = 0U; byteIndex < byteCount; byteIndex++) {
  288. txBuffer[3U + byteIndex] = 0U;
  289. }
  290. /* 响应将第一个请求线圈放入 bit 0,并保持最后一个数据字节中未使用的高位为 0。 */
  291. for (coilOffset = 0U; coilOffset < coilCount; coilOffset++) {
  292. if (ModbusReadCoilBit(slave, startAddress + coilOffset) != 0U) {
  293. byteIndex = coilOffset / MODBUS_BITS_PER_BYTE;
  294. txBuffer[3U + byteIndex] |= (uint8_t)(1U
  295. << (coilOffset % MODBUS_BITS_PER_BYTE));
  296. }
  297. }
  298. *txLength = 3U + byteCount;
  299. status = ModbusAppendCrc(txBuffer, txCapacity, txLength);
  300. if (status != MODBUS_STATUS_OK) {
  301. return MODBUS_REQUEST_RESPONSE_ERROR;
  302. }
  303. return MODBUS_REQUEST_OK;
  304. }
  305. /**
  306. * @brief 组建读取保持寄存器的响应帧。
  307. * @param[in] slave 从站数据对象。
  308. * @param[in] startAddress 首个保持寄存器地址。
  309. * @param[in] registerCount 待读取的保持寄存器数量。
  310. * @param[out] txBuffer 响应 ADU 缓冲区。
  311. * @param[in] txCapacity 响应 ADU 缓冲区容量,单位为字节。
  312. * @param[out] txLength 生成的响应长度,单位为字节。
  313. * @return 请求校验结果。
  314. */
  315. static MODBUS_REQUEST_RESULT ModbusBuildReadRegistersResponse(
  316. const MODBUS_SLAVE *slave, uint16_t startAddress,
  317. uint16_t registerCount, uint8_t *txBuffer, uint16_t txCapacity,
  318. uint16_t *txLength)
  319. {
  320. MODBUS_STATUS status;
  321. uint16_t byteCount;
  322. uint16_t registerOffset;
  323. if ((registerCount == 0U)
  324. || (registerCount > MODBUS_MAX_READ_REGISTER_COUNT)) {
  325. return MODBUS_REQUEST_ILLEGAL_VALUE;
  326. }
  327. if (ModbusIsDataRangeValid(startAddress, registerCount) == 0U) {
  328. return MODBUS_REQUEST_ILLEGAL_ADDRESS;
  329. }
  330. byteCount = registerCount * 2U;
  331. if (txCapacity < (3U + byteCount + MODBUS_RTU_CRC_LENGTH)) {
  332. return MODBUS_REQUEST_RESPONSE_ERROR;
  333. }
  334. txBuffer[MODBUS_RTU_ADDRESS_INDEX] = slave->slaveAddress;
  335. txBuffer[MODBUS_RTU_FUNCTION_INDEX] = MODBUS_FC_READ_HOLDING_REGS;
  336. txBuffer[MODBUS_RTU_DATA_INDEX] = (uint8_t)byteCount;
  337. for (registerOffset = 0U; registerOffset < registerCount;
  338. registerOffset++) {
  339. ModbusWriteU16(&txBuffer[3U + (registerOffset * 2U)],
  340. slave->holdingRegisters[startAddress + registerOffset]);
  341. }
  342. *txLength = 3U + byteCount;
  343. status = ModbusAppendCrc(txBuffer, txCapacity, txLength);
  344. if (status != MODBUS_STATUS_OK) {
  345. return MODBUS_REQUEST_RESPONSE_ERROR;
  346. }
  347. return MODBUS_REQUEST_OK;
  348. }
  349. /**
  350. * @brief 处理写单个线圈请求。
  351. * @param[in,out] slave 从站数据对象。
  352. * @param[in] rxBuffer 请求 ADU。
  353. * @param[in] rxLength 请求 ADU 长度。
  354. * @param[in] isBroadcast 广播请求时为 1,否则为 0。
  355. * @param[out] txBuffer 响应 ADU 缓冲区。
  356. * @param[in] txCapacity 响应 ADU 缓冲区容量。
  357. * @param[out] txLength 生成的响应长度。
  358. * @return 请求校验结果。
  359. */
  360. static MODBUS_REQUEST_RESULT ModbusProcessWriteSingleCoil(
  361. MODBUS_SLAVE *slave, const uint8_t *rxBuffer, uint16_t rxLength,
  362. uint8_t isBroadcast, uint8_t *txBuffer, uint16_t txCapacity,
  363. uint16_t *txLength)
  364. {
  365. uint16_t address;
  366. uint16_t coilValue;
  367. uint16_t index;
  368. /* 功能码 0x05 的 ADU 固定为 8 字节,必须检查两种合法线圈值;接受任意非零值
  369. * 会违反 Modbus 规范。 */
  370. if (rxLength != MODBUS_RTU_READ_REQUEST_LENGTH) {
  371. return MODBUS_REQUEST_ILLEGAL_VALUE;
  372. }
  373. address = ModbusReadU16(&rxBuffer[2U]);
  374. coilValue = ModbusReadU16(&rxBuffer[4U]);
  375. if (ModbusIsDataRangeValid(address, 1U) == 0U) {
  376. return MODBUS_REQUEST_ILLEGAL_ADDRESS;
  377. }
  378. if ((coilValue != MODBUS_COIL_ON_VALUE)
  379. && (coilValue != MODBUS_COIL_OFF_VALUE)) {
  380. return MODBUS_REQUEST_ILLEGAL_VALUE;
  381. }
  382. if ((isBroadcast == 0U)
  383. && (txCapacity < MODBUS_RTU_WRITE_RESPONSE_LENGTH)) {
  384. return MODBUS_REQUEST_RESPONSE_ERROR;
  385. }
  386. ModbusWriteCoilBit(slave, address, (uint8_t)(coilValue != 0U));
  387. if (isBroadcast != 0U) {
  388. *txLength = 0U;
  389. return MODBUS_REQUEST_OK;
  390. }
  391. /* 单播写响应必须完整回显请求(包括原 CRC)。广播写入会执行,但按规范不发送响应。 */
  392. for (index = 0U; index < rxLength; index++) {
  393. txBuffer[index] = rxBuffer[index];
  394. }
  395. *txLength = rxLength;
  396. return MODBUS_REQUEST_OK;
  397. }
  398. /**
  399. * @brief 处理写单个保持寄存器请求。
  400. * @param[in,out] slave 从站数据对象。
  401. * @param[in] rxBuffer 请求 ADU。
  402. * @param[in] rxLength 请求 ADU 长度。
  403. * @param[in] isBroadcast 广播请求时为 1,否则为 0。
  404. * @param[out] txBuffer 响应 ADU 缓冲区。
  405. * @param[in] txCapacity 响应 ADU 缓冲区容量。
  406. * @param[out] txLength 生成的响应长度。
  407. * @return 请求校验结果。
  408. */
  409. static MODBUS_REQUEST_RESULT ModbusProcessWriteSingleRegister(
  410. MODBUS_SLAVE *slave, const uint8_t *rxBuffer, uint16_t rxLength,
  411. uint8_t isBroadcast, uint8_t *txBuffer, uint16_t txCapacity,
  412. uint16_t *txLength)
  413. {
  414. uint16_t address;
  415. uint16_t index;
  416. if (rxLength != MODBUS_RTU_READ_REQUEST_LENGTH) {
  417. return MODBUS_REQUEST_ILLEGAL_VALUE;
  418. }
  419. address = ModbusReadU16(&rxBuffer[2U]);
  420. if (ModbusIsDataRangeValid(address, 1U) == 0U) {
  421. return MODBUS_REQUEST_ILLEGAL_ADDRESS;
  422. }
  423. if ((isBroadcast == 0U)
  424. && (txCapacity < MODBUS_RTU_WRITE_RESPONSE_LENGTH)) {
  425. return MODBUS_REQUEST_RESPONSE_ERROR;
  426. }
  427. slave->holdingRegisters[address] = ModbusReadU16(&rxBuffer[4U]);
  428. if (isBroadcast != 0U) {
  429. *txLength = 0U;
  430. return MODBUS_REQUEST_OK;
  431. }
  432. for (index = 0U; index < rxLength; index++) {
  433. txBuffer[index] = rxBuffer[index];
  434. }
  435. *txLength = rxLength;
  436. return MODBUS_REQUEST_OK;
  437. }
  438. /**
  439. * @brief 处理写多个线圈请求。
  440. * @param[in,out] slave 从站数据对象。
  441. * @param[in] rxBuffer 请求 ADU。
  442. * @param[in] rxLength 请求 ADU 长度。
  443. * @param[in] isBroadcast 广播请求时为 1,否则为 0。
  444. * @param[out] txBuffer 响应 ADU 缓冲区。
  445. * @param[in] txCapacity 响应 ADU 缓冲区容量。
  446. * @param[out] txLength 生成的响应长度。
  447. * @return 请求校验结果。
  448. */
  449. static MODBUS_REQUEST_RESULT ModbusProcessWriteMultipleCoils(
  450. MODBUS_SLAVE *slave, const uint8_t *rxBuffer, uint16_t rxLength,
  451. uint8_t isBroadcast, uint8_t *txBuffer, uint16_t txCapacity,
  452. uint16_t *txLength)
  453. {
  454. uint16_t address;
  455. uint16_t coilCount;
  456. uint16_t byteCount;
  457. uint16_t coilOffset;
  458. uint8_t coilValue;
  459. /* 读取数据或修改线圈前,先校验数量、字节数和总长度,防止畸形帧只修改请求范围
  460. * 的前一部分。 */
  461. if (rxLength < MODBUS_RTU_WRITE_REQUEST_BASE_LENGTH) {
  462. return MODBUS_REQUEST_ILLEGAL_VALUE;
  463. }
  464. address = ModbusReadU16(&rxBuffer[2U]);
  465. coilCount = ModbusReadU16(&rxBuffer[4U]);
  466. byteCount = rxBuffer[MODBUS_RTU_BYTE_COUNT_INDEX];
  467. if ((coilCount == 0U) || (coilCount > MODBUS_MAX_WRITE_COIL_COUNT)) {
  468. return MODBUS_REQUEST_ILLEGAL_VALUE;
  469. }
  470. if (byteCount != ((coilCount + 7U) / MODBUS_BITS_PER_BYTE)) {
  471. return MODBUS_REQUEST_ILLEGAL_VALUE;
  472. }
  473. if (rxLength != (MODBUS_RTU_WRITE_REQUEST_BASE_LENGTH + byteCount)) {
  474. return MODBUS_REQUEST_ILLEGAL_VALUE;
  475. }
  476. if (ModbusIsDataRangeValid(address, coilCount) == 0U) {
  477. return MODBUS_REQUEST_ILLEGAL_ADDRESS;
  478. }
  479. if ((isBroadcast == 0U)
  480. && (txCapacity < MODBUS_RTU_WRITE_RESPONSE_LENGTH)) {
  481. return MODBUS_REQUEST_RESPONSE_ERROR;
  482. }
  483. /* 请求数据区中的位采用与 0x01 相同的低位在前压缩方式。 */
  484. for (coilOffset = 0U; coilOffset < coilCount; coilOffset++) {
  485. coilValue = (uint8_t)((rxBuffer[MODBUS_RTU_WRITE_DATA_INDEX
  486. + (coilOffset / MODBUS_BITS_PER_BYTE)]
  487. >> (coilOffset % MODBUS_BITS_PER_BYTE)) & 0x01U);
  488. ModbusWriteCoilBit(slave, address + coilOffset, coilValue);
  489. }
  490. if (isBroadcast != 0U) {
  491. *txLength = 0U;
  492. return MODBUS_REQUEST_OK;
  493. }
  494. txBuffer[MODBUS_RTU_ADDRESS_INDEX] = slave->slaveAddress;
  495. txBuffer[MODBUS_RTU_FUNCTION_INDEX] = MODBUS_FC_WRITE_MULTIPLE_COILS;
  496. ModbusWriteU16(&txBuffer[2U], address);
  497. ModbusWriteU16(&txBuffer[4U], coilCount);
  498. *txLength = 6U;
  499. if (ModbusAppendCrc(txBuffer, txCapacity, txLength) != MODBUS_STATUS_OK) {
  500. return MODBUS_REQUEST_RESPONSE_ERROR;
  501. }
  502. return MODBUS_REQUEST_OK;
  503. }
  504. /**
  505. * @brief 处理写多个保持寄存器请求。
  506. * @param[in,out] slave 从站数据对象。
  507. * @param[in] rxBuffer 请求 ADU。
  508. * @param[in] rxLength 请求 ADU 长度。
  509. * @param[in] isBroadcast 广播请求时为 1,否则为 0。
  510. * @param[out] txBuffer 响应 ADU 缓冲区。
  511. * @param[in] txCapacity 响应 ADU 缓冲区容量。
  512. * @param[out] txLength 生成的响应长度。
  513. * @return 请求校验结果。
  514. */
  515. static MODBUS_REQUEST_RESULT ModbusProcessWriteMultipleRegisters(
  516. MODBUS_SLAVE *slave, const uint8_t *rxBuffer, uint16_t rxLength,
  517. uint8_t isBroadcast, uint8_t *txBuffer, uint16_t txCapacity,
  518. uint16_t *txLength)
  519. {
  520. uint16_t address;
  521. uint16_t registerCount;
  522. uint16_t byteCount;
  523. uint16_t registerOffset;
  524. /* 功能码 0x10 为每个寄存器携带一个大端序 16 位值。所有帧结构检查都在写入循环
  525. * 之前完成,避免部分写入。 */
  526. if (rxLength < MODBUS_RTU_WRITE_REQUEST_BASE_LENGTH) {
  527. return MODBUS_REQUEST_ILLEGAL_VALUE;
  528. }
  529. address = ModbusReadU16(&rxBuffer[2U]);
  530. registerCount = ModbusReadU16(&rxBuffer[4U]);
  531. byteCount = rxBuffer[MODBUS_RTU_BYTE_COUNT_INDEX];
  532. if ((registerCount == 0U)
  533. || (registerCount > MODBUS_MAX_WRITE_REGISTER_COUNT)) {
  534. return MODBUS_REQUEST_ILLEGAL_VALUE;
  535. }
  536. if (byteCount != (registerCount * 2U)) {
  537. return MODBUS_REQUEST_ILLEGAL_VALUE;
  538. }
  539. if (rxLength != (MODBUS_RTU_WRITE_REQUEST_BASE_LENGTH + byteCount)) {
  540. return MODBUS_REQUEST_ILLEGAL_VALUE;
  541. }
  542. if (ModbusIsDataRangeValid(address, registerCount) == 0U) {
  543. return MODBUS_REQUEST_ILLEGAL_ADDRESS;
  544. }
  545. if ((isBroadcast == 0U)
  546. && (txCapacity < MODBUS_RTU_WRITE_RESPONSE_LENGTH)) {
  547. return MODBUS_REQUEST_RESPONSE_ERROR;
  548. }
  549. for (registerOffset = 0U; registerOffset < registerCount;
  550. registerOffset++) {
  551. slave->holdingRegisters[address + registerOffset] = ModbusReadU16(
  552. &rxBuffer[MODBUS_RTU_WRITE_DATA_INDEX + (registerOffset * 2U)]);
  553. }
  554. if (isBroadcast != 0U) {
  555. *txLength = 0U;
  556. return MODBUS_REQUEST_OK;
  557. }
  558. txBuffer[MODBUS_RTU_ADDRESS_INDEX] = slave->slaveAddress;
  559. txBuffer[MODBUS_RTU_FUNCTION_INDEX] = MODBUS_FC_WRITE_MULTIPLE_REGS;
  560. ModbusWriteU16(&txBuffer[2U], address);
  561. ModbusWriteU16(&txBuffer[4U], registerCount);
  562. *txLength = 6U;
  563. if (ModbusAppendCrc(txBuffer, txCapacity, txLength) != MODBUS_STATUS_OK) {
  564. return MODBUS_REQUEST_RESPONSE_ERROR;
  565. }
  566. return MODBUS_REQUEST_OK;
  567. }
  568. /**
  569. * @brief 构建私有功能码 0x41 的扩展寄存器读取响应。
  570. * @note 该接口只接受地址 0x00010000 和数量 1,响应数据为一个 16 位值。
  571. */
  572. static MODBUS_REQUEST_RESULT ModbusBuildReadExtendedRegistersResponse(
  573. const MODBUS_SLAVE *slave, uint32_t startAddress,
  574. uint16_t registerCount, uint8_t *txBuffer, uint16_t txCapacity,
  575. uint16_t *txLength)
  576. {
  577. MODBUS_STATUS status;
  578. if (registerCount != 1U) {
  579. return MODBUS_REQUEST_ILLEGAL_VALUE;
  580. }
  581. if (ModbusIsExtendedRegisterAddressValid(slave, startAddress) == 0U) {
  582. return MODBUS_REQUEST_ILLEGAL_ADDRESS;
  583. }
  584. if (txCapacity < 7U) {
  585. return MODBUS_REQUEST_RESPONSE_ERROR;
  586. }
  587. txBuffer[MODBUS_RTU_ADDRESS_INDEX] = slave->slaveAddress;
  588. txBuffer[MODBUS_RTU_FUNCTION_INDEX] = MODBUS_FC_READ_EXTENDED_HOLDING_REGS;
  589. txBuffer[MODBUS_RTU_DATA_INDEX] = 2U;
  590. ModbusWriteU16(&txBuffer[3U], *slave->extendedHoldingRegister);
  591. *txLength = 5U;
  592. status = ModbusAppendCrc(txBuffer, txCapacity, txLength);
  593. if (status != MODBUS_STATUS_OK) {
  594. return MODBUS_REQUEST_RESPONSE_ERROR;
  595. }
  596. return MODBUS_REQUEST_OK;
  597. }
  598. /**
  599. * @brief 执行私有功能码 0x42 的扩展寄存器写入并回显地址和值。
  600. * @note 本函数只更新 RAM 中的扩展值;RTC 持久化由应用层在成功处理后触发。
  601. */
  602. static MODBUS_REQUEST_RESULT ModbusBuildWriteExtendedRegisterResponse(
  603. MODBUS_SLAVE *slave, uint32_t startAddress, uint16_t registerValue,
  604. uint8_t *txBuffer, uint16_t txCapacity, uint16_t *txLength)
  605. {
  606. MODBUS_STATUS status;
  607. if (ModbusIsExtendedRegisterAddressValid(slave, startAddress) == 0U) {
  608. return MODBUS_REQUEST_ILLEGAL_ADDRESS;
  609. }
  610. if (txCapacity < 10U) {
  611. return MODBUS_REQUEST_RESPONSE_ERROR;
  612. }
  613. /* 私有扩展使用 32 位地址字段表示 0x00010000,不改变标准功能码的 16 位地址模型。 */
  614. *slave->extendedHoldingRegister = registerValue;
  615. txBuffer[MODBUS_RTU_ADDRESS_INDEX] = slave->slaveAddress;
  616. txBuffer[MODBUS_RTU_FUNCTION_INDEX] = MODBUS_FC_WRITE_EXTENDED_HOLDING_REGS;
  617. ModbusWriteU32(&txBuffer[2U], startAddress);
  618. ModbusWriteU16(&txBuffer[6U], registerValue);
  619. *txLength = 8U;
  620. status = ModbusAppendCrc(txBuffer, txCapacity, txLength);
  621. if (status != MODBUS_STATUS_OK) {
  622. return MODBUS_REQUEST_RESPONSE_ERROR;
  623. }
  624. return MODBUS_REQUEST_OK;
  625. }
  626. /**
  627. * @brief 构建私有功能码 0x43 的连续奇数地址读取响应。
  628. * @note 数量必须为 1 至 123 的奇数。偶数起始基准地址自动上调到下一奇数,
  629. * 例如起始地址 0 依次返回地址 1、3、5 等的寄存器值。
  630. */
  631. static MODBUS_REQUEST_RESULT ModbusBuildReadOddAddressRegistersResponse(
  632. const MODBUS_SLAVE *slave, uint16_t startAddress,
  633. uint16_t registerCount, uint8_t *txBuffer, uint16_t txCapacity,
  634. uint16_t *txLength)
  635. {
  636. MODBUS_STATUS status;
  637. uint16_t byteCount;
  638. uint16_t firstOddAddress;
  639. uint16_t readAddress;
  640. uint16_t registerOffset;
  641. if ((registerCount == 0U)
  642. || (registerCount > MODBUS_MAX_ODD_COUNT_REGISTER_COUNT)
  643. || ((registerCount & 0x0001U) == 0U)) {
  644. return MODBUS_REQUEST_ILLEGAL_VALUE;
  645. }
  646. /* 请求数量表示返回的奇数地址寄存器个数,而不是连续地址跨度。从偶数地址开始时
  647. * 会跳过该地址,从下一个奇数地址开始;后续每个地址递增 2。 */
  648. firstOddAddress = (uint16_t)(startAddress | 0x0001U);
  649. if ((firstOddAddress > MODBUS_DATA_ADDRESS_MAX)
  650. || (registerCount
  651. > (((MODBUS_DATA_ADDRESS_MAX - firstOddAddress) / 2U) + 1U))) {
  652. return MODBUS_REQUEST_ILLEGAL_ADDRESS;
  653. }
  654. byteCount = registerCount * 2U;
  655. if (txCapacity < (3U + byteCount + MODBUS_RTU_CRC_LENGTH)) {
  656. return MODBUS_REQUEST_RESPONSE_ERROR;
  657. }
  658. txBuffer[MODBUS_RTU_ADDRESS_INDEX] = slave->slaveAddress;
  659. txBuffer[MODBUS_RTU_FUNCTION_INDEX] = MODBUS_FC_READ_ODD_COUNT_REGS;
  660. txBuffer[MODBUS_RTU_DATA_INDEX] = (uint8_t)byteCount;
  661. for (registerOffset = 0U; registerOffset < registerCount;
  662. registerOffset++) {
  663. readAddress = (uint16_t)(firstOddAddress + (registerOffset * 2U));
  664. ModbusWriteU16(&txBuffer[3U + (registerOffset * 2U)],
  665. slave->holdingRegisters[readAddress]);
  666. }
  667. *txLength = 3U + byteCount;
  668. status = ModbusAppendCrc(txBuffer, txCapacity, txLength);
  669. if (status != MODBUS_STATUS_OK) {
  670. return MODBUS_REQUEST_RESPONSE_ERROR;
  671. }
  672. return MODBUS_REQUEST_OK;
  673. }
  674. /**
  675. * @brief 计算 Modbus RTU 的 CRC16 校验值。
  676. * @param[in] buffer 不包含 CRC 字段的数据缓冲区。
  677. * @param[in] length 数据缓冲区长度,单位为字节。
  678. * @return CRC16 校验值,发送时低字节在前。
  679. */
  680. uint16_t ModbusCrc16(const uint8_t *buffer, uint16_t length)
  681. {
  682. uint16_t crcValue;
  683. uint16_t byteIndex;
  684. uint8_t bitIndex;
  685. if (buffer == NULL) {
  686. return 0U;
  687. }
  688. /* Modbus CRC-16 使用初值 0xFFFF 和反射多项式 0xA001。按最低有效位优先处理,
  689. * 与 RTU 总线字节序一致,也不依赖 MCU 本身的字节序。 */
  690. crcValue = MODBUS_RTU_CRC_INIT;
  691. for (byteIndex = 0U; byteIndex < length; byteIndex++) {
  692. crcValue ^= (uint16_t)buffer[byteIndex];
  693. for (bitIndex = 0U; bitIndex < MODBUS_BITS_PER_BYTE; bitIndex++) {
  694. if ((crcValue & 0x0001U) != 0U) {
  695. crcValue = (crcValue >> 1U) ^ MODBUS_RTU_CRC_POLYNOMIAL;
  696. }
  697. else {
  698. crcValue >>= 1U;
  699. }
  700. }
  701. }
  702. return crcValue;
  703. }
  704. /**
  705. * @brief 初始化全部从站数据点并配置从站地址。
  706. * @param[out] slave 待初始化的从站数据对象。
  707. * @param[in] slaveAddress 范围为 1 至 247 的单播从站地址。
  708. * @return 成功返回 MODBUS_STATUS_OK,否则返回错误状态。
  709. */
  710. MODBUS_STATUS ModbusSlaveInit(
  711. MODBUS_SLAVE *slave,
  712. uint8_t slaveAddress,
  713. volatile uint16_t *holdingRegisterStorage,
  714. volatile uint8_t *coilBitStorage)
  715. {
  716. uint16_t registerIndex;
  717. uint16_t coilByteIndex;
  718. uint16_t coilAddress;
  719. if ((slave == NULL) || (holdingRegisterStorage == NULL)
  720. || (coilBitStorage == NULL)) {
  721. return MODBUS_STATUS_INVALID_ARGUMENT;
  722. }
  723. if (ModbusIsUnicastAddress(slaveAddress) == 0U) {
  724. return MODBUS_STATUS_INVALID_ARGUMENT;
  725. }
  726. slave->slaveAddress = slaveAddress;
  727. slave->holdingRegisters = holdingRegisterStorage;
  728. slave->coilStorage = coilBitStorage;
  729. slave->extendedHoldingRegister = NULL;
  730. /* 启动时清空完整的普通数据模型。扩展值单独初始化,因为随后可能从 RTC 备份
  731. * 寄存器恢复。 */
  732. for (registerIndex = 0U; registerIndex < MODBUS_DATA_POINT_COUNT;
  733. registerIndex++) {
  734. slave->holdingRegisters[registerIndex] = 0U;
  735. }
  736. for (coilByteIndex = 0U; coilByteIndex < MODBUS_COIL_STORAGE_SIZE;
  737. coilByteIndex++) {
  738. slave->coilStorage[coilByteIndex] = 0U;
  739. }
  740. /* 线圈按位压缩存储,初始置位线圈 0x0 至 0x2。 */
  741. for (coilAddress = 0U; coilAddress < MODBUS_INITIAL_ON_COIL_COUNT;
  742. coilAddress++) {
  743. ModbusWriteCoilBit(slave, coilAddress, 1U);
  744. }
  745. slave->holdingRegisters[10] = 0x0000;
  746. slave->holdingRegisters[11] = 0x0001;
  747. slave->holdingRegisters[12] = 0x0010;
  748. slave->holdingRegisters[13] = 0x0011;
  749. slave->holdingRegisters[14] = 0x0100;
  750. slave->holdingRegisters[15] = 0x0110;
  751. return MODBUS_STATUS_OK;
  752. }
  753. MODBUS_STATUS ModbusConfigureExtendedHoldingRegister(
  754. MODBUS_SLAVE *slave, volatile uint16_t *storage)
  755. {
  756. if ((slave == NULL) || (storage == NULL)) {
  757. return MODBUS_STATUS_INVALID_ARGUMENT;
  758. }
  759. slave->extendedHoldingRegister = storage;
  760. /* 首次启动的默认值;AppTaskModbus 随后可能用 RTC 备份值覆盖它。 */
  761. /* 只有不存在有效 RTC 备份时才使用这个默认值。 */
  762. *slave->extendedHoldingRegister = 0x1000U;
  763. return MODBUS_STATUS_OK;
  764. }
  765. /**
  766. * @brief 从从站数据模型中读取线圈值。
  767. * @param[in] slave 从站数据对象。
  768. * @param[in] address 范围为 0 至 0x270F 的线圈地址。
  769. * @param[out] value 线圈值,0 表示关闭,1 表示开启。
  770. * @return 成功返回 MODBUS_STATUS_OK,否则返回错误状态。
  771. */
  772. MODBUS_STATUS ModbusGetCoil(const MODBUS_SLAVE *slave, uint16_t address,
  773. uint8_t *value)
  774. {
  775. if ((slave == NULL) || (value == NULL)) {
  776. return MODBUS_STATUS_INVALID_ARGUMENT;
  777. }
  778. if (ModbusIsDataRangeValid(address, 1U) == 0U) {
  779. return MODBUS_STATUS_FRAME_ERROR;
  780. }
  781. *value = ModbusReadCoilBit(slave, address);
  782. return MODBUS_STATUS_OK;
  783. }
  784. /**
  785. * @brief 向从站数据模型中写入线圈值。
  786. * @param[in,out] slave 从站数据对象。
  787. * @param[in] address 范围为 0 至 0x270F 的线圈地址。
  788. * @param[in] value 线圈值,0 表示关闭,非 0 表示开启。
  789. * @return 成功返回 MODBUS_STATUS_OK,否则返回错误状态。
  790. */
  791. MODBUS_STATUS ModbusSetCoil(MODBUS_SLAVE *slave, uint16_t address,
  792. uint8_t value)
  793. {
  794. if (slave == NULL) {
  795. return MODBUS_STATUS_INVALID_ARGUMENT;
  796. }
  797. if (ModbusIsDataRangeValid(address, 1U) == 0U) {
  798. return MODBUS_STATUS_FRAME_ERROR;
  799. }
  800. ModbusWriteCoilBit(slave, address, value);
  801. return MODBUS_STATUS_OK;
  802. }
  803. /**
  804. * @brief 从从站数据模型中读取保持寄存器值。
  805. * @param[in] slave 从站数据对象。
  806. * @param[in] address 范围为 0 至 0x270F 的保持寄存器地址。
  807. * @param[out] value 保持寄存器值。
  808. * @return 成功返回 MODBUS_STATUS_OK,否则返回错误状态。
  809. */
  810. MODBUS_STATUS ModbusGetHoldingRegister(const MODBUS_SLAVE *slave,
  811. uint16_t address, uint16_t *value)
  812. {
  813. if ((slave == NULL) || (value == NULL)) {
  814. return MODBUS_STATUS_INVALID_ARGUMENT;
  815. }
  816. if (ModbusIsDataRangeValid(address, 1U) == 0U) {
  817. return MODBUS_STATUS_FRAME_ERROR;
  818. }
  819. *value = slave->holdingRegisters[address];
  820. return MODBUS_STATUS_OK;
  821. }
  822. /**
  823. * @brief 向从站数据模型中写入保持寄存器值。
  824. * @param[in,out] slave 从站数据对象。
  825. * @param[in] address 范围为 0 至 0x270F 的保持寄存器地址。
  826. * @param[in] value 保持寄存器值。
  827. * @return 成功返回 MODBUS_STATUS_OK,否则返回错误状态。
  828. */
  829. MODBUS_STATUS ModbusSetHoldingRegister(MODBUS_SLAVE *slave,
  830. uint16_t address, uint16_t value)
  831. {
  832. if (slave == NULL) {
  833. return MODBUS_STATUS_INVALID_ARGUMENT;
  834. }
  835. if (ModbusIsDataRangeValid(address, 1U) == 0U) {
  836. return MODBUS_STATUS_FRAME_ERROR;
  837. }
  838. slave->holdingRegisters[address] = value;
  839. return MODBUS_STATUS_OK;
  840. }
  841. /**
  842. * @brief 解析 Modbus RTU 请求并生成从站响应。
  843. * @param[in,out] slave 从站数据对象。
  844. * @param[in] rxBuffer 包含 CRC 的完整请求 ADU。
  845. * @param[in] rxLength 请求 ADU 长度,单位为字节。
  846. * @param[out] txBuffer 响应 ADU 缓冲区。
  847. * @param[in] txCapacity 响应 ADU 缓冲区容量,单位为字节。
  848. * @param[out] txLength 生成的响应长度,单位为字节。
  849. * @return 请求处理状态;广播写入请求不生成响应。
  850. * @note 处理顺序固定为参数、帧长度、CRC、站号、功能码和字段校验,
  851. * 因而 CRC 错误或非本机帧绝不会写入数据区。
  852. */
  853. MODBUS_STATUS ModbusProcessFrame(MODBUS_SLAVE *slave,
  854. const uint8_t *rxBuffer, uint16_t rxLength,
  855. uint8_t *txBuffer, uint16_t txCapacity,
  856. uint16_t *txLength)
  857. {
  858. MODBUS_STATUS status;
  859. MODBUS_REQUEST_RESULT requestResult;
  860. uint8_t requestAddress;
  861. uint8_t functionCode;
  862. uint8_t isBroadcast;
  863. uint16_t startAddress;
  864. uint16_t dataCount;
  865. uint16_t registerValue;
  866. uint32_t extendedAddress;
  867. if ((slave == NULL) || (rxBuffer == NULL) || (txBuffer == NULL)
  868. || (txLength == NULL)) {
  869. return MODBUS_STATUS_INVALID_ARGUMENT;
  870. }
  871. /* 始终从空响应开始。被忽略的帧、CRC 错误帧和广播写入都必须保持 txLength 为 0。 */
  872. *txLength = 0U;
  873. if (rxLength > MODBUS_RTU_ADU_MAX_LENGTH) {
  874. return MODBUS_STATUS_FRAME_ERROR;
  875. }
  876. /* 校验顺序是有意设计的:先拒绝格式错误或 CRC 错误的帧,再检查地址,最后才访问
  877. * 数据模型。 */
  878. status = ModbusValidateCrc(rxBuffer, rxLength);
  879. if (status != MODBUS_STATUS_OK) {
  880. return status;
  881. }
  882. requestAddress = rxBuffer[MODBUS_RTU_ADDRESS_INDEX];
  883. if ((requestAddress != slave->slaveAddress)
  884. && (requestAddress != MODBUS_BROADCAST_ADDRESS)) {
  885. return MODBUS_STATUS_IGNORED;
  886. }
  887. functionCode = rxBuffer[MODBUS_RTU_FUNCTION_INDEX];
  888. if ((functionCode & MODBUS_RTU_EXCEPTION_MASK) != 0U) {
  889. return MODBUS_STATUS_FRAME_ERROR;
  890. }
  891. /* Modbus RTU 的广播地址 0 只允许写入。广播读取和私有读取扩展会被忽略且不响应。 */
  892. isBroadcast = (uint8_t)(requestAddress == MODBUS_BROADCAST_ADDRESS);
  893. requestResult = MODBUS_REQUEST_OK;
  894. /* 每个处理函数负责校验自身的固定/可变帧格式和数据边界,然后返回内部结果,
  895. * 由下面的逻辑映射为标准异常响应。 */
  896. switch (functionCode) {
  897. case MODBUS_FC_READ_COILS:
  898. if (isBroadcast != 0U) {
  899. return MODBUS_STATUS_IGNORED;
  900. }
  901. if (rxLength != MODBUS_RTU_READ_REQUEST_LENGTH) {
  902. requestResult = MODBUS_REQUEST_ILLEGAL_VALUE;
  903. break;
  904. }
  905. startAddress = ModbusReadU16(&rxBuffer[2U]);
  906. dataCount = ModbusReadU16(&rxBuffer[4U]);
  907. requestResult = ModbusBuildReadCoilsResponse(
  908. slave, startAddress, dataCount, txBuffer, txCapacity, txLength);
  909. break;
  910. case MODBUS_FC_READ_HOLDING_REGS:
  911. if (isBroadcast != 0U) {
  912. return MODBUS_STATUS_IGNORED;
  913. }
  914. if (rxLength != MODBUS_RTU_READ_REQUEST_LENGTH) {
  915. requestResult = MODBUS_REQUEST_ILLEGAL_VALUE;
  916. break;
  917. }
  918. startAddress = ModbusReadU16(&rxBuffer[2U]);
  919. dataCount = ModbusReadU16(&rxBuffer[4U]);
  920. requestResult = ModbusBuildReadRegistersResponse(
  921. slave, startAddress, dataCount, txBuffer, txCapacity, txLength);
  922. break;
  923. case MODBUS_FC_READ_EXTENDED_HOLDING_REGS:
  924. if (isBroadcast != 0U) {
  925. return MODBUS_STATUS_IGNORED;
  926. }
  927. if (rxLength != 10U) {
  928. requestResult = MODBUS_REQUEST_ILLEGAL_VALUE;
  929. break;
  930. }
  931. extendedAddress = ModbusReadU32(&rxBuffer[2U]);
  932. dataCount = ModbusReadU16(&rxBuffer[6U]);
  933. requestResult = ModbusBuildReadExtendedRegistersResponse(
  934. slave, extendedAddress, dataCount, txBuffer, txCapacity,
  935. txLength);
  936. break;
  937. case MODBUS_FC_WRITE_EXTENDED_HOLDING_REGS:
  938. if (isBroadcast != 0U) {
  939. return MODBUS_STATUS_IGNORED;
  940. }
  941. if (rxLength != 10U) {
  942. requestResult = MODBUS_REQUEST_ILLEGAL_VALUE;
  943. break;
  944. }
  945. extendedAddress = ModbusReadU32(&rxBuffer[2U]);
  946. registerValue = ModbusReadU16(&rxBuffer[6U]);
  947. requestResult = ModbusBuildWriteExtendedRegisterResponse(
  948. slave, extendedAddress, registerValue, txBuffer, txCapacity,
  949. txLength);
  950. break;
  951. case MODBUS_FC_READ_ODD_COUNT_REGS:
  952. if (isBroadcast != 0U) {
  953. return MODBUS_STATUS_IGNORED;
  954. }
  955. if (rxLength != MODBUS_RTU_READ_REQUEST_LENGTH) {
  956. requestResult = MODBUS_REQUEST_ILLEGAL_VALUE;
  957. break;
  958. }
  959. startAddress = ModbusReadU16(&rxBuffer[2U]);
  960. dataCount = ModbusReadU16(&rxBuffer[4U]);
  961. requestResult = ModbusBuildReadOddAddressRegistersResponse(
  962. slave, startAddress, dataCount, txBuffer, txCapacity, txLength);
  963. break;
  964. case MODBUS_FC_WRITE_SINGLE_COIL:
  965. requestResult = ModbusProcessWriteSingleCoil(
  966. slave, rxBuffer, rxLength, isBroadcast, txBuffer, txCapacity,
  967. txLength);
  968. break;
  969. case MODBUS_FC_WRITE_SINGLE_REG:
  970. requestResult = ModbusProcessWriteSingleRegister(
  971. slave, rxBuffer, rxLength, isBroadcast, txBuffer, txCapacity,
  972. txLength);
  973. break;
  974. case MODBUS_FC_WRITE_MULTIPLE_COILS:
  975. requestResult = ModbusProcessWriteMultipleCoils(
  976. slave, rxBuffer, rxLength, isBroadcast, txBuffer, txCapacity,
  977. txLength);
  978. break;
  979. case MODBUS_FC_WRITE_MULTIPLE_REGS:
  980. requestResult = ModbusProcessWriteMultipleRegisters(
  981. slave, rxBuffer, rxLength, isBroadcast, txBuffer, txCapacity,
  982. txLength);
  983. break;
  984. default:
  985. if (isBroadcast != 0U) {
  986. return MODBUS_STATUS_IGNORED;
  987. }
  988. return ModbusBuildException(slave->slaveAddress, functionCode,
  989. MODBUS_EXCEPTION_ILLEGAL_FUNC,
  990. txBuffer, txCapacity, txLength);
  991. }
  992. /* 响应缓冲区或内部处理失败不是协议异常,应该报告给应用层,而不能伪造一帧
  993. * 发送到总线上的异常响应。 */
  994. if (requestResult == MODBUS_REQUEST_RESPONSE_ERROR) {
  995. return MODBUS_STATUS_FRAME_ERROR;
  996. }
  997. if (requestResult != MODBUS_REQUEST_OK) {
  998. if (isBroadcast != 0U) {
  999. return MODBUS_STATUS_IGNORED;
  1000. }
  1001. return ModbusBuildException(slave->slaveAddress, functionCode,
  1002. (uint8_t)requestResult, txBuffer,
  1003. txCapacity, txLength);
  1004. }
  1005. if (isBroadcast != 0U) {
  1006. return MODBUS_STATUS_BROADCAST;
  1007. }
  1008. return MODBUS_STATUS_OK;
  1009. }