FreeModbus版本:1.6
当功能码为04时,也就是读输入寄存器MB_FUNC_READ_INPUT_REGISTER
看一下它是怎么调用读输入寄存器处理函数的
当功能码为04时,调用读输入寄存器处理函数
 
 这个函数在数组xFuncHandlers中,也就是eMBFuncReadInputRegister这个函数
 
来看一下这个函数
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 ) )//2个地址+2个寄存器数量+1个功能码
    {
        //获取寄存器地址
        usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 );//pucFrame[1]   0为功能码
        usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] );//1为寄存器地址高字节  2为寄存器地址低字节
        usRegAddress++;//地址+1
        //获取寄存器数量
        usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF] << 8 );//pucFrame[3]
        usRegCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF + 1] );//3为寄存器数量高字节  4为寄存器数量低字节
        /* 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];//将当前数据帧指针指向数据帧的起始位置,并重置数据帧长度
            *usLen = MB_PDU_FUNC_OFF;//MB_PDU_FUNC_OFF为功能码偏移  0
            /* First byte contains the function code. */
            *pucFrameCur++ = MB_FUNC_READ_INPUT_REGISTER;//在响应帧中添加功能码,长度+1
            *usLen += 1;
            /* Second byte in the response contain the number of bytes. */
            *pucFrameCur++ = ( UCHAR )( usRegCount * 2 );//在响应帧中添加寄存器数量,长度+1。
            *usLen += 1;//一个寄存器为16位,所以需要乘以2个字节
            //调用回调函数读取寄存器数据
            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;//响应数据长度 + 寄存器数量*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;
}
eMBFuncReadInputRegister这个函数用于打包帧
 输入参数是两个指针:
 UCHAR * pucFrame, 帧 Modbus PDU 不包括 从机地址 和 CRC
 USHORT * usLen帧长度
也就是说经过eMBFuncReadInputRegister这个函数处理之后,打包好的帧已经放在了pucFrame,长度放在了usLen。然后打包好的帧,会在eMBPoll轮询中被发送函数调用。然后通过发送状态机发送给主机。
来看一下eMBFuncReadInputRegister是怎么把帧打包的
首先定义了一些变量
 
 变量里重要的是这个 UCHAR *pucFrameCur;//当前帧指针
 pucFrameCur是指向这一帧 的 指针 ,可能有点绕
 就是说这个指针pucFrameCur刚开始指向pucFrame这一帧数据的起始位置,然后打包一个数据,pucFrameCur+一个。
pucFrame是不包括从机地址 和 CRC的,也就是它的长度为5个字节 ,如下
 
 (图取自安富莱modbus教程,安富莱教程写的非常好)
先检查帧的长度,2个地址+2个寄存器数量+1个功能码
 
 然后获取寄存器地址和寄存器数量
 
然后开始打包数据
 
 先将当前数据帧指针指向数据帧的起始位置,并重置数据帧长度
先把功能码打包 长度 +1
 在把寄存器数量打包 长度+1
 在看一下下面该打包啥了
 看一下响应帧组成
 
 (图取自安富莱modbus教程,安富莱教程写的非常好)
读取数据寄存器嘛,下面该打包 读取 到的 数据了
调用回调函数eMBRegInputCB读取寄存器数据,eMBRegInputCB这个函数会把读到的数据继续存放在pucFrameCur 帧里。
如果读取成功 则 usLen += usRegCount * 2; 响应数据长度 + 寄存器数量*2个字节
至此打包完成 。
在回到轮询函数eMBPoll
 
打包完之后 响应帧就已经存在ucMBFrame这里面了。
如果打包过程顺利的话
下面会调用peMBFrameSendCur开始发送响应帧(并不是这个函数发送,这个函数补充打包了地址+CRC)
如果万一打包不顺利,发生了错误。
则会将功能码 + 0x80 ,
 在加一个异常码,
 长度变为2。
 如下图所示
 
 (图取自安富莱modbus教程,安富莱教程写的非常好)
打包数据过程中的 读寄存器数据 那个回调函数怎么实现的呢
继续看一下eMBRegInputCB
eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    int             iRegIndex;//寄存器数组索引
		usAddress = usAddress - 1;//传进来的地址+1了,这里要减1
	//判断地址是否在输入寄存器范围内
    if( ( usAddress >= REG_INPUT_START ) && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
    {
        iRegIndex = ( int )( usAddress - usRegInputStart );//地址 - 开始地址  =  索引
        while( usNRegs > 0 )
        {
            *pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );//寄存器值高位
            *pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );//寄存器值低位
            iRegIndex++;
            usNRegs--;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }
    return eStatus;
}
输入参数:
 pucRegBuffer: 读到的寄存器数据
 usAddress: 寄存器地址
 usNRegs: 要读取的寄存器个数
这个函数并不复杂
 读寄存器数据,其实就是读数组嘛,寄存器是16位的,所以定义数组也是16位的SHORT型。
读数组自然得有数组索引
索引 = 参数地址 - 开始地址
 iRegIndex = ( int )( usAddress - usRegInputStart );获取索引
 然后将数组中的值赋值给 * pucRegBuffer就好了



















