mbascii.c
上传用户:kongshuqi
上传日期:2013-10-09
资源大小:59k
文件大小:15k
源码类别:

通讯编程

开发平台:

Visual C++

  1.  /*
  2.   * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
  3.   * Copyright (C) 2006 Christian Walter <wolti@sil.at>
  4.   *
  5.   * This library is free software; you can redistribute it and/or
  6.   * modify it under the terms of the GNU Lesser General Public
  7.   * License as published by the Free Software Foundation; either
  8.   * version 2.1 of the License, or (at your option) any later version.
  9.   *
  10.   * This library is distributed in the hope that it will be useful,
  11.   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13.   * Lesser General Public License for more details.
  14.   *
  15.   * You should have received a copy of the GNU Lesser General Public
  16.   * License along with this library; if not, write to the Free Software
  17.   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  18.   *
  19.   * File: $Id: mbascii.c,v 1.11 2006/06/18 09:57:03 wolti Exp $
  20.   */
  21. /* ----------------------- System includes ----------------------------------*/
  22. #include "stdlib.h"
  23. #include "string.h"
  24. /* ----------------------- Platform includes --------------------------------*/
  25. #include "port.h"
  26. /* ----------------------- Modbus includes ----------------------------------*/
  27. #include "mb.h"
  28. #include "mbconfig.h"
  29. #include "mbascii.h"
  30. #include "mbframe.h"
  31. #include "mbcrc.h"
  32. #include "mbport.h"
  33. #if MB_ASCII_ENABLED > 0
  34. /* ----------------------- Defines ------------------------------------------*/
  35. #define MB_ASCII_DEFAULT_CR     'r'    /*!< Default CR character for Modbus ASCII. */
  36. #define MB_ASCII_DEFAULT_LF     'n'    /*!< Default LF character for Modbus ASCII. */
  37. #define MB_SER_PDU_SIZE_MIN     3       /*!< Minimum size of a Modbus ASCII frame. */
  38. #define MB_SER_PDU_SIZE_MAX     256     /*!< Maximum size of a Modbus ASCII frame. */
  39. #define MB_SER_PDU_SIZE_LRC     1       /*!< Size of LRC field in PDU. */
  40. #define MB_SER_PDU_ADDR_OFF     0       /*!< Offset of slave address in Ser-PDU. */
  41. #define MB_SER_PDU_PDU_OFF      1       /*!< Offset of Modbus-PDU in Ser-PDU. */
  42. /* ----------------------- Type definitions ---------------------------------*/
  43. typedef enum
  44. {
  45.     STATE_RX_IDLE,              /*!< Receiver is in idle state. */
  46.     STATE_RX_RCV,               /*!< Frame is beeing received. */
  47.     STATE_RX_WAIT_EOF           /*!< Wait for End of Frame. */
  48. } eMBRcvState;
  49. typedef enum
  50. {
  51.     STATE_TX_IDLE,              /*!< Transmitter is in idle state. */
  52.     STATE_TX_START,             /*!< Starting transmission (':' sent). */
  53.     STATE_TX_DATA,              /*!< Sending of data (Address, Data, LRC). */
  54.     STATE_TX_END,               /*!< End of transmission. */
  55.     STATE_TX_NOTIFY,            /*!< Notify sender that the frame has been sent. */
  56. } eMBSndState;
  57. typedef enum
  58. {
  59.     BYTE_HIGH_NIBBLE,           /*!< Character for high nibble of byte. */
  60.     BYTE_LOW_NIBBLE             /*!< Character for low nibble of byte. */
  61. } eMBBytePos;
  62. /* ----------------------- Static functions ---------------------------------*/
  63. static UCHAR    prvucMBCHAR2BIN( UCHAR ucCharacter );
  64. static UCHAR    prvucMBBIN2CHAR( UCHAR ucByte );
  65. static UCHAR    prvucMBLRC( UCHAR * pucFrame, USHORT usLen );
  66. /* ----------------------- Static variables ---------------------------------*/
  67. static volatile eMBSndState eSndState;
  68. static volatile eMBRcvState eRcvState;
  69. /* We reuse the Modbus RTU buffer because only one buffer is needed and the
  70.  * RTU buffer is bigger. */
  71. extern volatile UCHAR ucRTUBuf[];
  72. static volatile UCHAR *ucASCIIBuf = ucRTUBuf;
  73. static volatile USHORT usRcvBufferPos;
  74. static volatile eMBBytePos eBytePos;
  75. static volatile UCHAR *pucSndBufferCur;
  76. static volatile USHORT usSndBufferCount;
  77. static volatile UCHAR ucLRC;
  78. static volatile UCHAR ucMBLFCharacter;
  79. /* ----------------------- Start implementation -----------------------------*/
  80. eMBErrorCode
  81. eMBASCIIInit( UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate,
  82.               eMBParity eParity )
  83. {
  84.     eMBErrorCode    eStatus = MB_ENOERR;
  85.     ENTER_CRITICAL_SECTION(  );
  86.     ucMBLFCharacter = MB_ASCII_DEFAULT_LF;
  87.     if( xMBPortSerialInit( ucPort, ulBaudRate, 7, eParity ) != TRUE )
  88.     {
  89.         eStatus = MB_EPORTERR;
  90.     }
  91.     if( xMBPortTimersInit( MB_ASCII_TIMEOUT_SEC * 20000UL ) != TRUE )
  92.     {
  93.         eStatus = MB_EPORTERR;
  94.     }
  95.     EXIT_CRITICAL_SECTION(  );
  96.     return eStatus;
  97. }
  98. void
  99. eMBASCIIStart( void )
  100. {
  101.     ENTER_CRITICAL_SECTION(  );
  102.     vMBPortSerialEnable( TRUE, FALSE );
  103.     eRcvState = STATE_RX_IDLE;
  104.     EXIT_CRITICAL_SECTION(  );
  105.     /* No special startup required for ASCII. */
  106.     ( void )xMBPortEventPost( EV_READY );
  107. }
  108. void
  109. eMBASCIIStop( void )
  110. {
  111.     ENTER_CRITICAL_SECTION(  );
  112.     vMBPortSerialEnable( FALSE, FALSE );
  113.     vMBPortTimersDisable(  );
  114.     EXIT_CRITICAL_SECTION(  );
  115. }
  116. eMBErrorCode
  117. eMBASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
  118. {
  119.     eMBErrorCode    eStatus = MB_ENOERR;
  120.     ENTER_CRITICAL_SECTION(  );
  121.     assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX );
  122.     /* Length and CRC check */
  123.     if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN )
  124.         && ( prvucMBLRC( ( UCHAR * ) ucASCIIBuf, usRcvBufferPos ) == 0 ) )
  125.     {
  126.         /* Save the address field. All frames are passed to the upper layed
  127.          * and the decision if a frame is used is done there.
  128.          */
  129.         *pucRcvAddress = ucASCIIBuf[MB_SER_PDU_ADDR_OFF];
  130.         /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
  131.          * size of address field and CRC checksum.
  132.          */
  133.         *pusLength = usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_LRC;
  134.         /* Return the start of the Modbus PDU to the caller. */
  135.         *pucFrame = ( UCHAR * ) & ucASCIIBuf[MB_SER_PDU_PDU_OFF];
  136.     }
  137.     else
  138.     {
  139.         eStatus = MB_EIO;
  140.     }
  141.     EXIT_CRITICAL_SECTION(  );
  142.     return eStatus;
  143. }
  144. eMBErrorCode
  145. eMBASCIISend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
  146. {
  147.     eMBErrorCode    eStatus = MB_ENOERR;
  148.     UCHAR           usLRC;
  149.     ENTER_CRITICAL_SECTION(  );
  150.     /* Check if the receiver is still in idle state. If not we where to
  151.      * slow with processing the received frame and the master sent another
  152.      * frame on the network. We have to abort sending the frame.
  153.      */
  154.     if( eRcvState == STATE_RX_IDLE )
  155.     {
  156.         /* First byte before the Modbus-PDU is the slave address. */
  157.         pucSndBufferCur = ( UCHAR * ) pucFrame - 1;
  158.         usSndBufferCount = 1;
  159.         /* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
  160.         pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
  161.         usSndBufferCount += usLength;
  162.         /* Calculate LRC checksum for Modbus-Serial-Line-PDU. */
  163.         usLRC = prvucMBLRC( ( UCHAR * ) pucSndBufferCur, usSndBufferCount );
  164.         ucASCIIBuf[usSndBufferCount++] = usLRC;
  165.         /* Activate the transmitter. */
  166.         eSndState = STATE_TX_START;
  167.         vMBPortSerialEnable( FALSE, TRUE );
  168.     }
  169.     else
  170.     {
  171.         eStatus = MB_EIO;
  172.     }
  173.     EXIT_CRITICAL_SECTION(  );
  174.     return eStatus;
  175. }
  176. BOOL
  177. xMBASCIIReceiveFSM( void )
  178. {
  179.     BOOL            xNeedPoll = FALSE;
  180.     UCHAR           ucByte;
  181.     UCHAR           ucResult;
  182.     assert( eSndState == STATE_TX_IDLE );
  183.     ( void )xMBPortSerialGetByte( &ucByte );
  184.     switch ( eRcvState )
  185.     {
  186.             /* A new character is received. If the character is a ':' the input
  187.              * buffer is cleared. A CR-character signals the end of the data
  188.              * block. Other characters are part of the data block and their
  189.              * ASCII value is converted back to a binary representation.
  190.              */
  191.         case STATE_RX_RCV:
  192.             /* Enable timer for character timeout. */
  193.             vMBPortTimersEnable(  );
  194.             if( ucByte == ':' )
  195.             {
  196.                 /* Empty receive buffer. */
  197.                 eBytePos = BYTE_HIGH_NIBBLE;
  198.                 usRcvBufferPos = 0;
  199.             }
  200.             else if( ucByte == MB_ASCII_DEFAULT_CR )
  201.             {
  202.                 eRcvState = STATE_RX_WAIT_EOF;
  203.             }
  204.             else
  205.             {
  206.                 ucResult = prvucMBCHAR2BIN( ucByte );
  207.                 switch ( eBytePos )
  208.                 {
  209.                         /* High nibble of the byte comes first. We check for
  210.                          * a buffer overflow here. */
  211.                     case BYTE_HIGH_NIBBLE:
  212.                         if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX )
  213.                         {
  214.                             ucASCIIBuf[usRcvBufferPos] = ucResult << 4;
  215.                             eBytePos = BYTE_LOW_NIBBLE;
  216.                             break;
  217.                         }
  218.                         else
  219.                         {
  220.                             /* not handled in Modbus specification but seems
  221.                              * a resonable implementation. */
  222.                             eRcvState = STATE_RX_IDLE;
  223.                             /* Disable previously activated timer because of error state. */
  224.                             vMBPortTimersDisable(  );
  225.                         }
  226.                         break;
  227.                     case BYTE_LOW_NIBBLE:
  228.                         ucASCIIBuf[usRcvBufferPos++] |= ucResult;
  229.                         eBytePos = BYTE_HIGH_NIBBLE;
  230.                         break;
  231.                 }
  232.             }
  233.             break;
  234.         case STATE_RX_WAIT_EOF:
  235.             if( ucByte == ucMBLFCharacter )
  236.             {
  237.                 /* Disable character timeout timer because all characters are
  238.                  * received. */
  239.                 vMBPortTimersDisable(  );
  240.                 /* Receiver is again in idle state. */
  241.                 eRcvState = STATE_RX_IDLE;
  242.                 /* Notify the caller of eMBASCIIReceive that a new frame
  243.                  * was received. */
  244.                 xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED );
  245.             }
  246.             else if( ucByte == ':' )
  247.             {
  248.                 /* Empty receive buffer and back to receive state. */
  249.                 eBytePos = BYTE_HIGH_NIBBLE;
  250.                 usRcvBufferPos = 0;
  251.                 eRcvState = STATE_RX_RCV;
  252.                 /* Enable timer for character timeout. */
  253.                 vMBPortTimersEnable(  );
  254.             }
  255.             else
  256.             {
  257.                 /* Frame is not okay. Delete entire frame. */
  258.                 eRcvState = STATE_RX_IDLE;
  259.             }
  260.             break;
  261.         case STATE_RX_IDLE:
  262.             if( ucByte == ':' )
  263.             {
  264.                 /* Enable timer for character timeout. */
  265.                 vMBPortTimersEnable(  );
  266.                 /* Reset the input buffers to store the frame. */
  267.                 usRcvBufferPos = 0;;
  268.                 eBytePos = BYTE_HIGH_NIBBLE;
  269.                 eRcvState = STATE_RX_RCV;
  270.             }
  271.             break;
  272.     }
  273.     return xNeedPoll;
  274. }
  275. BOOL
  276. xMBASCIITransmitFSM( void )
  277. {
  278.     BOOL            xNeedPoll = FALSE;
  279.     UCHAR           ucByte;
  280.     assert( eRcvState == STATE_RX_IDLE );
  281.     switch ( eSndState )
  282.     {
  283.             /* Start of transmission. The start of a frame is defined by sending
  284.              * the character ':'. */
  285.         case STATE_TX_START:
  286.             ucByte = ':';
  287.             xMBPortSerialPutByte( ucByte );
  288.             eSndState = STATE_TX_DATA;
  289.             eBytePos = BYTE_HIGH_NIBBLE;
  290.             break;
  291.             /* Send the data block. Each data byte is encoded as a character hex
  292.              * stream with the high nibble sent first and the low nibble sent
  293.              * last. If all data bytes are exhausted we send a 'r' character
  294.              * to end the transmission. */
  295.         case STATE_TX_DATA:
  296.             if( usSndBufferCount > 0 )
  297.             {
  298.                 switch ( eBytePos )
  299.                 {
  300.                     case BYTE_HIGH_NIBBLE:
  301.                         ucByte = prvucMBBIN2CHAR( *pucSndBufferCur >> 4 );
  302.                         xMBPortSerialPutByte( ucByte );
  303.                         eBytePos = BYTE_LOW_NIBBLE;
  304.                         break;
  305.                     case BYTE_LOW_NIBBLE:
  306.                         ucByte = prvucMBBIN2CHAR( *pucSndBufferCur & 0x0F );
  307.                         xMBPortSerialPutByte( ucByte );
  308.                         pucSndBufferCur++;
  309.                         eBytePos = BYTE_HIGH_NIBBLE;
  310.                         usSndBufferCount--;
  311.                         break;
  312.                 }
  313.             }
  314.             else
  315.             {
  316.                 xMBPortSerialPutByte( MB_ASCII_DEFAULT_CR );
  317.                 eSndState = STATE_TX_END;
  318.             }
  319.             break;
  320.             /* Finish the frame by sending a LF character. */
  321.         case STATE_TX_END:
  322.             xMBPortSerialPutByte( ucMBLFCharacter );
  323.             /* We need another state to make sure that the CR character has
  324.              * been sent. */
  325.             eSndState = STATE_TX_NOTIFY;
  326.             break;
  327.             /* Notify the task which called eMBASCIISend that the frame has
  328.              * been sent. */
  329.         case STATE_TX_NOTIFY:
  330.             eSndState = STATE_TX_IDLE;
  331.             xNeedPoll = xMBPortEventPost( EV_FRAME_SENT );
  332.             /* Disable transmitter. This prevents another transmit buffer
  333.              * empty interrupt. */
  334.             vMBPortSerialEnable( TRUE, FALSE );
  335.             eSndState = STATE_TX_IDLE;
  336.             break;
  337.             /* We should not get a transmitter event if the transmitter is in
  338.              * idle state.  */
  339.         case STATE_TX_IDLE:
  340.             /* enable receiver/disable transmitter. */
  341.             vMBPortSerialEnable( TRUE, FALSE );
  342.             break;
  343.     }
  344.     return xNeedPoll;
  345. }
  346. BOOL
  347. xMBASCIITimerT1SExpired( void )
  348. {
  349.     switch ( eRcvState )
  350.     {
  351.             /* If we have a timeout we go back to the idle state and wait for
  352.              * the next frame.
  353.              */
  354.         case STATE_RX_RCV:
  355.         case STATE_RX_WAIT_EOF:
  356.             eRcvState = STATE_RX_IDLE;
  357.             break;
  358.         default:
  359.             assert( ( eRcvState == STATE_RX_RCV )
  360.                     || ( eRcvState == STATE_RX_WAIT_EOF ) );
  361.             break;
  362.     }
  363.     vMBPortTimersDisable(  );
  364.     /* no context switch required. */
  365.     return FALSE;
  366. }
  367. UCHAR
  368. prvucMBCHAR2BIN( UCHAR ucCharacter )
  369. {
  370.     if( ( ucCharacter >= '0' ) && ( ucCharacter <= '9' ) )
  371.     {
  372.         return ucCharacter - '0';
  373.     }
  374.     else if( ( ucCharacter >= 'A' ) && ( ucCharacter <= 'F' ) )
  375.     {
  376.         return ucCharacter - 'A' + 0x0A;
  377.     }
  378.     else
  379.     {
  380.         return 0xFF;
  381.     }
  382. }
  383. UCHAR
  384. prvucMBBIN2CHAR( UCHAR ucByte )
  385. {
  386.     if( ucByte <= 0x09 )
  387.     {
  388.         return '0' + ucByte;
  389.     }
  390.     else if( ( ucByte >= 0x0A ) && ( ucByte <= 0x0F ) )
  391.     {
  392.         return ucByte - 0x0A + 'A';
  393.     }
  394.     else
  395.     {
  396.         /* Programming error. */
  397.         assert( 0 );
  398.     }
  399.     return '0';
  400. }
  401. UCHAR
  402. prvucMBLRC( UCHAR * pucFrame, USHORT usLen )
  403. {
  404.     UCHAR           ucLRC = 0;  /* LRC char initialized */
  405.     while( usLen-- )
  406.     {
  407.         ucLRC += *pucFrame++;   /* Add buffer byte without carry */
  408.     }
  409.     /* Return twos complement */
  410.     ucLRC = ( UCHAR ) ( -( ( CHAR ) ucLRC ) );
  411.     return ucLRC;
  412. }
  413. #endif