Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 
 
 

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