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