FreeModbus TCP传输

首先,在使能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协议。

FreeModbus TCP传输_第1张图片

/* 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;
}

 

你可能感兴趣的:(Modbus)