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—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_SIZE  2048
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文件,实现这样几个函数

BOOL  xMBTCPPortInit( 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;
}

BOOL  xMBTCPPortGetRequest( 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;
}


void  vMBTCPPortClose( 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客户端测试,如下图 

 5、多客户端支持

主函数这样修改

 while(1)
    {    

        for(sn=1;sn<8;sn++)

         modbus_tcps(sn,port);

    }

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

展开阅读全文

没有更多推荐了,返回首页