测试用架构:stm32f103c8t6 + enc28j60 +  EncEthernet + freemodbus TCP。
EncEthernet实现了arp + icmp + tcp协议。
让freemodbus支持tcp,需要修改在mbconfig.h
define MB_ASCII_ENABLED                        (  0)
 #define MB_RTU_ENABLED                        (  0 )
 #define MB_TCP_ENABLED                        (  1 )main函数里主要代码:
//定义modbus server 全局变量
 #define REG_INPUT_START 1  //初始地址为0会有问题
 #define REG_INPUT_NREGS 8
 static USHORT   usRegInputBuf[REG_INPUT_NREGS] = {0x0102, 0x0304, 0x0506, 0x0708, 0x0910, 0x1112, 0x1314, 0x1516};
 #define REG_HOLDING_START 1   //初始地址为0会有问题
 #define REG_HOLDING_NREGS 8
 uint16_t usRegHoldingBuf[REG_HOLDING_NREGS] = {0x0102, 0x0304, 0x0506, 0x0708, 0x0910, 0x1112, 0x1314, 0x1516};
 #define REG_COILS_START 1  //初始地址为0会有问题
 #define REG_COILS_SIZE 32
 static UCHAR       ucRegCoilsBuf[REG_COILS_SIZE / 8 + (REG_COILS_SIZE % 8 ? 1 : 0)] = {0};
 #define REG_DISCRETE_START 1  //初始地址为0会有问题
 #define REG_DISCRETE_SIZE 32
 static UCHAR       ucRegDiscreteBuf[REG_DISCRETE_SIZE / 8 + (REG_DISCRETE_SIZE % 8 ? 1 : 0)] = {0}; 
#define BUFFER_SIZE 1518
 unsigned char buf[BUFFER_SIZE + 1];  //定义enc28j60缓冲区,收发共用!
 unsigned char mymac[6] = {0x54, 0x55, 0x58, 0x10, 0x00, 0x24}; //定义网卡mac地址
 unsigned char myip[4] = {192, 168, 2, 8}; //定义网卡IP
 unsigned char * info_addr ; //保存tcp数据的起始地址,buf数据=以太网头+IP头+TCP头+TCP数据。
 extern unsigned int  info_data_len; //tcp数据长度,这个定义在EncEthernet里定义了
 unsigned int info_len; //tcp数据长度
 int main()
 {
          unsigned int plen; //enc28j60接收数据长度
          unsigned int dat_p; //TCP数据在buf里的位置
          eMBErrorCode    eStatus; 
          enc28j60_init(mymac);  //enc28j60初始化,包含了SPI初始化
          init_ip_arp_udp_tcp(mymac, myip); //EncEthernet初始化,设置mac和IP地址
         eStatus = eMBTCPInit(502 ); //设置freemodbus tcp 接收端口
         eStatus = eMBEnable(); //启用freemodbus
         while(1) {
                    plen = enc28j60_packet_receive(buf, BUFFER_SIZE); //从enc28j60读取数据
                   if(plen > 0) { //如果有数据
                         if(eth_type_is_arp_and_my_ip(buf, plen )) //如果是arp请求,则回应       
                                 make_arp_answer_from_request(buf); 
                         else if(eth_type_is_ip_and_my_ip(buf, plen ) >0){ //如果包的目的ip是本机
                                 if(buf[IP_PROTO_P] == IP_PROTO_ICMP_V && buf[ICMP_TYPE_P] == ICMP_TYPE_ECHOREQUEST_V)    make_echo_reply_from_request(buf, plen ); //如果是ping包,则回应
                                 else if (buf[IP_PROTO_P] == IP_PROTO_TCP_V && buf[TCP_DST_PORT_H_P] == (tcpport>>8) && buf[TCP_DST_PORT_L_P] == (tcpport &0xff)){ //如果是发给本机的tcp包
                                        if (buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V) // 如果是握手sync,则tcp握手
                                               make_tcp_synack_from_syn(buf);
                                        else if (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V){ //如果报里有ack标志
                                              init_len_info(buf); //计算数据包的长度,存储在info_data_len
                                              dat_p = get_tcp_data_pointer(); //获得数据位置
                          if (dat_p == 0) { //没有数据
                                 if (buf[TCP_FLAGS_P] & TCP_FLAGS_FIN_V) //如果包有Fin标志,对方请求结束连接,则回应
                                         make_tcp_ack_from_any(buf,0);    
                          }else{   //如果包含有ack并携带数据
                               info_addr=&buf[dat_p]; //存储数据首地址
                               info_len=info_data_len;将数据长度转储到info_len里,因为info_data_len是freemodbus定义的
                               //info_len=buf[IP_TOTLEN_H_P]*8 + buf[IP_TOTLEN_L_P]-IP_HEADER_LEN-4*(buf[TCP_HEADER_LEN_P]>>4); //或者这样计算info_len
                               xMBPortEventPost( EV_FRAME_RECEIVED );  //通知有数据到来,如果没有这一步,则eMBPoll不处理数据
                               ( void)eMBPoll(  ); //数据处理
                          }
                 }
         }                         
 } 
 
 
freemodbus TCP server 还需要实现几个函数
  BOOL  xMBTCPPortInit( USHORT usTCPPort )
 {
     if( usTCPPort == 0 ) return FALSE;
     else return TRUE;
 }void vMBTCPPortDisable( )
 {}FreeModbusTCP 从xMBTCPPortGetRequest函数获得TCP数据,经过处理后,调用xMBTCPPortSendResponse函数,将处理结果数据作为参数传递,用户在该函数获得处理后的modbus报文,进行进一步处理。
 BOOL  xMBTCPPortGetRequest( UCHAR ** ppucMBTCPFrame, USHORT * usTCPLength )
 {
     *ppucMBTCPFrame = info_addr; //TCP数据地址
     *usTCPLength = info_len; //TCP数据长度
     return TRUE;
 }
 BOOL xMBTCPPortSendResponse( const UCHAR * pucMBTCPFrame, USHORT usTCPLength )
 {    info_len=usTCPLength;
     info_data_len=info_len;
     make_tcp_ack_with_data(buf,usTCPLength); //发送数据
     return TRUE;
 }此外eMBPoll函数还需小修改一下,确保一次调用eMBPoll能把数据处理完毕。
     将  if( xMBPortEventGet( &eEvent ) == TRUE )   修改为  while( xMBPortEventGet( &eEvent ) == TRUE )实现Modbus协议,还需添加以下函数,modbus RTU和modbus TCP通用,这个是从网上抄的,感谢!
 eMBErrorCode  eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
 {
     eMBErrorCode    eStatus = MB_ENOERR;
     int             iRegIndex;    if( ( usAddress >= REG_INPUT_START )
             && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
     {
         iRegIndex = ( int )( usAddress - REG_INPUT_START );
         while( usNRegs > 0 )
         {
             *pucRegBuffer++ =
                 ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );
             *pucRegBuffer++ =
                 ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
             iRegIndex++;
             usNRegs--;
         }
     }
     else
     {
         eStatus = MB_ENOREG;
     }    return eStatus;
 }eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
                  eMBRegisterMode eMode )
 {    eMBErrorCode eStatus = MB_ENOERR;
 //偏移量
     int16_t iRegIndex;//判断寄存器是不是在范围内
     if( ( (int16_t)usAddress >= REG_HOLDING_START ) && ( (usAddress + usNRegs) <= (REG_HOLDING_START + REG_HOLDING_NREGS) ) )
     {
 //计算偏移量
         iRegIndex = ( int16_t )( usAddress - REG_HOLDING_START);        switch ( eMode )
         {
 //读处理函数
         case MB_REG_READ:
             while( usNRegs > 0 )
             {
                 *pucRegBuffer++ = ( uint8_t )( usRegHoldingBuf[iRegIndex] >> 8 );
                 *pucRegBuffer++ = ( uint8_t )( usRegHoldingBuf[iRegIndex] & 0xFF );
                 iRegIndex++;
                 usNRegs--;
             }
             break;//写处理函数
         case MB_REG_WRITE:
             while( usNRegs > 0 )
             {
                 usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
                 usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
                 iRegIndex++;
                 usNRegs--;
             }
             break;
         }
     }
     else
     {
 //返回错误状态
         eStatus = MB_ENOREG;
     }
     return eStatus;
 //    return MB_ENOREG;
 } eMBErrorCode
 eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,
                eMBRegisterMode eMode )
 {    //错误状态
     eMBErrorCode eStatus = MB_ENOERR;
 //寄存器个数
     int16_t iNCoils = ( int16_t )usNCoils;
 //寄存器偏移量
     int16_t usBitOffset;
 //检查寄存器是否在指定范围内
     if( ( (int16_t)usAddress >= REG_COILS_START ) &&
             ( usAddress + usNCoils <= REG_COILS_START + REG_COILS_SIZE ) )
     {
 //计算寄存器偏移量
         usBitOffset = ( int16_t )( usAddress - REG_COILS_START );
         switch ( eMode )
         {
 //读操作
         case MB_REG_READ:
             while( iNCoils > 0 )
             {
                 *pucRegBuffer++ = xMBUtilGetBits( ucRegCoilsBuf, usBitOffset,
                                                   ( uint8_t )( iNCoils > 8 ? 8 : iNCoils ) );
                 iNCoils -= 8;
                 usBitOffset += 8;
             }
             break;//写操作
         case MB_REG_WRITE:
             while( iNCoils > 0 )
             {
                 xMBUtilSetBits( ucRegCoilsBuf, usBitOffset,
                                 ( uint8_t )( iNCoils > 8 ? 8 : iNCoils ),
                                 *pucRegBuffer++ );
                 iNCoils -= 8;
             }
             break;
         }    }
     else
     {
         eStatus = MB_ENOREG;
     }
     return eStatus;
     //return MB_ENOREG;
 }eMBErrorCode
 eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
 {
     //错误状态
     eMBErrorCode eStatus = MB_ENOERR;
 //操作寄存器个数
     int16_t iNDiscrete = ( int16_t )usNDiscrete;
 //偏移量
     uint16_t usBitOffset;
 //判断寄存器时候再制定范围内
     if( ( (int16_t)usAddress >= REG_DISCRETE_START ) &&
             ( usAddress + usNDiscrete <= REG_DISCRETE_START + REG_DISCRETE_SIZE ) )
     {
 //获得偏移量
         usBitOffset = ( uint16_t )( usAddress - REG_DISCRETE_START );        while( iNDiscrete > 0 )
         {
             *pucRegBuffer++ = xMBUtilGetBits( ucRegDiscreteBuf, usBitOffset,
                                               ( uint8_t)( iNDiscrete > 8 ? 8 : iNDiscrete ) );
             iNDiscrete -= 8;
             usBitOffset += 8;
         }    }
     else
     {
         eStatus = MB_ENOREG;
     }
     return eStatus;
     //return MB_ENOREG;
 }
  后注: 网上EncEthernet代码需要进行一些修改才能用,比如TCP端口,如果是unsigned char类型,则要改为unsigned int类型,不然端口号不能超过255,所有处理端口号的语句都要检查一遍。make_tcp_ack_with_data也要修改,因为原版里这样的
// you must have called init_len_info at some time before calling this function
 // dlen is the amount of tcp data (http data) we send in this packet
 // You can use this function only immediately after make_tcp_ack_from_any
 // This is because this function will NOT modify the eth/ip/tcp header except for
 // length and checksum
 void make_tcp_ack_with_data(unsigned char *buf, unsigned  int dlen)意思是调用make_tcp_ack_with_data之前必须先给init_len_info赋值,并且调用make_tcp_ack_from_any后才能调用make_tcp_ack_with_data,要修改make_tcp_ack_with_data,使之能独立重建一个tcp包。
*这个程序在结束TCP连接的时候只用了两次挥手,而不是四次挥手,但是用起来没有问题。