Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 
 

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