選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 
 
 

835 行
23 KiB

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