mb.c 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921
  1. /*
  2. *********************************************************************************************************
  3. * uC/Modbus
  4. * The Embedded Modbus Stack
  5. *
  6. * Copyright 2003-2020 Silicon Laboratories Inc. www.silabs.com
  7. *
  8. * SPDX-License-Identifier: APACHE-2.0
  9. *
  10. * This software is subject to an open source license and is distributed by
  11. * Silicon Laboratories Inc. pursuant to the terms of the Apache License,
  12. * Version 2.0 available at www.apache.org/licenses/LICENSE-2.0.
  13. *
  14. *********************************************************************************************************
  15. */
  16. /*
  17. *********************************************************************************************************
  18. * uC/MODBUS Source Code
  19. *
  20. * Filename : mb.c
  21. * Version : V2.14.00
  22. *********************************************************************************************************
  23. */
  24. /*
  25. *********************************************************************************************************
  26. * INCLUDE FILES
  27. *********************************************************************************************************
  28. */
  29. #define MB_MODULE
  30. #include "mb.h"
  31. /*
  32. *********************************************************************************************************
  33. * LOCAL DEFINES
  34. *********************************************************************************************************
  35. */
  36. /*
  37. *********************************************************************************************************
  38. * LOCAL CONSTANTS
  39. *********************************************************************************************************
  40. */
  41. /*
  42. *********************************************************************************************************
  43. * LOCAL DATA TYPES
  44. *********************************************************************************************************
  45. */
  46. /*
  47. *********************************************************************************************************
  48. * LOCAL TABLES
  49. *********************************************************************************************************
  50. */
  51. /*
  52. *********************************************************************************************************
  53. * LOCAL GLOBAL VARIABLES
  54. *********************************************************************************************************
  55. */
  56. /* RAM Storage Requirements. */
  57. #if (MODBUS_CFG_RTU_EN == DEF_ENABLED)
  58. CPU_INT32U const MB_TotalRAMSize = sizeof(MB_RTU_Freq)
  59. + sizeof(MB_RTU_TmrCtr)
  60. + sizeof(MB_ChTbl);
  61. #else
  62. CPU_INT32U const MB_TotalRAMSize = sizeof(MB_ChTbl);
  63. #endif
  64. CPU_INT16U const MB_ChSize = sizeof(MODBUS_CH);
  65. /*
  66. *********************************************************************************************************
  67. * LOCAL FUNCTION PROTOTYPES
  68. *********************************************************************************************************
  69. */
  70. /*
  71. *********************************************************************************************************
  72. * LOCAL CONFIGURATION ERRORS
  73. *********************************************************************************************************
  74. */
  75. /*
  76. *********************************************************************************************************
  77. * MB_Init()
  78. *
  79. * Description : Handle either Modbus ASCII or Modbus RTU received packets.
  80. *
  81. * Argument(s) : freq Specifies the Modbus RTU timer frequency (in Hz)
  82. *
  83. * Return(s) : none.
  84. *
  85. * Caller(s) : Application
  86. *
  87. * Note(s) : none.
  88. *********************************************************************************************************
  89. */
  90. void MB_Init (CPU_INT32U freq)
  91. {
  92. CPU_INT08U ch;
  93. MODBUS_CH *pch;
  94. #if (MODBUS_CFG_RTU_EN == DEF_ENABLED)
  95. MB_RTU_Freq = freq; /* Save the RTU frequency */
  96. #endif
  97. pch = &MB_ChTbl[0]; /* Save Modbus channel number in data structure */
  98. for (ch = 0; ch < MODBUS_CFG_MAX_CH; ch++) { /* Initialize default values */
  99. pch->Ch = ch;
  100. pch->NodeAddr = 1;
  101. pch->MasterSlave = MODBUS_SLAVE; /* Channel defaults to MODBUS_SLAVE mode */
  102. pch->Mode = MODBUS_MODE_ASCII;
  103. pch->RxBufByteCtr = 0;
  104. pch->RxBufPtr = &pch->RxBuf[0];
  105. pch->WrEn = MODBUS_WR_EN;
  106. pch->WrCtr = 0;
  107. #if (MODBUS_CFG_RTU_EN == DEF_ENABLED)
  108. pch->RTU_TimeoutEn = DEF_TRUE;
  109. #endif
  110. #if (MODBUS_CFG_SLAVE_EN == DEF_ENABLED) && \
  111. (MODBUS_CFG_FC08_EN == DEF_ENABLED)
  112. MBS_StatInit(pch);
  113. #endif
  114. pch++;
  115. }
  116. MB_ChCtr = 0;
  117. MB_OS_Init(); /* Initialize OS interface functions */
  118. #if (MODBUS_CFG_RTU_EN == DEF_ENABLED) /* MODBUS 'RTU' Initialization */
  119. MB_RTU_TmrInit();
  120. #else
  121. (void)&freq;
  122. #endif
  123. }
  124. /*
  125. *********************************************************************************************************
  126. * MB_Exit()
  127. *
  128. * Description : This function is called to terminate all Modbus communications
  129. *
  130. * Argument(s) : none.
  131. *
  132. * Return(s) : none.
  133. *
  134. * Caller(s) : Application.
  135. *
  136. * Note(s) : none.
  137. *********************************************************************************************************
  138. */
  139. void MB_Exit (void)
  140. {
  141. #if (MODBUS_CFG_RTU_EN == DEF_ENABLED)
  142. MB_RTU_TmrExit(); /* Stop the RTU timer interrupts */
  143. #endif
  144. MB_CommExit(); /* Disable all communications */
  145. MB_OS_Exit(); /* Stop RTOS services */
  146. }
  147. /*
  148. *********************************************************************************************************
  149. * MB_CfgCh()
  150. *
  151. * Description : This function must be called after calling MB_Init() to initialize each of the Modbus
  152. * channels in your system.
  153. *
  154. * Argument(s) : node_addr is the Modbus node address that the channel is assigned to.
  155. *
  156. * master_slave specifies whether the channel is a MODBUS_MASTER or a MODBUS_SLAVE
  157. *
  158. * rx_timeout amount of time Master will wait for a response from the slave.
  159. *
  160. * modbus_mode specifies the type of modbus channel. The choices are:
  161. * MODBUS_MODE_ASCII
  162. * MODBUS_MODE_RTU
  163. *
  164. * port_nbr is the UART port number associated with the channel
  165. *
  166. * baud is the desired baud rate
  167. *
  168. * parity is the UART's parity setting:
  169. * MODBUS_PARITY_NONE
  170. * MODBUS_PARITY_ODD
  171. * MODBUS_PARITY_EVEN
  172. *
  173. * bits UART's number of bits (7 or 8)
  174. *
  175. * stops Number of stops bits (1 or 2)
  176. *
  177. * wr_en This argument determines whether a Modbus WRITE request will be accepted.
  178. * The choices are:
  179. * MODBUS_WR_EN
  180. * MODBUS_WR_DIS
  181. *
  182. * Return(s) : none.
  183. *
  184. * Caller(s) : Application.
  185. *
  186. * Note(s) : none.
  187. *********************************************************************************************************
  188. */
  189. MODBUS_CH *MB_CfgCh (CPU_INT08U node_addr,
  190. CPU_INT08U master_slave,
  191. CPU_INT32U rx_timeout,
  192. CPU_INT08U modbus_mode,
  193. CPU_INT08U port_nbr,
  194. CPU_INT32U baud,
  195. CPU_INT08U bits,
  196. CPU_INT08U parity,
  197. CPU_INT08U stops,
  198. CPU_INT08U wr_en)
  199. {
  200. MODBUS_CH *pch;
  201. #if (MODBUS_CFG_RTU_EN == DEF_ENABLED)
  202. CPU_INT16U cnts;
  203. #endif
  204. if (MB_ChCtr < MODBUS_CFG_MAX_CH) {
  205. pch = &MB_ChTbl[MB_ChCtr];
  206. MB_MasterTimeoutSet(pch, rx_timeout);
  207. MB_NodeAddrSet(pch, node_addr);
  208. MB_ModeSet(pch, master_slave, modbus_mode);
  209. MB_WrEnSet(pch, wr_en);
  210. MB_ChToPortMap(pch, port_nbr);
  211. MB_CommPortCfg(pch, port_nbr, baud, bits, parity, stops);
  212. #if (MODBUS_CFG_RTU_EN == DEF_ENABLED)
  213. if (pch->MasterSlave == MODBUS_MASTER) {
  214. pch->RTU_TimeoutEn = DEF_FALSE;
  215. }
  216. cnts = ((CPU_INT32U)MB_RTU_Freq * 5L * 10L) / baud; /* Freq * 5 char * 10 bits/char * 1/BaudRate */
  217. if (cnts <= 1) {
  218. cnts = 2;
  219. }
  220. pch->RTU_TimeoutCnts = cnts;
  221. pch->RTU_TimeoutCtr = cnts;
  222. #endif
  223. MB_ChCtr++;
  224. return (pch);
  225. } else {
  226. return ((MODBUS_CH *)0);
  227. }
  228. }
  229. /*
  230. *********************************************************************************************************
  231. * MB_MasterTimeoutSet()
  232. *
  233. * Description : This function is called to change the operating mode of a Modbus channel.
  234. *
  235. * Argument(s) : pch is a pointer to the Modbus channel to change
  236. *
  237. * modbus_mode specifies the type of modbus channel. The choices are:
  238. * MODBUS_MODE_ASCII
  239. * MODBUS_MODE_RTU
  240. *
  241. * Return(s) : none.
  242. *
  243. * Caller(s) : Application.
  244. *
  245. * Note(s) : none.
  246. *********************************************************************************************************
  247. */
  248. void MB_MasterTimeoutSet (MODBUS_CH *pch,
  249. CPU_INT32U timeout)
  250. {
  251. if (pch != (MODBUS_CH *)0) {
  252. pch->RxTimeout = timeout;
  253. }
  254. }
  255. /*
  256. *********************************************************************************************************
  257. * MB_ModeSet()
  258. *
  259. * Description : This function is called to change the operating mode of a Modbus channel.
  260. *
  261. * Argument(s) : pch is a pointer to the Modbus channel to change
  262. *
  263. * modbus_mode specifies the type of modbus channel. The choices are:
  264. * MODBUS_MODE_ASCII
  265. * MODBUS_MODE_RTU
  266. *
  267. * Return(s) : none.
  268. *
  269. * Caller(s) : Application.
  270. *
  271. * Note(s) : none.
  272. *********************************************************************************************************
  273. */
  274. void MB_ModeSet (MODBUS_CH *pch,
  275. CPU_INT08U master_slave,
  276. CPU_INT08U mode)
  277. {
  278. if (pch != (MODBUS_CH *)0) {
  279. switch (master_slave) {
  280. case MODBUS_MASTER:
  281. pch->MasterSlave = MODBUS_MASTER;
  282. break;
  283. case MODBUS_SLAVE:
  284. default:
  285. pch->MasterSlave = MODBUS_SLAVE;
  286. break;
  287. }
  288. switch (mode) {
  289. #if (MODBUS_CFG_ASCII_EN == DEF_ENABLED)
  290. case MODBUS_MODE_ASCII:
  291. pch->Mode = MODBUS_MODE_ASCII;
  292. break;
  293. #endif
  294. #if (MODBUS_CFG_RTU_EN == DEF_ENABLED)
  295. case MODBUS_MODE_RTU:
  296. pch->Mode = MODBUS_MODE_RTU;
  297. break;
  298. #endif
  299. default:
  300. #if (MODBUS_CFG_RTU_EN == DEF_ENABLED)
  301. pch->Mode = MODBUS_MODE_RTU;
  302. #else
  303. pch->Mode = MODBUS_MODE_ASCII;
  304. #endif
  305. break;
  306. }
  307. }
  308. }
  309. /*
  310. *********************************************************************************************************
  311. * MB_NodeAddrSet()
  312. *
  313. * Description : This function is called to change the Modbus node address that the channel will respond to.
  314. *
  315. * Argument(s) : pch is a pointer to the Modbus channel to change
  316. *
  317. * node_addr is the Modbus node address that the channel is assigned to.
  318. *
  319. * Return(s) : none.
  320. *
  321. * Caller(s) : Application.
  322. *
  323. * Note(s) : none.
  324. *********************************************************************************************************
  325. */
  326. void MB_NodeAddrSet (MODBUS_CH *pch,
  327. CPU_INT08U node_addr)
  328. {
  329. if (pch != (MODBUS_CH *)0) {
  330. pch->NodeAddr = node_addr;
  331. }
  332. }
  333. /*
  334. *********************************************************************************************************
  335. * MB_WrEnSet()
  336. *
  337. * Description : This function is called to enable or disable write accesses to the data.
  338. *
  339. * Argument(s) : ch is the Modbus channel to change
  340. *
  341. * wr_en This argument determines whether a Modbus WRITE request will be accepted.
  342. * The choices are:
  343. * MODBUS_WR_EN
  344. * MODBUS_WR_DIS
  345. *
  346. * Return(s) : none.
  347. *
  348. * Caller(s) : Application.
  349. *
  350. * Note(s) : none.
  351. *********************************************************************************************************
  352. */
  353. void MB_WrEnSet (MODBUS_CH *pch,
  354. CPU_INT08U wr_en)
  355. {
  356. if (pch != (MODBUS_CH *)0) {
  357. pch->WrEn = wr_en;
  358. }
  359. }
  360. /*
  361. *********************************************************************************************************
  362. * MB_ChToPortMap()
  363. *
  364. * Description : This function is called to change the physical port number of the Modbus channel.
  365. *
  366. * Argument(s) : pch is a pointer to the Modbus channel to change
  367. *
  368. * port_nbr This argument determines the physical port number of the Modbus channel
  369. *
  370. * Return(s) : none.
  371. *
  372. * Caller(s) : Application.
  373. *
  374. * Note(s) : none.
  375. *********************************************************************************************************
  376. */
  377. void MB_ChToPortMap (MODBUS_CH *pch,
  378. CPU_INT08U port_nbr)
  379. {
  380. if (pch != (MODBUS_CH *)0) {
  381. pch->PortNbr = port_nbr;
  382. }
  383. }
  384. /*
  385. *********************************************************************************************************
  386. * MB_RxByte()
  387. *
  388. * Description : A byte has been received from a serial port. We just store it in the buffer for processing
  389. * when a complete packet has been received.
  390. *
  391. * Argument(s) : pch Is a pointer to the Modbus channel's data structure.
  392. *
  393. * rx_byte Is the byte received.
  394. *
  395. * Return(s) : none.
  396. *
  397. * Caller(s) : MB_CommRxTxISR_Handler().
  398. *
  399. * Note(s) : none.
  400. *********************************************************************************************************
  401. */
  402. void MB_RxByte (MODBUS_CH *pch,
  403. CPU_INT08U rx_byte)
  404. {
  405. switch (pch->Mode) {
  406. #if (MODBUS_CFG_ASCII_EN == DEF_ENABLED)
  407. case MODBUS_MODE_ASCII:
  408. MB_ASCII_RxByte(pch, rx_byte & 0x7F);
  409. break;
  410. #endif
  411. #if (MODBUS_CFG_RTU_EN == DEF_ENABLED)
  412. case MODBUS_MODE_RTU:
  413. MB_RTU_RxByte(pch, rx_byte);
  414. break;
  415. #endif
  416. default:
  417. break;
  418. }
  419. }
  420. /*
  421. *********************************************************************************************************
  422. * MB_RxTask()
  423. *
  424. * Description : This function is called when a packet needs to be processed.
  425. *
  426. * Argument(s) : pch Is a pointer to the Modbus channel's data structure.
  427. *
  428. * Return(s) : none.
  429. *
  430. * Caller(s) : MB_OS_RxTask().
  431. *
  432. * Note(s) : none.
  433. *********************************************************************************************************
  434. */
  435. void MB_RxTask (MODBUS_CH *pch)
  436. {
  437. #if (MODBUS_CFG_SLAVE_EN == DEF_ENABLED)
  438. if (pch != (MODBUS_CH *)0) {
  439. if (pch->MasterSlave == MODBUS_SLAVE) {
  440. MBS_RxTask(pch);
  441. }
  442. }
  443. #endif
  444. }
  445. /*
  446. *********************************************************************************************************
  447. * MB_ASCII_RxByte()
  448. *
  449. * Description : A byte has been received from a serial port. We just store it in the buffer for processing
  450. * when a complete packet has been received.
  451. *
  452. * Argument(s) : pch Is a pointer to the Modbus channel's data structure.
  453. *
  454. * rx_byte Is the byte received.
  455. *
  456. * Return(s) : none.
  457. *
  458. * Caller(s) : MB_RxByte().
  459. *
  460. * Return(s) : none.
  461. *********************************************************************************************************
  462. */
  463. #if (MODBUS_CFG_ASCII_EN == DEF_ENABLED)
  464. void MB_ASCII_RxByte (MODBUS_CH *pch,
  465. CPU_INT08U rx_byte)
  466. {
  467. CPU_INT08U node_addr;
  468. CPU_INT08U *phex;
  469. pch->RxCtr++; /* Increment the number of bytes received */
  470. if (rx_byte == ':') { /* Is it the start of frame character? */
  471. pch->RxBufPtr = &pch->RxBuf[0]; /* Yes, Restart a new frame */
  472. pch->RxBufByteCtr = 0;
  473. }
  474. if (pch->RxBufByteCtr < MODBUS_CFG_BUF_SIZE) { /* No, add received byte to buffer */
  475. *pch->RxBufPtr++ = rx_byte;
  476. pch->RxBufByteCtr++; /* Increment byte counter to see if we have Rx ... */
  477. /* ... activity */
  478. }
  479. if (rx_byte == MODBUS_ASCII_END_FRAME_CHAR2) { /* See if we received a complete ASCII frame */
  480. phex = &pch->RxBuf[1];
  481. node_addr = MB_ASCII_HexToBin(phex);
  482. if ((node_addr == pch->NodeAddr) || /* Is the address for us? */
  483. (node_addr == 0)) { /* ... or a 'broadcast'? */
  484. MB_OS_RxSignal(pch); /* Yes, Let task handle reply */
  485. } else {
  486. pch->RxBufPtr = &pch->RxBuf[0]; /* No, Wipe out anything, we have to re-synchronize. */
  487. pch->RxBufByteCtr = 0;
  488. }
  489. }
  490. }
  491. #endif
  492. /*
  493. *********************************************************************************************************
  494. * MB_ASCII_Rx()
  495. *
  496. * Description : Parses and converts an ASCII style message into a Modbus frame. A check is performed
  497. * to verify that the Modbus packet is valid.
  498. *
  499. * Argument(s) : pch Is a pointer to the Modbus channel's data structure.
  500. *
  501. * Return(s) : DEF_TRUE If all checks pass.
  502. * DEF_FALSE If any checks fail.
  503. *
  504. * Caller(s) : MBM_RxReply().
  505. *
  506. * Note(s) : none.
  507. *********************************************************************************************************
  508. */
  509. #if (MODBUS_CFG_ASCII_EN == DEF_ENABLED)
  510. CPU_BOOLEAN MB_ASCII_Rx (MODBUS_CH *pch)
  511. {
  512. CPU_INT08U *pmsg;
  513. CPU_INT08U *prx_data;
  514. CPU_INT16U rx_size;
  515. pmsg = &pch->RxBuf[0];
  516. rx_size = pch->RxBufByteCtr;
  517. prx_data = &pch->RxFrameData[0];
  518. if ((rx_size & 0x01) && /* Message should have an ODD nbr of bytes. */
  519. (rx_size > MODBUS_ASCII_MIN_MSG_SIZE) && /* Check if message is long enough */
  520. (pmsg[0] == MODBUS_ASCII_START_FRAME_CHAR) && /* Check the first char. */
  521. (pmsg[rx_size - 2] == MODBUS_ASCII_END_FRAME_CHAR1) && /* Check the last two. */
  522. (pmsg[rx_size - 1] == MODBUS_ASCII_END_FRAME_CHAR2)) {
  523. rx_size -= 3; /* Take away for the ':', CR, and LF */
  524. pmsg++; /* Point past the ':' to the address. */
  525. pch->RxFrameNDataBytes = 0; /* Get the data from the message */
  526. while (rx_size > 2) {
  527. *prx_data++ = MB_ASCII_HexToBin(pmsg);
  528. pmsg += 2;
  529. rx_size -= 2;
  530. pch->RxFrameNDataBytes++; /* Increment the number of Modbus packets received */
  531. }
  532. pch->RxFrameNDataBytes -= 2; /* Subtract the Address and function code */
  533. pch->RxFrameCRC = (CPU_INT16U)MB_ASCII_HexToBin(pmsg); /* Extract the message's LRC */
  534. return (DEF_TRUE);
  535. } else {
  536. return (DEF_FALSE);
  537. }
  538. }
  539. #endif
  540. /*
  541. *********************************************************************************************************
  542. * MB_ASCII_Tx()
  543. *
  544. * Description : The format of the message is ASCII. The actual information is taken from the given
  545. * MODBUS frame.
  546. *
  547. * Argument(s) : pch Is a pointer to the Modbus channel's data structure.
  548. *
  549. * Return(s) : none.
  550. *
  551. * Caller(s) : MBM_TxCmd(),
  552. * MBS_ASCII_Task().
  553. *
  554. * Note(s) : none.
  555. *********************************************************************************************************
  556. */
  557. #if (MODBUS_CFG_ASCII_EN == DEF_ENABLED)
  558. void MB_ASCII_Tx (MODBUS_CH *pch)
  559. {
  560. CPU_INT08U *ptx_data;
  561. CPU_INT08U *pbuf;
  562. CPU_INT16U i;
  563. CPU_INT16U tx_bytes;
  564. CPU_INT08U lrc;
  565. ptx_data = &pch->TxFrameData[0];
  566. pbuf = &pch->TxBuf[0];
  567. *pbuf++ = MODBUS_ASCII_START_FRAME_CHAR; /* Place the start-of-frame character into output buffer */
  568. pbuf = MB_ASCII_BinToHex(*ptx_data++,
  569. pbuf);
  570. pbuf = MB_ASCII_BinToHex(*ptx_data++,
  571. pbuf);
  572. tx_bytes = 5;
  573. i = (CPU_INT08U)pch->TxFrameNDataBytes; /* Transmit the actual data */
  574. while (i > 0) {
  575. pbuf = MB_ASCII_BinToHex(*ptx_data++,
  576. pbuf);
  577. tx_bytes += 2;
  578. i--;
  579. }
  580. lrc = MB_ASCII_TxCalcLRC(pch, /* Compute outbound packet LRC */
  581. tx_bytes);
  582. pbuf = MB_ASCII_BinToHex(lrc, /* Add the LRC checksum in the packet */
  583. pbuf);
  584. *pbuf++ = MODBUS_ASCII_END_FRAME_CHAR1; /* Add 1st end-of-frame character (0x0D) to output buffer */
  585. *pbuf++ = MODBUS_ASCII_END_FRAME_CHAR2; /* Add 2nd end-of-frame character (0x0A) to output buffer */
  586. tx_bytes += 4;
  587. pch->TxFrameCRC = (CPU_INT16U)lrc; /* Save the computed LRC into the channel */
  588. pch->TxBufByteCtr = tx_bytes; /* Update the total number of bytes to send */
  589. MB_Tx(pch); /* Send it out the communication driver. */
  590. }
  591. #endif
  592. /*
  593. *********************************************************************************************************
  594. * MB_RTU_RxByte()
  595. *
  596. * Description : A byte has been received from a serial port. We just store it in the buffer for processing
  597. * when a complete packet has been received.
  598. *
  599. * Argument(s) : pch Is a pointer to the Modbus channel's data structure.
  600. *
  601. * rx_byte Is the byte received.
  602. *
  603. * Return(s) : none.
  604. *
  605. * Caller(s) : MB_RxByte().
  606. *
  607. * Note(s) : none.
  608. *********************************************************************************************************
  609. */
  610. #if (MODBUS_CFG_RTU_EN == DEF_ENABLED)
  611. void MB_RTU_RxByte (MODBUS_CH *pch,
  612. CPU_INT08U rx_byte)
  613. {
  614. MB_RTU_TmrReset(pch); /* Reset the timeout timer on a new character */
  615. #if (MODBUS_CFG_MASTER_EN == DEF_ENABLED)
  616. if (pch->MasterSlave == MODBUS_MASTER) {
  617. pch->RTU_TimeoutEn = MODBUS_TRUE;
  618. }
  619. #endif
  620. if (pch->RxBufByteCtr < MODBUS_CFG_BUF_SIZE) { /* No, add received byte to buffer */
  621. pch->RxCtr++; /* Increment the number of bytes received */
  622. *pch->RxBufPtr++ = rx_byte;
  623. pch->RxBufByteCtr++; /* Increment byte counter to see if we have Rx activity */
  624. }
  625. }
  626. #endif
  627. /*
  628. *********************************************************************************************************
  629. * MB_RTU_Rx()
  630. *
  631. * Description : Parses a Modbus RTU packet and processes the request if the packet is valid.
  632. *
  633. * Argument(s) : pch Is a pointer to the Modbus channel's data structure.
  634. *
  635. * Return(s) : DEF_TRUE If all checks pass.
  636. * DEF_FALSE If any checks fail.
  637. *
  638. * Caller(s) : MBM_RxReply(),
  639. * MBS_RTU_Task().
  640. *
  641. * Note(s) : none.
  642. *********************************************************************************************************
  643. */
  644. #if (MODBUS_CFG_RTU_EN == DEF_ENABLED)
  645. CPU_BOOLEAN MB_RTU_Rx (MODBUS_CH *pch)
  646. {
  647. CPU_INT08U *prx_data;
  648. CPU_INT08U *pmsg;
  649. CPU_INT16U rx_size;
  650. CPU_INT16U crc;
  651. pmsg = &pch->RxBuf[0];
  652. rx_size = pch->RxBufByteCtr;
  653. if (rx_size >= MODBUS_RTU_MIN_MSG_SIZE) { /* Is the message long enough? */
  654. if (rx_size <= MODBUS_CFG_BUF_SIZE) {
  655. prx_data = &pch->RxFrameData[0];
  656. *prx_data++ = *pmsg++; /* Transfer the node address */
  657. rx_size--;
  658. *prx_data++ = *pmsg++; /* Transfer the function code */
  659. rx_size--;
  660. pch->RxFrameNDataBytes = 0; /* Transfer the data */
  661. while (rx_size > 2) {
  662. *prx_data++ = *pmsg++;
  663. pch->RxFrameNDataBytes++;
  664. rx_size--;
  665. }
  666. crc = (CPU_INT16U)*pmsg++; /* Transfer the CRC over. It's LSB first, then MSB. */
  667. crc += (CPU_INT16U)*pmsg << 8;
  668. pch->RxFrameCRC = crc;
  669. return (DEF_TRUE);
  670. } else {
  671. return (DEF_FALSE);
  672. }
  673. } else {
  674. return (DEF_FALSE);
  675. }
  676. }
  677. #endif
  678. /*
  679. *********************************************************************************************************
  680. * MB_RTU_Tx()
  681. *
  682. * Description : A MODBUS message is formed into a buffer and sent to the appropriate communication port.
  683. * The actual reply is taken from the given MODBUS Frame.
  684. *
  685. * Argument(s) : pch Is a pointer to the Modbus channel's data structure.
  686. *
  687. * Return(s) : none.
  688. *
  689. * Caller(s) : MBM_TxCmd(),
  690. * MBS_RTU_Task().
  691. *
  692. * Note(s) : none.
  693. *********************************************************************************************************
  694. */
  695. #if (MODBUS_CFG_RTU_EN == DEF_ENABLED)
  696. void MB_RTU_Tx (MODBUS_CH *pch)
  697. {
  698. CPU_INT08U *ptx_data;
  699. CPU_INT08U *pbuf;
  700. CPU_INT08U i;
  701. CPU_INT16U tx_bytes;
  702. CPU_INT16U crc;
  703. tx_bytes = 0;
  704. pbuf = &pch->TxBuf[0]; /* Point to the beginning of the output buffer. */
  705. ptx_data = &(pch->TxFrameData[0]);
  706. i = (CPU_INT08U)pch->TxFrameNDataBytes + 2; /* Include the actual data in the buffer */
  707. while (i > 0) {
  708. *pbuf++ = *ptx_data++;
  709. tx_bytes++;
  710. i--;
  711. }
  712. crc = MB_RTU_TxCalcCRC(pch);
  713. *pbuf++ = (CPU_INT08U)(crc & 0x00FF); /* Add in the CRC checksum. Low byte first! */
  714. *pbuf = (CPU_INT08U)(crc >> 8);
  715. tx_bytes += 2;
  716. pch->TxFrameCRC = crc; /* Save the calculated CRC in the channel */
  717. pch->TxBufByteCtr = tx_bytes;
  718. MB_Tx(pch); /* Send it out the communication driver. */
  719. }
  720. #endif
  721. /*
  722. *********************************************************************************************************
  723. * MB_RTU_TmrReset()
  724. *
  725. * Description : This function is called when a byte a received and thus, we reset the RTU timeout timer value
  726. * indicating that we are not done receiving a complete RTU packet.
  727. *
  728. * Argument(s) : none.
  729. *
  730. * Return(s) : none.
  731. *
  732. * Caller(s) : MB_RTU_TmrResetAll().
  733. *
  734. * Note(s) : none.
  735. *********************************************************************************************************
  736. */
  737. #if (MODBUS_CFG_RTU_EN == DEF_ENABLED)
  738. void MB_RTU_TmrReset (MODBUS_CH *pch)
  739. {
  740. pch->RTU_TimeoutCtr = pch->RTU_TimeoutCnts;
  741. }
  742. #endif
  743. /*
  744. *********************************************************************************************************
  745. * MB_RTU_TmrResetAll()
  746. *
  747. * Description : This function is used to reset all the RTU timers for all Modbus channels.
  748. *
  749. * Argument(s) : none.
  750. *
  751. * Return(s) : none.
  752. *
  753. * Caller(s) : MB_RTU_TmrInit().
  754. *
  755. * Note(s) : none.
  756. *********************************************************************************************************
  757. */
  758. #if (MODBUS_CFG_RTU_EN == DEF_ENABLED)
  759. void MB_RTU_TmrResetAll (void)
  760. {
  761. CPU_INT08U ch;
  762. MODBUS_CH *pch;
  763. pch = &MB_ChTbl[0];
  764. for (ch = 0; ch < MODBUS_CFG_MAX_CH; ch++) {
  765. if (pch->Mode == MODBUS_MODE_RTU) {
  766. MB_RTU_TmrReset(pch);
  767. }
  768. pch++;
  769. }
  770. }
  771. #endif
  772. /*
  773. *********************************************************************************************************
  774. * MB_RTU_TmrUpdate()
  775. *
  776. * Description : This function is called when the application supplied RTU framing timer expires.
  777. *
  778. * Argument(s) : none.
  779. *
  780. * Return(s) : none.
  781. *
  782. * Caller(s) : MB_RTU_TmrISR_Handler().
  783. *
  784. * Note(s) : none.
  785. *********************************************************************************************************
  786. */
  787. #if (MODBUS_CFG_RTU_EN == DEF_ENABLED)
  788. void MB_RTU_TmrUpdate (void)
  789. {
  790. CPU_INT08U ch;
  791. MODBUS_CH *pch;
  792. pch = &MB_ChTbl[0];
  793. for (ch = 0; ch < MODBUS_CFG_MAX_CH; ch++) {
  794. if (pch->Mode == MODBUS_MODE_RTU) {
  795. if (pch->RTU_TimeoutEn == DEF_TRUE) {
  796. if (pch->RTU_TimeoutCtr > 0) {
  797. pch->RTU_TimeoutCtr--;
  798. if (pch->RTU_TimeoutCtr == 0) {
  799. #if (MODBUS_CFG_RTU_EN == DEF_ENABLED)
  800. if (pch->MasterSlave == MODBUS_MASTER) {
  801. pch->RTU_TimeoutEn = DEF_FALSE;
  802. }
  803. #endif
  804. MB_OS_RxSignal(pch); /* RTU Timer expired for this Modbus channel */
  805. }
  806. }
  807. } else {
  808. pch->RTU_TimeoutCtr = pch->RTU_TimeoutCnts;
  809. }
  810. }
  811. pch++;
  812. }
  813. }
  814. #endif