关于STM32与W5500组成的MODBUS-2(freemodbus)

[复制链接]
101|17
 楼主 | 2017-12-12 14:13 | 显示全部楼层 |阅读模式
上一节 我们刚刚探讨完了W5500 最基本的读写函数,现在我们来看 FREEMODBUS的处理部分!
 楼主 | 2017-12-12 14:14 | 显示全部楼层
关键的内容就是怎样获取W5500新接收的数据包,并发送给Modbus事件状态机驱动协议的执行,数据的处理。
主要参考Freemodbus  demo里的Modbus-TCP协议实现的思路,获取缓存区的读写与发送响应。
 楼主 | 2017-12-12 14:14 | 显示全部楼层
1、理解freemodbus的运行机制         
在W5500平台上移植freemodbus,主要就是要理解eMBpoll()函数的状态机,在理解过程中我主要参考这样几篇文章,甚至可以说是抄袭吧(部分代码),在此表示衷心感谢!这两篇文章讲的如如何将freemodbus RTU模式的移植,希望大家能仔细领悟,一篇详细描述了移植过程病给了移植的代码,另一篇对freemodbus的运行机制讲的比较透彻,仔细阅读会对整个移植过程有个比较深刻的理解。
 楼主 | 2017-12-12 14:15 | 显示全部楼层
如果能在理解freemodbus RTU,我们再来看modbus TCP到底在W500平台上如何实现。这里我先首先说明,在此之前还要明白W5500的运行过程
 楼主 | 2017-12-12 14:15 | 显示全部楼层
根据函数名字和库中的注释,我们这里也对要实现的函数做个简单的说明
wizchip_cris_enter :进入临界区的函数,可以不管
wizchip_cris_exit :退出临界区的函数,也可以不管
wizchip_cs_select :输出有效片选信号的函数,也就是控制CS输出低电平的函数,必须实现
wizchip_cs_deselect :控制CS输出高电平的函数,必须实现
wizchip_bus_readbyte :SPI总线读取一字节数据函数,必须实现
wizchip_bus_writebyte :SPI总线写一字节数据函数,必须实现

根据以上可知,只要实现了SPI的基本操作,移植基本完成,是不是很简单

下面我们就新建一个spi.c的文件来实现这几个函数,当然这些函数名字可以不和这个结构体里面的函数名字一样,到时候可以调用相关的函数注册下即可
 楼主 | 2017-12-12 15:04 | 显示全部楼层
(1)移植环境搭建
     在以上两个前提下我们来移植freemodbus—TCP。首先我们按照http://bbs.**/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);
 楼主 | 2017-12-12 15:05 | 显示全部楼层
(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;   //是否进行发送响应判断
 楼主 | 2017-12-12 15:07 | 显示全部楼层
建立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;
}
 楼主 | 2017-12-12 15:08 | 显示全部楼层
BOOL  xMBTCPPortGetRequest( UCHAR **ppucMBTCPFrame, USHORT * usTCPLength )
{
    *ppucMBTCPFrame = (uint8_t *) &ucTCPRequestFrame[0];  
    *usTCPLength = ucTCPRequestLen;   
    /* Reset the buffer. */  
    ucTCPRequestLen = 0;  
    return TRUE;  
}
 楼主 | 2017-12-12 15:08 | 显示全部楼层
BOOL xMBTCPPortSendResponse( const UCHAR *pucMBTCPFrame, USHORT usTCPLength )
{  
      memcpy(ucTCPResponseFrame,pucMBTCPFrame , usTCPLength);  
      ucTCPResponseLen = usTCPLength;  
      bFrameSent = TRUE; // 通过W5500发送数据  
      return bFrameSent;
}
 楼主 | 2017-12-12 15:09 | 显示全部楼层
void  vMBTCPPortClose( void )
{
};
 楼主 | 2017-12-12 15:09 | 显示全部楼层
void vMBTCPPortDisable( void )
{
};
 楼主 | 2017-12-12 15:10 | 显示全部楼层
这其中 xMBTCPPortInit()函数的内容写不写,貌似影响不大。
 楼主 | 2017-12-12 15:10 | 显示全部楼层
来看一下modbus_tcps(sn,port)函数:



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;
   }
}
 楼主 | 2017-12-12 15:11 | 显示全部楼层
这个函数就是不断检查W5500的状态,如果检查到有收到数据包之后开始接受数据,并启动freemodbus状态机。具体流程是,W5500接受数据包存入ucTCPRequestFrame数组中 → 发送EV_FRAME_RECEIVED事件 → 两次调用freemodbus状态机,完成对接受报文的解析,并生成响应的报文存入ucTCPRequestFrame数组中 → 最后W5500将数组中的报文中发送出去。
    即每次收到数据包之后就执行这样一个流程即可。ucTCPRequestFrame,ucTCPResponseFrame正是W5500和freemodbus结合的媒介。
 楼主 | 2017-12-12 15:12 | 显示全部楼层
多客户端支持
主函数这样修改
while(1)
    {   
        for(sn=1;sn<8;sn++)
         modbus_tcps(sn,port);
    }
w5500具有8个Socket,TCPserver模式下最多支持8个客户端同时访问。
 楼主 | 2017-12-12 15:13 | 显示全部楼层
测试图1

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册 手机登录

x
| 2018-1-15 14:20 | 显示全部楼层
楼主这帖子不错。可以加个好友QQ2574989918,可以一起学习交流。
我们是w5500代理商炜世科技  提供技术支持0755-86568556 support@wisioe.com
扫描二维码,随时随地手机跟帖
您需要登录后才可以回帖 登录 | 注册 手机登录

本版积分规则

快速回复

您需要登录后才可以回帖
登录 | 注册 手机登录
高级模式

论坛热帖

关闭

热门推荐上一条 /4 下一条

分享 快速回复 返回顶部 返回列表