Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 
 
 

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