Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 
 

1170 righe
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. return MODBUS_REQUEST_ILLEGAL_VALUE;
  644. }
  645. /* 请求数量表示返回的奇数地址寄存器个数,而不是连续地址跨度。从偶数地址开始时
  646. * 会跳过该地址,从下一个奇数地址开始;后续每个地址递增 2。 */
  647. firstOddAddress = (uint16_t)(startAddress | 0x0001U);
  648. if ((firstOddAddress > MODBUS_DATA_ADDRESS_MAX)
  649. || (registerCount
  650. > (((MODBUS_DATA_ADDRESS_MAX - firstOddAddress) / 2U) + 1U))) {
  651. return MODBUS_REQUEST_ILLEGAL_ADDRESS;
  652. }
  653. byteCount = registerCount * 2U;
  654. if (txCapacity < (3U + byteCount + MODBUS_RTU_CRC_LENGTH)) {
  655. return MODBUS_REQUEST_RESPONSE_ERROR;
  656. }
  657. txBuffer[MODBUS_RTU_ADDRESS_INDEX] = slave->slaveAddress;
  658. txBuffer[MODBUS_RTU_FUNCTION_INDEX] = MODBUS_FC_READ_ODD_COUNT_REGS;
  659. txBuffer[MODBUS_RTU_DATA_INDEX] = (uint8_t)byteCount;
  660. for (registerOffset = 0U; registerOffset < registerCount;
  661. registerOffset++) {
  662. readAddress = (uint16_t)(firstOddAddress + (registerOffset * 2U));
  663. ModbusWriteU16(&txBuffer[3U + (registerOffset * 2U)],
  664. slave->holdingRegisters[readAddress]);
  665. }
  666. *txLength = 3U + byteCount;
  667. status = ModbusAppendCrc(txBuffer, txCapacity, txLength);
  668. if (status != MODBUS_STATUS_OK) {
  669. return MODBUS_REQUEST_RESPONSE_ERROR;
  670. }
  671. return MODBUS_REQUEST_OK;
  672. }
  673. /**
  674. * @brief 计算 Modbus RTU 的 CRC16 校验值。
  675. * @param[in] buffer 不包含 CRC 字段的数据缓冲区。
  676. * @param[in] length 数据缓冲区长度,单位为字节。
  677. * @return CRC16 校验值,发送时低字节在前。
  678. */
  679. uint16_t ModbusCrc16(const uint8_t *buffer, uint16_t length)
  680. {
  681. uint16_t crcValue;
  682. uint16_t byteIndex;
  683. uint8_t bitIndex;
  684. if (buffer == NULL) {
  685. return 0U;
  686. }
  687. /* Modbus CRC-16 使用初值 0xFFFF 和反射多项式 0xA001。按最低有效位优先处理,
  688. * 与 RTU 总线字节序一致,也不依赖 MCU 本身的字节序。 */
  689. crcValue = MODBUS_RTU_CRC_INIT;
  690. for (byteIndex = 0U; byteIndex < length; byteIndex++) {
  691. crcValue ^= (uint16_t)buffer[byteIndex];
  692. for (bitIndex = 0U; bitIndex < MODBUS_BITS_PER_BYTE; bitIndex++) {
  693. if ((crcValue & 0x0001U) != 0U) {
  694. crcValue = (crcValue >> 1U) ^ MODBUS_RTU_CRC_POLYNOMIAL;
  695. }
  696. else {
  697. crcValue >>= 1U;
  698. }
  699. }
  700. }
  701. return crcValue;
  702. }
  703. /**
  704. * @brief 初始化全部从站数据点并配置从站地址。
  705. * @param[out] slave 待初始化的从站数据对象。
  706. * @param[in] slaveAddress 范围为 1 至 247 的单播从站地址。
  707. * @return 成功返回 MODBUS_STATUS_OK,否则返回错误状态。
  708. */
  709. MODBUS_STATUS ModbusSlaveInit(
  710. MODBUS_SLAVE *slave,
  711. uint8_t slaveAddress,
  712. volatile uint16_t *holdingRegisterStorage,
  713. volatile uint8_t *coilBitStorage)
  714. {
  715. uint16_t registerIndex;
  716. uint16_t coilByteIndex;
  717. uint16_t coilAddress;
  718. if ((slave == NULL) || (holdingRegisterStorage == NULL)
  719. || (coilBitStorage == NULL)) {
  720. return MODBUS_STATUS_INVALID_ARGUMENT;
  721. }
  722. if (ModbusIsUnicastAddress(slaveAddress) == 0U) {
  723. return MODBUS_STATUS_INVALID_ARGUMENT;
  724. }
  725. slave->slaveAddress = slaveAddress;
  726. slave->holdingRegisters = holdingRegisterStorage;
  727. slave->coilStorage = coilBitStorage;
  728. slave->extendedHoldingRegister = NULL;
  729. /* 启动时清空完整的普通数据模型。扩展值单独初始化,因为随后可能从 RTC 备份
  730. * 寄存器恢复。 */
  731. for (registerIndex = 0U; registerIndex < MODBUS_DATA_POINT_COUNT;
  732. registerIndex++) {
  733. slave->holdingRegisters[registerIndex] = 0U;
  734. }
  735. for (coilByteIndex = 0U; coilByteIndex < MODBUS_COIL_STORAGE_SIZE;
  736. coilByteIndex++) {
  737. slave->coilStorage[coilByteIndex] = 0U;
  738. }
  739. /* 线圈按位压缩存储,初始置位线圈 0x0 至 0x2。 */
  740. for (coilAddress = 0U; coilAddress < MODBUS_INITIAL_ON_COIL_COUNT;
  741. coilAddress++) {
  742. ModbusWriteCoilBit(slave, coilAddress, 1U);
  743. }
  744. slave->holdingRegisters[10] = 0x0000;
  745. slave->holdingRegisters[11] = 0x0001;
  746. slave->holdingRegisters[12] = 0x0010;
  747. slave->holdingRegisters[13] = 0x0011;
  748. slave->holdingRegisters[14] = 0x0100;
  749. slave->holdingRegisters[15] = 0x0110;
  750. return MODBUS_STATUS_OK;
  751. }
  752. MODBUS_STATUS ModbusConfigureExtendedHoldingRegister(
  753. MODBUS_SLAVE *slave, volatile uint16_t *storage)
  754. {
  755. if ((slave == NULL) || (storage == NULL)) {
  756. return MODBUS_STATUS_INVALID_ARGUMENT;
  757. }
  758. slave->extendedHoldingRegister = storage;
  759. /* 首次启动的默认值;AppTaskModbus 随后可能用 RTC 备份值覆盖它。 */
  760. /* 只有不存在有效 RTC 备份时才使用这个默认值。 */
  761. *slave->extendedHoldingRegister = 0x1000U;
  762. return MODBUS_STATUS_OK;
  763. }
  764. /**
  765. * @brief 从从站数据模型中读取线圈值。
  766. * @param[in] slave 从站数据对象。
  767. * @param[in] address 范围为 0 至 0x270F 的线圈地址。
  768. * @param[out] value 线圈值,0 表示关闭,1 表示开启。
  769. * @return 成功返回 MODBUS_STATUS_OK,否则返回错误状态。
  770. */
  771. MODBUS_STATUS ModbusGetCoil(const MODBUS_SLAVE *slave, uint16_t address,
  772. uint8_t *value)
  773. {
  774. if ((slave == NULL) || (value == NULL)) {
  775. return MODBUS_STATUS_INVALID_ARGUMENT;
  776. }
  777. if (ModbusIsDataRangeValid(address, 1U) == 0U) {
  778. return MODBUS_STATUS_FRAME_ERROR;
  779. }
  780. *value = ModbusReadCoilBit(slave, address);
  781. return MODBUS_STATUS_OK;
  782. }
  783. /**
  784. * @brief 向从站数据模型中写入线圈值。
  785. * @param[in,out] slave 从站数据对象。
  786. * @param[in] address 范围为 0 至 0x270F 的线圈地址。
  787. * @param[in] value 线圈值,0 表示关闭,非 0 表示开启。
  788. * @return 成功返回 MODBUS_STATUS_OK,否则返回错误状态。
  789. */
  790. MODBUS_STATUS ModbusSetCoil(MODBUS_SLAVE *slave, uint16_t address,
  791. uint8_t value)
  792. {
  793. if (slave == NULL) {
  794. return MODBUS_STATUS_INVALID_ARGUMENT;
  795. }
  796. if (ModbusIsDataRangeValid(address, 1U) == 0U) {
  797. return MODBUS_STATUS_FRAME_ERROR;
  798. }
  799. ModbusWriteCoilBit(slave, address, value);
  800. return MODBUS_STATUS_OK;
  801. }
  802. /**
  803. * @brief 从从站数据模型中读取保持寄存器值。
  804. * @param[in] slave 从站数据对象。
  805. * @param[in] address 范围为 0 至 0x270F 的保持寄存器地址。
  806. * @param[out] value 保持寄存器值。
  807. * @return 成功返回 MODBUS_STATUS_OK,否则返回错误状态。
  808. */
  809. MODBUS_STATUS ModbusGetHoldingRegister(const MODBUS_SLAVE *slave,
  810. uint16_t address, uint16_t *value)
  811. {
  812. if ((slave == NULL) || (value == NULL)) {
  813. return MODBUS_STATUS_INVALID_ARGUMENT;
  814. }
  815. if (ModbusIsDataRangeValid(address, 1U) == 0U) {
  816. return MODBUS_STATUS_FRAME_ERROR;
  817. }
  818. *value = slave->holdingRegisters[address];
  819. return MODBUS_STATUS_OK;
  820. }
  821. /**
  822. * @brief 向从站数据模型中写入保持寄存器值。
  823. * @param[in,out] slave 从站数据对象。
  824. * @param[in] address 范围为 0 至 0x270F 的保持寄存器地址。
  825. * @param[in] value 保持寄存器值。
  826. * @return 成功返回 MODBUS_STATUS_OK,否则返回错误状态。
  827. */
  828. MODBUS_STATUS ModbusSetHoldingRegister(MODBUS_SLAVE *slave,
  829. uint16_t address, uint16_t value)
  830. {
  831. if (slave == NULL) {
  832. return MODBUS_STATUS_INVALID_ARGUMENT;
  833. }
  834. if (ModbusIsDataRangeValid(address, 1U) == 0U) {
  835. return MODBUS_STATUS_FRAME_ERROR;
  836. }
  837. slave->holdingRegisters[address] = value;
  838. return MODBUS_STATUS_OK;
  839. }
  840. /**
  841. * @brief 解析 Modbus RTU 请求并生成从站响应。
  842. * @param[in,out] slave 从站数据对象。
  843. * @param[in] rxBuffer 包含 CRC 的完整请求 ADU。
  844. * @param[in] rxLength 请求 ADU 长度,单位为字节。
  845. * @param[out] txBuffer 响应 ADU 缓冲区。
  846. * @param[in] txCapacity 响应 ADU 缓冲区容量,单位为字节。
  847. * @param[out] txLength 生成的响应长度,单位为字节。
  848. * @return 请求处理状态;广播写入请求不生成响应。
  849. * @note 处理顺序固定为参数、帧长度、CRC、站号、功能码和字段校验,
  850. * 因而 CRC 错误或非本机帧绝不会写入数据区。
  851. */
  852. MODBUS_STATUS ModbusProcessFrame(MODBUS_SLAVE *slave,
  853. const uint8_t *rxBuffer, uint16_t rxLength,
  854. uint8_t *txBuffer, uint16_t txCapacity,
  855. uint16_t *txLength)
  856. {
  857. MODBUS_STATUS status;
  858. MODBUS_REQUEST_RESULT requestResult;
  859. uint8_t requestAddress;
  860. uint8_t functionCode;
  861. uint8_t isBroadcast;
  862. uint16_t startAddress;
  863. uint16_t dataCount;
  864. uint16_t registerValue;
  865. uint32_t extendedAddress;
  866. if ((slave == NULL) || (rxBuffer == NULL) || (txBuffer == NULL)
  867. || (txLength == NULL)) {
  868. return MODBUS_STATUS_INVALID_ARGUMENT;
  869. }
  870. /* 始终从空响应开始。被忽略的帧、CRC 错误帧和广播写入都必须保持 txLength 为 0。 */
  871. *txLength = 0U;
  872. if (rxLength > MODBUS_RTU_ADU_MAX_LENGTH) {
  873. return MODBUS_STATUS_FRAME_ERROR;
  874. }
  875. /* 校验顺序是有意设计的:先拒绝格式错误或 CRC 错误的帧,再检查地址,最后才访问
  876. * 数据模型。 */
  877. status = ModbusValidateCrc(rxBuffer, rxLength);
  878. if (status != MODBUS_STATUS_OK) {
  879. return status;
  880. }
  881. requestAddress = rxBuffer[MODBUS_RTU_ADDRESS_INDEX];
  882. if ((requestAddress != slave->slaveAddress)
  883. && (requestAddress != MODBUS_BROADCAST_ADDRESS)) {
  884. return MODBUS_STATUS_IGNORED;
  885. }
  886. functionCode = rxBuffer[MODBUS_RTU_FUNCTION_INDEX];
  887. if ((functionCode & MODBUS_RTU_EXCEPTION_MASK) != 0U) {
  888. return MODBUS_STATUS_FRAME_ERROR;
  889. }
  890. /* Modbus RTU 的广播地址 0 只允许写入。广播读取和私有读取扩展会被忽略且不响应。 */
  891. isBroadcast = (uint8_t)(requestAddress == MODBUS_BROADCAST_ADDRESS);
  892. requestResult = MODBUS_REQUEST_OK;
  893. /* 每个处理函数负责校验自身的固定/可变帧格式和数据边界,然后返回内部结果,
  894. * 由下面的逻辑映射为标准异常响应。 */
  895. switch (functionCode) {
  896. case MODBUS_FC_READ_COILS:
  897. if (isBroadcast != 0U) {
  898. return MODBUS_STATUS_IGNORED;
  899. }
  900. if (rxLength != MODBUS_RTU_READ_REQUEST_LENGTH) {
  901. requestResult = MODBUS_REQUEST_ILLEGAL_VALUE;
  902. break;
  903. }
  904. startAddress = ModbusReadU16(&rxBuffer[2U]);
  905. dataCount = ModbusReadU16(&rxBuffer[4U]);
  906. requestResult = ModbusBuildReadCoilsResponse(
  907. slave, startAddress, dataCount, txBuffer, txCapacity, txLength);
  908. break;
  909. case MODBUS_FC_READ_HOLDING_REGS:
  910. if (isBroadcast != 0U) {
  911. return MODBUS_STATUS_IGNORED;
  912. }
  913. if (rxLength != MODBUS_RTU_READ_REQUEST_LENGTH) {
  914. requestResult = MODBUS_REQUEST_ILLEGAL_VALUE;
  915. break;
  916. }
  917. startAddress = ModbusReadU16(&rxBuffer[2U]);
  918. dataCount = ModbusReadU16(&rxBuffer[4U]);
  919. requestResult = ModbusBuildReadRegistersResponse(
  920. slave, startAddress, dataCount, txBuffer, txCapacity, txLength);
  921. break;
  922. case MODBUS_FC_READ_EXTENDED_HOLDING_REGS:
  923. if (isBroadcast != 0U) {
  924. return MODBUS_STATUS_IGNORED;
  925. }
  926. if (rxLength != 10U) {
  927. requestResult = MODBUS_REQUEST_ILLEGAL_VALUE;
  928. break;
  929. }
  930. extendedAddress = ModbusReadU32(&rxBuffer[2U]);
  931. dataCount = ModbusReadU16(&rxBuffer[6U]);
  932. requestResult = ModbusBuildReadExtendedRegistersResponse(
  933. slave, extendedAddress, dataCount, txBuffer, txCapacity,
  934. txLength);
  935. break;
  936. case MODBUS_FC_WRITE_EXTENDED_HOLDING_REGS:
  937. if (isBroadcast != 0U) {
  938. return MODBUS_STATUS_IGNORED;
  939. }
  940. if (rxLength != 10U) {
  941. requestResult = MODBUS_REQUEST_ILLEGAL_VALUE;
  942. break;
  943. }
  944. extendedAddress = ModbusReadU32(&rxBuffer[2U]);
  945. registerValue = ModbusReadU16(&rxBuffer[6U]);
  946. requestResult = ModbusBuildWriteExtendedRegisterResponse(
  947. slave, extendedAddress, registerValue, txBuffer, txCapacity,
  948. txLength);
  949. break;
  950. case MODBUS_FC_READ_ODD_COUNT_REGS:
  951. if (isBroadcast != 0U) {
  952. return MODBUS_STATUS_IGNORED;
  953. }
  954. if (rxLength != MODBUS_RTU_READ_REQUEST_LENGTH) {
  955. requestResult = MODBUS_REQUEST_ILLEGAL_VALUE;
  956. break;
  957. }
  958. startAddress = ModbusReadU16(&rxBuffer[2U]);
  959. dataCount = ModbusReadU16(&rxBuffer[4U]);
  960. requestResult = ModbusBuildReadOddAddressRegistersResponse(
  961. slave, startAddress, dataCount, txBuffer, txCapacity, txLength);
  962. break;
  963. case MODBUS_FC_WRITE_SINGLE_COIL:
  964. requestResult = ModbusProcessWriteSingleCoil(
  965. slave, rxBuffer, rxLength, isBroadcast, txBuffer, txCapacity,
  966. txLength);
  967. break;
  968. case MODBUS_FC_WRITE_SINGLE_REG:
  969. requestResult = ModbusProcessWriteSingleRegister(
  970. slave, rxBuffer, rxLength, isBroadcast, txBuffer, txCapacity,
  971. txLength);
  972. break;
  973. case MODBUS_FC_WRITE_MULTIPLE_COILS:
  974. requestResult = ModbusProcessWriteMultipleCoils(
  975. slave, rxBuffer, rxLength, isBroadcast, txBuffer, txCapacity,
  976. txLength);
  977. break;
  978. case MODBUS_FC_WRITE_MULTIPLE_REGS:
  979. requestResult = ModbusProcessWriteMultipleRegisters(
  980. slave, rxBuffer, rxLength, isBroadcast, txBuffer, txCapacity,
  981. txLength);
  982. break;
  983. default:
  984. if (isBroadcast != 0U) {
  985. return MODBUS_STATUS_IGNORED;
  986. }
  987. return ModbusBuildException(slave->slaveAddress, functionCode,
  988. MODBUS_EXCEPTION_ILLEGAL_FUNC,
  989. txBuffer, txCapacity, txLength);
  990. }
  991. /* 响应缓冲区或内部处理失败不是协议异常,应该报告给应用层,而不能伪造一帧
  992. * 发送到总线上的异常响应。 */
  993. if (requestResult == MODBUS_REQUEST_RESPONSE_ERROR) {
  994. return MODBUS_STATUS_FRAME_ERROR;
  995. }
  996. if (requestResult != MODBUS_REQUEST_OK) {
  997. if (isBroadcast != 0U) {
  998. return MODBUS_STATUS_IGNORED;
  999. }
  1000. return ModbusBuildException(slave->slaveAddress, functionCode,
  1001. (uint8_t)requestResult, txBuffer,
  1002. txCapacity, txLength);
  1003. }
  1004. if (isBroadcast != 0U) {
  1005. return MODBUS_STATUS_BROADCAST;
  1006. }
  1007. return MODBUS_STATUS_OK;
  1008. }