You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

667 regels
17 KiB

  1. #include "modbus_rtu_slave.h"
  2. #include <string.h>
  3. /* Modbus RTU最大ADU为256字节。 */
  4. #define MODBUS_RTU_ADU_SIZE_MAX 256U
  5. /*
  6. * 当前串口为115200 bit/s。Modbus规范建议波特率高于19200时,
  7. * 帧间隔固定使用1.75ms,这里向上取整为2ms。
  8. */
  9. #define MODBUS_RTU_FRAME_GAP_MS 2U
  10. #define MODBUS_BROADCAST_ADDRESS 0U
  11. #define MODBUS_FC_READ_COILS 0x01U
  12. #define MODBUS_FC_READ_HOLDING_REGS 0x03U
  13. #define MODBUS_FC_WRITE_SINGLE_REG 0x06U
  14. #define MODBUS_FC_WRITE_MULTIPLE_COILS 0x0FU
  15. #define MODBUS_FC_WRITE_MULTIPLE_REGS 0x10U
  16. #define MODBUS_EX_ILLEGAL_FUNCTION 0x01U
  17. #define MODBUS_EX_ILLEGAL_ADDRESS 0x02U
  18. #define MODBUS_EX_ILLEGAL_VALUE 0x03U
  19. #define MODBUS_READ_COILS_MAX 2000U
  20. #define MODBUS_READ_REGS_MAX 125U
  21. #define MODBUS_WRITE_COILS_MAX 1968U
  22. #define MODBUS_WRITE_REGS_MAX 123U
  23. static UART_HandleTypeDef *s_uart;
  24. static uint8_t s_slave_address;
  25. /*
  26. * DMA缓冲区只由DMA写入;帧缓冲区在接收回调中完成一次快照,
  27. * 随后由任务解析,避免DMA重启后覆盖尚未处理的数据。
  28. */
  29. static uint8_t s_rx_dma_buffer[MODBUS_RTU_ADU_SIZE_MAX];
  30. static uint8_t s_rx_frame[MODBUS_RTU_ADU_SIZE_MAX];
  31. static uint8_t s_tx_frame[MODBUS_RTU_ADU_SIZE_MAX];
  32. static volatile uint16_t s_rx_frame_length;
  33. static volatile uint8_t s_rx_frame_ready;
  34. static volatile uint8_t s_tx_busy;
  35. static volatile uint32_t s_rx_event_tick;
  36. static volatile uint32_t s_last_valid_frame_tick;
  37. static volatile uint8_t s_has_received_valid_frame;
  38. /* 10000个保持寄存器占20KB;10000个线圈按位存储,占1250字节。 */
  39. static uint16_t s_holding_registers[MODBUS_MAP_ITEM_COUNT];
  40. static uint8_t s_coils[(MODBUS_MAP_ITEM_COUNT + 7U) / 8U];
  41. volatile ModbusSlaveStats g_modbus_slave_stats;
  42. static uint16_t Modbus_Crc16(const uint8_t *data, uint16_t length)
  43. {
  44. uint16_t crc = 0xFFFFU;
  45. uint16_t index;
  46. uint8_t bit;
  47. for (index = 0U; index < length; index++)
  48. {
  49. crc ^= data[index];
  50. for (bit = 0U; bit < 8U; bit++)
  51. {
  52. if ((crc & 0x0001U) != 0U)
  53. {
  54. crc = (uint16_t)((crc >> 1U) ^ 0xA001U);
  55. }
  56. else
  57. {
  58. crc >>= 1U;
  59. }
  60. }
  61. }
  62. return crc;
  63. }
  64. static uint16_t Modbus_GetU16Be(const uint8_t *data)
  65. {
  66. return (uint16_t)(((uint16_t)data[0] << 8U) | data[1]);
  67. }
  68. static uint8_t Modbus_AddressRangeIsValid(uint16_t start, uint16_t quantity)
  69. {
  70. if ((quantity == 0U) || (start >= MODBUS_MAP_ITEM_COUNT))
  71. {
  72. return 0U;
  73. }
  74. /* 先做减法再比较,避免start + quantity发生16位溢出。 */
  75. return (quantity <= (MODBUS_MAP_ITEM_COUNT - start)) ? 1U : 0U;
  76. }
  77. static uint8_t Modbus_CoilGetUnchecked(uint16_t address)
  78. {
  79. uint8_t mask = (uint8_t)(1U << (address & 0x0007U));
  80. return ((s_coils[address >> 3U] & mask) != 0U) ? 1U : 0U;
  81. }
  82. static void Modbus_CoilSetUnchecked(uint16_t address, uint8_t state)
  83. {
  84. uint8_t mask = (uint8_t)(1U << (address & 0x0007U));
  85. if (state != 0U)
  86. {
  87. s_coils[address >> 3U] |= mask;
  88. }
  89. else
  90. {
  91. s_coils[address >> 3U] &= (uint8_t)(~mask);
  92. }
  93. }
  94. static HAL_StatusTypeDef Modbus_StartReceive(void)
  95. {
  96. HAL_StatusTypeDef status;
  97. if ((s_uart == NULL) || (s_tx_busy != 0U))
  98. {
  99. return HAL_BUSY;
  100. }
  101. status = HAL_UARTEx_ReceiveToIdle_DMA(s_uart,
  102. s_rx_dma_buffer,
  103. sizeof(s_rx_dma_buffer));
  104. if ((status == HAL_OK) && (s_uart->hdmarx != NULL))
  105. {
  106. /*
  107. * 普通Modbus帧只应在IDLE或缓冲区满时交给应用。
  108. * 关闭DMA半传输中断,避免长帧在一半位置被误认为完整帧。
  109. */
  110. __HAL_DMA_DISABLE_IT(s_uart->hdmarx, DMA_IT_HT);
  111. }
  112. return status;
  113. }
  114. static void Modbus_StopReceive(void)
  115. {
  116. if (s_uart != NULL)
  117. {
  118. (void)HAL_UART_AbortReceive(s_uart);
  119. }
  120. }
  121. static void Modbus_AppendCrc(uint8_t *frame, uint16_t payload_length)
  122. {
  123. uint16_t crc = Modbus_Crc16(frame, payload_length);
  124. /* Modbus RTU在线路上传输CRC低字节在前、高字节在后。 */
  125. frame[payload_length] = (uint8_t)(crc & 0x00FFU);
  126. frame[payload_length + 1U] = (uint8_t)(crc >> 8U);
  127. }
  128. static uint16_t Modbus_BuildException(uint8_t function, uint8_t exception)
  129. {
  130. s_tx_frame[0] = s_slave_address;
  131. s_tx_frame[1] = (uint8_t)(function | 0x80U);
  132. s_tx_frame[2] = exception;
  133. Modbus_AppendCrc(s_tx_frame, 3U);
  134. return 5U;
  135. }
  136. static uint16_t Modbus_ProcessReadCoils(const uint8_t *request,
  137. uint16_t request_length)
  138. {
  139. uint16_t start;
  140. uint16_t quantity;
  141. uint16_t index;
  142. uint8_t byte_count;
  143. if (request_length != 8U)
  144. {
  145. g_modbus_slave_stats.illegal_value_count++;
  146. return Modbus_BuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  147. }
  148. start = Modbus_GetU16Be(&request[2]);
  149. quantity = Modbus_GetU16Be(&request[4]);
  150. if ((quantity == 0U) || (quantity > MODBUS_READ_COILS_MAX))
  151. {
  152. g_modbus_slave_stats.illegal_value_count++;
  153. return Modbus_BuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  154. }
  155. if (Modbus_AddressRangeIsValid(start, quantity) == 0U)
  156. {
  157. g_modbus_slave_stats.illegal_address_count++;
  158. return Modbus_BuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  159. }
  160. byte_count = (uint8_t)((quantity + 7U) / 8U);
  161. s_tx_frame[0] = s_slave_address;
  162. s_tx_frame[1] = MODBUS_FC_READ_COILS;
  163. s_tx_frame[2] = byte_count;
  164. (void)memset(&s_tx_frame[3], 0, byte_count);
  165. /* Modbus规定请求的第一个线圈放在响应数据首字节的最低位。 */
  166. for (index = 0U; index < quantity; index++)
  167. {
  168. if (Modbus_CoilGetUnchecked((uint16_t)(start + index)) != 0U)
  169. {
  170. s_tx_frame[3U + (index >> 3U)] |=
  171. (uint8_t)(1U << (index & 0x0007U));
  172. }
  173. }
  174. Modbus_AppendCrc(s_tx_frame, (uint16_t)(3U + byte_count));
  175. return (uint16_t)(5U + byte_count);
  176. }
  177. static uint16_t Modbus_ProcessReadHolding(const uint8_t *request,
  178. uint16_t request_length)
  179. {
  180. uint16_t start;
  181. uint16_t quantity;
  182. uint16_t index;
  183. uint16_t value;
  184. if (request_length != 8U)
  185. {
  186. g_modbus_slave_stats.illegal_value_count++;
  187. return Modbus_BuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  188. }
  189. start = Modbus_GetU16Be(&request[2]);
  190. quantity = Modbus_GetU16Be(&request[4]);
  191. if ((quantity == 0U) || (quantity > MODBUS_READ_REGS_MAX))
  192. {
  193. g_modbus_slave_stats.illegal_value_count++;
  194. return Modbus_BuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  195. }
  196. if (Modbus_AddressRangeIsValid(start, quantity) == 0U)
  197. {
  198. g_modbus_slave_stats.illegal_address_count++;
  199. return Modbus_BuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  200. }
  201. s_tx_frame[0] = s_slave_address;
  202. s_tx_frame[1] = MODBUS_FC_READ_HOLDING_REGS;
  203. s_tx_frame[2] = (uint8_t)(quantity * 2U);
  204. for (index = 0U; index < quantity; index++)
  205. {
  206. value = s_holding_registers[start + index];
  207. s_tx_frame[3U + index * 2U] = (uint8_t)(value >> 8U);
  208. s_tx_frame[4U + index * 2U] = (uint8_t)(value & 0x00FFU);
  209. }
  210. Modbus_AppendCrc(s_tx_frame, (uint16_t)(3U + quantity * 2U));
  211. return (uint16_t)(5U + quantity * 2U);
  212. }
  213. static uint16_t Modbus_ProcessWriteSingleRegister(const uint8_t *request,
  214. uint16_t request_length,
  215. uint8_t is_broadcast)
  216. {
  217. uint16_t address;
  218. uint16_t value;
  219. if (request_length != 8U)
  220. {
  221. g_modbus_slave_stats.illegal_value_count++;
  222. return (is_broadcast != 0U) ? 0U :
  223. Modbus_BuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  224. }
  225. address = Modbus_GetU16Be(&request[2]);
  226. value = Modbus_GetU16Be(&request[4]);
  227. if (address >= MODBUS_MAP_ITEM_COUNT)
  228. {
  229. g_modbus_slave_stats.illegal_address_count++;
  230. return (is_broadcast != 0U) ? 0U :
  231. Modbus_BuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  232. }
  233. s_holding_registers[address] = value;
  234. if (is_broadcast != 0U)
  235. {
  236. return 0U;
  237. }
  238. /* 0x06的正常响应必须原样回显请求的前6个字节。 */
  239. (void)memcpy(s_tx_frame, request, 6U);
  240. Modbus_AppendCrc(s_tx_frame, 6U);
  241. return 8U;
  242. }
  243. static uint16_t Modbus_ProcessWriteMultipleCoils(const uint8_t *request,
  244. uint16_t request_length,
  245. uint8_t is_broadcast)
  246. {
  247. uint16_t start;
  248. uint16_t quantity;
  249. uint16_t index;
  250. uint8_t byte_count;
  251. uint8_t expected_byte_count;
  252. if (request_length < 9U)
  253. {
  254. g_modbus_slave_stats.illegal_value_count++;
  255. return (is_broadcast != 0U) ? 0U :
  256. Modbus_BuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  257. }
  258. start = Modbus_GetU16Be(&request[2]);
  259. quantity = Modbus_GetU16Be(&request[4]);
  260. byte_count = request[6];
  261. expected_byte_count = (uint8_t)((quantity + 7U) / 8U);
  262. if ((quantity == 0U) ||
  263. (quantity > MODBUS_WRITE_COILS_MAX) ||
  264. (byte_count != expected_byte_count) ||
  265. (request_length != (uint16_t)(9U + byte_count)))
  266. {
  267. g_modbus_slave_stats.illegal_value_count++;
  268. return (is_broadcast != 0U) ? 0U :
  269. Modbus_BuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  270. }
  271. if (Modbus_AddressRangeIsValid(start, quantity) == 0U)
  272. {
  273. g_modbus_slave_stats.illegal_address_count++;
  274. return (is_broadcast != 0U) ? 0U :
  275. Modbus_BuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  276. }
  277. for (index = 0U; index < quantity; index++)
  278. {
  279. Modbus_CoilSetUnchecked((uint16_t)(start + index),
  280. (uint8_t)((request[7U + (index >> 3U)] >> (index & 0x0007U)) & 0x01U));
  281. }
  282. if (is_broadcast != 0U)
  283. {
  284. return 0U;
  285. }
  286. s_tx_frame[0] = s_slave_address;
  287. s_tx_frame[1] = MODBUS_FC_WRITE_MULTIPLE_COILS;
  288. (void)memcpy(&s_tx_frame[2], &request[2], 4U);
  289. Modbus_AppendCrc(s_tx_frame, 6U);
  290. return 8U;
  291. }
  292. static uint16_t Modbus_ProcessWriteMultipleRegisters(const uint8_t *request,
  293. uint16_t request_length,
  294. uint8_t is_broadcast)
  295. {
  296. uint16_t start;
  297. uint16_t quantity;
  298. uint16_t index;
  299. uint8_t byte_count;
  300. if (request_length < 9U)
  301. {
  302. g_modbus_slave_stats.illegal_value_count++;
  303. return (is_broadcast != 0U) ? 0U :
  304. Modbus_BuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  305. }
  306. start = Modbus_GetU16Be(&request[2]);
  307. quantity = Modbus_GetU16Be(&request[4]);
  308. byte_count = request[6];
  309. if ((quantity == 0U) ||
  310. (quantity > MODBUS_WRITE_REGS_MAX) ||
  311. (byte_count != (uint8_t)(quantity * 2U)) ||
  312. (request_length != (uint16_t)(9U + byte_count)))
  313. {
  314. g_modbus_slave_stats.illegal_value_count++;
  315. return (is_broadcast != 0U) ? 0U :
  316. Modbus_BuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
  317. }
  318. if (Modbus_AddressRangeIsValid(start, quantity) == 0U)
  319. {
  320. g_modbus_slave_stats.illegal_address_count++;
  321. return (is_broadcast != 0U) ? 0U :
  322. Modbus_BuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
  323. }
  324. for (index = 0U; index < quantity; index++)
  325. {
  326. s_holding_registers[start + index] =
  327. Modbus_GetU16Be(&request[7U + index * 2U]);
  328. }
  329. if (is_broadcast != 0U)
  330. {
  331. return 0U;
  332. }
  333. s_tx_frame[0] = s_slave_address;
  334. s_tx_frame[1] = MODBUS_FC_WRITE_MULTIPLE_REGS;
  335. (void)memcpy(&s_tx_frame[2], &request[2], 4U);
  336. Modbus_AppendCrc(s_tx_frame, 6U);
  337. return 8U;
  338. }
  339. static uint16_t Modbus_ProcessRequest(const uint8_t *request,
  340. uint16_t request_length)
  341. {
  342. uint16_t calculated_crc;
  343. uint16_t received_crc;
  344. uint8_t is_broadcast;
  345. if (request_length < 4U)
  346. {
  347. return 0U;
  348. }
  349. calculated_crc = Modbus_Crc16(request, (uint16_t)(request_length - 2U));
  350. received_crc = (uint16_t)(request[request_length - 2U] |
  351. ((uint16_t)request[request_length - 1U] << 8U));
  352. if (calculated_crc != received_crc)
  353. {
  354. g_modbus_slave_stats.crc_error_count++;
  355. return 0U;
  356. }
  357. if ((request[0] != s_slave_address) &&
  358. (request[0] != MODBUS_BROADCAST_ADDRESS))
  359. {
  360. g_modbus_slave_stats.ignored_address_count++;
  361. return 0U;
  362. }
  363. is_broadcast = (request[0] == MODBUS_BROADCAST_ADDRESS) ? 1U : 0U;
  364. g_modbus_slave_stats.valid_frame_count++;
  365. s_last_valid_frame_tick = HAL_GetTick();
  366. s_has_received_valid_frame = 1U;
  367. switch (request[1])
  368. {
  369. case MODBUS_FC_READ_COILS:
  370. /* 广播请求不允许读取,从站不作响应。 */
  371. return (is_broadcast != 0U) ? 0U :
  372. Modbus_ProcessReadCoils(request, request_length);
  373. case MODBUS_FC_READ_HOLDING_REGS:
  374. return (is_broadcast != 0U) ? 0U :
  375. Modbus_ProcessReadHolding(request, request_length);
  376. case MODBUS_FC_WRITE_SINGLE_REG:
  377. return Modbus_ProcessWriteSingleRegister(request,
  378. request_length,
  379. is_broadcast);
  380. case MODBUS_FC_WRITE_MULTIPLE_COILS:
  381. return Modbus_ProcessWriteMultipleCoils(request,
  382. request_length,
  383. is_broadcast);
  384. case MODBUS_FC_WRITE_MULTIPLE_REGS:
  385. return Modbus_ProcessWriteMultipleRegisters(request,
  386. request_length,
  387. is_broadcast);
  388. default:
  389. g_modbus_slave_stats.illegal_function_count++;
  390. return (is_broadcast != 0U) ? 0U :
  391. Modbus_BuildException(request[1], MODBUS_EX_ILLEGAL_FUNCTION);
  392. }
  393. }
  394. HAL_StatusTypeDef ModbusSlave_Init(UART_HandleTypeDef *huart,
  395. uint8_t slave_address)
  396. {
  397. if ((huart == NULL) ||
  398. (slave_address == MODBUS_BROADCAST_ADDRESS) ||
  399. (slave_address > 247U))
  400. {
  401. return HAL_ERROR;
  402. }
  403. s_uart = huart;
  404. s_slave_address = slave_address;
  405. s_rx_frame_length = 0U;
  406. s_rx_frame_ready = 0U;
  407. s_tx_busy = 0U;
  408. s_has_received_valid_frame = 0U;
  409. (void)memset(s_holding_registers, 0, sizeof(s_holding_registers));
  410. (void)memset(s_coils, 0, sizeof(s_coils));
  411. (void)memset((void *)&g_modbus_slave_stats, 0,
  412. sizeof(g_modbus_slave_stats));
  413. s_holding_registers[HMI_REG_DEVICE_ID] = 0xF407U;
  414. return Modbus_StartReceive();
  415. }
  416. void ModbusSlave_Poll(void)
  417. {
  418. uint16_t response_length;
  419. if ((s_rx_frame_ready == 0U) || (s_tx_busy != 0U))
  420. {
  421. return;
  422. }
  423. /*
  424. * IDLE事件在约1个字符静默后产生;再等待到2ms,
  425. * 满足当前115200波特率下的Modbus RTU帧间隔要求。
  426. */
  427. if ((HAL_GetTick() - s_rx_event_tick) < MODBUS_RTU_FRAME_GAP_MS)
  428. {
  429. return;
  430. }
  431. response_length = Modbus_ProcessRequest(s_rx_frame, s_rx_frame_length);
  432. if (response_length > 0U)
  433. {
  434. /*
  435. * 发送期间停止接收,避免自动收发电路可能产生的本机回波
  436. * 被误当成下一条主站请求。
  437. */
  438. Modbus_StopReceive();
  439. s_tx_busy = 1U;
  440. if (HAL_UART_Transmit_DMA(s_uart, s_tx_frame, response_length) == HAL_OK)
  441. {
  442. g_modbus_slave_stats.tx_frame_count++;
  443. }
  444. else
  445. {
  446. s_tx_busy = 0U;
  447. g_modbus_slave_stats.uart_error_count++;
  448. (void)Modbus_StartReceive();
  449. }
  450. }
  451. /*
  452. * 处理期间保持ready=1,使中断不会覆盖当前帧;
  453. * 到这里再释放帧槽。
  454. */
  455. s_rx_frame_ready = 0U;
  456. }
  457. void ModbusSlave_OnRxEvent(UART_HandleTypeDef *huart, uint16_t size)
  458. {
  459. if ((huart != s_uart) || (s_uart == NULL))
  460. {
  461. return;
  462. }
  463. g_modbus_slave_stats.rx_event_count++;
  464. if ((size > 0U) && (size <= MODBUS_RTU_ADU_SIZE_MAX))
  465. {
  466. if (s_rx_frame_ready == 0U)
  467. {
  468. (void)memcpy(s_rx_frame, s_rx_dma_buffer, size);
  469. s_rx_frame_length = size;
  470. s_rx_event_tick = HAL_GetTick();
  471. s_rx_frame_ready = 1U;
  472. }
  473. else
  474. {
  475. /* 单帧槽尚未处理完成,丢弃新帧而不覆盖旧帧。 */
  476. g_modbus_slave_stats.dropped_frame_count++;
  477. }
  478. }
  479. else
  480. {
  481. g_modbus_slave_stats.dropped_frame_count++;
  482. }
  483. /* DMA普通模式在IDLE事件后已经停止,需要显式重新启动。 */
  484. if (s_tx_busy == 0U)
  485. {
  486. (void)Modbus_StartReceive();
  487. }
  488. }
  489. void ModbusSlave_OnTxComplete(UART_HandleTypeDef *huart)
  490. {
  491. if ((huart != s_uart) || (s_uart == NULL))
  492. {
  493. return;
  494. }
  495. s_tx_busy = 0U;
  496. (void)Modbus_StartReceive();
  497. }
  498. void ModbusSlave_OnUartError(UART_HandleTypeDef *huart)
  499. {
  500. if ((huart != s_uart) || (s_uart == NULL))
  501. {
  502. return;
  503. }
  504. g_modbus_slave_stats.uart_error_count++;
  505. s_tx_busy = 0U;
  506. (void)HAL_UART_Abort(huart);
  507. (void)Modbus_StartReceive();
  508. }
  509. uint8_t ModbusSlave_SetHoldingRegister(uint16_t address, uint16_t value)
  510. {
  511. if (address >= MODBUS_MAP_ITEM_COUNT)
  512. {
  513. return 0U;
  514. }
  515. s_holding_registers[address] = value;
  516. return 1U;
  517. }
  518. uint8_t ModbusSlave_GetHoldingRegister(uint16_t address, uint16_t *value)
  519. {
  520. if ((address >= MODBUS_MAP_ITEM_COUNT) || (value == NULL))
  521. {
  522. return 0U;
  523. }
  524. *value = s_holding_registers[address];
  525. return 1U;
  526. }
  527. uint8_t ModbusSlave_SetCoil(uint16_t address, uint8_t state)
  528. {
  529. if (address >= MODBUS_MAP_ITEM_COUNT)
  530. {
  531. return 0U;
  532. }
  533. Modbus_CoilSetUnchecked(address, state);
  534. return 1U;
  535. }
  536. uint8_t ModbusSlave_GetCoil(uint16_t address, uint8_t *state)
  537. {
  538. if ((address >= MODBUS_MAP_ITEM_COUNT) || (state == NULL))
  539. {
  540. return 0U;
  541. }
  542. *state = Modbus_CoilGetUnchecked(address);
  543. return 1U;
  544. }
  545. uint8_t ModbusSlave_IsConnected(uint32_t timeout_ms)
  546. {
  547. if (s_has_received_valid_frame == 0U)
  548. {
  549. return 0U;
  550. }
  551. return ((HAL_GetTick() - s_last_valid_frame_tick) <= timeout_ms) ? 1U : 0U;
  552. }