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.
 
 
 
 
 
 

1103 wiersze
30 KiB

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