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就好了