Freemodbus 源码讲解系列 (三)

这次主要讲解从机数据的接收流程。接收流程分为两个阶段:串口中断接收和数据解析。第一阶段为中断接收函数prvvUARTRxISR(xMBRTUReceiveFSM)和定时器中断回调函数xMBRTUTimerT35Expired(),第二阶段为数据解析eMBPoll(  )。

一、串口中断接收

从机正常状态下,串口设置为接收中断模式,也不启动定时器。当检测到有数据时,中断函数调用xMBRTUReceiveFSM()函数,通过该函数中的ucRTUBuf数组存储接收帧,用usRcvBufferPos存储数据帧长度。具体函数:

1、中断接收函数。

void UART1_IRQ(void)
{

    if(USART_GetITStatus(USART1,USART_IT_TXE))
    {
        pxMBFrameCBTransmitterEmpty(  );
    }
    else if(USART_GetITStatus(USART1,USART_IT_RXNE))//接收中断
    {
        pxMBFrameCBByteReceived(  );//pxMBFrameCBByteReceived = xMBRTUReceiveFSM;
    }
}

2、xMBRTUReceiveFSM()函数。只要进人该函数,就会启动定时器中断。从机的接收状态:STATE_RX_INIT(协议栈初始化)、STATE_RX_ERROR(接受错误)、STATE_RX_IDLE(接收空闲,接收帧头,常用)、STATE_RX_RCV(接收所有数据,常用)。注意:接收数据时,只要接收到数据,定时器就会清零,重新计数。

BOOL
xMBRTUReceiveFSM( void )
{
    BOOL            xTaskNeedSwitch = FALSE;
    UCHAR           ucByte;

    assert( eSndState == STATE_TX_IDLE );

    /* Always read the character. */
    ( void )xMBPortSerialGetByte( ( CHAR * ) & ucByte );

    switch ( eRcvState )
    {
        /* If we have received a character in the init state we have to
         * wait until the frame is finished.
         */
    case STATE_RX_INIT:
        vMBPortTimersEnable(  );
        break;

        /* In the error state we wait until all characters in the
         * damaged frame are transmitted.
         */
    case STATE_RX_ERROR:
        vMBPortTimersEnable(  );
        break;

        /* In the idle state we wait for a new character. If a character
         * is received the t1.5 and t3.5 timers are started and the
         * receiver is in the state STATE_RX_RECEIVCE.
         */
    case STATE_RX_IDLE:
        usRcvBufferPos = 0;
        ucRTUBuf[usRcvBufferPos++] = ucByte;
        eRcvState = STATE_RX_RCV;

        /* Enable t3.5 timers. */
        vMBPortTimersEnable(  );
        break;

        /* We are currently receiving a frame. Reset the timer after
         * every character received. If more than the maximum possible
         * number of bytes in a modbus frame is received the frame is
         * ignored.
         */
    case STATE_RX_RCV:
        if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX )
        {
            ucRTUBuf[usRcvBufferPos++] = ucByte;
        }
        else
        {
            eRcvState = STATE_RX_ERROR;
        }
        vMBPortTimersEnable(  );
        break;
    }
    return xTaskNeedSwitch;
}

二、数据解析涉及到的函数 eMBPoll、eMBRTUReceive和xFuncHandlers[i]是结构体数组。

1、eMBPoll函数。eMBPoll函数调用eMBRTUReceive和xFuncHandlers[i]是结构体数组。

eMBErrorCode
eMBPoll( void )
{
    static UCHAR   *ucMBFrame;
    static UCHAR    ucRcvAddress;
    static UCHAR    ucFunctionCode;
    static USHORT   usLength;
    static eMBException eException;

    int             i;
    eMBErrorCode    eStatus = MB_ENOERR;
    eMBEventType    eEvent;

    /* Check if the protocol stack is ready. */
    if( eMBState != STATE_ENABLED )
    {
        return MB_EILLSTATE;
    }

    /* Check if there is a event available. If not return control to caller.
     * Otherwise we will handle the event. */
    if( xMBPortEventGet( &eEvent ) == TRUE )
    {
        switch ( eEvent )
        {
        case EV_READY:
            break;

        case EV_FRAME_RECEIVED:
            eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength );
            if( eStatus == MB_ENOERR )
            {
                /* Check if the frame is for us. If not ignore the frame. */
                if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) )
                {
                    ( void )xMBPortEventPost( EV_EXECUTE );
                }
            }
            break;

        case EV_EXECUTE:
            ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];//把接收的数据传递给ucFunctionCode
            eException = MB_EX_ILLEGAL_FUNCTION;
            for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
            {
                /* No more function handlers registered. Abort. */
                if( xFuncHandlers[i].ucFunctionCode == 0 )
                {
                    break;
                }
                else if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode )//查询匹配的功能码,
                {
                    eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength );//调用相应的功能函数
                    break;
                }
            }

            /* If the request was not sent to the broadcast address we
             * return a reply. */
            if( ucRcvAddress != MB_ADDRESS_BROADCAST )
            {
                if( eException != MB_EX_NONE )//如果接收有问题
                {
                    /* An exception occured. Build an error frame. */
                    usLength = 0;
                    ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR );//功能码与0x80或之后的为                                                                                                                                                         //异常码
                    ucMBFrame[usLength++] = eException;//非法功能代码
                }
                if( ( eMBCurrentMode == MB_ASCII ) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS )
                {
                    vMBPortTimersDelay( MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS );
                }                
                eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength );//发送数据
            }
            break;

        case EV_FRAME_SENT:
            break;
        }
    }
    return MB_ENOERR;
}

 

2、peMBFrameReceiveCur()函数在Freemodbus 源码讲解系列 (二)中已经介绍,不再赘述

3、主要讲一下xFuncHandlers[i],它是结构体数组,存放的是功能码以及对应的报文解析函数。

typedef struct { UCHAR ucFunctionCode; pxMBFunctionHandler pxHandler; } xMBFunctionHandler;

static xMBFunctionHandler xFuncHandlers[MB_FUNC_HANDLERS_MAX] = {
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED > 0
    {MB_FUNC_OTHER_REPORT_SLAVEID, eMBFuncReportSlaveID},
#endif
#if MB_FUNC_READ_INPUT_ENABLED > 0
    {MB_FUNC_READ_INPUT_REGISTER, eMBFuncReadInputRegister},
#endif
#if MB_FUNC_READ_HOLDING_ENABLED > 0
    {MB_FUNC_READ_HOLDING_REGISTER, eMBFuncReadHoldingRegister},
#endif
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0
    {MB_FUNC_WRITE_MULTIPLE_REGISTERS, eMBFuncWriteMultipleHoldingRegister},
#endif
#if MB_FUNC_WRITE_HOLDING_ENABLED > 0
    {MB_FUNC_WRITE_REGISTER, eMBFuncWriteHoldingRegister},
#endif
#if MB_FUNC_READWRITE_HOLDING_ENABLED > 0
    {MB_FUNC_READWRITE_MULTIPLE_REGISTERS, eMBFuncReadWriteMultipleHoldingRegister},
#endif
#if MB_FUNC_READ_COILS_ENABLED > 0
    {MB_FUNC_READ_COILS, eMBFuncReadCoils},
#endif
#if MB_FUNC_WRITE_COIL_ENABLED > 0
    {MB_FUNC_WRITE_SINGLE_COIL, eMBFuncWriteCoil},
#endif
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
    {MB_FUNC_WRITE_MULTIPLE_COILS, eMBFuncWriteMultipleCoils},
#endif
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED > 0
    {MB_FUNC_READ_DISCRETE_INPUTS, eMBFuncReadDiscreteInputs},
#endif
};

 以功能码04为例,读取输入寄存器。

eMBException
eMBFuncReadInputRegister( UCHAR * pucFrame, USHORT * usLen )
{
    USHORT          usRegAddress;
    USHORT          usRegCount;
    UCHAR          *pucFrameCur;

    eMBException    eStatus = MB_EX_NONE;
    eMBErrorCode    eRegStatus;

    if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) )//计算帧长度,除了地址位和两位的CRC校验位
    {
        usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 );//读取寄存器的首地址
        usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] );
        usRegAddress++;

        usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF] << 8 );//计算读取寄存器的长度
        usRegCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF + 1] );

        /* Check if the number of registers to read is valid. If not
         * return Modbus illegal data value exception. 
         */
        if( ( usRegCount >= 1 )
            && ( usRegCount < MB_PDU_FUNC_READ_REGCNT_MAX ) )//读取寄存器的长度不越界
        {
            /* Set the current PDU data pointer to the beginning. */
            pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];//把数据帧赋值给pucFrameCur
            *usLen = MB_PDU_FUNC_OFF;

            /* First byte contains the function code. */
            *pucFrameCur++ = MB_FUNC_READ_INPUT_REGISTER;//
            *usLen += 1;

            /* Second byte in the response contain the number of bytes. */
            *pucFrameCur++ = ( UCHAR )( usRegCount * 2 );
            *usLen += 1;

            eRegStatus =
                eMBRegInputCB( pucFrameCur, usRegAddress, usRegCount );

            /* If an error occured convert it into a Modbus exception. */
            if( eRegStatus != MB_ENOERR )
            {
                eStatus = prveMBError2Exception( eRegStatus );
            }
            else
            {
                *usLen += usRegCount * 2;
            }
        }
        else
        {
            eStatus = MB_EX_ILLEGAL_DATA_VALUE;
        }
    }
    else
    {
        /* Can't be a valid read input register request because the length
         * is incorrect. */
        eStatus = MB_EX_ILLEGAL_DATA_VALUE;
    }
    return eStatus;
}

 

eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    int             iRegIndex;

    if( ( usAddress >= REG_INPUT_START )
        && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
    {
        iRegIndex = ( int )( usAddress - REG_INPUT_START );
        while( usNRegs > 0 )
        {
            *pucRegBuffer++ =
                ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );
            *pucRegBuffer++ =
                ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
            iRegIndex++;
            usNRegs--;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;

以上就是整个接收数据、解析数据的过程。 

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: freemodbus是一种用于实现Modbus通信协议的开源软件库,它提供了一套完整的Modbus主站和从站的应用层协议栈。要下载freemodbus源码,您可以按照以下步骤进行: 1. 打开您的互联网浏览器。 2. 在浏览器的搜索栏中输入"freemodbus"或者直接访问一些知名的开源代码托管平台,例如GitHub、GitLab等。 3. 在搜索结果或者代码托管平台的搜索框中输入"freemodbus"。 4. 点击进入相关的搜索结果或者进入代码托管平台。 5. 在相关页面中,您可以找到freemodbus的仓库地址或者下载源码的链接。 6. 点击相应链接即可开始下载freemodbus源码文件。 需要注意的是,freemodbus是开源软件,因此您可以在遵守开源协议的前提下自由下载和使用它。此外,在下载和使用源码之前,建议您先阅读相关的文档或者官方文档,以了解如何正确使用和配置freemodbus库。希望这个回答对您有所帮助! ### 回答2: freemodbus源码可以在多个地方下载。其中,官方网站是最常见的下载来源。您可以通过访问freemodbus官方网站(https://www.freemodbus.org/)来获取源码。 在官方网站上,您可以找到freemodbus源码的各个版本和相关的文档。 除了官方网站,您还可以在一些代码托管平台上找到freemodbus源码,比如GitHub(https://github.com/)。通过在GitHub上搜索关键词"freemodbus",您会找到很多与freemodbus相关的项目。在这些项目中,您可以找到freemodbus源码和其他开发者的改进或补丁。 另外,一些开源软件库和开发者社区也会提供freemodbus源码下载。您可以在这些网站上搜索freemodbus来找到相关的下载链接。 需要注意的是,无论从哪个来源下载freemodbus源码,请务必遵守开源软件的相关协议和条款,了解和遵守开源许可证的规定。 ### 回答3: FreeModbus源码可以通过以下几个途径进行下载: 1. 官方网站:你可以访问FreeModbus官方网站(www.freemodbus.org)来下载源代码。在网站的下载页面上,你可以找到不同版本的源代码,选择适合你的平台和需求的版本进行下载。 2. 开源社区网站:FreeModbus是一个开源项目,因此你可以在各种开源社区网站(如GitHub、SourceForge等)上搜索并下载源代码。这些网站通常提供最新版本的源代码,并且允许用户共享和贡献代码改进。 3. 相关论坛和社区:你可以加入和参与与FreeModbus相关的论坛和社区,如FreeModbus Google Group,向其他开发者寻求帮助和资源。在这些论坛和社区中,你可以与其他用户分享、交流和获取最新的源代码。 无论你选择在哪里下载FreeModbus源代码,建议你仔细阅读相关的文档和指南,以便了解如何使用和配置该源代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值