freemodbus|freemodbus 在stm32+W5500平台上的移植

1、理解freemodbus的运行机制
在W5500平台上移植freemodbus,主要就是要理解eMBpoll()函数的状态机,在理解过程中我主要参考这样几篇文章,甚至可以说是抄袭吧(部分代码),在此表示衷心感谢!http://bbs.eeworld.com.cn/thread-362508-1-1.html,http://bbs.eeworld.com.cn/thread-362508-1-1.html,这两篇文章讲的如如何将freemodbus RTU模式的移植,希望大家能仔细领悟,一篇详细描述了移植过程病给了移植的代码,另一篇对freemodbus的运行机制讲的比较透彻,仔细阅读会对整个移植过程有个比较深刻的理解。
2、理解W5500的运行机制

如果能在理解freemodbus RTU,我们再来看modbus TCP到底在W500平台上如何实现。这里我先首先说明,在此之前还要明白W5500的运行过程,如果这个也不懂请参考http://blog.csdn.net/wiznet2012/article/details/41279113这篇文章,将W5500库函数移植一定要看明白,并且读懂其中int32_t loopback_tcps这个函数,我们移植的平台就是基于这样一个函数的简单改编而已。
3、移植过程
(1)移植环境搭建
【freemodbus|freemodbus 在stm32+W5500平台上的移植】在以上两个前提下我们来移植freemodbus—TCP。首先我们按照http://bbs.eeworld.com.cn/thread-362508-1-1.html这篇博文的要求,建立起freemodbus_RTU环境,这几个回调函数写的很好,既然我人家写好了,我就比较懒没有自己重写,直接抄了过来。
eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs );
eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode );
eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress,USHORT usNCoils,eMBRegisterMode eMode);
eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete);
(2)编写porttcp.c文件
在此我参考这篇博文http://blog.csdn.net/xukai871105/article/details/21652287,这篇博文讲的是采用uIP协议实现移植,对于modbus的移植有着非常好参考意义,移植前请仔细阅读。主要是建立
//modbus 接收发送寄存器,全局变量
#define MB_TCP_BUF_SIZE2048
uint8_t ucTCPRequestFrame[MB_TCP_BUF_SIZE]; //接收寄存器
uint16_t ucTCPRequestLen;
uint8_t ucTCPResponseFrame[MB_TCP_BUF_SIZE]; //发送寄存器
uint16_t ucTCPResponseLen;
uint8_t bFrameSent = FALSE; //是否进行发送响应判断
建立porttcp.c文件,实现这样几个函数
BOOLxMBTCPPortInit( USHORT usTCPPort )
{
SOCKET sn;
sn=0;
if(getSn_SR(sn)==SOCK_CLOSED)
{
socket(sn,Sn_MR_TCP,usTCPPort,0x00); //打开socket
}
if (getSn_SR(sn)==SOCK_INIT)
{
listen(sn); //监听
return TRUE;
}
return FALSE;
}
BOOLxMBTCPPortGetRequest( UCHAR **ppucMBTCPFrame, USHORT * usTCPLength )
{
*ppucMBTCPFrame = (uint8_t *) &ucTCPRequestFrame[0];
*usTCPLength = ucTCPRequestLen;
/* Reset the buffer. */
ucTCPRequestLen = 0;
return TRUE;
}

BOOL xMBTCPPortSendResponse( const UCHAR *pucMBTCPFrame, USHORT usTCPLength )
{
memcpy(ucTCPResponseFrame,pucMBTCPFrame , usTCPLength);
ucTCPResponseLen = usTCPLength;
bFrameSent = TRUE; // 通过W5500发送数据
return bFrameSent;
}

voidvMBTCPPortClose( void )
{
};

void vMBTCPPortDisable( void )
{
};

这其中 xMBTCPPortInit()函数的内容写不写,貌似影响不大。
(3)编写main.c文件
以上说了半天都是参考别人的,而且和W5500没什么关系。现在是freemodbus 和 w5500 库函数如何结合使用的时候了。看一下main函数
int main(void)
{
uint8_t tmp;
uint8_t sn=0;
uint16_t port=MBTCP_PORT;
eMBErrorCode eStatus;
w5500_SPI_Init();
reg_callback_func(); //注册W5500回调函数,移植W5500库函数必须
Socket_buffer_init(); //W5500 SOCKET Buffer 初始化
/* PHY link 状态检查*/
if(ctlwizchip(CW_GET_PHYLINK, (void*)&tmp) == -1){
// printf("Unknown PHY Link stauts.\r\n");
}
/* 网络初始化 */
network_init();
//启动FreeModbus
eStatus = eMBTCPInit(port );
eStatus =eMBEnable();
while(1)
{
modbus_tcps(sn,port);
}
}

w5500_SPI_Init();
reg_callback_func(); //注册W5500回调函数,移植W5500库函数必须
Socket_buffer_init(); //W5500 SOCKET Buffer 初始化
/* PHY link 状态检查*/
if(ctlwizchip(CW_GET_PHYLINK, (void*)&tmp)
}
/* 网络初始化 */
network_init();

等都是W5500初始化的过程,
eStatus = eMBTCPInit(port );
eStatus =eMBEnable();
是freemodbus初始化的过程。
来看一下modbus_tcps(sn,port)函数
//W5500在TCP server模式下,接收、发送modbus-TCP报文
void modbus_tcps(uint8_t sn, uint16_t port)
{
switch(getSn_SR(sn))//获取socket状态
{
case SOCK_CLOSED://socket处于关闭状态
socket(sn,Sn_MR_TCP,port,0x00); //打开socket
break;
case SOCK_INIT ://socket处于已经初始化状态
listen(sn); //监听
case SOCK_ESTABLISHED ://socket处于连接状态
if(getSn_IR(sn) & Sn_IR_CON)
{
setSn_IR(sn,Sn_IR_CON);
}
ucTCPRequestLen = getSn_RX_RSR(sn); //获取接收数据长度
if(ucTCPRequestLen>0)
{
recv(sn,ucTCPRequestFrame, ucTCPRequestLen); //W5500接收数据
xMBPortEventPost(EV_FRAME_RECEIVED); //发送EV_FRAME_RECEIVED事件,以驱动eMBpoll()函数中的状态机
eMBPoll(); //处理EV_FRAME_RECEIVED事件
eMBPoll(); //处理EV_EXECUTE事件
if(bFrameSent)
{
bFrameSent = FALSE;
//W5500发送Modbus应答数据包
send(sn,ucTCPResponseFrame,ucTCPResponseLen);
}
}
break;
case SOCK_CLOSE_WAIT ://socket处于等待关闭状态
disconnect(sn); //关闭连接
break;
default:
break;
}
}
这个函数就是不断检查W5500的状态,如果检查到有收到数据包之后开始接受数据,并启动freemodbus状态机。具体流程是,W5500接受数据包存入ucTCPRequestFrame数组中 → 发送EV_FRAME_RECEIVED事件 → 两次调用freemodbus状态机,完成对接受报文的解析,并生成响应的报文存入ucTCPRequestFrame数组中 → 最后W5500将数组中的报文中发送出去。
即每次收到数据包之后就执行这样一个流程即可。ucTCPRequestFrame,ucTCPResponseFrame正是W5500和freemodbus结合的媒介。
4、测试
利用modsan32客户端测试,如下图 freemodbus|freemodbus 在stm32+W5500平台上的移植
文章图片

5、多客户端支持
主函数这样修改
while(1)
{
for(sn=1; sn<8; sn++)
modbus_tcps(sn,port);
}

w5500具有8个Socket,TCPserver模式下最多支持8个客户端同时访问。

    推荐阅读