mbrtu_m.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. /*
  2. * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
  3. * Copyright (c) 2013 China Beijing Armink <armink.ztl@gmail.com>
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * 3. The name of the author may not be used to endorse or promote products
  15. * derived from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  18. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. *
  28. * File: $Id: mbrtu_m.c,v 1.60 2013/08/17 11:42:56 Armink Add Master Functions $
  29. */
  30. /* ----------------------- System includes ----------------------------------*/
  31. #include "stdlib.h"
  32. #include "string.h"
  33. /* ----------------------- Platform includes --------------------------------*/
  34. #include "port.h"
  35. /* ----------------------- Modbus includes ----------------------------------*/
  36. #include "mb.h"
  37. #include "mb_m.h"
  38. #include "mbrtu.h"
  39. #include "mbframe.h"
  40. #include "mbcrc.h"
  41. #include "mbport.h"
  42. #if MB_MASTER_RTU_ENABLED > 0
  43. /* ----------------------- Defines ------------------------------------------*/
  44. #define MB_SER_PDU_SIZE_MIN 4 /*!< Minimum size of a Modbus RTU frame. */
  45. #define MB_SER_PDU_SIZE_MAX 256 /*!< Maximum size of a Modbus RTU frame. */
  46. #define MB_SER_PDU_SIZE_CRC 2 /*!< Size of CRC field in PDU. */
  47. #define MB_SER_PDU_ADDR_OFF 0 /*!< Offset of slave address in Ser-PDU. */
  48. #define MB_SER_PDU_PDU_OFF 1 /*!< Offset of Modbus-PDU in Ser-PDU. */
  49. /* ----------------------- Type definitions ---------------------------------*/
  50. typedef enum
  51. {
  52. STATE_M_RX_INIT, /*!< Receiver is in initial state. */
  53. STATE_M_RX_IDLE, /*!< Receiver is in idle state. */
  54. STATE_M_RX_RCV, /*!< Frame is beeing received. */
  55. STATE_M_RX_ERROR, /*!< If the frame is invalid. */
  56. } eMBMasterRcvState;
  57. typedef enum
  58. {
  59. STATE_M_TX_IDLE, /*!< Transmitter is in idle state. */
  60. STATE_M_TX_XMIT, /*!< Transmitter is in transfer state. */
  61. STATE_M_TX_XFWR, /*!< Transmitter is in transfer finish and wait receive state. */
  62. } eMBMasterSndState;
  63. /* ----------------------- Static variables ---------------------------------*/
  64. static volatile eMBMasterSndState eSndState;
  65. static volatile eMBMasterRcvState eRcvState;
  66. static volatile UCHAR ucMasterRTUSndBuf[MB_PDU_SIZE_MAX];
  67. static volatile UCHAR ucMasterRTURcvBuf[MB_SER_PDU_SIZE_MAX];
  68. static volatile UCHAR ucMasterSendPDULength;
  69. static volatile UCHAR *pucMasterSndBufferCur;
  70. static volatile USHORT usMasterSndBufferCount;
  71. static volatile USHORT usMasterRcvBufferPos;
  72. static volatile BOOL xFrameIsBroadcast = FALSE;
  73. static volatile eMBMasterTimerMode eMasterCurTimerMode;
  74. /* ----------------------- Start implementation -----------------------------*/
  75. eMBErrorCode
  76. eMBMasterRTUInit(UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
  77. {
  78. eMBErrorCode eStatus = MB_ENOERR;
  79. ULONG usTimerT35_50us;
  80. ENTER_CRITICAL_SECTION( );
  81. /* Modbus RTU uses 8 Databits. */
  82. if( xMBMasterPortSerialInit( ucPort, ulBaudRate, 8, eParity ) != TRUE )
  83. {
  84. eStatus = MB_EPORTERR;
  85. }
  86. else
  87. {
  88. /* If baudrate > 19200 then we should use the fixed timer values
  89. * t35 = 1750us. Otherwise t35 must be 3.5 times the character time.
  90. */
  91. if( ulBaudRate > 19200 )
  92. {
  93. usTimerT35_50us = 35; /* 1800us. */
  94. }
  95. else
  96. {
  97. /* The timer reload value for a character is given by:
  98. *
  99. * ChTimeValue = Ticks_per_1s / ( Baudrate / 11 )
  100. * = 11 * Ticks_per_1s / Baudrate
  101. * = 220000 / Baudrate
  102. * The reload for t3.5 is 1.5 times this value and similary
  103. * for t3.5.
  104. */
  105. usTimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate );
  106. }
  107. if( xMBMasterPortTimersInit( ( USHORT ) usTimerT35_50us ) != TRUE )
  108. {
  109. eStatus = MB_EPORTERR;
  110. }
  111. }
  112. EXIT_CRITICAL_SECTION( );
  113. return eStatus;
  114. }
  115. void
  116. eMBMasterRTUStart( void )
  117. {
  118. ENTER_CRITICAL_SECTION( );
  119. /* Initially the receiver is in the state STATE_M_RX_INIT. we start
  120. * the timer and if no character is received within t3.5 we change
  121. * to STATE_M_RX_IDLE. This makes sure that we delay startup of the
  122. * modbus protocol stack until the bus is free.
  123. */
  124. eRcvState = STATE_M_RX_INIT;
  125. vMBMasterPortSerialEnable( TRUE, FALSE );
  126. vMBMasterPortTimersT35Enable( );
  127. EXIT_CRITICAL_SECTION( );
  128. }
  129. void
  130. eMBMasterRTUStop( void )
  131. {
  132. ENTER_CRITICAL_SECTION( );
  133. vMBMasterPortSerialEnable( FALSE, FALSE );
  134. vMBMasterPortTimersDisable( );
  135. EXIT_CRITICAL_SECTION( );
  136. }
  137. eMBErrorCode
  138. eMBMasterRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
  139. {
  140. eMBErrorCode eStatus = MB_ENOERR;
  141. ENTER_CRITICAL_SECTION( );
  142. assert_param( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX );
  143. /* Length and CRC check */
  144. if( ( usMasterRcvBufferPos >= MB_SER_PDU_SIZE_MIN )
  145. && ( usMBCRC16( ( UCHAR * ) ucMasterRTURcvBuf, usMasterRcvBufferPos ) == 0 ) )
  146. {
  147. /* Save the address field. All frames are passed to the upper layed
  148. * and the decision if a frame is used is done there.
  149. */
  150. *pucRcvAddress = ucMasterRTURcvBuf[MB_SER_PDU_ADDR_OFF];
  151. /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
  152. * size of address field and CRC checksum.
  153. */
  154. *pusLength = ( USHORT )( usMasterRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC );
  155. /* Return the start of the Modbus PDU to the caller. */
  156. *pucFrame = ( UCHAR * ) & ucMasterRTURcvBuf[MB_SER_PDU_PDU_OFF];
  157. }
  158. else
  159. {
  160. eStatus = MB_EIO;
  161. }
  162. EXIT_CRITICAL_SECTION( );
  163. return eStatus;
  164. }
  165. eMBErrorCode
  166. eMBMasterRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
  167. {
  168. eMBErrorCode eStatus = MB_ENOERR;
  169. USHORT usCRC16;
  170. if ( ucSlaveAddress > MB_MASTER_TOTAL_SLAVE_NUM ) return MB_EINVAL;
  171. ENTER_CRITICAL_SECTION( );
  172. /* Check if the receiver is still in idle state. If not we where to
  173. * slow with processing the received frame and the master sent another
  174. * frame on the network. We have to abort sending the frame.
  175. */
  176. if( eRcvState == STATE_M_RX_IDLE )
  177. {
  178. /* First byte before the Modbus-PDU is the slave address. */
  179. pucMasterSndBufferCur = ( UCHAR * ) pucFrame - 1;
  180. usMasterSndBufferCount = 1;
  181. /* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
  182. pucMasterSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
  183. usMasterSndBufferCount += usLength;
  184. /* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */
  185. usCRC16 = usMBCRC16( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount );
  186. ucMasterRTUSndBuf[usMasterSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );
  187. ucMasterRTUSndBuf[usMasterSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );
  188. /* Activate the transmitter. */
  189. eSndState = STATE_M_TX_XMIT;
  190. vMBMasterPortSerialEnable( FALSE, TRUE );
  191. }
  192. else
  193. {
  194. eStatus = MB_EIO;
  195. }
  196. EXIT_CRITICAL_SECTION( );
  197. return eStatus;
  198. }
  199. BOOL
  200. xMBMasterRTUReceiveFSM( void )
  201. {
  202. BOOL xTaskNeedSwitch = FALSE;
  203. UCHAR ucByte;
  204. assert_param( eSndState == STATE_M_TX_IDLE );
  205. /* Always read the character. */
  206. ( void )xMBMasterPortSerialGetByte( ( CHAR * ) & ucByte );
  207. switch ( eRcvState )
  208. {
  209. /* If we have received a character in the init state we have to
  210. * wait until the frame is finished.
  211. */
  212. case STATE_M_RX_INIT:
  213. vMBMasterPortTimersT35Enable( );
  214. break;
  215. /* In the error state we wait until all characters in the
  216. * damaged frame are transmitted.
  217. */
  218. case STATE_M_RX_ERROR:
  219. vMBMasterPortTimersT35Enable( );
  220. break;
  221. /* In the idle state we wait for a new character. If a character
  222. * is received the t1.5 and t3.5 timers are started and the
  223. * receiver is in the state STATE_RX_RECEIVCE and disable early
  224. * the timer of respond timeout .
  225. */
  226. case STATE_M_RX_IDLE:
  227. /* In time of respond timeout,the receiver receive a frame.
  228. * Disable timer of respond timeout and change the transmiter state to idle.
  229. */
  230. vMBMasterPortTimersDisable( );
  231. eSndState = STATE_M_TX_IDLE;
  232. usMasterRcvBufferPos = 0;
  233. ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte;
  234. eRcvState = STATE_M_RX_RCV;
  235. /* Enable t3.5 timers. */
  236. vMBMasterPortTimersT35Enable( );
  237. break;
  238. /* We are currently receiving a frame. Reset the timer after
  239. * every character received. If more than the maximum possible
  240. * number of bytes in a modbus frame is received the frame is
  241. * ignored.
  242. */
  243. case STATE_M_RX_RCV:
  244. if( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX )
  245. {
  246. ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte;
  247. }
  248. else
  249. {
  250. eRcvState = STATE_M_RX_ERROR;
  251. }
  252. vMBMasterPortTimersT35Enable();
  253. break;
  254. }
  255. return xTaskNeedSwitch;
  256. }
  257. BOOL
  258. xMBMasterRTUTransmitFSM( void )
  259. {
  260. BOOL xNeedPoll = FALSE;
  261. assert_param( eRcvState == STATE_M_RX_IDLE );
  262. switch ( eSndState )
  263. {
  264. /* We should not get a transmitter event if the transmitter is in
  265. * idle state. */
  266. case STATE_M_TX_IDLE:
  267. /* enable receiver/disable transmitter. */
  268. vMBMasterPortSerialEnable( TRUE, FALSE );
  269. break;
  270. case STATE_M_TX_XMIT:
  271. /* check if we are finished. */
  272. if( usMasterSndBufferCount != 0 )
  273. {
  274. xMBMasterPortSerialPutByte( ( CHAR )*pucMasterSndBufferCur );
  275. pucMasterSndBufferCur++; /* next byte in sendbuffer. */
  276. usMasterSndBufferCount--;
  277. }
  278. else
  279. {
  280. xFrameIsBroadcast = ( ucMasterRTUSndBuf[MB_SER_PDU_ADDR_OFF] == MB_ADDRESS_BROADCAST ) ? TRUE : FALSE;
  281. /* Disable transmitter. This prevents another transmit buffer
  282. * empty interrupt. */
  283. vMBMasterPortSerialEnable( TRUE, FALSE );
  284. eSndState = STATE_M_TX_XFWR;
  285. /* If the frame is broadcast ,master will enable timer of convert delay,
  286. * else master will enable timer of respond timeout. */
  287. if ( xFrameIsBroadcast == TRUE )
  288. {
  289. vMBMasterPortTimersConvertDelayEnable( );
  290. }
  291. else
  292. {
  293. vMBMasterPortTimersRespondTimeoutEnable( );
  294. }
  295. }
  296. break;
  297. }
  298. return xNeedPoll;
  299. }
  300. BOOL
  301. xMBMasterRTUTimerExpired(void)
  302. {
  303. BOOL xNeedPoll = FALSE;
  304. switch (eRcvState)
  305. {
  306. /* Timer t35 expired. Startup phase is finished. */
  307. case STATE_M_RX_INIT:
  308. xNeedPoll = xMBMasterPortEventPost(EV_MASTER_READY);
  309. break;
  310. /* A frame was received and t35 expired. Notify the listener that
  311. * a new frame was received. */
  312. case STATE_M_RX_RCV:
  313. xNeedPoll = xMBMasterPortEventPost(EV_MASTER_FRAME_RECEIVED);
  314. break;
  315. /* An error occured while receiving the frame. */
  316. case STATE_M_RX_ERROR:
  317. break;
  318. /* Function called in an illegal state. */
  319. default:
  320. assert_param(
  321. ( eRcvState == STATE_M_RX_INIT ) || ( eRcvState == STATE_M_RX_RCV ) || ( eRcvState == STATE_M_RX_ERROR ));
  322. break;
  323. }
  324. eRcvState = STATE_M_RX_IDLE;
  325. switch (eSndState)
  326. {
  327. /* A frame was send finish and convert delay or respond timeout expired.
  328. * If the frame is broadcast,The master will idle,and if the frame is not
  329. * broadcast.Notify the listener process error.*/
  330. case STATE_M_TX_XFWR:
  331. if ( xFrameIsBroadcast == FALSE ) xNeedPoll = xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS);
  332. break;
  333. /* Function called in an illegal state. */
  334. default:
  335. assert_param( eSndState == STATE_M_TX_XFWR );
  336. break;
  337. }
  338. eSndState = STATE_M_TX_IDLE;
  339. vMBMasterPortTimersDisable( );
  340. /* If timer mode is convert delay ,then Master is idel now. */
  341. if (eMasterCurTimerMode == MB_TMODE_CONVERT_DELAY) vMBMasterSetIsBusy( FALSE );
  342. return xNeedPoll;
  343. }
  344. /* Get Modbus Master send RTU's buffer address pointer.*/
  345. void vMBMasterGetRTUSndBuf( UCHAR ** pucFrame )
  346. {
  347. *pucFrame = ( UCHAR * ) ucMasterRTUSndBuf;
  348. }
  349. /* Get Modbus Master send PDU's buffer address pointer.*/
  350. void vMBMasterGetPDUSndBuf( UCHAR ** pucFrame )
  351. {
  352. *pucFrame = ( UCHAR * ) &ucMasterRTUSndBuf[MB_SER_PDU_PDU_OFF];
  353. }
  354. /* Set Modbus Master send PDU's buffer length.*/
  355. void vMBMasterSetPDUSndLength( UCHAR SendPDULength )
  356. {
  357. ucMasterSendPDULength = SendPDULength;
  358. }
  359. /* Get Modbus Master send PDU's buffer length.*/
  360. UCHAR ucMBMasterGetPDUSndLength( void )
  361. {
  362. return ucMasterSendPDULength;
  363. }
  364. /* Set Modbus Master current timer mode.*/
  365. void vMBMasterSetCurTimerMode( eMBMasterTimerMode eMBTimerMode )
  366. {
  367. eMasterCurTimerMode = eMBTimerMode;
  368. }
  369. #endif