您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

797 行
22 KiB

  1. /**
  2. * @file modbus_rtu_slave.c
  3. * @brief Modbus RTU 从站协议处理模块。
  4. */
  5. #include "modbus_rtu_slave.h"
  6. #include <string.h>
  7. /** @brief Modbus RTU 最大 ADU 长度,单位为字节。 */
  8. #define MODBUS_RTU_ADU_SIZE_MAX (256U)
  9. /**
  10. * @brief 当前通信参数下使用的 RTU 帧间隔。
  11. *
  12. * 当前波特率为 115200 bit/s。Modbus 规范建议波特率高于
  13. * 19200 bit/s 时采用固定 1.75 ms 帧间隔,此处向上取整为 2 ms。
  14. */
  15. #define MODBUS_RTU_FRAME_GAP_MS (2U)
  16. #define MODBUS_BROADCAST_ADDRESS (0U)
  17. #define MODBUS_EX_ILLEGAL_FUNCTION (0x01U)
  18. #define MODBUS_EX_ILLEGAL_ADDRESS (0x02U)
  19. #define MODBUS_EX_ILLEGAL_VALUE (0x03U)
  20. #define MODBUS_READ_COILS_MAX (2000U)
  21. #define MODBUS_READ_REGS_MAX (125U)
  22. #define MODBUS_WRITE_COILS_MAX (1968U)
  23. #define MODBUS_WRITE_REGS_MAX (123U)
  24. #define MODBUS_COIL_VALUE_ON (0xFF00U)
  25. #define MODBUS_COIL_VALUE_OFF (0x0000U)
  26. static UART_HandleTypeDef *ModbusUart;
  27. static uint8_t ModbusSlaveAddress;
  28. /**
  29. * DMA 缓冲区只由 DMA 写入;帧缓冲区在接收回调中完成一次快照,
  30. * 随后由任务解析,避免 DMA 重启后覆盖尚未处理的数据。
  31. */
  32. static uint8_t ModbusRxDmaBuffer[MODBUS_RTU_ADU_SIZE_MAX];
  33. static uint8_t ModbusRxFrame[MODBUS_RTU_ADU_SIZE_MAX];
  34. static uint8_t ModbusTxFrame[MODBUS_RTU_ADU_SIZE_MAX];
  35. static volatile uint16_t ModbusRxFrameLength;
  36. static volatile uint8_t ModbusRxFrameReady;
  37. static volatile uint8_t ModbusTxBusy;
  38. static volatile uint32_t ModbusRxEventTick;
  39. static volatile uint32_t ModbusLastValidFrameTick;
  40. static volatile uint8_t ModbusHasReceivedValidFrame;
  41. /**
  42. * 10000 个保持寄存器占用 20000 字节;10000 个线圈按位存储,
  43. * 占用 1250 字节。
  44. */
  45. static uint16_t ModbusHoldingRegisters[MODBUS_MAP_ITEM_COUNT];
  46. static uint8_t ModbusCoils[(MODBUS_MAP_ITEM_COUNT + 7U) / 8U];
  47. volatile MODBUS_SLAVE_STATS ModbusSlaveStatistics;
  48. /**
  49. * @brief 计算 Modbus RTU CRC16 校验值。
  50. * @param[in] data 待校验数据。
  51. * @param[in] length 待校验数据长度。
  52. * @return CRC16 校验值。
  53. */
  54. static uint16_t ModbusCrc16(const uint8_t *data, uint16_t length)
  55. {
  56. uint16_t crc = 0xFFFFU;
  57. uint16_t index;
  58. uint8_t bit;
  59. for (index = 0U; index < length; index++)
  60. {
  61. crc ^= data[index];
  62. for (bit = 0U; bit < 8U; bit++)
  63. {
  64. if ((crc & 0x0001U) != 0U)
  65. {
  66. crc = (uint16_t)((crc >> 1U) ^ 0xA001U);
  67. }
  68. else
  69. {
  70. crc >>= 1U;
  71. }
  72. }
  73. }
  74. return crc;
  75. }
  76. /**
  77. * @brief 从网络字节序数据中读取一个 16 位无符号整数。
  78. * @param[in] data 至少包含两个字节的数据地址。
  79. * @return 转换后的 16 位无符号整数。
  80. */
  81. static uint16_t ModbusGetU16Be(const uint8_t *data)
  82. {
  83. return (uint16_t)(((uint16_t)data[0] << 8U) | data[1]);
  84. }
  85. /**
  86. * @brief 检查连续数据地址范围是否合法。
  87. * @param[in] start 起始地址。
  88. * @param[in] quantity 数据项数量。
  89. * @retval 1 地址范围合法。
  90. * @retval 0 地址范围非法。
  91. */
  92. static uint8_t ModbusAddressRangeIsValid(uint16_t start, uint16_t quantity)
  93. {
  94. if ((quantity == 0U) || (start >= MODBUS_MAP_ITEM_COUNT))
  95. {
  96. return 0U;
  97. }
  98. /* 先做减法再比较,避免 start + quantity 发生 16 位溢出。 */
  99. return (quantity <= (MODBUS_MAP_ITEM_COUNT - start)) ? 1U : 0U;
  100. }
  101. /**
  102. * @brief 读取已经确认地址合法的线圈。
  103. * @param[in] address 线圈地址。
  104. * @return 线圈状态,取值为 0 或 1。
  105. */
  106. static uint8_t ModbusCoilGetUnchecked(uint16_t address)
  107. {
  108. uint8_t mask = (uint8_t)(1U << (address & 0x0007U));
  109. return ((ModbusCoils[address >> 3U] & mask) != 0U) ? 1U : 0U;
  110. }
  111. /**
  112. * @brief 设置已经确认地址合法的线圈。
  113. * @param[in] address 线圈地址。
  114. * @param[in] state 0 表示复位,非 0 表示置位。
  115. */
  116. static void ModbusCoilSetUnchecked(uint16_t address, uint8_t state)
  117. {
  118. uint8_t mask = (uint8_t)(1U << (address & 0x0007U));
  119. if (state != 0U)
  120. {
  121. ModbusCoils[address >> 3U] |= mask;
  122. }
  123. else
  124. {
  125. ModbusCoils[address >> 3U] &= (uint8_t)(~mask);
  126. }
  127. }
  128. /**
  129. * @brief 启动 USART DMA 空闲接收。
  130. * @retval HAL_OK DMA 接收启动成功。
  131. * @retval HAL_BUSY 串口未配置或发送尚未结束。
  132. * @return 其他 HAL 状态表示 DMA 接收启动失败。
  133. */
  134. static HAL_StatusTypeDef ModbusStartReceive(void)
  135. {
  136. HAL_StatusTypeDef status;
  137. if (ModbusTxBusy != 0U)
  138. {
  139. return HAL_BUSY;
  140. }
  141. status = HAL_UARTEx_ReceiveToIdle_DMA(ModbusUart,
  142. ModbusRxDmaBuffer,
  143. sizeof(ModbusRxDmaBuffer));
  144. if ((status == HAL_OK) && (ModbusUart->hdmarx != NULL))
  145. {
  146. /*
  147. * 普通 Modbus 帧只应在 IDLE 或缓冲区满时交给应用。
  148. * 关闭 DMA 半传输中断,避免长帧在一半位置被误认为完整帧。
  149. */
  150. __HAL_DMA_DISABLE_IT(ModbusUart->hdmarx, DMA_IT_HT);
  151. }
  152. return status;
  153. }
  154. /**
  155. * @brief 在 RTU 帧末尾追加 CRC 低字节和高字节。
  156. * @param[in,out] frame 待追加 CRC 的帧缓冲区。
  157. * @param[in] payloadLength 不包含 CRC 的有效载荷长度。
  158. */
  159. static void ModbusAppendCrc(uint8_t *frame, uint16_t payloadLength)
  160. {
  161. uint16_t crc = ModbusCrc16(frame, payloadLength);
  162. /* Modbus RTU 在线路上传输 CRC 低字节在前、高字节在后。 */
  163. frame[payloadLength] = (uint8_t)(crc & 0x00FFU);
  164. frame[payloadLength + 1U] = (uint8_t)(crc >> 8U);
  165. }
  166. /**
  167. * @brief 构造 Modbus 异常响应。
  168. * @param[in] function 请求功能码。
  169. * @param[in] exception 异常码。
  170. * @return 异常响应 ADU 长度。
  171. */
  172. static uint16_t ModbusBuildException(uint8_t function, uint8_t exception)
  173. {
  174. ModbusTxFrame[0] = ModbusSlaveAddress;
  175. ModbusTxFrame[1] = (uint8_t)(function | 0x80U);
  176. ModbusTxFrame[2] = exception;
  177. ModbusAppendCrc(ModbusTxFrame, 3U);
  178. return 5U;
  179. }
  180. /**
  181. * @brief 处理读线圈功能码 0x01。
  182. * @param[in] request RTU 请求帧。
  183. * @param[in] requestLength 请求帧长度。
  184. * @return 待发送响应长度,异常请求返回异常响应长度。
  185. */
  186. static uint16_t ModbusProcessReadCoils(const uint8_t *request,
  187. uint16_t requestLength)
  188. {
  189. uint16_t start;
  190. uint16_t quantity;
  191. uint16_t index;
  192. uint8_t byteCount;
  193. if (requestLength != 8U)
  194. {
  195. ModbusSlaveStatistics.illegalValueCount++;
  196. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  197. }
  198. start = ModbusGetU16Be(&request[2]);
  199. quantity = ModbusGetU16Be(&request[4]);
  200. if ((quantity == 0U) || (quantity > MODBUS_READ_COILS_MAX))
  201. {
  202. ModbusSlaveStatistics.illegalValueCount++;
  203. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  204. }
  205. if (ModbusAddressRangeIsValid(start, quantity) == 0U)
  206. {
  207. ModbusSlaveStatistics.illegalAddressCount++;
  208. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  209. }
  210. byteCount = (uint8_t)((quantity + 7U) / 8U);
  211. ModbusTxFrame[0] = ModbusSlaveAddress;
  212. ModbusTxFrame[1] = 0X01;
  213. ModbusTxFrame[2] = byteCount;
  214. (void)memset(&ModbusTxFrame[3], 0, byteCount);
  215. /* Modbus 规定第一个线圈放在响应数据首字节的最低位。 */
  216. for (index = 0U; index < quantity; index++)
  217. {
  218. if (ModbusCoilGetUnchecked((uint16_t)(start + index)) != 0U)
  219. {
  220. ModbusTxFrame[3U + (index >> 3U)] |=
  221. (uint8_t)(1U << (index & 0x0007U));
  222. }
  223. }
  224. ModbusAppendCrc(ModbusTxFrame, (uint16_t)(3U + byteCount));
  225. return (uint16_t)(5U + byteCount);
  226. }
  227. /**
  228. * @brief 处理读保持寄存器功能码 0x03。
  229. * @param[in] request RTU 请求帧。
  230. * @param[in] requestLength 请求帧长度。
  231. * @return 待发送响应长度,异常请求返回异常响应长度。
  232. */
  233. static uint16_t ModbusProcessReadHolding(const uint8_t *request,
  234. uint16_t requestLength)
  235. {
  236. uint16_t start;
  237. uint16_t quantity;
  238. uint16_t index;
  239. uint16_t value;
  240. if (requestLength != 8U)
  241. {
  242. ModbusSlaveStatistics.illegalValueCount++;
  243. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  244. }
  245. start = ModbusGetU16Be(&request[2]);
  246. quantity = ModbusGetU16Be(&request[4]);
  247. if ((quantity == 0U) || (quantity > MODBUS_READ_REGS_MAX))
  248. {
  249. ModbusSlaveStatistics.illegalValueCount++;
  250. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  251. }
  252. if (ModbusAddressRangeIsValid(start, quantity) == 0U)
  253. {
  254. ModbusSlaveStatistics.illegalAddressCount++;
  255. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  256. }
  257. ModbusTxFrame[0] = ModbusSlaveAddress;
  258. ModbusTxFrame[1] = 0X03;
  259. ModbusTxFrame[2] = (uint8_t)(quantity * 2U);
  260. for (index = 0U; index < quantity; index++)
  261. {
  262. value = ModbusHoldingRegisters[start + index];
  263. ModbusTxFrame[3U + index * 2U] = (uint8_t)(value >> 8U);
  264. ModbusTxFrame[4U + index * 2U] = (uint8_t)(value & 0x00FFU);
  265. }
  266. ModbusAppendCrc(ModbusTxFrame, (uint16_t)(3U + quantity * 2U));
  267. return (uint16_t)(5U + quantity * 2U);
  268. }
  269. /**
  270. * @brief 处理写单个保持寄存器功能码 0x06。
  271. * @param[in] request RTU 请求帧。
  272. * @param[in] requestLength 请求帧长度。
  273. * @param[in] isBroadcast 非 0 表示当前请求为广播。
  274. * @return 单播响应长度;广播或无法响应时返回 0。
  275. */
  276. static uint16_t ModbusProcessWriteSingleRegister(
  277. const uint8_t *request,
  278. uint16_t requestLength,
  279. uint8_t isBroadcast)
  280. {
  281. uint16_t address;
  282. uint16_t value;
  283. if (requestLength != 8U)
  284. {
  285. ModbusSlaveStatistics.illegalValueCount++;
  286. return (isBroadcast != 0U) ? 0U :
  287. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  288. }
  289. address = ModbusGetU16Be(&request[2]);
  290. value = ModbusGetU16Be(&request[4]);
  291. if (address >= MODBUS_MAP_ITEM_COUNT)
  292. {
  293. ModbusSlaveStatistics.illegalAddressCount++;
  294. return (isBroadcast != 0U) ? 0U :
  295. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  296. }
  297. ModbusHoldingRegisters[address] = value;
  298. if (isBroadcast != 0U)
  299. {
  300. return 0U;
  301. }
  302. /* 0x06 的正常响应必须原样回显请求的前 6 个字节。 */
  303. (void)memcpy(ModbusTxFrame, request, 6U);
  304. ModbusAppendCrc(ModbusTxFrame, 6U);
  305. return 8U;
  306. }
  307. /**
  308. * @brief 处理写单个线圈功能码 0x05。
  309. * @param[in] request RTU 请求帧。
  310. * @param[in] requestLength 请求帧长度。
  311. * @param[in] isBroadcast 非 0 表示当前请求为广播。
  312. * @return 单播响应长度;广播或无法响应时返回 0。
  313. */
  314. static uint16_t ModbusProcessWriteSingleCoil(
  315. const uint8_t *request,
  316. uint16_t requestLength,
  317. uint8_t isBroadcast)
  318. {
  319. uint16_t address;
  320. uint16_t value;
  321. if (requestLength != 8U)
  322. {
  323. ModbusSlaveStatistics.illegalValueCount++;
  324. return (isBroadcast != 0U) ? 0U :
  325. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  326. }
  327. address = ModbusGetU16Be(&request[2]);
  328. value = ModbusGetU16Be(&request[4]);
  329. /*
  330. * 0x05 只接受 0xFF00(线圈置位)和 0x0000(线圈复位);
  331. * 其他数值属于非法数据值。
  332. */
  333. if ((value != MODBUS_COIL_VALUE_ON)
  334. && (value != MODBUS_COIL_VALUE_OFF))
  335. {
  336. ModbusSlaveStatistics.illegalValueCount++;
  337. return (isBroadcast != 0U) ? 0U :
  338. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  339. }
  340. if (address >= MODBUS_MAP_ITEM_COUNT)
  341. {
  342. ModbusSlaveStatistics.illegalAddressCount++;
  343. return (isBroadcast != 0U) ? 0U :
  344. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  345. }
  346. ModbusCoilSetUnchecked(
  347. address,
  348. (value == MODBUS_COIL_VALUE_ON) ? 1U : 0U);
  349. if (isBroadcast != 0U)
  350. {
  351. return 0U;
  352. }
  353. /* 0x05 正常应答原样回显请求的前 6 个字节,再追加 CRC。 */
  354. (void)memcpy(ModbusTxFrame, request, 6U);
  355. ModbusAppendCrc(ModbusTxFrame, 6U);
  356. return 8U;
  357. }
  358. /**
  359. * @brief 处理写多个线圈功能码 0x0F。
  360. * @param[in] request RTU 请求帧。
  361. * @param[in] requestLength 请求帧长度。
  362. * @param[in] isBroadcast 非 0 表示当前请求为广播。
  363. * @return 单播响应长度;广播或无法响应时返回 0。
  364. */
  365. static uint16_t ModbusProcessWriteMultipleCoils(
  366. const uint8_t *request,
  367. uint16_t requestLength,
  368. uint8_t isBroadcast)
  369. {
  370. uint16_t start;
  371. uint16_t quantity;
  372. uint16_t index;
  373. uint8_t byteCount;
  374. uint8_t expectedByteCount;
  375. if (requestLength < 9U)
  376. {
  377. ModbusSlaveStatistics.illegalValueCount++;
  378. return (isBroadcast != 0U) ? 0U :
  379. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  380. }
  381. start = ModbusGetU16Be(&request[2]);
  382. quantity = ModbusGetU16Be(&request[4]);
  383. byteCount = request[6];
  384. expectedByteCount = (uint8_t)((quantity + 7U) / 8U);
  385. if ((quantity == 0U)
  386. || (quantity > MODBUS_WRITE_COILS_MAX)
  387. || (byteCount != expectedByteCount)
  388. || (requestLength != (uint16_t)(9U + byteCount)))
  389. {
  390. ModbusSlaveStatistics.illegalValueCount++;
  391. return (isBroadcast != 0U) ? 0U :
  392. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  393. }
  394. if (ModbusAddressRangeIsValid(start, quantity) == 0U)
  395. {
  396. ModbusSlaveStatistics.illegalAddressCount++;
  397. return (isBroadcast != 0U) ? 0U :
  398. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  399. }
  400. for (index = 0U; index < quantity; index++)
  401. {
  402. uint8_t state;
  403. state = (uint8_t)((request[7U + (index >> 3U)]
  404. >> (index & 0x0007U))
  405. & 0x01U);
  406. ModbusCoilSetUnchecked((uint16_t)(start + index), state);
  407. }
  408. if (isBroadcast != 0U)
  409. {
  410. return 0U;
  411. }
  412. ModbusTxFrame[0] = ModbusSlaveAddress;
  413. ModbusTxFrame[1] = 0X0F;
  414. (void)memcpy(&ModbusTxFrame[2], &request[2], 4U);
  415. ModbusAppendCrc(ModbusTxFrame, 6U);
  416. return 8U;
  417. }
  418. /**
  419. * @brief 处理写多个保持寄存器功能码 0x10。
  420. * @param[in] request RTU 请求帧。
  421. * @param[in] requestLength 请求帧长度。
  422. * @param[in] isBroadcast 非 0 表示当前请求为广播。
  423. * @return 单播响应长度;广播或无法响应时返回 0。
  424. */
  425. static uint16_t ModbusProcessWriteMultipleRegisters(
  426. const uint8_t *request,
  427. uint16_t requestLength,
  428. uint8_t isBroadcast)
  429. {
  430. uint16_t start;
  431. uint16_t quantity;
  432. uint16_t index;
  433. uint8_t byteCount;
  434. if (requestLength < 9U)
  435. {
  436. ModbusSlaveStatistics.illegalValueCount++;
  437. return (isBroadcast != 0U) ? 0U :
  438. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  439. }
  440. start = ModbusGetU16Be(&request[2]);
  441. quantity = ModbusGetU16Be(&request[4]);
  442. byteCount = request[6];
  443. if ((quantity == 0U)
  444. || (quantity > MODBUS_WRITE_REGS_MAX)
  445. || (byteCount != (uint8_t)(quantity * 2U))
  446. || (requestLength != (uint16_t)(9U + byteCount)))
  447. {
  448. ModbusSlaveStatistics.illegalValueCount++;
  449. return (isBroadcast != 0U) ? 0U :
  450. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  451. }
  452. if (ModbusAddressRangeIsValid(start, quantity) == 0U)
  453. {
  454. ModbusSlaveStatistics.illegalAddressCount++;
  455. return (isBroadcast != 0U) ? 0U :
  456. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  457. }
  458. for (index = 0U; index < quantity; index++)
  459. {
  460. ModbusHoldingRegisters[start + index] =
  461. ModbusGetU16Be(&request[7U + index * 2U]);
  462. }
  463. if (isBroadcast != 0U)
  464. {
  465. return 0U;
  466. }
  467. ModbusTxFrame[0] = ModbusSlaveAddress;
  468. ModbusTxFrame[1] = 0X10;
  469. (void)memcpy(&ModbusTxFrame[2], &request[2], 4U);
  470. ModbusAppendCrc(ModbusTxFrame, 6U);
  471. return 8U;
  472. }
  473. /**
  474. * @brief 校验并分发一帧 Modbus RTU 请求。
  475. * @param[in] request RTU 请求帧。
  476. * @param[in] requestLength 请求帧长度。
  477. * @return 待发送响应长度;无需响应时返回 0。
  478. */
  479. static uint16_t ModbusProcessRequest(const uint8_t *request,
  480. uint16_t requestLength)
  481. {
  482. uint16_t calculatedCrc;
  483. uint16_t receivedCrc;
  484. uint8_t isBroadcast;
  485. if (requestLength < 4U)
  486. {
  487. return 0U;
  488. }
  489. calculatedCrc = ModbusCrc16(request, (uint16_t)(requestLength - 2U));
  490. receivedCrc =
  491. (uint16_t)(request[requestLength - 2U]
  492. | ((uint16_t)request[requestLength - 1U] << 8U));
  493. if (calculatedCrc != receivedCrc)
  494. {
  495. ModbusSlaveStatistics.crcErrorCount++;
  496. return 0U;
  497. }
  498. if ((request[0] != ModbusSlaveAddress)
  499. && (request[0] != MODBUS_BROADCAST_ADDRESS))
  500. {
  501. ModbusSlaveStatistics.ignoredAddressCount++;
  502. return 0U;
  503. }
  504. isBroadcast = (request[0] == MODBUS_BROADCAST_ADDRESS) ? 1U : 0U;
  505. ModbusSlaveStatistics.validFrameCount++;
  506. ModbusLastValidFrameTick = HAL_GetTick();
  507. ModbusHasReceivedValidFrame = 1U;
  508. switch (request[1])
  509. {
  510. case 0X01U://读线圈
  511. // 广播请求不允许读取,从站不作响应。
  512. return (isBroadcast != 0U) ? 0U :
  513. ModbusProcessReadCoils(request, requestLength);
  514. case 0X03U://读保持寄存器
  515. return (isBroadcast != 0U) ? 0U :
  516. ModbusProcessReadHolding(request, requestLength);
  517. case 0x05U://写单个线圈
  518. return ModbusProcessWriteSingleCoil(request,
  519. requestLength,
  520. isBroadcast);
  521. case 0x06U://写单个保持寄存器
  522. return ModbusProcessWriteSingleRegister(request,
  523. requestLength,
  524. isBroadcast);
  525. case 0x0FU://写多个线圈
  526. return ModbusProcessWriteMultipleCoils(request,
  527. requestLength,
  528. isBroadcast);
  529. case 0x10U://写多个保持寄存器
  530. return ModbusProcessWriteMultipleRegisters(request,
  531. requestLength,
  532. isBroadcast);
  533. default://未知功能码
  534. ModbusSlaveStatistics.illegalFunctionCount++;
  535. return (isBroadcast != 0U) ? 0U :
  536. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_FUNCTION);
  537. }
  538. }
  539. HAL_StatusTypeDef ModbusSlaveInit(UART_HandleTypeDef *huart,
  540. uint8_t slaveAddress)
  541. {
  542. ModbusUart = huart;
  543. ModbusSlaveAddress = slaveAddress;
  544. ModbusHoldingRegisters[HMI_REG_DEVICE_ID] = 0xF407U;
  545. return ModbusStartReceive();
  546. }
  547. void ModbusSlavePoll(void)
  548. {
  549. uint16_t responseLength;
  550. if ((ModbusRxFrameReady == 0U) || (ModbusTxBusy != 0U))
  551. {
  552. return;
  553. }
  554. responseLength = ModbusProcessRequest(ModbusRxFrame, ModbusRxFrameLength);
  555. if (responseLength > 0U)
  556. {
  557. ModbusTxBusy = 1U;
  558. if (HAL_UART_Transmit_DMA(ModbusUart,
  559. ModbusTxFrame,
  560. responseLength) == HAL_OK)
  561. {
  562. ModbusSlaveStatistics.txFrameCount++;
  563. }
  564. else
  565. {
  566. ModbusTxBusy = 0U;
  567. ModbusSlaveStatistics.uartErrorCount++;
  568. (void)ModbusStartReceive();//重启DMA接收
  569. }
  570. }
  571. else
  572. {
  573. (void)ModbusStartReceive();
  574. }
  575. /*
  576. * 当前帧;处理完成后再释放帧槽。
  577. */
  578. ModbusRxFrameReady = 0U;
  579. }
  580. void ModbusSlaveOnRxEvent(UART_HandleTypeDef *huart, uint16_t size)
  581. {
  582. ModbusSlaveStatistics.rxEventCount++;//串口接收事件计数
  583. if ((size > 0U) && (size <= MODBUS_RTU_ADU_SIZE_MAX))
  584. {
  585. if (ModbusRxFrameReady == 0U)
  586. {
  587. (void)memcpy(ModbusRxFrame, ModbusRxDmaBuffer, size);
  588. ModbusRxFrameLength = size;
  589. ModbusRxEventTick = HAL_GetTick();
  590. ModbusRxFrameReady = 1U;
  591. }
  592. else
  593. {
  594. //前一帧未处理完毕
  595. ModbusSlaveStatistics.droppedFrameCount++;
  596. }
  597. }
  598. else
  599. {
  600. //长度异常帧
  601. ModbusSlaveStatistics.droppedFrameCount++;
  602. (void)ModbusStartReceive();
  603. }
  604. }
  605. void ModbusSlaveOnTxComplete(UART_HandleTypeDef *huart)
  606. {
  607. if ((huart != ModbusUart) || (ModbusUart == NULL))
  608. {
  609. return;
  610. }
  611. ModbusTxBusy = 0U;
  612. (void)ModbusStartReceive();//重启DMA接收
  613. }
  614. void ModbusSlaveOnUartError(UART_HandleTypeDef *huart)
  615. {
  616. if ((huart != ModbusUart) || (ModbusUart == NULL))
  617. {
  618. return;
  619. }
  620. ModbusSlaveStatistics.uartErrorCount++;
  621. ModbusTxBusy = 0U;
  622. (void)HAL_UART_Abort(huart);//立即终止这个串口当前正在进行的发送和接收操作
  623. (void)ModbusStartReceive();//重启DMA接收
  624. }
  625. uint8_t ModbusSlaveSetHoldingRegister(uint16_t address, uint16_t value)
  626. {
  627. if (address >= MODBUS_MAP_ITEM_COUNT)
  628. {
  629. return 0U;
  630. }
  631. ModbusHoldingRegisters[address] = value;
  632. return 1U;
  633. }
  634. uint8_t ModbusSlaveGetHoldingRegister(uint16_t address, uint16_t *value)
  635. {
  636. if ((address >= MODBUS_MAP_ITEM_COUNT) || (value == NULL))
  637. {
  638. return 0U;
  639. }
  640. *value = ModbusHoldingRegisters[address];
  641. return 1U;
  642. }
  643. uint8_t ModbusSlaveSetCoil(uint16_t address, uint8_t state)
  644. {
  645. if (address >= MODBUS_MAP_ITEM_COUNT)
  646. {
  647. return 0U;
  648. }
  649. ModbusCoilSetUnchecked(address, state);
  650. return 1U;
  651. }
  652. uint8_t ModbusSlaveGetCoil(uint16_t address, uint8_t *state)
  653. {
  654. if ((address >= MODBUS_MAP_ITEM_COUNT) || (state == NULL))
  655. {
  656. return 0U;
  657. }
  658. *state = ModbusCoilGetUnchecked(address);
  659. return 1U;
  660. }
  661. uint8_t ModbusSlaveIsConnected(uint32_t timeoutMs)
  662. {
  663. if (ModbusHasReceivedValidFrame == 0U)
  664. {
  665. return 0U;
  666. }
  667. return ((HAL_GetTick() - ModbusLastValidFrameTick) <= timeoutMs)
  668. ? 1U
  669. : 0U;
  670. }