您的位置:首页 > 理论基础 > 计算机网络

freemodbus modbus TCP 学习笔记

2014-03-20 22:38 381 查看
1.前言 使用modbus有些时间了,期间使用过modbus RTU也使用过modbus TCP,通过博文和大家分享一些MODBUS TCP的东西。在嵌入式中实现TCP就需要借助一个以太网协议栈,在这里我选择最简单的uIP协议栈。uIP协议栈简单易用方便上手,相比于LwIP无论是移植还是使用难度都低些,这样就可以把更多的精力花在modbus tcp协议本身而不必花大量的时间研究以太网协议栈。modbus协议栈为freemodbus
【其他有用的博文】 【1】uIP学习笔记 【2】MODBUS协议整理——汇总
【工程代码】 示例代码托管于GitHub——【Github Clone】 如果有问题我会及时更新。【使用说明】 【1】工具链为IAR 6.5 【2】从机IP为固定IP 192.168.1.15,请保证从机和路由器位于同一个网段中。 【3】modbus tcp的侦听端口号为502
2.MODBUS TCP注意点2.1 主机和从机、服务端和客户端


图1 MODBUS请求响应模型【在modbus协议中】主机发送modbus请求,从机根据请求内容向主机返回响应。在modbus协议中,主机总是主动方,从机总是被动方。【在网络应用中】在网络应用中存在客户端和服务器端,客户端(例如浏览器)发送请求到服务器,服务器向客户端返回内容(例如HTML文本)。【在modbus tcp中】主机是客户端,而从机是服务器端。千万不要以为服务器端重要,主机也重要,所以主机就是服务器端。
2.2 是否可以多主机 通过前面的分析,主机为客户端那么modbus tcp支持多个主机,在一个局域网中可存在多个主机和多个从机。从机的连接能力(连接主机的数量)由uIP的最大TCP连接个数决定。
2.3 modbus TCP协议简述modbus TCP和modbus RTU基本相同,但是也存在一些区别 【1】从机地址变得不再重要,多数情况下忽略。从某种意义上说从机地址被IP地址取代 【2】CRC校验变得不再重要,甚至可以忽略。由于TCP数据包中已经存在校验,为了不重复造轮子,modbus TCP干脆取消了CRC校验。
modbus TCP和modbus RTU的区别可使用下图概括


图2 modbus TCP数据包和modbus RTU数据包比较
在modbus TCP中包含一个MBAP头,该头包含以下几个部分
区域长度描述客户端服务器
传输标志2字节MODBUS 请求和响应传输过程中序列号客户端生成应答时复制该值
协议标志2字节Modbus协议默认为0客户端生成应答时复制该值
长度2字节剩余部分的长度客户端生成应答时由服务器端生成
单元标志1字节从机标志(从机地址)客户端生成应答时复制该值
【注意】【1】传输标志可理解为序列号,防止MODBUS TCP通信错位,例如后发生的响应先到了主机,而早发生的响应后到主机【2】单元标志可理解为从机地址,此时已经不再重要
2.4 modbus tcp 和 TCP IP的关系 modbus TCP可以理解为发生在TCP上的应用层协议,既然是TCP协议那么一个完整的MODBUS TCP报文必然包括TCP首部,IP首部和Ethernet首部。 下面就通过uIP协议栈来实现modbus TCP
3.代码实现3.1 侦听502端口
BOOL
xMBTCPPortInit( USHORT usTCPPort )
{
    BOOL bOkay = FALSE;
   
    USHORT usPort;
    if( usTCPPort == 0 )
    {
        usPort = MB_TCP_DEFAULT_PORT;
    }
    else
    {
        usPort = (USHORT)usTCPPort;
    }
   
    // 侦听端口 502端口
    uip_listen(HTONS(usPort));
   
    bOkay = TRUE;
    return bOkay;
}
【代码说明】 【1】uip_listen(HTONS(usPort)) 侦听502端口,注意大小端变化。
3.2 uIP循环处理——porttcp.c
void uip_modbus_appcall(void)
{
    if(uip_connected())
    {
        PRINTF("connected!\r\n");
    }
   
    if(uip_closed())
    {
        PRINTF("closed\r\n");
    }
   
    if(uip_newdata())
    {
        PRINTF("request!\r\n");
        // 获得modbus请求
        memcpy(ucTCPRequestFrame, uip_appdata, uip_len );
        ucTCPRequestLen = uip_len;
        // 向 modbus poll发送消息
        xMBPortEventPost( EV_FRAME_RECEIVED );
    }
   
    if(uip_poll())
    {
        if(bFrameSent)
        {
            bFrameSent = FALSE;
            // uIP发送Modbus应答数据包
            uip_send( ucTCPResponseFrame , ucTCPResponseLen );
        }
    }
}
【代码说明】 【1】uip_newdata()返回为True表示存在新的数据 【2】复制uip_appdate中的数据到ucTCPRequestFrame,该变量为全局变量,通过该全部变量”中转“到modbus处理的缓冲区中。然后向modbus协议栈发送消息,消息内容为EV_FRAME_RECEIVED 。由于没有使用操作系统 【3】如果处理完成则通过uip_send发送响应。 【4】ucTCPRequestFrame和ucTCPResponseFrame均为全局数组,用于和modbus缓冲区交换数据。static UCHAR ucTCPRequestFrame[MB_TCP_BUF_SIZE];static USHORT ucTCPRequestLen;static UCHAR ucTCPResponseFrame[MB_TCP_BUF_SIZE];static USHORT ucTCPResponseLen;
3.3 modbus 接收处理
BOOL
xMBTCPPortGetRequest( UCHAR ** ppucMBTCPFrame, USHORT * usTCPLength )
{
    *ppucMBTCPFrame = &ucTCPRequestFrame[0];
    *usTCPLength = ucTCPRequestLen;
   
    /* Reset the buffer. */
    ucTCPRequestLen = 0;
    return TRUE;
}
【代码说明】 【1】** ppucMBTCPFrame为一个指向数据的指针,而*ppucMBTCPFrame可以指向一个数组,在这里可把ucTCPRequestFrame复制给该变量,配合usTCPLength,那么从uIP接收到的内容就”转移“到freemodbus中。
3.4 modbus 发送处理
BOOL
xMBTCPPortSendResponse( const UCHAR * pucMBTCPFrame, USHORT usTCPLength )
{
    memcpy( ucTCPResponseFrame , pucMBTCPFrame , usTCPLength);
    ucTCPResponseLen = usTCPLength;
   
    bFrameSent = TRUE; // 通过uip_poll发送数据
    return bFrameSent;
}
【代码说明】【1】把传入的内容 pucMBTCPFrame复制给ucTCPResponseFrame,并设置bFrameSent为True,那么在下一次uip_poll时便会把响应发送会主机。
4.测试与分析 【1】连接从机 选择IP地址为192.168.1.15,端口号为502


图3 打开modbus tcp连接 【2】尝试读出保持寄存器


图4 读取保持寄存器 【3】抓包分析 请使用ip.addr == 192.168.1.15 表达式过滤报文,其中192.168.1.100为PC机,此处为modbus 主机 【简单分析】 【1】115行为modbus主机请求,此时传输标志为25. 【2】116行为modbus从机给出的TCP应答,TCP应答为TCP协议规定的内容,TCP应答中不包含modbus 响应 【3】117行为modbus从机响应,此时传输标志依然为25. 【4】118行为modbus主机 TCP应答,同16行。


图5 抓包分析
6.参考资料【基于EncEthernet的FreeModbus-TCP 在stm32上的移植与测试】【simple modbus
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: