25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 
 
 
 

1114 satır
33 KiB

  1. #include "modbus_rtu_slave.h"
  2. #include <string.h>
  3. #define MODBUS_RTU_ADU_SIZE_MAX (256U) // Modbus RTU 最大 ADU 长度,单位为字节
  4. #define MODBUS_RTU_T15_US (750UL) // 高波特率下固定T1.5时间,单位us
  5. #define MODBUS_RTU_T35_US (1750UL) // 高波特率下固定T3.5时间,单位us
  6. #define MODBUS_RTU_BITS_PER_CHAR (11UL) // 8E1包含11个传输位
  7. #define MODBUS_RTU_HIGH_BAUD_LIMIT (19200UL) // 高低波特率计算方式的分界值
  8. #define MODBUS_BROADCAST_ADDRESS (0U) // 广播地址
  9. #define MODBUS_EX_ILLEGAL_FUNCTION (0x01U) // 非法功能码的异常码
  10. #define MODBUS_EX_ILLEGAL_ADDRESS (0x02U) // 非法地址的异常码
  11. #define MODBUS_EX_ILLEGAL_VALUE (0x03U) // 非法数据的异常码
  12. #define MODBUS_READ_COILS_MAX (2000U) // 一次ADU最大线圈读取数量
  13. #define MODBUS_READ_REGS_MAX (125U) // 一次ADU最大寄存器读取数量
  14. #define MODBUS_WRITE_COILS_MAX (1968U) // 一次ADU最大线圈写入数量
  15. #define MODBUS_WRITE_REGS_MAX (123U) // 一次ADU最大寄存器写入数量
  16. #define MODBUS_COIL_VALUE_ON (0xFF00U)
  17. #define MODBUS_COIL_VALUE_OFF (0x0000U)
  18. static UART_HandleTypeDef *ModbusUart;
  19. static uint8_t ModbusSlaveAddress;
  20. static HAL_StatusTypeDef ModbusStartReceive(void);
  21. /*最近一次字节尾到字节头的间隔周期数*/
  22. static volatile uint32_t ModbusLastInterFrameGapCycles;
  23. /**
  24. * DMA 缓冲区只由 DMA 写入;帧缓冲区在接收回调中完成一次快照,
  25. * 随后由任务解析,避免 DMA 重启后覆盖尚未处理的数据
  26. */
  27. static uint8_t ModbusRxDmaBuffer[MODBUS_RTU_ADU_SIZE_MAX];
  28. /* 保存被UART IDLE事件分开的DMA片段,达到T3.5后再提交解析 */
  29. static uint8_t ModbusRxAssemblyBuffer[MODBUS_RTU_ADU_SIZE_MAX];
  30. static uint8_t ModbusRxFrame[MODBUS_RTU_ADU_SIZE_MAX];
  31. static uint8_t ModbusTxFrame[MODBUS_RTU_ADU_SIZE_MAX];
  32. /* D100~D120 上一次已保存的值,用于检测数据是否变化 */
  33. static uint16_t ModbusRetainedSnapshot[MODBUS_RETAINED_D_COUNT];
  34. static volatile uint16_t ModbusRxFrameLength;
  35. static volatile uint8_t ModbusRxFrameReady;
  36. static volatile uint8_t ModbusTxBusy;
  37. static volatile uint16_t ModbusRxAssemblyLength; // 当前拼帧长度
  38. static volatile uint8_t ModbusRxAssemblyInvalid; // 帧内间隔超过T1.5时置1
  39. static volatile uint32_t ModbusRxLastByteCycle; // 上一片段末字节结束时刻
  40. static uint32_t ModbusRtuT15Cycles; // T1.5对应的CPU周期数
  41. static uint32_t ModbusRtuT35Cycles; // T3.5对应的CPU周期数
  42. static uint32_t ModbusRtuCharCycles; // 一个UART字符对应的CPU周期数
  43. static volatile uint32_t ModbusLastValidFrameTick;
  44. static volatile uint8_t ModbusHasReceivedValidFrame;
  45. static volatile MODBUS_BACKUP_DATA *ModbusBackupData =
  46. (volatile MODBUS_BACKUP_DATA *)BKPSRAM_BASE;
  47. /**
  48. * 10000 个保持寄存器占用 20000 字节;10000 个线圈按位存储,
  49. * 占用 1250 字节
  50. */
  51. static uint16_t ModbusHoldingRegisters[20000];
  52. #pragma location = ".ccmram"
  53. #pragma data_alignment = 4
  54. __root static uint16_t ModbusRegistersCcm[29999];
  55. static uint8_t ModbusCoils[(MODBUS_MAP_ITEM_COUNT + 7U) / 8U];
  56. volatile MODBUS_SLAVE_STATS ModbusSlaveStatistics;
  57. /**
  58. * @brief 计算 Modbus RTU CRC16 校验值
  59. * @param[in] data 待校验数据
  60. * @param[in] length 待校验数据长度
  61. * @return CRC16 校验值
  62. */
  63. static uint16_t ModbusCrc16(const uint8_t *data, uint16_t length)
  64. {
  65. uint16_t crc = 0xFFFFU;
  66. uint16_t index;
  67. uint8_t bit;
  68. for (index = 0U; index < length; index++)
  69. {
  70. crc ^= data[index];
  71. for (bit = 0U; bit < 8U; bit++)
  72. {
  73. if ((crc & 0x0001U) != 0U)
  74. {
  75. crc = (uint16_t)((crc >> 1U) ^ 0xA001U);
  76. }
  77. else
  78. {
  79. crc >>= 1U;
  80. }
  81. }
  82. }
  83. return crc;
  84. }
  85. /**
  86. * @brief 提取一个 16 位无符号整数
  87. * @param[in] data 两个字节的数据地址
  88. * @return 转换后的 16 位无符号整数
  89. */
  90. static uint16_t ModbusGetU16Be(const uint8_t *data)
  91. {
  92. return (uint16_t)(((uint16_t)data[0] << 8U) | data[1]);
  93. }
  94. /**
  95. * @brief 提取一个 24 位无符号整数
  96. * @param[in] data 3个字节的数据地址
  97. * @return 转换后的 24 位无符号整数
  98. */
  99. static uint32_t ModbusGetU24Be(const uint8_t *data)
  100. {
  101. return (uint32_t)(((uint32_t)data[0] << 16U) | ((uint32_t)data[1] << 8U)
  102. | (uint32_t)data[2]);
  103. }
  104. /**
  105. * @brief 检查连续数据地址范围是否合法
  106. * @param[in] start 起始地址
  107. * @param[in] quantity 数据项数量
  108. */
  109. static uint8_t ModbusAddressRangeIsValid(uint16_t start, uint16_t quantity)
  110. {
  111. if (start >= MODBUS_MAP_ITEM_COUNT)
  112. {
  113. return 0U;
  114. }
  115. // 先做减法再比较,避免溢出
  116. return (quantity <= (MODBUS_MAP_ITEM_COUNT - start)) ? 1U : 0U;
  117. }
  118. /**
  119. * @brief 读取已经确认地址合法的线圈
  120. * @param[in] address 线圈地址
  121. * @return 线圈状态,取值为 0 或 1
  122. */
  123. static uint8_t ModbusCoilGetUnchecked(uint16_t address)
  124. {
  125. uint8_t mask = (uint8_t)(1U << (address & 0x0007U));
  126. return ((ModbusCoils[address >> 3U] & mask) != 0U) ? 1U : 0U;
  127. }
  128. /**
  129. * @brief 设置已经确认地址合法的线圈
  130. * @param[in] address 线圈地址
  131. * @param[in] state 0 表示复位,非 0 表示置位
  132. */
  133. static void ModbusCoilSetUnchecked(uint16_t address, uint8_t state)
  134. {
  135. uint8_t mask = (uint8_t)(1U << (address & 0x0007U));
  136. if (state != 0U)
  137. {
  138. ModbusCoils[address >> 3U] |= mask;
  139. }
  140. else
  141. {
  142. ModbusCoils[address >> 3U] &= (uint8_t)(~mask);
  143. }
  144. }
  145. /**
  146. * @brief 初始化用于RTU帧间隔测量的DWT周期计数器
  147. * @param[in] baudRate 当前串口波特率
  148. */
  149. static void ModbusRtuTimingInit(uint32_t baudRate)
  150. {
  151. uint64_t coreClock;
  152. /* 开启DWT周期计数器,CYCCNT每经过一个CPU时钟周期自动加1 */
  153. CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
  154. DWT->CYCCNT = 0U;
  155. DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
  156. coreClock = SystemCoreClock;
  157. /* 一个字符周期数=CPU频率*每字符位数/波特率 */
  158. ModbusRtuCharCycles =
  159. (uint32_t)((coreClock * MODBUS_RTU_BITS_PER_CHAR) / baudRate);
  160. if (baudRate > MODBUS_RTU_HIGH_BAUD_LIMIT)
  161. {
  162. /* 高波特率使用固定750us和1750us,999999用于向上取整 */
  163. ModbusRtuT15Cycles =
  164. (uint32_t)((coreClock * MODBUS_RTU_T15_US + 999999UL) / 1000000UL);
  165. ModbusRtuT35Cycles =
  166. (uint32_t)((coreClock * MODBUS_RTU_T35_US + 999999UL) / 1000000UL);
  167. }
  168. else
  169. {
  170. /* 低波特率按照1.5个字符时间和3.5个字符时间计算 */
  171. ModbusRtuT15Cycles =
  172. (uint32_t)(((uint64_t)ModbusRtuCharCycles * 3UL + 1UL) / 2UL);
  173. ModbusRtuT35Cycles =
  174. (uint32_t)(((uint64_t)ModbusRtuCharCycles * 7UL + 1UL) / 2UL);
  175. }
  176. }
  177. /**
  178. * @brief 结束当前RTU接收组帧
  179. * @note 调用本函数时必须保证不会与串口接收中断并发执行
  180. */
  181. static void ModbusRxAssemblyFinalize(void)
  182. {
  183. if (ModbusRxAssemblyLength == 0U)
  184. {
  185. return;
  186. }
  187. /*拼帧区是否有数据*/
  188. if ((ModbusRxAssemblyInvalid == 0U) && (ModbusRxFrameReady == 0U))
  189. {
  190. /* 当前帧未违反T1.5且解析缓冲区空闲时提交完整帧 */
  191. (void)memcpy(ModbusRxFrame, ModbusRxAssemblyBuffer,
  192. ModbusRxAssemblyLength);
  193. ModbusRxFrameLength = ModbusRxAssemblyLength;
  194. ModbusRxFrameReady = 1U;
  195. }
  196. else
  197. {
  198. /* T1.5无效帧或上一帧尚未处理完成时丢弃 */
  199. ModbusSlaveStatistics.droppedFrameCount++;
  200. }
  201. ModbusRxAssemblyLength = 0U;
  202. ModbusRxAssemblyInvalid = 0U;
  203. }
  204. /**
  205. * @brief 静默时间达到T3.5后,将接收数据交给协议解析任务
  206. */
  207. static void ModbusTryFinalizeReceive(void)
  208. {
  209. uint32_t now;
  210. if (ModbusRxAssemblyLength == 0U)
  211. {
  212. return;
  213. }
  214. /* DMA缓冲区出现新数据时,说明串口仍在接收当前片段 */
  215. if ((ModbusUart->hdmarx != NULL) && /*串口DMA使能*/
  216. ((ModbusUart->Instance->CR3 & USART_CR3_DMAR) != 0U)
  217. && (__HAL_DMA_GET_COUNTER(ModbusUart->hdmarx)
  218. < MODBUS_RTU_ADU_SIZE_MAX))
  219. {
  220. return;
  221. }
  222. now = DWT->CYCCNT;
  223. /* 从末字节结束时刻开始计算静默时间,未达到T3.5时继续等待 */
  224. if ((uint32_t)(now - ModbusRxLastByteCycle) < ModbusRtuT35Cycles)
  225. {
  226. return;
  227. }
  228. /* 静默达到T3.5,当前RTU帧结束,发送响应前停止接收DMA */
  229. (void)HAL_UART_AbortReceive(ModbusUart);
  230. __disable_irq();
  231. ModbusRxAssemblyFinalize();
  232. __enable_irq();
  233. /* 无效帧被丢弃后,重新启动DMA接收。 */
  234. if (ModbusRxFrameReady == 0U)
  235. {
  236. (void)ModbusStartReceive();
  237. }
  238. }
  239. /**
  240. * @brief 启动 USART DMA 空闲接收
  241. * @retval HAL_OK DMA 接收启动成功
  242. * @retval HAL_BUSY 串口未配置或发送尚未结束
  243. * @return 其他 HAL 状态表示 DMA 接收启动失败
  244. */
  245. static HAL_StatusTypeDef ModbusStartReceive(void)
  246. {
  247. HAL_StatusTypeDef status;
  248. if (ModbusTxBusy != 0U)
  249. {
  250. return HAL_BUSY;
  251. }
  252. status = HAL_UARTEx_ReceiveToIdle_DMA(ModbusUart, ModbusRxDmaBuffer,
  253. sizeof(ModbusRxDmaBuffer));
  254. if ((status == HAL_OK) && (ModbusUart->hdmarx != NULL))
  255. {
  256. /*
  257. * 普通 Modbus 帧只应在 IDLE 或缓冲区满时交给应用
  258. * 关闭 DMA 半传输中断,避免长帧在一半位置被误认为完整帧
  259. */
  260. __HAL_DMA_DISABLE_IT(ModbusUart->hdmarx, DMA_IT_HT);
  261. }
  262. return status;
  263. }
  264. /**
  265. * @brief 在 RTU 帧末尾追加 CRC 低字节和高字节
  266. * @param[in,out] frame 待追加 CRC 的帧缓冲区
  267. * @param[in] payloadLength 不包含 CRC 的有效载荷长度
  268. */
  269. static void ModbusAppendCrc(uint8_t *frame, uint16_t payloadLength)
  270. {
  271. uint16_t crc = ModbusCrc16(frame, payloadLength);
  272. /* Modbus RTU 在线路上传输 CRC 低字节在前、高字节在后 */
  273. frame[payloadLength] = (uint8_t)(crc & 0x00FFU);
  274. frame[payloadLength + 1U] = (uint8_t)(crc >> 8U);
  275. }
  276. /**
  277. * @brief 构造 Modbus 异常响应
  278. * @param[in] function 请求功能码
  279. * @param[in] exception 异常码
  280. * @return 异常响应 ADU 长度
  281. */
  282. static uint16_t ModbusBuildException(uint8_t function, uint8_t exception)
  283. {
  284. ModbusTxFrame[0] = ModbusSlaveAddress;
  285. ModbusTxFrame[1] = (uint8_t)(function | 0x80U);
  286. ModbusTxFrame[2] = exception;
  287. ModbusAppendCrc(ModbusTxFrame, 3U);
  288. return 5U;
  289. }
  290. /**
  291. * @brief 处理读线圈功能码 0x01
  292. * @param[in] request RTU 请求帧
  293. * @param[in] requestLength 请求帧长度
  294. * @return 待发送响应长度,异常请求返回异常响应长度
  295. */
  296. static uint16_t ModbusProcessReadCoils(const uint8_t *request,
  297. uint16_t requestLength)
  298. {
  299. uint16_t start;
  300. uint16_t quantity;
  301. uint16_t index;
  302. uint8_t byteCount;
  303. if (requestLength != 8U)
  304. {
  305. ModbusSlaveStatistics.illegalValueCount++;
  306. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  307. }
  308. start = ModbusGetU16Be(&request[2]);
  309. quantity = ModbusGetU16Be(&request[4]);
  310. if ((quantity == 0U)
  311. || (quantity > MODBUS_READ_COILS_MAX)) // 数量0或者数量大于最大值
  312. {
  313. ModbusSlaveStatistics.illegalValueCount++;
  314. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  315. }
  316. if (ModbusAddressRangeIsValid(start, quantity) == 0U) // 地址检查
  317. {
  318. ModbusSlaveStatistics.illegalAddressCount++;
  319. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  320. }
  321. byteCount = (uint8_t)((quantity + 7U) / 8U);
  322. ModbusTxFrame[0] = ModbusSlaveAddress;
  323. ModbusTxFrame[1] = 0X01;
  324. ModbusTxFrame[2] = byteCount;
  325. (void)memset(&ModbusTxFrame[3], 0, byteCount);
  326. for (index = 0U; index < quantity; index++)
  327. {
  328. if (ModbusCoilGetUnchecked((uint16_t)(start + index)) != 0U)
  329. {
  330. ModbusTxFrame[3U + (index >> 3U)] |=
  331. (uint8_t)(1U << (index & 0x0007U));
  332. }
  333. }
  334. ModbusAppendCrc(ModbusTxFrame, (uint16_t)(3U + byteCount));
  335. return (uint16_t)(5U + byteCount);
  336. }
  337. /**
  338. * @brief 处理读保持寄存器功能码 0x03
  339. * @param[in] request RTU 请求帧
  340. * @param[in] requestLength 请求帧长度
  341. * @return 待发送响应长度,异常请求返回异常响应长度
  342. */
  343. static uint16_t ModbusProcessReadHolding(const uint8_t *request,
  344. uint16_t requestLength)
  345. {
  346. uint16_t start;
  347. uint16_t quantity;
  348. uint16_t index;
  349. uint16_t value;
  350. if (requestLength != 8U)
  351. {
  352. ModbusSlaveStatistics.illegalValueCount++;
  353. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  354. }
  355. start = ModbusGetU16Be(&request[2]);
  356. quantity = ModbusGetU16Be(&request[4]);
  357. if ((quantity == 0U)
  358. || (quantity > MODBUS_READ_REGS_MAX)) // 数量0或者数量大于最大值
  359. {
  360. ModbusSlaveStatistics.illegalValueCount++;
  361. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  362. }
  363. if (ModbusAddressRangeIsValid(start, quantity) == 0U) // 地址检查
  364. {
  365. if ((start >= 20000U) && (start < 25000U) && (quantity > 0U)
  366. && (quantity <= (25000U - start)))
  367. goto tx;
  368. ModbusSlaveStatistics.illegalAddressCount++;
  369. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  370. }
  371. tx:
  372. ModbusTxFrame[0] = ModbusSlaveAddress;
  373. ModbusTxFrame[1] = 0X03;
  374. ModbusTxFrame[2] = (uint8_t)(quantity * 2U);
  375. for (index = 0U; index < quantity; index++)
  376. {
  377. uint16_t address = start + index;
  378. /* D20000~D24999映射到D1、D3、D5……D9999 */
  379. if (address >= 20000U)
  380. {
  381. address = (address - 20000U) * 2U + 1U;
  382. }
  383. value = ModbusHoldingRegisters[address];
  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. ModbusHoldingRegisters[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. ModbusHoldingRegisters[start + index] =
  560. ModbusGetU16Be(&request[7U + index * 2U]);
  561. }
  562. if (isBroadcast != 0U)
  563. {
  564. return 0U;
  565. }
  566. ModbusTxFrame[0] = ModbusSlaveAddress;
  567. ModbusTxFrame[1] = 0X10;
  568. (void)memcpy(&ModbusTxFrame[2], &request[2], 4U);
  569. ModbusAppendCrc(ModbusTxFrame, 6U);
  570. return 8U;
  571. }
  572. /**
  573. * @brief 处理读取扩展地址保持寄存器功能码0x48
  574. * @param[in] request RTU请求帧
  575. * @param[in] requestLength 请求帧长度
  576. * @return 待发送响应长度,异常请求返回异常响应长度
  577. */
  578. static uint16_t ModbusProcessReadBigHolding(const uint8_t *request,
  579. uint16_t requestLength)
  580. {
  581. uint32_t start;
  582. uint32_t currentAddress;
  583. uint16_t quantity;
  584. uint16_t index;
  585. uint16_t value;
  586. /* 请求帧:站号1 + 功能码1 + 地址3 + 数量2 + CRC2 */
  587. if (requestLength != 9U)
  588. {
  589. ModbusSlaveStatistics.illegalValueCount++;
  590. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  591. }
  592. /* 提取24位起始地址和16位寄存器数量 */
  593. start = ModbusGetU24Be(&request[2]);
  594. quantity = ModbusGetU16Be(&request[5]);
  595. /* 一次最多读取125个寄存器 */
  596. if ((quantity == 0U) || (quantity > MODBUS_READ_REGS_MAX))
  597. {
  598. ModbusSlaveStatistics.illegalValueCount++;
  599. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  600. }
  601. /*
  602. * 普通SRAM有40000个寄存器,CCMRAM有29999个寄存器,
  603. * 总地址范围为0~69998
  604. */
  605. if ((start >= 69999UL) || ((uint32_t)quantity > (69999UL - start)))
  606. {
  607. ModbusSlaveStatistics.illegalAddressCount++;
  608. return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  609. }
  610. /* 组成正常响应帧头 */
  611. ModbusTxFrame[0] = ModbusSlaveAddress;
  612. ModbusTxFrame[1] = 0x48U;
  613. ModbusTxFrame[2] = (uint8_t)(quantity * 2U);
  614. for (index = 0U; index < quantity; index++)
  615. {
  616. currentAddress = start + (uint32_t)index;
  617. /*
  618. * 地址0~39999位于普通SRAM;
  619. * 地址40000~69998位于CCMRAM
  620. */
  621. if (currentAddress < 40000UL)
  622. {
  623. value = ModbusHoldingRegisters[currentAddress];
  624. }
  625. else
  626. {
  627. value = ModbusRegistersCcm[currentAddress - 40000UL];
  628. }
  629. /* 每个寄存器按照高字节、低字节装入响应帧 */
  630. ModbusTxFrame[3U + index * 2U] = (uint8_t)(value >> 8U);
  631. ModbusTxFrame[4U + index * 2U] = (uint8_t)(value & 0x00FFU);
  632. }
  633. /* 添加CRC */
  634. ModbusAppendCrc(ModbusTxFrame, (uint16_t)(3U + quantity * 2U));
  635. return (uint16_t)(5U + quantity * 2U);
  636. }
  637. /**
  638. * @brief 校验并分发一帧 Modbus RTU 请求
  639. * @param[in] request RTU 请求帧
  640. * @param[in] requestLength 请求帧长度
  641. * @return 待发送响应长度;无需响应时返回 0
  642. */
  643. static uint16_t ModbusProcessRequest(const uint8_t *request,
  644. uint16_t requestLength)
  645. {
  646. uint16_t calculatedCrc;
  647. uint16_t receivedCrc;
  648. uint8_t isBroadcast;
  649. if (requestLength < 4U)
  650. {
  651. return 0U;
  652. }
  653. calculatedCrc = ModbusCrc16(request, (uint16_t)(requestLength - 2U));
  654. receivedCrc = (uint16_t)(request[requestLength - 2U]
  655. | ((uint16_t)request[requestLength - 1U] << 8U));
  656. if (calculatedCrc != receivedCrc) // CRC校验
  657. {
  658. ModbusSlaveStatistics.crcErrorCount++;
  659. return 0U;
  660. }
  661. if ((request[0] != ModbusSlaveAddress)
  662. && (request[0] != MODBUS_BROADCAST_ADDRESS)) // 非法从站地址
  663. {
  664. ModbusSlaveStatistics.ignoredAddressCount++;
  665. return 0U;
  666. }
  667. isBroadcast =
  668. (request[0] == MODBUS_BROADCAST_ADDRESS) ? 1U : 0U; // 是否广播请求
  669. ModbusSlaveStatistics.validFrameCount++;
  670. ModbusLastValidFrameTick = HAL_GetTick();
  671. ModbusHasReceivedValidFrame = 1U;
  672. switch (request[1])
  673. {
  674. case 0X01U: // 读线圈
  675. // 广播请求不允许读取,从站不作响应
  676. return (isBroadcast != 0U)
  677. ? 0U
  678. : ModbusProcessReadCoils(request, requestLength);
  679. case 0X03U: // 读保持寄存器
  680. return (isBroadcast != 0U)
  681. ? 0U
  682. : ModbusProcessReadHolding(request, requestLength);
  683. case 0x05U: // 写单个线圈
  684. return ModbusProcessWriteSingleCoil(request, requestLength,
  685. isBroadcast);
  686. case 0x06U: // 写单个保持寄存器
  687. return ModbusProcessWriteSingleRegister(request, requestLength,
  688. isBroadcast);
  689. case 0x0FU: // 写多个线圈
  690. return ModbusProcessWriteMultipleCoils(request, requestLength,
  691. isBroadcast);
  692. case 0x10U: // 写多个保持寄存器
  693. return ModbusProcessWriteMultipleRegisters(request, requestLength,
  694. isBroadcast);
  695. case 0x48U: // 读大地址保持寄存器
  696. return (isBroadcast != 0U)
  697. ? 0U
  698. : ModbusProcessReadBigHolding(request, requestLength);
  699. default: // 未知功能码
  700. ModbusSlaveStatistics.illegalFunctionCount++;
  701. return (isBroadcast != 0U)
  702. ? 0U
  703. : ModbusBuildException(request[1],
  704. MODBUS_EX_ILLEGAL_FUNCTION);
  705. }
  706. }
  707. HAL_StatusTypeDef ModbusSlaveInit(UART_HandleTypeDef *huart,
  708. uint8_t slaveAddress)
  709. {
  710. ModbusUart = huart;
  711. ModbusSlaveAddress = slaveAddress;
  712. /* 清空拼帧状态并根据当前波特率初始化T1.5和T3.5 */
  713. ModbusRxAssemblyLength = 0U;
  714. ModbusRxAssemblyInvalid = 0U;
  715. ModbusRxFrameReady = 0U;
  716. ModbusTxBusy = 0U;
  717. ModbusRtuTimingInit(huart->Init.BaudRate);
  718. //ModbusHoldingRegisters[HMI_REG_DEVICE_ID] = 0xF407U;
  719. return ModbusStartReceive();
  720. }
  721. void ModbusSlavePoll(void)
  722. {
  723. uint16_t responseLength;
  724. /* 检查末字节后的静默时间是否已经达到T3.5 */
  725. ModbusTryFinalizeReceive();
  726. if ((ModbusRxFrameReady == 0U) || (ModbusTxBusy != 0U))
  727. {
  728. return;
  729. }
  730. responseLength = ModbusProcessRequest(ModbusRxFrame, ModbusRxFrameLength);
  731. if (responseLength > 0U)
  732. {
  733. ModbusTxBusy = 1U;
  734. if (HAL_UART_Transmit_DMA(ModbusUart, ModbusTxFrame, responseLength)
  735. == HAL_OK)
  736. {
  737. ModbusSlaveStatistics.txFrameCount++;
  738. }
  739. else
  740. {
  741. ModbusTxBusy = 0U;
  742. ModbusSlaveStatistics.uartErrorCount++;
  743. (void)ModbusStartReceive(); // 重启DMA接收
  744. }
  745. }
  746. else
  747. {
  748. (void)ModbusStartReceive();
  749. }
  750. /*
  751. * 当前帧;处理完成后再释放帧槽
  752. */
  753. ModbusRxFrameReady = 0U;
  754. }
  755. void ModbusSlaveOnRxEvent(UART_HandleTypeDef *huart, uint16_t size)
  756. {
  757. HAL_UART_RxEventTypeTypeDef eventType;
  758. uint32_t now;
  759. uint32_t lastByteCycle;
  760. uint32_t firstByteCycle;
  761. uint32_t chunkCycles;
  762. uint32_t interFrameGap;
  763. ModbusSlaveStatistics.rxEventCount++; // 串口接收事件计数
  764. if ((huart != ModbusUart) || (ModbusUart == NULL))
  765. {
  766. return;
  767. }
  768. if ((size > 0U) && (size <= MODBUS_RTU_ADU_SIZE_MAX))
  769. {
  770. now = DWT->CYCCNT;
  771. eventType = HAL_UARTEx_GetRxEventType(huart);
  772. /* IDLE事件比末字节结束晚约一个字符时间,减去字符时间得到末字节时刻 */
  773. lastByteCycle = now;
  774. if (eventType == HAL_UART_RXEVENT_IDLE)
  775. {
  776. lastByteCycle -= ModbusRtuCharCycles;
  777. }
  778. /* 根据本次接收字节数反推DMA片段首字节的开始时刻 */
  779. chunkCycles = (uint32_t)((uint64_t)size * ModbusRtuCharCycles);
  780. firstByteCycle = lastByteCycle - chunkCycles;
  781. if (ModbusRxAssemblyLength > 0U)
  782. {
  783. interFrameGap = (uint32_t)(firstByteCycle - ModbusRxLastByteCycle);
  784. ModbusLastInterFrameGapCycles = interFrameGap;
  785. if (interFrameGap >= ModbusRtuT35Cycles)
  786. {
  787. /* 间隔达到T3.5,结束上一帧,本片段作为新帧开始 */
  788. ModbusRxAssemblyFinalize();
  789. }
  790. else if (interFrameGap > ModbusRtuT15Cycles)
  791. {
  792. /* 帧内静默超过T1.5但不足T3.5,标记整帧无效 */
  793. ModbusRxAssemblyInvalid = 1U;
  794. }
  795. else
  796. {
  797. /* 间隔不超过T1.5,当前片段继续拼入同一帧 */
  798. }
  799. }
  800. if (size
  801. <= (uint16_t)(MODBUS_RTU_ADU_SIZE_MAX - ModbusRxAssemblyLength))
  802. {
  803. (void)memcpy(&ModbusRxAssemblyBuffer[ModbusRxAssemblyLength],
  804. ModbusRxDmaBuffer, size);
  805. ModbusRxAssemblyLength += size;
  806. }
  807. else
  808. {
  809. ModbusRxAssemblyInvalid = 1U;
  810. }
  811. /* 保存末字节时刻并立即重启DMA,继续等待可能的后续片段 */
  812. ModbusRxLastByteCycle = lastByteCycle;
  813. (void)ModbusStartReceive();
  814. }
  815. else
  816. {
  817. // 长度异常帧
  818. ModbusSlaveStatistics.droppedFrameCount++;
  819. ModbusRxAssemblyLength = 0U;
  820. ModbusRxAssemblyInvalid = 0U;
  821. (void)ModbusStartReceive();
  822. }
  823. }
  824. void ModbusSlaveOnTxComplete(UART_HandleTypeDef *huart)
  825. {
  826. if ((huart != ModbusUart) || (ModbusUart == NULL))
  827. {
  828. return;
  829. }
  830. ModbusTxBusy = 0U;
  831. (void)ModbusStartReceive(); // 重启DMA接收
  832. }
  833. void ModbusSlaveOnUartError(UART_HandleTypeDef *huart)
  834. {
  835. if ((huart != ModbusUart) || (ModbusUart == NULL))
  836. {
  837. return;
  838. }
  839. ModbusSlaveStatistics.uartErrorCount++;
  840. ModbusTxBusy = 0U;
  841. ModbusRxAssemblyLength = 0U;
  842. ModbusRxAssemblyInvalid = 0U;
  843. (void)HAL_UART_Abort(huart); // 立即终止这个串口当前正在进行的发送和接收操作
  844. (void)ModbusStartReceive(); // 重启DMA接收
  845. }
  846. uint8_t ModbusSlaveSetHoldingRegister(uint16_t address, uint16_t value)
  847. {
  848. if (address >= MODBUS_MAP_ITEM_COUNT)
  849. {
  850. return 0U;
  851. }
  852. ModbusHoldingRegisters[address] = value;
  853. return 1U;
  854. }
  855. uint8_t ModbusSlaveGetHoldingRegister(uint16_t address, uint16_t *value)
  856. {
  857. if ((address >= MODBUS_MAP_ITEM_COUNT) || (value == NULL))
  858. {
  859. return 0U;
  860. }
  861. *value = ModbusHoldingRegisters[address];
  862. return 1U;
  863. }
  864. uint8_t ModbusSlaveSetCoil(uint16_t address, uint8_t state)
  865. {
  866. if (address >= MODBUS_MAP_ITEM_COUNT)
  867. {
  868. return 0U;
  869. }
  870. ModbusCoilSetUnchecked(address, state);
  871. return 1U;
  872. }
  873. uint8_t ModbusSlaveGetCoil(uint16_t address, uint8_t *state)
  874. {
  875. if ((address >= MODBUS_MAP_ITEM_COUNT) || (state == NULL))
  876. {
  877. return 0U;
  878. }
  879. *state = ModbusCoilGetUnchecked(address);
  880. return 1U;
  881. }
  882. uint8_t ModbusSlaveIsConnected(uint32_t timeoutMs)
  883. {
  884. if (ModbusHasReceivedValidFrame == 0U)
  885. {
  886. return 0U;
  887. }
  888. return ((HAL_GetTick() - ModbusLastValidFrameTick) <= timeoutMs) ? 1U : 0U;
  889. }
  890. // 上电恢复函数
  891. void ModbusRetainedRegistersLoad(void)
  892. {
  893. uint16_t index;
  894. if (ModbusBackupData->magic == MODBUS_BACKUP_MAGIC)
  895. {
  896. for (index = 0U; index < MODBUS_RETAINED_D_COUNT; index++)
  897. {
  898. ModbusHoldingRegisters[MODBUS_RETAINED_D_START + index] =
  899. ModbusBackupData->retainedD[index];
  900. }
  901. }
  902. else
  903. {
  904. for (index = 0U; index < MODBUS_RETAINED_D_COUNT; index++)
  905. {
  906. ModbusHoldingRegisters[MODBUS_RETAINED_D_START + index] = 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. value = ModbusHoldingRegisters[MODBUS_RETAINED_D_START + index];
  923. if (value != ModbusRetainedSnapshot[index])
  924. {
  925. ModbusBackupData->retainedD[index] = value;
  926. }
  927. ModbusRetainedSnapshot[index] = value;
  928. }
  929. }