首先,在使能modbus协议栈的时候,会调用pvMBFrameStartCur函数
/* 使能modbus */
eMBErrorCode eMBEnable(void)
{
eMBErrorCode eStatus = MB_ENOERR;
/* modbus还未使能 */
if(eMBState == STATE_DISABLED)
{
/* 启动modbus */
pvMBFrameStartCur();
/* 设置modbus状态为使能 */
eMBState = STATE_ENABLED;
}
else
{
/* 状态不合法 */
eStatus = MB_EILLSTATE;
}
return eStatus;
}
在tcp模式下pvMBFrameStartCur指针指向eMBTCPStart函数
/* modbus tcp启动 */
void eMBTCPStart(void)
{
}
因为在初始化的时候已经,已经开启tcp监听,并等待客户端连接了。因此eMBTCPStart函数什么也不用做。
主程序接收到接收完成事件之后,对数据帧进行校验和拆解,最后会得到PDU数据的指针和长度。并向主程序发送执行事件。
/* modbus轮询 */
eMBErrorCode eMBPoll(void)
{
......
/* 获取事件 */
if(xMBPortEventGet(&eEvent) == TRUE)
{
/* 判断事件类型 */
switch(eEvent)
{
......
/* 接收完成事件 */
case EV_FRAME_RECEIVED:
/* modbus接收函数,获取地址、PDU指针、PDU长度 */
eStatus = peMBFrameReceiveCur(&ucRcvAddress, &ucMBFrame, &usLength);
if(eStatus == MB_ENOERR)
{
/* 判断地址是否吻合 */
if((ucRcvAddress == ucMBAddress) || (ucRcvAddress == MB_ADDRESS_BROADCAST))
{
/* 发送执行事件 */
(void)xMBPortEventPost(EV_EXECUTE);
}
}
break;
......
}
}
return MB_ENOERR;
}
下面看一下peMBFrameReceiveCur调用的eMBTCPReceive函数。主要工作是对数据帧进行拆分,并区分是否是modbus协议。
/* modbus tcp接收 */
eMBErrorCode eMBTCPReceive(UCHAR *pucRcvAddress, UCHAR **ppucFrame, USHORT *pusLength)
{
eMBErrorCode eStatus = MB_EIO;
UCHAR *pucMBTCPFrame;
USHORT usLength;
USHORT usPID;
/* 接收到一帧数据 */
if(xMBTCPPortGetRequest(&pucMBTCPFrame, &usLength) != FALSE)
{
/* 协议ID */
usPID = pucMBTCPFrame[MB_TCP_PID] << 8U;
usPID |= pucMBTCPFrame[MB_TCP_PID + 1];
/* modbus协议 */
if(usPID == MB_TCP_PROTOCOL_ID)
{
/* PDU */
*ppucFrame = &pucMBTCPFrame[MB_TCP_FUNC];
/* 数据长度 */
*pusLength = usLength - MB_TCP_FUNC;
/* 成功 */
eStatus = MB_ENOERR;
/* 在服务器中只有0xFF地址 */
/* 在网关程序中,这里需要真正取出单元标识符 */
*pucRcvAddress = MB_TCP_PSEUDO_ADDRESS;
}
}
else
{
eStatus = MB_EIO;
}
return eStatus;
}
主程序接收到执行事件之后,判断功能码,调用相应功能函数。然后对主机进行响应。
/* modbus轮询 */
eMBErrorCode eMBPoll(void)
{
......
/* 获取事件 */
if(xMBPortEventGet(&eEvent) == TRUE)
{
/* 判断事件类型 */
switch(eEvent)
{
......
/* 执行事件 */
case EV_EXECUTE:
/* 功能码 */
ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];
eException = MB_EX_ILLEGAL_FUNCTION;
/* 遍历所有支持的功能码 */
for(i = 0; i < MB_FUNC_HANDLERS_MAX; i++)
{
/* 遍历完了 */
if(xFuncHandlers[i].ucFunctionCode == 0)
{
break;
}
/* 匹配到合适的功能码 */
else if(xFuncHandlers[i].ucFunctionCode == ucFunctionCode)
{
/* 调用相关功能 */
eException = xFuncHandlers[i].pxHandler(ucMBFrame, &usLength);
break;
}
}
/* 不是广播 */
if(ucRcvAddress != MB_ADDRESS_BROADCAST)
{
/* 出现异常 */
if(eException != MB_EX_NONE)
{
/* PDU长度初始化为0 */
usLength = 0;
/* 功能码+0x80则表示异常 */
ucMBFrame[usLength++] = (UCHAR)(ucFunctionCode | MB_FUNC_ERROR);
/* 异常码 */
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;
......
}
}
return MB_ENOERR;
}
peMBFrameSendCur指针调用eMBTCPSend对主机进行响应。主要工作为对PDU进行封装并发送。
/* modbus tcp发送 */
eMBErrorCode eMBTCPSend(UCHAR _unused, const UCHAR *pucFrame, USHORT usLength)
{
eMBErrorCode eStatus = MB_ENOERR;
/* 空出MBAP域 */
UCHAR *pucMBTCPFrame = (UCHAR *)pucFrame - MB_TCP_FUNC;
/* 数据包计算长度 */
USHORT usTCPLength = usLength + MB_TCP_FUNC;
/* 更改MBAP中的长度域,其它域保持和请求相同 */
pucMBTCPFrame[MB_TCP_LEN] = (usLength + 1) >> 8U;
pucMBTCPFrame[MB_TCP_LEN + 1] = (usLength + 1) & 0xFF;
/* 发送数据包 */
if(xMBTCPPortSendResponse(pucMBTCPFrame, usTCPLength) == FALSE)
{
eStatus = MB_EIO;
}
return eStatus;
}