Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 
 

825 wiersze
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 停止当前 USART DMA 接收。
  156. */
  157. static void ModbusStopReceive(void)
  158. {
  159. if (ModbusUart != NULL)
  160. {
  161. (void)HAL_UART_AbortReceive(ModbusUart);
  162. }
  163. }
  164. /**
  165. * @brief 在 RTU 帧末尾追加 CRC 低字节和高字节。
  166. * @param[in,out] frame 待追加 CRC 的帧缓冲区。
  167. * @param[in] payloadLength 不包含 CRC 的有效载荷长度。
  168. */
  169. static void ModbusAppendCrc(uint8_t *frame, uint16_t payloadLength)
  170. {
  171. uint16_t crc = ModbusCrc16(frame, payloadLength);
  172. /* Modbus RTU 在线路上传输 CRC 低字节在前、高字节在后。 */
  173. frame[payloadLength] = (uint8_t)(crc & 0x00FFU);
  174. frame[payloadLength + 1U] = (uint8_t)(crc >> 8U);
  175. }
  176. /**
  177. * @brief 构造 Modbus 异常响应。
  178. * @param[in] function 请求功能码。
  179. * @param[in] exception 异常码。
  180. * @return 异常响应 ADU 长度。
  181. */
  182. static uint16_t ModbusBuildException(uint8_t function, uint8_t exception)
  183. {
  184. ModbusTxFrame[0] = ModbusSlaveAddress;
  185. ModbusTxFrame[1] = (uint8_t)(function | 0x80U);
  186. ModbusTxFrame[2] = exception;
  187. ModbusAppendCrc(ModbusTxFrame, 3U);
  188. return 5U;
  189. }
  190. /**
  191. * @brief 处理读线圈功能码 0x01。
  192. * @param[in] request RTU 请求帧。
  193. * @param[in] requestLength 请求帧长度。
  194. * @return 待发送响应长度,异常请求返回异常响应长度。
  195. */
  196. static uint16_t ModbusProcessReadCoils(const uint8_t *request,
  197. uint16_t requestLength)
  198. {
  199. uint16_t start;
  200. uint16_t quantity;
  201. uint16_t index;
  202. uint8_t byteCount;
  203. if (requestLength != 8U)
  204. {
  205. ModbusSlaveStatistics.illegalValueCount++;
  206. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  207. }
  208. start = ModbusGetU16Be(&request[2]);
  209. quantity = ModbusGetU16Be(&request[4]);
  210. if ((quantity == 0U) || (quantity > MODBUS_READ_COILS_MAX))
  211. {
  212. ModbusSlaveStatistics.illegalValueCount++;
  213. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  214. }
  215. if (ModbusAddressRangeIsValid(start, quantity) == 0U)
  216. {
  217. ModbusSlaveStatistics.illegalAddressCount++;
  218. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  219. }
  220. byteCount = (uint8_t)((quantity + 7U) / 8U);
  221. ModbusTxFrame[0] = ModbusSlaveAddress;
  222. ModbusTxFrame[1] = 0X01;
  223. ModbusTxFrame[2] = byteCount;
  224. (void)memset(&ModbusTxFrame[3], 0, byteCount);
  225. /* Modbus 规定第一个线圈放在响应数据首字节的最低位。 */
  226. for (index = 0U; index < quantity; index++)
  227. {
  228. if (ModbusCoilGetUnchecked((uint16_t)(start + index)) != 0U)
  229. {
  230. ModbusTxFrame[3U + (index >> 3U)] |=
  231. (uint8_t)(1U << (index & 0x0007U));
  232. }
  233. }
  234. ModbusAppendCrc(ModbusTxFrame, (uint16_t)(3U + byteCount));
  235. return (uint16_t)(5U + byteCount);
  236. }
  237. /**
  238. * @brief 处理读保持寄存器功能码 0x03。
  239. * @param[in] request RTU 请求帧。
  240. * @param[in] requestLength 请求帧长度。
  241. * @return 待发送响应长度,异常请求返回异常响应长度。
  242. */
  243. static uint16_t ModbusProcessReadHolding(const uint8_t *request,
  244. uint16_t requestLength)
  245. {
  246. uint16_t start;
  247. uint16_t quantity;
  248. uint16_t index;
  249. uint16_t value;
  250. if (requestLength != 8U)
  251. {
  252. ModbusSlaveStatistics.illegalValueCount++;
  253. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  254. }
  255. start = ModbusGetU16Be(&request[2]);
  256. quantity = ModbusGetU16Be(&request[4]);
  257. if ((quantity == 0U) || (quantity > MODBUS_READ_REGS_MAX))
  258. {
  259. ModbusSlaveStatistics.illegalValueCount++;
  260. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  261. }
  262. if (ModbusAddressRangeIsValid(start, quantity) == 0U)
  263. {
  264. ModbusSlaveStatistics.illegalAddressCount++;
  265. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  266. }
  267. ModbusTxFrame[0] = ModbusSlaveAddress;
  268. ModbusTxFrame[1] = 0X03;
  269. ModbusTxFrame[2] = (uint8_t)(quantity * 2U);
  270. for (index = 0U; index < quantity; index++)
  271. {
  272. value = ModbusHoldingRegisters[start + index];
  273. ModbusTxFrame[3U + index * 2U] = (uint8_t)(value >> 8U);
  274. ModbusTxFrame[4U + index * 2U] = (uint8_t)(value & 0x00FFU);
  275. }
  276. ModbusAppendCrc(ModbusTxFrame, (uint16_t)(3U + quantity * 2U));
  277. return (uint16_t)(5U + quantity * 2U);
  278. }
  279. /**
  280. * @brief 处理写单个保持寄存器功能码 0x06。
  281. * @param[in] request RTU 请求帧。
  282. * @param[in] requestLength 请求帧长度。
  283. * @param[in] isBroadcast 非 0 表示当前请求为广播。
  284. * @return 单播响应长度;广播或无法响应时返回 0。
  285. */
  286. static uint16_t ModbusProcessWriteSingleRegister(
  287. const uint8_t *request,
  288. uint16_t requestLength,
  289. uint8_t isBroadcast)
  290. {
  291. uint16_t address;
  292. uint16_t value;
  293. if (requestLength != 8U)
  294. {
  295. ModbusSlaveStatistics.illegalValueCount++;
  296. return (isBroadcast != 0U) ? 0U :
  297. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  298. }
  299. address = ModbusGetU16Be(&request[2]);
  300. value = ModbusGetU16Be(&request[4]);
  301. if (address >= MODBUS_MAP_ITEM_COUNT)
  302. {
  303. ModbusSlaveStatistics.illegalAddressCount++;
  304. return (isBroadcast != 0U) ? 0U :
  305. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  306. }
  307. ModbusHoldingRegisters[address] = value;
  308. if (isBroadcast != 0U)
  309. {
  310. return 0U;
  311. }
  312. /* 0x06 的正常响应必须原样回显请求的前 6 个字节。 */
  313. (void)memcpy(ModbusTxFrame, request, 6U);
  314. ModbusAppendCrc(ModbusTxFrame, 6U);
  315. return 8U;
  316. }
  317. /**
  318. * @brief 处理写单个线圈功能码 0x05。
  319. * @param[in] request RTU 请求帧。
  320. * @param[in] requestLength 请求帧长度。
  321. * @param[in] isBroadcast 非 0 表示当前请求为广播。
  322. * @return 单播响应长度;广播或无法响应时返回 0。
  323. */
  324. static uint16_t ModbusProcessWriteSingleCoil(
  325. const uint8_t *request,
  326. uint16_t requestLength,
  327. uint8_t isBroadcast)
  328. {
  329. uint16_t address;
  330. uint16_t value;
  331. if (requestLength != 8U)
  332. {
  333. ModbusSlaveStatistics.illegalValueCount++;
  334. return (isBroadcast != 0U) ? 0U :
  335. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  336. }
  337. address = ModbusGetU16Be(&request[2]);
  338. value = ModbusGetU16Be(&request[4]);
  339. /*
  340. * 0x05 只接受 0xFF00(线圈置位)和 0x0000(线圈复位);
  341. * 其他数值属于非法数据值。
  342. */
  343. if ((value != MODBUS_COIL_VALUE_ON)
  344. && (value != MODBUS_COIL_VALUE_OFF))
  345. {
  346. ModbusSlaveStatistics.illegalValueCount++;
  347. return (isBroadcast != 0U) ? 0U :
  348. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  349. }
  350. if (address >= MODBUS_MAP_ITEM_COUNT)
  351. {
  352. ModbusSlaveStatistics.illegalAddressCount++;
  353. return (isBroadcast != 0U) ? 0U :
  354. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  355. }
  356. ModbusCoilSetUnchecked(
  357. address,
  358. (value == MODBUS_COIL_VALUE_ON) ? 1U : 0U);
  359. if (isBroadcast != 0U)
  360. {
  361. return 0U;
  362. }
  363. /* 0x05 正常应答原样回显请求的前 6 个字节,再追加 CRC。 */
  364. (void)memcpy(ModbusTxFrame, request, 6U);
  365. ModbusAppendCrc(ModbusTxFrame, 6U);
  366. return 8U;
  367. }
  368. /**
  369. * @brief 处理写多个线圈功能码 0x0F。
  370. * @param[in] request RTU 请求帧。
  371. * @param[in] requestLength 请求帧长度。
  372. * @param[in] isBroadcast 非 0 表示当前请求为广播。
  373. * @return 单播响应长度;广播或无法响应时返回 0。
  374. */
  375. static uint16_t ModbusProcessWriteMultipleCoils(
  376. const uint8_t *request,
  377. uint16_t requestLength,
  378. uint8_t isBroadcast)
  379. {
  380. uint16_t start;
  381. uint16_t quantity;
  382. uint16_t index;
  383. uint8_t byteCount;
  384. uint8_t expectedByteCount;
  385. if (requestLength < 9U)
  386. {
  387. ModbusSlaveStatistics.illegalValueCount++;
  388. return (isBroadcast != 0U) ? 0U :
  389. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  390. }
  391. start = ModbusGetU16Be(&request[2]);
  392. quantity = ModbusGetU16Be(&request[4]);
  393. byteCount = request[6];
  394. expectedByteCount = (uint8_t)((quantity + 7U) / 8U);
  395. if ((quantity == 0U)
  396. || (quantity > MODBUS_WRITE_COILS_MAX)
  397. || (byteCount != expectedByteCount)
  398. || (requestLength != (uint16_t)(9U + byteCount)))
  399. {
  400. ModbusSlaveStatistics.illegalValueCount++;
  401. return (isBroadcast != 0U) ? 0U :
  402. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  403. }
  404. if (ModbusAddressRangeIsValid(start, quantity) == 0U)
  405. {
  406. ModbusSlaveStatistics.illegalAddressCount++;
  407. return (isBroadcast != 0U) ? 0U :
  408. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  409. }
  410. for (index = 0U; index < quantity; index++)
  411. {
  412. uint8_t state;
  413. state = (uint8_t)((request[7U + (index >> 3U)]
  414. >> (index & 0x0007U))
  415. & 0x01U);
  416. ModbusCoilSetUnchecked((uint16_t)(start + index), state);
  417. }
  418. if (isBroadcast != 0U)
  419. {
  420. return 0U;
  421. }
  422. ModbusTxFrame[0] = ModbusSlaveAddress;
  423. ModbusTxFrame[1] = 0X0F;
  424. (void)memcpy(&ModbusTxFrame[2], &request[2], 4U);
  425. ModbusAppendCrc(ModbusTxFrame, 6U);
  426. return 8U;
  427. }
  428. /**
  429. * @brief 处理写多个保持寄存器功能码 0x10。
  430. * @param[in] request RTU 请求帧。
  431. * @param[in] requestLength 请求帧长度。
  432. * @param[in] isBroadcast 非 0 表示当前请求为广播。
  433. * @return 单播响应长度;广播或无法响应时返回 0。
  434. */
  435. static uint16_t ModbusProcessWriteMultipleRegisters(
  436. const uint8_t *request,
  437. uint16_t requestLength,
  438. uint8_t isBroadcast)
  439. {
  440. uint16_t start;
  441. uint16_t quantity;
  442. uint16_t index;
  443. uint8_t byteCount;
  444. if (requestLength < 9U)
  445. {
  446. ModbusSlaveStatistics.illegalValueCount++;
  447. return (isBroadcast != 0U) ? 0U :
  448. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  449. }
  450. start = ModbusGetU16Be(&request[2]);
  451. quantity = ModbusGetU16Be(&request[4]);
  452. byteCount = request[6];
  453. if ((quantity == 0U)
  454. || (quantity > MODBUS_WRITE_REGS_MAX)
  455. || (byteCount != (uint8_t)(quantity * 2U))
  456. || (requestLength != (uint16_t)(9U + byteCount)))
  457. {
  458. ModbusSlaveStatistics.illegalValueCount++;
  459. return (isBroadcast != 0U) ? 0U :
  460. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  461. }
  462. if (ModbusAddressRangeIsValid(start, quantity) == 0U)
  463. {
  464. ModbusSlaveStatistics.illegalAddressCount++;
  465. return (isBroadcast != 0U) ? 0U :
  466. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  467. }
  468. for (index = 0U; index < quantity; index++)
  469. {
  470. ModbusHoldingRegisters[start + index] =
  471. ModbusGetU16Be(&request[7U + index * 2U]);
  472. }
  473. if (isBroadcast != 0U)
  474. {
  475. return 0U;
  476. }
  477. ModbusTxFrame[0] = ModbusSlaveAddress;
  478. ModbusTxFrame[1] = 0X10;
  479. (void)memcpy(&ModbusTxFrame[2], &request[2], 4U);
  480. ModbusAppendCrc(ModbusTxFrame, 6U);
  481. return 8U;
  482. }
  483. /**
  484. * @brief 校验并分发一帧 Modbus RTU 请求。
  485. * @param[in] request RTU 请求帧。
  486. * @param[in] requestLength 请求帧长度。
  487. * @return 待发送响应长度;无需响应时返回 0。
  488. */
  489. static uint16_t ModbusProcessRequest(const uint8_t *request,
  490. uint16_t requestLength)
  491. {
  492. uint16_t calculatedCrc;
  493. uint16_t receivedCrc;
  494. uint8_t isBroadcast;
  495. if (requestLength < 4U)
  496. {
  497. return 0U;
  498. }
  499. calculatedCrc = ModbusCrc16(request, (uint16_t)(requestLength - 2U));
  500. receivedCrc =
  501. (uint16_t)(request[requestLength - 2U]
  502. | ((uint16_t)request[requestLength - 1U] << 8U));
  503. if (calculatedCrc != receivedCrc)
  504. {
  505. ModbusSlaveStatistics.crcErrorCount++;
  506. return 0U;
  507. }
  508. if ((request[0] != ModbusSlaveAddress)
  509. && (request[0] != MODBUS_BROADCAST_ADDRESS))
  510. {
  511. ModbusSlaveStatistics.ignoredAddressCount++;
  512. return 0U;
  513. }
  514. isBroadcast = (request[0] == MODBUS_BROADCAST_ADDRESS) ? 1U : 0U;
  515. ModbusSlaveStatistics.validFrameCount++;
  516. ModbusLastValidFrameTick = HAL_GetTick();
  517. ModbusHasReceivedValidFrame = 1U;
  518. switch (request[1])
  519. {
  520. case 0X01U://读线圈
  521. // 广播请求不允许读取,从站不作响应。
  522. return (isBroadcast != 0U) ? 0U :
  523. ModbusProcessReadCoils(request, requestLength);
  524. case 0X03U://读保持寄存器
  525. return (isBroadcast != 0U) ? 0U :
  526. ModbusProcessReadHolding(request, requestLength);
  527. case 0x05U://写单个线圈
  528. return ModbusProcessWriteSingleCoil(request,
  529. requestLength,
  530. isBroadcast);
  531. case 0x06U://写单个保持寄存器
  532. return ModbusProcessWriteSingleRegister(request,
  533. requestLength,
  534. isBroadcast);
  535. case 0x0FU://写多个线圈
  536. return ModbusProcessWriteMultipleCoils(request,
  537. requestLength,
  538. isBroadcast);
  539. case 0x10U://写多个保持寄存器
  540. return ModbusProcessWriteMultipleRegisters(request,
  541. requestLength,
  542. isBroadcast);
  543. default://未知功能码
  544. ModbusSlaveStatistics.illegalFunctionCount++;
  545. return (isBroadcast != 0U) ? 0U :
  546. ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_FUNCTION);
  547. }
  548. }
  549. HAL_StatusTypeDef ModbusSlaveInit(UART_HandleTypeDef *huart,
  550. uint8_t slaveAddress)
  551. {
  552. ModbusUart = huart;
  553. ModbusSlaveAddress = slaveAddress;
  554. ModbusHoldingRegisters[HMI_REG_DEVICE_ID] = 0xF407U;
  555. return ModbusStartReceive();
  556. }
  557. void ModbusSlavePoll(void)
  558. {
  559. uint16_t responseLength;
  560. if ((ModbusRxFrameReady == 0U) || (ModbusTxBusy != 0U))
  561. {
  562. return;
  563. }
  564. /*
  565. * IDLE 事件在约 1 个字符静默后产生;再等待到 2 ms,
  566. * 满足当前 115200 bit/s 下的 Modbus RTU 帧间隔要求。
  567. */
  568. // if ((HAL_GetTick() - ModbusRxEventTick) < MODBUS_RTU_FRAME_GAP_MS)
  569. // {
  570. // return;
  571. // }
  572. responseLength = ModbusProcessRequest(ModbusRxFrame, ModbusRxFrameLength);
  573. if (responseLength > 0U)
  574. {
  575. /*
  576. * 发送期间停止接收
  577. *
  578. */
  579. ModbusStopReceive();
  580. ModbusTxBusy = 1U;
  581. if (HAL_UART_Transmit_DMA(ModbusUart,
  582. ModbusTxFrame,
  583. responseLength) == HAL_OK)
  584. {
  585. ModbusSlaveStatistics.txFrameCount++;
  586. }
  587. else
  588. {
  589. ModbusTxBusy = 0U;
  590. ModbusSlaveStatistics.uartErrorCount++;
  591. (void)ModbusStartReceive();//重启DMA接收
  592. }
  593. }
  594. /*
  595. * 处理期间保持 ModbusRxFrameReady 为 1,使中断不会覆盖
  596. * 当前帧;处理完成后再释放帧槽。
  597. */
  598. ModbusRxFrameReady = 0U;
  599. }
  600. void ModbusSlaveOnRxEvent(UART_HandleTypeDef *huart, uint16_t size)
  601. {
  602. if ((huart != ModbusUart) || (ModbusUart == NULL))
  603. {
  604. return;
  605. }
  606. ModbusSlaveStatistics.rxEventCount++;//串口接收事件计数
  607. if ((size > 0U) && (size <= MODBUS_RTU_ADU_SIZE_MAX))
  608. {
  609. if (ModbusRxFrameReady == 0U)
  610. {
  611. (void)memcpy(ModbusRxFrame, ModbusRxDmaBuffer, size);
  612. ModbusRxFrameLength = size;
  613. ModbusRxEventTick = HAL_GetTick();
  614. ModbusRxFrameReady = 1U;
  615. }
  616. else
  617. {
  618. /* 单帧槽尚未处理完成,丢弃新帧而不覆盖旧帧。 */
  619. ModbusSlaveStatistics.droppedFrameCount++;
  620. }
  621. }
  622. else
  623. {
  624. ModbusSlaveStatistics.droppedFrameCount++;
  625. }
  626. /* DMA 普通模式在 IDLE 事件后停止,需要重新启动。 */
  627. if (ModbusTxBusy == 0U)
  628. {
  629. (void)ModbusStartReceive();
  630. }
  631. }
  632. void ModbusSlaveOnTxComplete(UART_HandleTypeDef *huart)
  633. {
  634. if ((huart != ModbusUart) || (ModbusUart == NULL))
  635. {
  636. return;
  637. }
  638. ModbusTxBusy = 0U;
  639. (void)ModbusStartReceive();//重启DMA接收
  640. }
  641. void ModbusSlaveOnUartError(UART_HandleTypeDef *huart)
  642. {
  643. if ((huart != ModbusUart) || (ModbusUart == NULL))
  644. {
  645. return;
  646. }
  647. ModbusSlaveStatistics.uartErrorCount++;
  648. ModbusTxBusy = 0U;
  649. (void)HAL_UART_Abort(huart);//立即终止这个串口当前正在进行的发送和接收操作
  650. (void)ModbusStartReceive();//重启DMA接收
  651. }
  652. uint8_t ModbusSlaveSetHoldingRegister(uint16_t address, uint16_t value)
  653. {
  654. if (address >= MODBUS_MAP_ITEM_COUNT)
  655. {
  656. return 0U;
  657. }
  658. ModbusHoldingRegisters[address] = value;
  659. return 1U;
  660. }
  661. uint8_t ModbusSlaveGetHoldingRegister(uint16_t address, uint16_t *value)
  662. {
  663. if ((address >= MODBUS_MAP_ITEM_COUNT) || (value == NULL))
  664. {
  665. return 0U;
  666. }
  667. *value = ModbusHoldingRegisters[address];
  668. return 1U;
  669. }
  670. uint8_t ModbusSlaveSetCoil(uint16_t address, uint8_t state)
  671. {
  672. if (address >= MODBUS_MAP_ITEM_COUNT)
  673. {
  674. return 0U;
  675. }
  676. ModbusCoilSetUnchecked(address, state);
  677. return 1U;
  678. }
  679. uint8_t ModbusSlaveGetCoil(uint16_t address, uint8_t *state)
  680. {
  681. if ((address >= MODBUS_MAP_ITEM_COUNT) || (state == NULL))
  682. {
  683. return 0U;
  684. }
  685. *state = ModbusCoilGetUnchecked(address);
  686. return 1U;
  687. }
  688. uint8_t ModbusSlaveIsConnected(uint32_t timeoutMs)
  689. {
  690. if (ModbusHasReceivedValidFrame == 0U)
  691. {
  692. return 0U;
  693. }
  694. return ((HAL_GetTick() - ModbusLastValidFrameTick) <= timeoutMs)
  695. ? 1U
  696. : 0U;
  697. }