Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 
 
 

1366 Zeilen
44 KiB

  1. #include "modbus_rtu_slave.h"
  2. #include "modbus_data_store.h"
  3. #include <string.h>
  4. #define MODBUS_RTU_ADU_SIZE_MAX (256U) // Modbus RTU 最大 ADU 长度,单位为字节
  5. #define MODBUS_RTU_T15_US (750UL) // 高波特率下固定T1.5时间,单位us
  6. #define MODBUS_RTU_T35_US (1750UL) // 高波特率下固定T3.5时间,单位us
  7. #define MODBUS_RTU_BITS_PER_CHAR (11UL) // 8E1包含11个传输位
  8. #define MODBUS_RTU_HIGH_BAUD_LIMIT (19200UL) // 高低波特率计算方式的分界值
  9. #define MODBUS_BROADCAST_ADDRESS (0U) // 广播地址
  10. #define MODBUS_EX_ILLEGAL_FUNCTION (0x01U) // 非法功能码的异常码
  11. #define MODBUS_EX_ILLEGAL_ADDRESS (0x02U) // 非法地址的异常码
  12. #define MODBUS_EX_ILLEGAL_VALUE (0x03U) // 非法数据的异常码
  13. #define MODBUS_READ_COILS_MAX (2000U) // 一次ADU最大线圈读取数量
  14. #define MODBUS_READ_REGS_MAX (125U) // 一次ADU最大寄存器读取数量
  15. #define MODBUS_WRITE_COILS_MAX (1968U) // 一次ADU最大线圈写入数量
  16. #define MODBUS_WRITE_REGS_MAX (123U) // 一次ADU最大寄存器写入数量
  17. #define MODBUS_COIL_VALUE_ON (0xFF00U)
  18. #define MODBUS_COIL_VALUE_OFF (0x0000U)
  19. static UART_HandleTypeDef *ModbusUart;
  20. static uint8_t ModbusSlaveAddress;
  21. static HAL_StatusTypeDef ModbusStartReceive(void);
  22. /*最近一次字节尾到字节头的间隔周期数*/
  23. static volatile uint32_t ModbusLastInterFrameGapCycles;
  24. /**
  25. * DMA 缓冲区只由 DMA 写入;帧缓冲区在接收回调中完成一次快照,
  26. * 随后由任务解析,避免 DMA 重启后覆盖尚未处理的数据
  27. */
  28. static uint8_t ModbusRxDmaBuffer[MODBUS_RTU_ADU_SIZE_MAX];
  29. /* 保存被UART IDLE事件分开的DMA片段,达到T3.5后再提交解析 */
  30. static uint8_t ModbusRxAssemblyBuffer[MODBUS_RTU_ADU_SIZE_MAX];
  31. static uint8_t ModbusRxFrame[MODBUS_RTU_ADU_SIZE_MAX];
  32. static uint8_t ModbusTxFrame[MODBUS_RTU_ADU_SIZE_MAX];
  33. static uint16_t ModbusWriteRegisterScratch[MODBUS_WRITE_REGS_MAX];
  34. /* D100~D120 上一次已保存的值,用于检测数据是否变化 */
  35. static uint16_t ModbusRetainedSnapshot[MODBUS_RETAINED_D_COUNT];
  36. static volatile uint16_t ModbusRxFrameLength;
  37. static volatile uint8_t ModbusRxFrameReady;
  38. static volatile uint8_t ModbusTxBusy;
  39. static volatile uint16_t ModbusRxAssemblyLength; // 当前拼帧长度
  40. static volatile uint8_t ModbusRxAssemblyInvalid; // 帧内间隔超过T1.5时置1
  41. static volatile uint32_t ModbusRxLastByteCycle; // 上一片段末字节结束时刻
  42. static uint32_t ModbusRtuT15Cycles; // T1.5对应的CPU周期数
  43. static uint32_t ModbusRtuT35Cycles; // T3.5对应的CPU周期数
  44. static uint32_t ModbusRtuCharCycles; // 一个UART字符对应的CPU周期数
  45. static volatile uint32_t ModbusLastValidFrameTick;
  46. static volatile uint8_t ModbusHasReceivedValidFrame;
  47. static volatile uint32_t ModbusReceiveRestartAttemptCount;
  48. static volatile uint32_t ModbusReceiveRestartFailureCount;
  49. static volatile uint32_t ModbusNextReceiveRetryTick;
  50. static volatile uint32_t ModbusLastUartErrorCode;
  51. static volatile uint8_t ModbusLastReceiveStartStatus = (uint8_t)HAL_ERROR;
  52. static volatile MODBUS_BACKUP_DATA *ModbusBackupData =
  53. (volatile MODBUS_BACKUP_DATA *)BKPSRAM_BASE;
  54. /* Word and bit images are owned by modbus_data_store.c. Standard Modbus
  55. * coils are the protocol view of the PLC M device space. */
  56. volatile MODBUS_SLAVE_STATS ModbusSlaveStatistics;
  57. /**
  58. * @brief 计算 Modbus RTU CRC16 校验值
  59. * @param[in] data 待校验数据
  60. * @param[in] length 待校验数据长度
  61. * @return CRC16 校验值
  62. */
  63. static uint16_t ModbusCrc16(const uint8_t *data, uint16_t length)
  64. {
  65. uint16_t crc = 0xFFFFU;
  66. uint16_t index;
  67. uint8_t bit;
  68. for (index = 0U; index < length; index++)
  69. {
  70. crc ^= data[index];
  71. for (bit = 0U; bit < 8U; bit++)
  72. {
  73. if ((crc & 0x0001U) != 0U)
  74. {
  75. crc = (uint16_t)((crc >> 1U) ^ 0xA001U);
  76. }
  77. else
  78. {
  79. crc >>= 1U;
  80. }
  81. }
  82. }
  83. return crc;
  84. }
  85. /**
  86. * @brief 提取一个 16 位无符号整数
  87. * @param[in] data 两个字节的数据地址
  88. * @return 转换后的 16 位无符号整数
  89. */
  90. static uint16_t ModbusGetU16Be(const uint8_t *data)
  91. {
  92. return (uint16_t)(((uint16_t)data[0] << 8U) | data[1]);
  93. }
  94. /**
  95. * @brief 提取一个 24 位无符号整数
  96. * @param[in] data 3个字节的数据地址
  97. * @return 转换后的 24 位无符号整数
  98. */
  99. static uint32_t ModbusGetU24Be(const uint8_t *data)
  100. {
  101. return (uint32_t)(((uint32_t)data[0] << 16U) | ((uint32_t)data[1] << 8U)
  102. | (uint32_t)data[2]);
  103. }
  104. /**
  105. * @brief 检查连续数据地址范围是否合法
  106. * @param[in] start 起始地址
  107. * @param[in] quantity 数据项数量
  108. */
  109. static uint8_t ModbusAddressRangeIsValid(uint16_t start, uint16_t quantity)
  110. {
  111. if (start >= MODBUS_MAP_ITEM_COUNT)
  112. {
  113. return 0U;
  114. }
  115. // 先做减法再比较,避免溢出
  116. return (quantity <= (MODBUS_MAP_ITEM_COUNT - start)) ? 1U : 0U;
  117. }
  118. /**
  119. * @brief 读取已经确认地址合法的线圈
  120. * @param[in] address 线圈地址
  121. * @return 线圈状态,取值为 0 或 1
  122. */
  123. static uint8_t ModbusCoilGetUnchecked(uint16_t address)
  124. {
  125. uint8_t value = 0U;
  126. (void)ModbusDataReadBit(MODBUS_BIT_DEVICE_M, address, &value);
  127. return value;
  128. }
  129. /**
  130. * @brief 设置已经确认地址合法的线圈
  131. * @param[in] address 线圈地址
  132. * @param[in] state 0 表示复位,非 0 表示置位
  133. */
  134. static void ModbusCoilSetUnchecked(uint16_t address, uint8_t state)
  135. {
  136. (void)ModbusDataWriteBit(MODBUS_BIT_DEVICE_M, address, state);
  137. }
  138. /**
  139. * @brief 初始化用于RTU帧间隔测量的DWT周期计数器
  140. * @param[in] baudRate 当前串口波特率
  141. */
  142. static void ModbusRtuTimingInit(uint32_t baudRate)
  143. {
  144. uint64_t coreClock;
  145. /* 开启DWT周期计数器,CYCCNT每经过一个CPU时钟周期自动加1 */
  146. CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
  147. DWT->CYCCNT = 0U;
  148. DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
  149. coreClock = SystemCoreClock;
  150. /* 一个字符周期数=CPU频率*每字符位数/波特率 */
  151. ModbusRtuCharCycles =
  152. (uint32_t)((coreClock * MODBUS_RTU_BITS_PER_CHAR) / baudRate);
  153. if (baudRate > MODBUS_RTU_HIGH_BAUD_LIMIT)
  154. {
  155. /* 高波特率使用固定750us和1750us,999999用于向上取整 */
  156. ModbusRtuT15Cycles =
  157. (uint32_t)((coreClock * MODBUS_RTU_T15_US + 999999UL) / 1000000UL);
  158. ModbusRtuT35Cycles =
  159. (uint32_t)((coreClock * MODBUS_RTU_T35_US + 999999UL) / 1000000UL);
  160. }
  161. else
  162. {
  163. /* 低波特率按照1.5个字符时间和3.5个字符时间计算 */
  164. ModbusRtuT15Cycles =
  165. (uint32_t)(((uint64_t)ModbusRtuCharCycles * 3UL + 1UL) / 2UL);
  166. ModbusRtuT35Cycles =
  167. (uint32_t)(((uint64_t)ModbusRtuCharCycles * 7UL + 1UL) / 2UL);
  168. }
  169. }
  170. /**
  171. * @brief 结束当前RTU接收组帧
  172. * @note 调用本函数时必须保证不会与串口接收中断并发执行
  173. */
  174. static void ModbusRxAssemblyFinalize(void)
  175. {
  176. if (ModbusRxAssemblyLength == 0U)
  177. {
  178. return;
  179. }
  180. /*拼帧区是否有数据*/
  181. if ((ModbusRxAssemblyInvalid == 0U) && (ModbusRxFrameReady == 0U))
  182. {
  183. /* 当前帧未违反T1.5且解析缓冲区空闲时提交完整帧 */
  184. (void)memcpy(ModbusRxFrame, ModbusRxAssemblyBuffer,
  185. ModbusRxAssemblyLength);
  186. ModbusRxFrameLength = ModbusRxAssemblyLength;
  187. ModbusRxFrameReady = 1U;
  188. }
  189. else
  190. {
  191. /* T1.5无效帧或上一帧尚未处理完成时丢弃 */
  192. ModbusSlaveStatistics.droppedFrameCount++;
  193. }
  194. ModbusRxAssemblyLength = 0U;
  195. ModbusRxAssemblyInvalid = 0U;
  196. }
  197. /**
  198. * @brief 静默时间达到T3.5后,将接收数据交给协议解析任务
  199. */
  200. static void ModbusTryFinalizeReceive(void)
  201. {
  202. uint32_t now;
  203. if (ModbusRxAssemblyLength == 0U)
  204. {
  205. return;
  206. }
  207. /* DMA缓冲区出现新数据时,说明串口仍在接收当前片段 */
  208. if ((ModbusUart->hdmarx != NULL) && /*串口DMA使能*/
  209. ((ModbusUart->Instance->CR3 & USART_CR3_DMAR) != 0U)
  210. && (__HAL_DMA_GET_COUNTER(ModbusUart->hdmarx)
  211. < MODBUS_RTU_ADU_SIZE_MAX))
  212. {
  213. return;
  214. }
  215. now = DWT->CYCCNT;
  216. /* 从末字节结束时刻开始计算静默时间,未达到T3.5时继续等待 */
  217. if ((uint32_t)(now - ModbusRxLastByteCycle) < ModbusRtuT35Cycles)
  218. {
  219. return;
  220. }
  221. /* 静默达到T3.5,当前RTU帧结束,发送响应前停止接收DMA */
  222. (void)HAL_UART_AbortReceive(ModbusUart);
  223. /* HAL_UART_AbortReceive has already stopped DMA/IDLE callbacks. The
  224. * finalize routine may copy a full RTU ADU, so it must not globally mask
  225. * the 100 kHz motion interrupts around that memcpy. */
  226. ModbusRxAssemblyFinalize();
  227. /* 无效帧被丢弃后,重新启动DMA接收。 */
  228. if (ModbusRxFrameReady == 0U)
  229. {
  230. (void)ModbusStartReceive();
  231. }
  232. }
  233. /**
  234. * @brief 启动 USART DMA 空闲接收
  235. * @retval HAL_OK DMA 接收启动成功
  236. * @retval HAL_BUSY 串口未配置或发送尚未结束
  237. * @return 其他 HAL 状态表示 DMA 接收启动失败
  238. */
  239. static HAL_StatusTypeDef ModbusStartReceive(void)
  240. {
  241. HAL_StatusTypeDef status;
  242. ModbusReceiveRestartAttemptCount++;
  243. if (ModbusUart == NULL)
  244. {
  245. status = HAL_ERROR;
  246. ModbusReceiveRestartFailureCount++;
  247. ModbusLastReceiveStartStatus = (uint8_t)status;
  248. return status;
  249. }
  250. if (ModbusTxBusy != 0U)
  251. {
  252. status = HAL_BUSY;
  253. ModbusReceiveRestartFailureCount++;
  254. ModbusLastReceiveStartStatus = (uint8_t)status;
  255. return status;
  256. }
  257. status = HAL_UARTEx_ReceiveToIdle_DMA(ModbusUart, ModbusRxDmaBuffer,
  258. sizeof(ModbusRxDmaBuffer));
  259. /* A duplicate start request can race with an already healthy DMA receiver.
  260. * Treat that specific HAL_BUSY case as operational, not as a recovery
  261. * failure; all other HAL_BUSY/HAL_ERROR results remain visible. */
  262. if ((status == HAL_BUSY)
  263. && (ModbusUart->RxState == HAL_UART_STATE_BUSY_RX)
  264. && ((ModbusUart->Instance->CR3 & USART_CR3_DMAR) != 0U))
  265. {
  266. status = HAL_OK;
  267. }
  268. if ((status == HAL_OK) && (ModbusUart->hdmarx != NULL))
  269. {
  270. /*
  271. * 普通 Modbus 帧只应在 IDLE 或缓冲区满时交给应用
  272. * 关闭 DMA 半传输中断,避免长帧在一半位置被误认为完整帧
  273. */
  274. __HAL_DMA_DISABLE_IT(ModbusUart->hdmarx, DMA_IT_HT);
  275. }
  276. if (status != HAL_OK)
  277. {
  278. ModbusReceiveRestartFailureCount++;
  279. ModbusLastUartErrorCode = ModbusUart->ErrorCode;
  280. ModbusNextReceiveRetryTick = HAL_GetTick() + 10UL;
  281. }
  282. else
  283. {
  284. ModbusNextReceiveRetryTick = 0UL;
  285. }
  286. ModbusLastReceiveStartStatus = (uint8_t)status;
  287. return status;
  288. }
  289. /**
  290. * @brief 在 RTU 帧末尾追加 CRC 低字节和高字节
  291. * @param[in,out] frame 待追加 CRC 的帧缓冲区
  292. * @param[in] payloadLength 不包含 CRC 的有效载荷长度
  293. */
  294. static void ModbusAppendCrc(uint8_t *frame, uint16_t payloadLength)
  295. {
  296. uint16_t crc = ModbusCrc16(frame, payloadLength);
  297. /* Modbus RTU 在线路上传输 CRC 低字节在前、高字节在后 */
  298. frame[payloadLength] = (uint8_t)(crc & 0x00FFU);
  299. frame[payloadLength + 1U] = (uint8_t)(crc >> 8U);
  300. }
  301. /**
  302. * @brief 构造 Modbus 异常响应
  303. * @param[in] function 请求功能码
  304. * @param[in] exception 异常码
  305. * @return 异常响应 ADU 长度
  306. */
  307. static uint16_t ModbusBuildException(uint8_t function, uint8_t exception)
  308. {
  309. ModbusTxFrame[0] = ModbusSlaveAddress;
  310. ModbusTxFrame[1] = (uint8_t)(function | 0x80U);
  311. ModbusTxFrame[2] = exception;
  312. ModbusAppendCrc(ModbusTxFrame, 3U);
  313. return 5U;
  314. }
  315. /**
  316. * @brief Build a bounded, read-only runtime diagnostic response (function 0x47).
  317. *
  318. * The request is: function, start word (BE), quantity (BE). Multi-word
  319. * 32-bit values use low word first, matching the PLSR diagnostic window.
  320. */
  321. static uint16_t ModbusProcessRuntimeDiagnostics(const uint8_t *request,
  322. uint16_t requestLength)
  323. {
  324. MODBUS_SLAVE_RUNTIME_DIAGNOSTICS diagnostics;
  325. uint16_t words[MODBUS_RUNTIME_DIAGNOSTICS_WORDS];
  326. uint32_t flags = 0U;
  327. uint32_t stats[10];
  328. uint16_t start;
  329. uint16_t quantity;
  330. uint16_t index;
  331. uint16_t responseLength;
  332. if (requestLength != 8U)
  333. {
  334. ModbusSlaveStatistics.illegalValueCount++;
  335. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  336. }
  337. start = ModbusGetU16Be(&request[2]);
  338. quantity = ModbusGetU16Be(&request[4]);
  339. if ((quantity == 0U)
  340. || (start >= MODBUS_RUNTIME_DIAGNOSTICS_WORDS)
  341. || (quantity > (MODBUS_RUNTIME_DIAGNOSTICS_WORDS - start)))
  342. {
  343. ModbusSlaveStatistics.illegalAddressCount++;
  344. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  345. }
  346. (void)ModbusSlaveGetRuntimeDiagnostics(MODBUS_CONNECTION_TIMEOUT_MS,
  347. &diagnostics);
  348. if (diagnostics.initialized != 0U)
  349. {
  350. flags |= MODBUS_RUNTIME_FLAG_INITIALIZED;
  351. }
  352. if (diagnostics.hasReceivedValidFrame != 0U)
  353. {
  354. flags |= MODBUS_RUNTIME_FLAG_VALID_FRAME_SEEN;
  355. }
  356. if (diagnostics.connected != 0U)
  357. {
  358. flags |= MODBUS_RUNTIME_FLAG_CONNECTED;
  359. }
  360. if (diagnostics.txBusy != 0U)
  361. {
  362. flags |= MODBUS_RUNTIME_FLAG_TX_BUSY;
  363. }
  364. if (diagnostics.rxFrameReady != 0U)
  365. {
  366. flags |= MODBUS_RUNTIME_FLAG_RX_FRAME_READY;
  367. }
  368. if (diagnostics.rxAssemblyInvalid != 0U)
  369. {
  370. flags |= MODBUS_RUNTIME_FLAG_RX_ASSEMBLY_INVALID;
  371. }
  372. if (diagnostics.lastReceiveStartStatus == (uint8_t)HAL_OK)
  373. {
  374. flags |= MODBUS_RUNTIME_FLAG_RX_RESTART_OK;
  375. }
  376. words[0] = MODBUS_RUNTIME_DIAGNOSTICS_SIGNATURE;
  377. words[1] = MODBUS_RUNTIME_DIAGNOSTICS_VERSION;
  378. words[2] = MODBUS_RUNTIME_DIAGNOSTICS_WORDS;
  379. words[3] = (uint16_t)flags;
  380. words[4] = (uint16_t)diagnostics.currentTick;
  381. words[5] = (uint16_t)(diagnostics.currentTick >> 16U);
  382. words[6] = (uint16_t)diagnostics.lastValidFrameTick;
  383. words[7] = (uint16_t)(diagnostics.lastValidFrameTick >> 16U);
  384. words[8] = (uint16_t)diagnostics.lastInterFrameGapCycles;
  385. words[9] = (uint16_t)(diagnostics.lastInterFrameGapCycles >> 16U);
  386. words[10] = (uint16_t)diagnostics.receiveRestartAttemptCount;
  387. words[11] = (uint16_t)(diagnostics.receiveRestartAttemptCount >> 16U);
  388. words[12] = (uint16_t)diagnostics.receiveRestartFailureCount;
  389. words[13] = (uint16_t)(diagnostics.receiveRestartFailureCount >> 16U);
  390. words[14] = (uint16_t)diagnostics.lastUartErrorCode;
  391. words[15] = (uint16_t)(diagnostics.lastUartErrorCode >> 16U);
  392. words[16] = diagnostics.lastReceiveStartStatus;
  393. words[17] = diagnostics.rxAssemblyLength;
  394. words[18] = diagnostics.rxFrameLength;
  395. words[19] = MODBUS_CONNECTION_TIMEOUT_MS;
  396. stats[0] = diagnostics.statistics.rxEventCount;
  397. stats[1] = diagnostics.statistics.validFrameCount;
  398. stats[2] = diagnostics.statistics.txFrameCount;
  399. stats[3] = diagnostics.statistics.crcErrorCount;
  400. stats[4] = diagnostics.statistics.ignoredAddressCount;
  401. stats[5] = diagnostics.statistics.illegalFunctionCount;
  402. stats[6] = diagnostics.statistics.illegalAddressCount;
  403. stats[7] = diagnostics.statistics.illegalValueCount;
  404. stats[8] = diagnostics.statistics.droppedFrameCount;
  405. stats[9] = diagnostics.statistics.uartErrorCount;
  406. for (index = 0U; index < 10U; index++)
  407. {
  408. words[20U + (index * 2U)] = (uint16_t)stats[index];
  409. words[21U + (index * 2U)] = (uint16_t)(stats[index] >> 16U);
  410. }
  411. ModbusTxFrame[0] = ModbusSlaveAddress;
  412. ModbusTxFrame[1] = MODBUS_RUNTIME_DIAGNOSTICS_FUNCTION;
  413. ModbusTxFrame[2] = (uint8_t)(quantity * 2U);
  414. for (index = 0U; index < quantity; index++)
  415. {
  416. uint16_t value = words[start + index];
  417. ModbusTxFrame[3U + (index * 2U)] = (uint8_t)(value >> 8U);
  418. ModbusTxFrame[4U + (index * 2U)] = (uint8_t)value;
  419. }
  420. responseLength = (uint16_t)(3U + (quantity * 2U));
  421. ModbusAppendCrc(ModbusTxFrame, responseLength);
  422. return (uint16_t)(responseLength + 2U);
  423. }
  424. /**
  425. * @brief Process read coils (0x01) or discrete inputs (0x02).
  426. * @param[in] request RTU request frame.
  427. * @param[in] requestLength Request frame length.
  428. * @param[in] functionCode Function code echoed in the response.
  429. * @param[in] device Backing PLC bit-device image.
  430. * @return Response ADU length, including an exception response when invalid.
  431. */
  432. static uint16_t ModbusProcessReadBits(const uint8_t *request,
  433. uint16_t requestLength,
  434. uint8_t functionCode,
  435. MODBUS_BIT_DEVICE device)
  436. {
  437. uint16_t start;
  438. uint16_t quantity;
  439. uint16_t index;
  440. uint8_t byteCount;
  441. if (requestLength != 8U)
  442. {
  443. ModbusSlaveStatistics.illegalValueCount++;
  444. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  445. }
  446. start = ModbusGetU16Be(&request[2]);
  447. quantity = ModbusGetU16Be(&request[4]);
  448. if ((quantity == 0U)
  449. || (quantity > MODBUS_READ_COILS_MAX)) // 数量0或者数量大于最大值
  450. {
  451. ModbusSlaveStatistics.illegalValueCount++;
  452. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  453. }
  454. if (ModbusAddressRangeIsValid(start, quantity) == 0U) // 地址检查
  455. {
  456. ModbusSlaveStatistics.illegalAddressCount++;
  457. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  458. }
  459. byteCount = (uint8_t)((quantity + 7U) / 8U);
  460. ModbusTxFrame[0] = ModbusSlaveAddress;
  461. ModbusTxFrame[1] = functionCode;
  462. ModbusTxFrame[2] = byteCount;
  463. (void)memset(&ModbusTxFrame[3], 0, byteCount);
  464. for (index = 0U; index < quantity; index++)
  465. {
  466. uint8_t value = 0U;
  467. if ((ModbusDataReadBit(device,
  468. (uint32_t)start + index,
  469. &value) != 0U)
  470. && (value != 0U))
  471. {
  472. ModbusTxFrame[3U + (index >> 3U)] |=
  473. (uint8_t)(1U << (index & 0x0007U));
  474. }
  475. }
  476. ModbusAppendCrc(ModbusTxFrame, (uint16_t)(3U + byteCount));
  477. return (uint16_t)(5U + byteCount);
  478. }
  479. /**
  480. * @brief 处理读保持寄存器功能码 0x03
  481. * @param[in] request RTU 请求帧
  482. * @param[in] requestLength 请求帧长度
  483. * @return 待发送响应长度,异常请求返回异常响应长度
  484. */
  485. static uint16_t ModbusProcessReadHolding(const uint8_t *request,
  486. uint16_t requestLength)
  487. {
  488. uint16_t start;
  489. uint16_t quantity;
  490. uint16_t index;
  491. uint16_t value;
  492. if (requestLength != 8U)
  493. {
  494. ModbusSlaveStatistics.illegalValueCount++;
  495. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  496. }
  497. start = ModbusGetU16Be(&request[2]);
  498. quantity = ModbusGetU16Be(&request[4]);
  499. if ((quantity == 0U)
  500. || (quantity > MODBUS_READ_REGS_MAX)) // 数量0或者数量大于最大值
  501. {
  502. ModbusSlaveStatistics.illegalValueCount++;
  503. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  504. }
  505. if (ModbusAddressRangeIsValid(start, quantity) == 0U) // 地址检查
  506. {
  507. if ((start >= 20000U) && (start < 25000U) && (quantity > 0U)
  508. && (quantity <= (25000U - start)))
  509. goto tx;
  510. ModbusSlaveStatistics.illegalAddressCount++;
  511. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  512. }
  513. tx:
  514. ModbusTxFrame[0] = ModbusSlaveAddress;
  515. ModbusTxFrame[1] = 0X03;
  516. ModbusTxFrame[2] = (uint8_t)(quantity * 2U);
  517. for (index = 0U; index < quantity; index++)
  518. {
  519. uint16_t address = start + index;
  520. /* D20000~D24999映射到D1、D3、D5……D9999 */
  521. if (address >= 20000U)
  522. {
  523. address = (address - 20000U) * 2U + 1U;
  524. }
  525. if (ModbusDataReadWord(MODBUS_DATA_DEVICE_D, address, &value) == 0U)
  526. {
  527. ModbusSlaveStatistics.illegalAddressCount++;
  528. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  529. }
  530. ModbusTxFrame[3U + index * 2U] = (uint8_t)(value >> 8U);
  531. ModbusTxFrame[4U + index * 2U] = (uint8_t)(value & 0x00FFU);
  532. }
  533. ModbusAppendCrc(ModbusTxFrame, (uint16_t)(3U + quantity * 2U));
  534. return (uint16_t)(5U + quantity * 2U);
  535. }
  536. /**
  537. * @brief 处理写单个保持寄存器功能码 0x06
  538. * @param[in] request RTU 请求帧
  539. * @param[in] requestLength 请求帧长度
  540. * @param[in] isBroadcast 非 0 表示当前请求为广播
  541. * @return 单播响应长度;广播或无法响应时返回 0
  542. */
  543. static uint16_t ModbusProcessWriteSingleRegister(const uint8_t *request,
  544. uint16_t requestLength,
  545. uint8_t isBroadcast)
  546. {
  547. uint16_t address;
  548. uint16_t value;
  549. if (requestLength != 8U)
  550. {
  551. ModbusSlaveStatistics.illegalValueCount++;
  552. return (isBroadcast != 0U) ? 0U: ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  553. }
  554. address = ModbusGetU16Be(&request[2]);
  555. value = ModbusGetU16Be(&request[4]);
  556. if (address >= MODBUS_MAP_ITEM_COUNT)
  557. {
  558. ModbusSlaveStatistics.illegalAddressCount++;
  559. return (isBroadcast != 0U) ? 0U : ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  560. }
  561. (void)ModbusDataWriteWord(MODBUS_DATA_DEVICE_D, address, value);
  562. if (isBroadcast != 0U)
  563. {
  564. return 0U;
  565. }
  566. /* 0x06 的正常响应必须原样回显请求的前 6 个字节 */
  567. (void)memcpy(ModbusTxFrame, request, 6U);
  568. ModbusAppendCrc(ModbusTxFrame, 6U);
  569. return 8U;
  570. }
  571. /**
  572. * @brief 处理写单个线圈功能码 0x05
  573. * @param[in] request RTU 请求帧
  574. * @param[in] requestLength 请求帧长度
  575. * @param[in] isBroadcast 非 0 表示当前请求为广播
  576. * @return 单播响应长度;广播或无法响应时返回 0
  577. */
  578. static uint16_t ModbusProcessWriteSingleCoil(const uint8_t *request,
  579. uint16_t requestLength,
  580. uint8_t isBroadcast)
  581. {
  582. uint16_t address;
  583. uint16_t value;
  584. if (requestLength != 8U)
  585. {
  586. ModbusSlaveStatistics.illegalValueCount++;
  587. return (isBroadcast != 0U)? 0U : ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  588. }
  589. address = ModbusGetU16Be(&request[2]);
  590. value = ModbusGetU16Be(&request[4]);
  591. /*
  592. * 0x05 只接受 0xFF00(线圈置位)和 0x0000(线圈复位);
  593. * 其他数值属于非法数据值
  594. */
  595. if ((value != MODBUS_COIL_VALUE_ON) && (value != MODBUS_COIL_VALUE_OFF))
  596. {
  597. ModbusSlaveStatistics.illegalValueCount++;
  598. return (isBroadcast != 0U)? 0U : ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  599. }
  600. if (address >= MODBUS_MAP_ITEM_COUNT)
  601. {
  602. ModbusSlaveStatistics.illegalAddressCount++;
  603. return (isBroadcast != 0U) ? 0U: ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  604. }
  605. ModbusCoilSetUnchecked(address, (value == MODBUS_COIL_VALUE_ON) ? 1U : 0U);
  606. if (isBroadcast != 0U)
  607. {
  608. return 0U;
  609. }
  610. /* 0x05 正常应答原样回显请求的前 6 个字节,再追加 CRC */
  611. (void)memcpy(ModbusTxFrame, request, 6U);
  612. ModbusAppendCrc(ModbusTxFrame, 6U);
  613. return 8U;
  614. }
  615. /**
  616. * @brief 处理写多个线圈功能码 0x0F
  617. * @param[in] request RTU 请求帧
  618. * @param[in] requestLength 请求帧长度
  619. * @param[in] isBroadcast 非 0 表示当前请求为广播
  620. * @return 单播响应长度;广播或无法响应时返回 0
  621. */
  622. static uint16_t ModbusProcessWriteMultipleCoils(const uint8_t *request,
  623. uint16_t requestLength,
  624. uint8_t isBroadcast)
  625. {
  626. uint16_t start;
  627. uint16_t quantity;
  628. uint16_t index;
  629. uint8_t byteCount;
  630. uint8_t expectedByteCount;
  631. if (requestLength < 9U)
  632. {
  633. ModbusSlaveStatistics.illegalValueCount++;
  634. return (isBroadcast != 0U) ? 0U: ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  635. }
  636. start = ModbusGetU16Be(&request[2]);
  637. quantity = ModbusGetU16Be(&request[4]);
  638. byteCount = request[6];
  639. expectedByteCount = (uint8_t)((quantity + 7U) / 8U);
  640. if ((quantity == 0U) || (quantity > MODBUS_WRITE_COILS_MAX)
  641. || (byteCount != expectedByteCount)
  642. || (requestLength != (uint16_t)(9U + byteCount)))
  643. {
  644. ModbusSlaveStatistics.illegalValueCount++;
  645. return (isBroadcast != 0U)? 0U : ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  646. }
  647. if (ModbusAddressRangeIsValid(start, quantity) == 0U)
  648. {
  649. ModbusSlaveStatistics.illegalAddressCount++;
  650. return (isBroadcast != 0U) ? 0U: ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  651. }
  652. for (index = 0U; index < quantity; index++)
  653. {
  654. uint8_t state;
  655. state = (uint8_t)((request[7U + (index >> 3U)] >> (index & 0x0007U)) & 0x01U);
  656. ModbusCoilSetUnchecked((uint16_t)(start + index), state);
  657. }
  658. if (isBroadcast != 0U)
  659. {
  660. return 0U;
  661. }
  662. ModbusTxFrame[0] = ModbusSlaveAddress;
  663. ModbusTxFrame[1] = 0X0F;
  664. (void)memcpy(&ModbusTxFrame[2], &request[2], 4U);
  665. ModbusAppendCrc(ModbusTxFrame, 6U);
  666. return 8U;
  667. }
  668. /**
  669. * @brief 处理写多个保持寄存器功能码 0x10
  670. * @param[in] request RTU 请求帧
  671. * @param[in] requestLength 请求帧长度
  672. * @param[in] isBroadcast 非 0 表示当前请求为广播
  673. * @return 单播响应长度;广播或无法响应时返回 0
  674. */
  675. static uint16_t ModbusProcessWriteMultipleRegisters(const uint8_t *request,
  676. uint16_t requestLength,
  677. uint8_t isBroadcast)
  678. {
  679. uint16_t start;
  680. uint16_t quantity;
  681. uint16_t index;
  682. uint8_t byteCount;
  683. if (requestLength < 9U)
  684. {
  685. ModbusSlaveStatistics.illegalValueCount++;
  686. return (isBroadcast != 0U) ? 0U : ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  687. }
  688. start = ModbusGetU16Be(&request[2]);
  689. quantity = ModbusGetU16Be(&request[4]);
  690. byteCount = request[6];
  691. if ((quantity == 0U) || (quantity > MODBUS_WRITE_REGS_MAX)
  692. || (byteCount != (uint8_t)(quantity * 2U))
  693. || (requestLength != (uint16_t)(9U + byteCount)))
  694. {
  695. ModbusSlaveStatistics.illegalValueCount++;
  696. return (isBroadcast != 0U) ? 0U : ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  697. }
  698. if (ModbusAddressRangeIsValid(start, quantity) == 0U)
  699. {
  700. ModbusSlaveStatistics.illegalAddressCount++;
  701. return (isBroadcast != 0U) ? 0U : ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  702. }
  703. for (index = 0U; index < quantity; index++)
  704. {
  705. ModbusWriteRegisterScratch[index] =
  706. ModbusGetU16Be(&request[7U + index * 2U]);
  707. }
  708. (void)ModbusDataWriteWords(MODBUS_DATA_DEVICE_D,
  709. start,
  710. ModbusWriteRegisterScratch,
  711. quantity);
  712. if (isBroadcast != 0U)
  713. {
  714. return 0U;
  715. }
  716. ModbusTxFrame[0] = ModbusSlaveAddress;
  717. ModbusTxFrame[1] = 0X10;
  718. (void)memcpy(&ModbusTxFrame[2], &request[2], 4U);
  719. ModbusAppendCrc(ModbusTxFrame, 6U);
  720. return 8U;
  721. }
  722. /**
  723. * @brief 处理读取扩展地址保持寄存器功能码0x48
  724. * @param[in] request RTU请求帧
  725. * @param[in] requestLength 请求帧长度
  726. * @return 待发送响应长度,异常请求返回异常响应长度
  727. */
  728. static uint16_t ModbusProcessReadBigHolding(const uint8_t *request,
  729. uint16_t requestLength)
  730. {
  731. uint32_t start;
  732. uint32_t currentAddress;
  733. uint16_t quantity;
  734. uint16_t index;
  735. uint16_t value;
  736. /* 请求帧:站号1 + 功能码1 + 地址3 + 数量2 + CRC2 */
  737. if (requestLength != 9U)
  738. {
  739. ModbusSlaveStatistics.illegalValueCount++;
  740. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  741. }
  742. /* 提取24位起始地址和16位寄存器数量 */
  743. start = ModbusGetU24Be(&request[2]);
  744. quantity = ModbusGetU16Be(&request[5]);
  745. /* 一次最多读取125个寄存器 */
  746. if ((quantity == 0U) || (quantity > MODBUS_READ_REGS_MAX))
  747. {
  748. ModbusSlaveStatistics.illegalValueCount++;
  749. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  750. }
  751. /* Function 0x48 retains the existing sparse range: SRAM 0..19999 and
  752. * CCMRAM 40000..69998. The unallocated 20000..39999 gap is rejected. */
  753. if ((start >= 69999UL) || ((uint32_t)quantity > (69999UL - start))
  754. || ((start < 40000UL)
  755. && ((start >= 20000UL)
  756. || ((start + quantity) > 20000UL))))
  757. {
  758. ModbusSlaveStatistics.illegalAddressCount++;
  759. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  760. }
  761. /* 组成正常响应帧头 */
  762. ModbusTxFrame[0] = ModbusSlaveAddress;
  763. ModbusTxFrame[1] = 0x48U;
  764. ModbusTxFrame[2] = (uint8_t)(quantity * 2U);
  765. for (index = 0U; index < quantity; index++)
  766. {
  767. currentAddress = start + (uint32_t)index;
  768. /* The data-store layer validates the sparse physical range. */
  769. if (ModbusDataReadLinear(currentAddress, &value) == 0U)
  770. {
  771. ModbusSlaveStatistics.illegalAddressCount++;
  772. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  773. }
  774. /* 每个寄存器按照高字节、低字节装入响应帧 */
  775. ModbusTxFrame[3U + index * 2U] = (uint8_t)(value >> 8U);
  776. ModbusTxFrame[4U + index * 2U] = (uint8_t)(value & 0x00FFU);
  777. }
  778. /* 添加CRC */
  779. ModbusAppendCrc(ModbusTxFrame, (uint16_t)(3U + quantity * 2U));
  780. return (uint16_t)(5U + quantity * 2U);
  781. }
  782. /**
  783. * @brief 校验并分发一帧 Modbus RTU 请求
  784. * @param[in] request RTU 请求帧
  785. * @param[in] requestLength 请求帧长度
  786. * @return 待发送响应长度;无需响应时返回 0
  787. */
  788. static uint16_t ModbusProcessRequest(const uint8_t *request,
  789. uint16_t requestLength)
  790. {
  791. uint16_t calculatedCrc;
  792. uint16_t receivedCrc;
  793. uint8_t isBroadcast;
  794. if (requestLength < 4U)
  795. {
  796. return 0U;
  797. }
  798. calculatedCrc = ModbusCrc16(request, (uint16_t)(requestLength - 2U));
  799. receivedCrc = (uint16_t)(request[requestLength - 2U]
  800. | ((uint16_t)request[requestLength - 1U] << 8U));
  801. if (calculatedCrc != receivedCrc) // CRC校验
  802. {
  803. ModbusSlaveStatistics.crcErrorCount++;
  804. return 0U;
  805. }
  806. if ((request[0] != ModbusSlaveAddress)
  807. && (request[0] != MODBUS_BROADCAST_ADDRESS)) // 非法从站地址
  808. {
  809. ModbusSlaveStatistics.ignoredAddressCount++;
  810. return 0U;
  811. }
  812. isBroadcast =
  813. (request[0] == MODBUS_BROADCAST_ADDRESS) ? 1U : 0U; // 是否广播请求
  814. ModbusSlaveStatistics.validFrameCount++;
  815. ModbusLastValidFrameTick = HAL_GetTick();
  816. ModbusHasReceivedValidFrame = 1U;
  817. switch (request[1])
  818. {
  819. case 0X01U: // 读线圈
  820. // 广播请求不允许读取,从站不作响应
  821. return (isBroadcast != 0U)
  822. ? 0U
  823. : ModbusProcessReadBits(request,
  824. requestLength,
  825. 0X01U,
  826. MODBUS_BIT_DEVICE_M);
  827. case 0X02U: /* Read discrete inputs: protocol view of PLC X. */
  828. return (isBroadcast != 0U)
  829. ? 0U
  830. : ModbusProcessReadBits(request,
  831. requestLength,
  832. 0X02U,
  833. MODBUS_BIT_DEVICE_X);
  834. case 0X03U: // 读保持寄存器
  835. return (isBroadcast != 0U)
  836. ? 0U
  837. : ModbusProcessReadHolding(request, requestLength);
  838. case 0x05U: // 写单个线圈
  839. return ModbusProcessWriteSingleCoil(request, requestLength,
  840. isBroadcast);
  841. case 0x06U: // 写单个保持寄存器
  842. return ModbusProcessWriteSingleRegister(request, requestLength,
  843. isBroadcast);
  844. case 0x0FU: // 写多个线圈
  845. return ModbusProcessWriteMultipleCoils(request, requestLength,
  846. isBroadcast);
  847. case 0x10U: // 写多个保持寄存器
  848. return ModbusProcessWriteMultipleRegisters(request, requestLength,
  849. isBroadcast);
  850. case MODBUS_RUNTIME_DIAGNOSTICS_FUNCTION:
  851. return (isBroadcast != 0U)
  852. ? 0U
  853. : ModbusProcessRuntimeDiagnostics(request,
  854. requestLength);
  855. case 0x48U: // 读大地址保持寄存器
  856. return (isBroadcast != 0U)
  857. ? 0U
  858. : ModbusProcessReadBigHolding(request, requestLength);
  859. default: // 未知功能码
  860. ModbusSlaveStatistics.illegalFunctionCount++;
  861. return (isBroadcast != 0U)
  862. ? 0U
  863. : ModbusBuildException(request[1],
  864. MODBUS_EX_ILLEGAL_FUNCTION);
  865. }
  866. }
  867. HAL_StatusTypeDef ModbusSlaveInit(UART_HandleTypeDef *huart,
  868. uint8_t slaveAddress)
  869. {
  870. if ((huart == NULL) || (slaveAddress == 0U) || (slaveAddress > 247U))
  871. {
  872. return HAL_ERROR;
  873. }
  874. ModbusUart = huart;
  875. ModbusSlaveAddress = slaveAddress;
  876. /* 清空拼帧状态并根据当前波特率初始化T1.5和T3.5 */
  877. ModbusRxAssemblyLength = 0U;
  878. ModbusRxAssemblyInvalid = 0U;
  879. ModbusRxFrameReady = 0U;
  880. ModbusTxBusy = 0U;
  881. ModbusNextReceiveRetryTick = 0UL;
  882. ModbusRtuTimingInit(huart->Init.BaudRate);
  883. return ModbusStartReceive();
  884. }
  885. void ModbusSlavePoll(void)
  886. {
  887. uint16_t responseLength;
  888. /* Recover from a failed DMA rearm even when no later UART callback occurs.
  889. * Limit retries to 100 Hz so a persistent HAL fault cannot monopolize the
  890. * 1 ms application task. */
  891. if ((ModbusUart != NULL) && (ModbusTxBusy == 0U)
  892. && (ModbusRxFrameReady == 0U)
  893. && ((ModbusUart->RxState != HAL_UART_STATE_BUSY_RX)
  894. || ((ModbusUart->Instance->CR3 & USART_CR3_DMAR) == 0U))
  895. && ((ModbusNextReceiveRetryTick == 0UL)
  896. || ((int32_t)(HAL_GetTick() - ModbusNextReceiveRetryTick) >= 0)))
  897. {
  898. (void)ModbusStartReceive();
  899. }
  900. /* 检查末字节后的静默时间是否已经达到T3.5 */
  901. ModbusTryFinalizeReceive();
  902. if ((ModbusRxFrameReady == 0U) || (ModbusTxBusy != 0U))
  903. {
  904. return;
  905. }
  906. responseLength = ModbusProcessRequest(ModbusRxFrame, ModbusRxFrameLength);
  907. if (responseLength > 0U)
  908. {
  909. ModbusTxBusy = 1U;
  910. if (HAL_UART_Transmit_DMA(ModbusUart, ModbusTxFrame, responseLength)
  911. == HAL_OK)
  912. {
  913. ModbusSlaveStatistics.txFrameCount++;
  914. }
  915. else
  916. {
  917. ModbusTxBusy = 0U;
  918. ModbusSlaveStatistics.uartErrorCount++;
  919. ModbusLastUartErrorCode = ModbusUart->ErrorCode;
  920. (void)ModbusStartReceive(); // 重启DMA接收
  921. }
  922. }
  923. else
  924. {
  925. (void)ModbusStartReceive();
  926. }
  927. /*
  928. * 当前帧;处理完成后再释放帧槽
  929. */
  930. ModbusRxFrameReady = 0U;
  931. }
  932. void ModbusSlaveOnRxEvent(UART_HandleTypeDef *huart, uint16_t size)
  933. {
  934. HAL_UART_RxEventTypeTypeDef eventType;
  935. uint32_t now;
  936. uint32_t lastByteCycle;
  937. uint32_t firstByteCycle;
  938. uint32_t chunkCycles;
  939. uint32_t interFrameGap;
  940. ModbusSlaveStatistics.rxEventCount++; // 串口接收事件计数
  941. if ((huart != ModbusUart) || (ModbusUart == NULL))
  942. {
  943. return;
  944. }
  945. if ((size > 0U) && (size <= MODBUS_RTU_ADU_SIZE_MAX))
  946. {
  947. now = DWT->CYCCNT;
  948. eventType = HAL_UARTEx_GetRxEventType(huart);
  949. /* IDLE事件比末字节结束晚约一个字符时间,减去字符时间得到末字节时刻 */
  950. lastByteCycle = now;
  951. if (eventType == HAL_UART_RXEVENT_IDLE)
  952. {
  953. lastByteCycle -= ModbusRtuCharCycles;
  954. }
  955. /* 根据本次接收字节数反推DMA片段首字节的开始时刻 */
  956. chunkCycles = (uint32_t)((uint64_t)size * ModbusRtuCharCycles);
  957. firstByteCycle = lastByteCycle - chunkCycles;
  958. if (ModbusRxAssemblyLength > 0U)
  959. {
  960. interFrameGap = (uint32_t)(firstByteCycle - ModbusRxLastByteCycle);
  961. ModbusLastInterFrameGapCycles = interFrameGap;
  962. if (interFrameGap >= ModbusRtuT35Cycles)
  963. {
  964. /* 间隔达到T3.5,结束上一帧,本片段作为新帧开始 */
  965. ModbusRxAssemblyFinalize();
  966. }
  967. else if (interFrameGap > ModbusRtuT15Cycles)
  968. {
  969. /* 帧内静默超过T1.5但不足T3.5,标记整帧无效 */
  970. ModbusRxAssemblyInvalid = 1U;
  971. }
  972. else
  973. {
  974. /* 间隔不超过T1.5,当前片段继续拼入同一帧 */
  975. }
  976. }
  977. if (size
  978. <= (uint16_t)(MODBUS_RTU_ADU_SIZE_MAX - ModbusRxAssemblyLength))
  979. {
  980. (void)memcpy(&ModbusRxAssemblyBuffer[ModbusRxAssemblyLength],
  981. ModbusRxDmaBuffer, size);
  982. ModbusRxAssemblyLength += size;
  983. }
  984. else
  985. {
  986. ModbusRxAssemblyInvalid = 1U;
  987. }
  988. /* 保存末字节时刻并立即重启DMA,继续等待可能的后续片段 */
  989. ModbusRxLastByteCycle = lastByteCycle;
  990. (void)ModbusStartReceive();
  991. }
  992. else
  993. {
  994. // 长度异常帧
  995. ModbusSlaveStatistics.droppedFrameCount++;
  996. ModbusRxAssemblyLength = 0U;
  997. ModbusRxAssemblyInvalid = 0U;
  998. (void)ModbusStartReceive();
  999. }
  1000. }
  1001. void ModbusSlaveOnTxComplete(UART_HandleTypeDef *huart)
  1002. {
  1003. if ((huart != ModbusUart) || (ModbusUart == NULL))
  1004. {
  1005. return;
  1006. }
  1007. ModbusTxBusy = 0U;
  1008. (void)ModbusStartReceive(); // 重启DMA接收
  1009. }
  1010. void ModbusSlaveOnUartError(UART_HandleTypeDef *huart)
  1011. {
  1012. if ((huart != ModbusUart) || (ModbusUart == NULL))
  1013. {
  1014. return;
  1015. }
  1016. ModbusSlaveStatistics.uartErrorCount++;
  1017. ModbusLastUartErrorCode = huart->ErrorCode;
  1018. ModbusTxBusy = 0U;
  1019. ModbusRxAssemblyLength = 0U;
  1020. ModbusRxAssemblyInvalid = 0U;
  1021. (void)HAL_UART_Abort(huart); // 立即终止这个串口当前正在进行的发送和接收操作
  1022. (void)ModbusStartReceive(); // 重启DMA接收
  1023. }
  1024. uint8_t ModbusSlaveSetHoldingRegister(uint16_t address, uint16_t value)
  1025. {
  1026. if (address >= MODBUS_MAP_ITEM_COUNT)
  1027. {
  1028. return 0U;
  1029. }
  1030. return ModbusDataWriteWord(MODBUS_DATA_DEVICE_D, address, value);
  1031. }
  1032. uint8_t ModbusSlaveGetHoldingRegister(uint16_t address, uint16_t *value)
  1033. {
  1034. if ((address >= MODBUS_MAP_ITEM_COUNT) || (value == NULL))
  1035. {
  1036. return 0U;
  1037. }
  1038. return ModbusDataReadWord(MODBUS_DATA_DEVICE_D, address, value);
  1039. }
  1040. uint8_t ModbusSlaveSetCoil(uint16_t address, uint8_t state)
  1041. {
  1042. if (address >= MODBUS_MAP_ITEM_COUNT)
  1043. {
  1044. return 0U;
  1045. }
  1046. ModbusCoilSetUnchecked(address, state);
  1047. return 1U;
  1048. }
  1049. uint8_t ModbusSlaveGetCoil(uint16_t address, uint8_t *state)
  1050. {
  1051. if ((address >= MODBUS_MAP_ITEM_COUNT) || (state == NULL))
  1052. {
  1053. return 0U;
  1054. }
  1055. *state = ModbusCoilGetUnchecked(address);
  1056. return 1U;
  1057. }
  1058. uint8_t ModbusSlaveIsConnected(uint32_t timeoutMs)
  1059. {
  1060. if (ModbusHasReceivedValidFrame == 0U)
  1061. {
  1062. return 0U;
  1063. }
  1064. return ((HAL_GetTick() - ModbusLastValidFrameTick) <= timeoutMs) ? 1U : 0U;
  1065. }
  1066. uint8_t ModbusSlaveGetRuntimeDiagnostics(
  1067. uint32_t timeoutMs,
  1068. MODBUS_SLAVE_RUNTIME_DIAGNOSTICS *diagnostics)
  1069. {
  1070. if (diagnostics == NULL)
  1071. {
  1072. return 0U;
  1073. }
  1074. /* Every source field is naturally aligned and 32-bit-or-smaller on the
  1075. * Cortex-M4. A diagnostic snapshot may span adjacent frame events, but
  1076. * must never mask the 100 kHz motion/AB fast-gate interrupts. */
  1077. __DMB();
  1078. diagnostics->statistics.rxEventCount =
  1079. ModbusSlaveStatistics.rxEventCount;
  1080. diagnostics->statistics.validFrameCount =
  1081. ModbusSlaveStatistics.validFrameCount;
  1082. diagnostics->statistics.txFrameCount =
  1083. ModbusSlaveStatistics.txFrameCount;
  1084. diagnostics->statistics.crcErrorCount =
  1085. ModbusSlaveStatistics.crcErrorCount;
  1086. diagnostics->statistics.ignoredAddressCount =
  1087. ModbusSlaveStatistics.ignoredAddressCount;
  1088. diagnostics->statistics.illegalFunctionCount =
  1089. ModbusSlaveStatistics.illegalFunctionCount;
  1090. diagnostics->statistics.illegalAddressCount =
  1091. ModbusSlaveStatistics.illegalAddressCount;
  1092. diagnostics->statistics.illegalValueCount =
  1093. ModbusSlaveStatistics.illegalValueCount;
  1094. diagnostics->statistics.droppedFrameCount =
  1095. ModbusSlaveStatistics.droppedFrameCount;
  1096. diagnostics->statistics.uartErrorCount =
  1097. ModbusSlaveStatistics.uartErrorCount;
  1098. diagnostics->currentTick = HAL_GetTick();
  1099. diagnostics->lastValidFrameTick = ModbusLastValidFrameTick;
  1100. diagnostics->lastInterFrameGapCycles = ModbusLastInterFrameGapCycles;
  1101. diagnostics->receiveRestartAttemptCount =
  1102. ModbusReceiveRestartAttemptCount;
  1103. diagnostics->receiveRestartFailureCount =
  1104. ModbusReceiveRestartFailureCount;
  1105. diagnostics->lastUartErrorCode = ModbusLastUartErrorCode;
  1106. diagnostics->rxAssemblyLength = ModbusRxAssemblyLength;
  1107. diagnostics->rxFrameLength = ModbusRxFrameLength;
  1108. diagnostics->lastReceiveStartStatus = ModbusLastReceiveStartStatus;
  1109. diagnostics->initialized = (ModbusUart != NULL) ? 1U : 0U;
  1110. diagnostics->hasReceivedValidFrame = ModbusHasReceivedValidFrame;
  1111. diagnostics->connected =
  1112. ((ModbusHasReceivedValidFrame != 0U)
  1113. && ((diagnostics->currentTick - ModbusLastValidFrameTick)
  1114. <= timeoutMs))
  1115. ? 1U
  1116. : 0U;
  1117. diagnostics->txBusy = ModbusTxBusy;
  1118. diagnostics->rxFrameReady = ModbusRxFrameReady;
  1119. diagnostics->rxAssemblyInvalid = ModbusRxAssemblyInvalid;
  1120. __DMB();
  1121. return 1U;
  1122. }
  1123. // 上电恢复函数
  1124. void ModbusRetainedRegistersLoad(void)
  1125. {
  1126. uint16_t index;
  1127. if (ModbusBackupData->magic == MODBUS_BACKUP_MAGIC)
  1128. {
  1129. for (index = 0U; index < MODBUS_RETAINED_D_COUNT; index++)
  1130. {
  1131. (void)ModbusDataWriteWord(
  1132. MODBUS_DATA_DEVICE_D,
  1133. MODBUS_RETAINED_D_START + index,
  1134. ModbusBackupData->retainedD[index]);
  1135. }
  1136. }
  1137. else
  1138. {
  1139. for (index = 0U; index < MODBUS_RETAINED_D_COUNT; index++)
  1140. {
  1141. (void)ModbusDataWriteWord(MODBUS_DATA_DEVICE_D,
  1142. MODBUS_RETAINED_D_START + index,
  1143. 0U);
  1144. ModbusBackupData->retainedD[index] = 0U;
  1145. }
  1146. /*
  1147. * 数据初始化完成后最后写magic,避免初始化中途断电
  1148. * 却把不完整数据标记成有效
  1149. */
  1150. ModbusBackupData->magic = MODBUS_BACKUP_MAGIC;
  1151. }
  1152. }
  1153. void ModbusRetainedRegistersPoll(void)
  1154. {
  1155. uint16_t index;
  1156. uint16_t value;
  1157. for (index = 0; index < MODBUS_RETAINED_D_COUNT; index++)
  1158. {
  1159. (void)ModbusDataReadWord(MODBUS_DATA_DEVICE_D,
  1160. MODBUS_RETAINED_D_START + index,
  1161. &value);
  1162. if (value != ModbusRetainedSnapshot[index])
  1163. {
  1164. ModbusBackupData->retainedD[index] = value;
  1165. }
  1166. ModbusRetainedSnapshot[index] = value;
  1167. }
  1168. }