当前位置:网站首页>Stm32 w5500 implements TCP, DHCP and web server

Stm32 w5500 implements TCP, DHCP and web server

2022-06-23 21:14:00 AnieaLanie

1. W5500 Modbus

1.1 Modbus Protocol Brief

Modbus A protocol is a message structure , It is widely used to establish master-slave communication between intelligent devices . From master station to slave station Modbus The message contains the slave address 、“ command ”( for example “ Read register ” or “ Write register ”)、 Data and checksum (LRC or CRC).

because Modbus The protocol is just a message passing structure , It is independent of the underlying physical layer . It's traditionally used RS232、RS422 or RS485 Realization

Modbus The main reasons why it is more widely used than other communication protocols are :

  • Published without copyright requirements
  • Easy to deploy and maintain
  • For suppliers , There are no restrictions on changing the local bit or byte

1.2 Modbus Of master/slave framework

Modbus It's a Lord / from (master/slave) Architecture protocol , A node is master node , The other nodes are slave node . every last slave Each node has a unique address . In serial and MB+ In the network , Only specified as master The node of the node can start a command . On the Ethernet , Any device can send a Modbus command , But there is usually only one master Node device startup instruction .

1.3 Modbus command

One Modbus The command includes the... Of the device to be executed Modbus Address . All devices will receive commands , But only the device with the corresponding address will run and respond to the command ( Address 0 With the exception of , Specify the address 0 The command of is a broadcast command , All devices that receive commands will run , But no response ). be-all Modbus The command contains the check code , To make sure that the incoming command is not broken . Basic Modbus A command can command a RTU ( Remote terminal unit ) Change a value of its register , Control or read a I/O port , And the command equipment will send the data in one or more registers .

1.4 Modbus request

The function code in the request tells the addressed slave what action to perform . The data bytes contain any additional information required by the slave station to perform this function . for example , Function code 03 The request reads the holding register from the host and responds to its contents . The data field must contain information that tells the slave which register to start with and how many registers to read . The error check field provides a way for the slave to verify the integrity of the message content .

1.5 Modbus Respond to

If the slave makes a normal response , Then the function code in the response is the echo of the function code in the request . The data bytes contain the data collected from the station , For example, register value or status . If an error occurs , Then modify the function code to indicate that the response is an error response , And the data bytes contain code that describes the error . The error check field allows the host to confirm that the message content is valid .

1.6 Modbus transmission mode

In standard Modbus When communicating on the network , The controller can be set to use one of two transmission modes :ASCII or RTU.

ASCII Pattern When the controller is set to use ASCII( American standard code for information exchange ) Patterns in Modbus When communicating on the network , Each octet in the message is treated as two ASCII Character sending . The main advantage of this mode is that it allows up to a second of time between characters without causing errors .

coding system Hexadecimal ASCII Printable characters 0 ... 9, A ... F Bits per byte 1 Starting bits 7 Data bits , The least significant bit is sent first 1 Bits are used for even / Odd parity - No bits for no parity 1 Stop bit if parity if no parity Error checking Longitudinal redundancy check (LRC), Then use 2 position

ASCII frame

stay ASCII In mode , The message is colon (:) character (ASCII 3A Hexadecimal ) Start , And return to the next line (CRLF) Yes (ASCII 0D and 0A Hexadecimal ) end . The allowed characters transmitted for all other fields are hexadecimal 0 ... 9、A ... F. Networked devices constantly monitor the network bus for colon characters . When you receive one , Each device will set the next field ( Address field ) decode , To determine if it is an addressable device . Characters in the message can be separated by up to one second . If there is a larger interval , Then the receiving device assumes that an error has occurred . A typical message frame is shown below .:

RTU Pattern When the controller is set to use RTU( Remote terminal unit ) Patterns in Modbus When communicating on the network , Each octet in the message contains two four digit hexadecimal characters . The main advantage of this mode is that at the same baud rate , Its greater character density is allowed than ASCII Better data throughput . Each message must be transmitted in a continuous stream .

coding system Eight bit binary 、 Hexadecimal 0 ... 9、A ... F Two hexadecimal characters contained in each octet field of the message Bits per byte 1 Start bit 8 Data bits , The least significant bit is sent first 1 Bits are used for even numbers / Odd parity - No parity position If parity is used 1 Stop bits - If there is no parity Error checking field by 2 position Cyclic redundancy check (CRC)

RTU frame stay RTU In mode , The message is at least 3.5 A silence interval of characters begins . This is most easily implemented as a multiple of the character time of the baud rate being used on the network ( Shown in the following figure as T1-T2-T3-T4). Then the first field transmitted is the device address . The allowed characters transmitted for all fields are hexadecimal 0 ... 9、A ... F. Networked devices continuously monitor the network bus , Included during silent intervals . When the first field is received ( Address field ) when , Each device decodes it , To determine if it is an addressable device . After the last transmitted character , At least 3.5 A similar interval of characters marks the end of the message . After this interval, you can start a new message . The entire message frame must be transmitted as a continuous stream . If more than... Occurs before the frame is completed 1.5 A silence interval of characters , The receiving device will refresh the incomplete message and assume that the next byte will be the address field of the new message . Similarly , If the start time of the new message is earlier than that of the previous message 3.5 Character time , The receiving device will regard it as a continuation of the previous message . This will set an error , Because in the end CRC The value in the field will not be valid for combined messages . A typical message frame is shown below .:

1.7 Message frame

Address field The address field of the message frame contains two characters (ASCII) Or octet (RTU). The address range allocated by each slave station equipment is 1 ... 247.

Function fields

The function code field tells the addressed slave what functions to perform . Modbus poll (Modbus Main Simulator ) The following functions are supported :

01 Read coil status 02 Read input status 03 Read holding register 04 Read input register 05 Write a single coil 06 Write to a single register 15 Write multiple coils 16 Write multiple registers

The data field contains the requested or sent data .

Error checking the contents of the field standard Modbus The network uses two error checking methods . The contents of the error check field depend on the method being used .

ASCII When ASCII When the mode is used for character framing , The error check field contains two ASCII character . The error check character is a vertical redundancy check performed on the message content (LRC) The result of the calculation , Start colon and end... Are not included CRLF character . LRC Character as CRLF The last field before the character is appended to the message .

BYTE LRC (BYTE *nData, WORD wLength)
{
BYTE nLRC = 0 ; // LRC char initialized
​
for (int i = 0; i < wLength; i++)
nLRC += *nData++;
​
return (BYTE)(-nLRC);
​
} // End: LRC

RTU When RTU When the mode is used for character frames , The error check field contains a 16 A value , Implemented as two 8 Bit byte . The error check value is the result of the cyclic redundancy check calculation performed on the message content . CRC Field is attached to the message as the last field in the message . After completion , First, append the lower byte of the field , Then the high byte .CRC The upper byte is the last byte to be sent in the message .

WORD CRC16 (const BYTE *nData, WORD wLength)
{
static const WORD wCRCTable[] = {
   0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241,
   0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440,
   0XCC01, 0X0CC0, 0X0D80, 0XCD41, 0X0F00, 0XCFC1, 0XCE81, 0X0E40,
   0X0A00, 0XCAC1, 0XCB81, 0X0B40, 0XC901, 0X09C0, 0X0880, 0XC841,
   0XD801, 0X18C0, 0X1980, 0XD941, 0X1B00, 0XDBC1, 0XDA81, 0X1A40,
   0X1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41,
   0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641,
   0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040,
   0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240,
   0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441,
   0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41,
   0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840,
   0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41,
   0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40,
   0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640,
   0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041,
   0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240,
   0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441,
   0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41,
   0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840,
   0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41,
   0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40,
   0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640,
   0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041,
   0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241,
   0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440,
   0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40,
   0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841,
   0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40,
   0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41,
   0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641,
   0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040 };
​
BYTE nTemp;
WORD wCRCWord = 0xFFFF;
​
   while (wLength--)
   {
      nTemp = *nData++ ^ wCRCWord;
      wCRCWord >>= 8;
      wCRCWord  ^= wCRCTable[nTemp];
   }
   return wCRCWord;
} // End: CRC16

1.8 experimental result

2. DHCP

2.1 DHCP brief introduction

DHCP( Dynamic Host Configuration Protocol ) Is a LAN network protocol . It refers to a period of time controlled by the server IP Address range , When the client logs in to the server, it can automatically get the server assigned IP Address and subnet mask .

2.2 DHCP function

  1. Guarantee any IP The address can only be set by one DHCP Used by the client .
  2. DHCP It should be possible to assign permanent IP Address .
  3. DHCP Should be available in the same way as in other ways IP Address host co-existence ( Such as manual configuration IP Address of the host ).
  4. DHCP The server should be directed to the existing BOOTP Clients provide services .

2.3 DHCP The way of distribution

DHCP There are three mechanisms for distribution IP Address :

  1. Automatic allocation method (Automatic Allocation),DHCP The server specifies a permanent IP Address , once DHCP The first successful client from DHCP Server side lease to IP After the address , You can use the address permanently .
  2. Dynamic distribution mode (Dynamic Allocation),DHCP The server specifies a time limited IP Address , When the time expires or the host explicitly gives up the address , This address can be used by other hosts .
  3. Job assignment method (Manual Allocation), Client's IP The address is specified by the network administrator ,DHCP The server will only specify IP Address to client host .

2.4 DHCP working principle

DHCP The agreement adopts UDP As a transport protocol , The host sends a request message to DHCP Server's 67 Port no. ,DHCP The server responds to the message sent to the host 68 Port no. . The detailed interaction process is as follows :

2.5 W5500+STM32F103 Realization DHCP Code

W5500 As DHCP client , Router as DHCP Server side , After connecting to the router , Routers are dynamically assigned to W5500IP Address .

stay DHCP During request , Include 4 There are two main stages : The discovery phase 、 Provision phase 、 Select phase and confirm phase .

First W5500 The client sends DHCP DISCOVER news (IP Address rental application ), The message was broadcast , In all networks DHCP The server will receive this message . And then , Network DHCP The server responds with one DHCPOFFER news (IP Address rental offers ), At this time, the client does not have a network address , therefore DHCP OFFER It is also sent by broadcast . after , Send... To the server DHCP REQUEST news . stay DHCP REQUEST The message will contain the... Requested by the client IP Address . Last ,DHCP The server will echo DHCP ACK To notify the client that the IP Address , This confirmation contains the assigned IP Address and a stable term lease for that address ( The default is 8 God ), And update at the same time DHCP database .

Main code :

while(1)
{
​
 DHCP_run();
​
}
​
uint8_t DHCP_run(void)
{
​
  uint8_t  type;
​
  uint8_t  ret;
​
  if(dhcp_state == STATE_DHCP_STOP) return DHCP_STOPPED;
​
  if(getSn_SR(SOCK_DHCP) != SOCK_UDP)
​
  socket(SOCK_DHCP, Sn_MR_UDP, DHCP_CLIENT_PORT, 0x00);
​
  ret = DHCP_RUNNING;
​
  type = parseDHCPMSG();
​
  switch ( dhcp_state )
​
  {
​
      case STATE_DHCP_READY :
​
          DHCP_allocated_ip[0] = 0;
​
          DHCP_allocated_ip[1] = 0;
​
          DHCP_allocated_ip[2] = 0;
​
          DHCP_allocated_ip[3] = 0;   
​
          send_DHCP_DISCOVER();
​
          dhcp_time = 0;
​
          dhcp_state = STATE_DHCP_DISCOVER;
​
      break;
​
      case STATE_DHCP_DISCOVER :
​
          if (type == DHCP_OFFER)
​
          {
​
              #ifdef _DHCP_DEBUG_
​
              printf("> Receive DHCP_OFFER\r\n");
​
              #endif
​
              DHCP_allocated_ip[0] = pDHCPMSG->yiaddr[0];
​
              DHCP_allocated_ip[1] = pDHCPMSG->yiaddr[1];
​
              DHCP_allocated_ip[2] = pDHCPMSG->yiaddr[2];
​
              DHCP_allocated_ip[3] = pDHCPMSG->yiaddr[3]; 
​
              send_DHCP_REQUEST();
​
              dhcp_time = 0;
​
              dhcp_state = STATE_DHCP_REQUEST;
​
          }
​
              else
​
              ret = check_DHCP_timeout();
​
      break;
​
      case STATE_DHCP_REQUEST :
​
          if (type == DHCP_ACK)
​
              {
​
                  #ifdef _DHCP_DEBUG_
​
                  printf("> Receive DHCP_ACK\r\n");
​
                  #endif
​
              if (check_DHCP_leasedIP())
​
              {
​
                  printf("ip:%d.%d.%d.%d\r\n",DHCP_allocated_ip[0],DHCP_allocated_ip[1],DHCP_allocated_ip[2],DHCP_allocated_ip[3]);
​
                  printf("sn:%d.%d.%d.%d\r\n",DHCP_allocated_sn[0],DHCP_allocated_sn[1],DHCP_allocated_sn[2],DHCP_allocated_sn[3]);
​
                  printf("gw:%d.%d.%d.%d\r\n",DHCP_allocated_gw[0],DHCP_allocated_gw[1],DHCP_allocated_gw[2],DHCP_allocated_gw[3]);
​
                  dhcp_ip_assign();
​
                  reset_DHCP_timeout();
​
                  hcp_state = STATE_DHCP_LEASED;
​
              }
​
              else
​
              {
​
                  reset_DHCP_timeout();
​
                  dhcp_ip_conflict();
​
                  dhcp_state = STATE_DHCP_READY;
​
              }
​
              }
​
              else if (type == DHCP_NAK)
​
              {
​
                  #ifdef _DHCP_DEBUG_
​
                  printf("> Receive DHCP_NACK\r\n");
​
                  #endif
​
                  reset_DHCP_timeout();
​
                  dhcp_state = STATE_DHCP_DISCOVER;
​
              }
​
              else
​
              ret = check_DHCP_timeout();
​
      break;
​
      case STATE_DHCP_LEASED :
​
          ret = DHCP_IP_LEASED;        
​
          if ((dhcp_lease_time != DEFAULT_LEASETIME) && ((dhcp_lease_time/2) < dhcp_time))
​
          {  
​
              #ifdef _DHCP_DEBUG_
​
              printf("> Maintains the IP address \r\n");
​
              #endif
​
              type = 0;
​
              OLD_allocated_ip[0] = DHCP_allocated_ip[0];
​
              OLD_allocated_ip[1] = DHCP_allocated_ip[1];
​
              OLD_allocated_ip[2] = DHCP_allocated_ip[2];
​
              OLD_allocated_ip[3] = DHCP_allocated_ip[3];
​
              DHCP_XID++;
​
              send_DHCP_REQUEST();               
​
              reset_DHCP_timeout();
​
              dhcp_state = STATE_DHCP_REREQUEST;
​
          }
​
      break;
​
      case STATE_DHCP_REREQUEST :
​
          ret = DHCP_IP_LEASED;
​
          if (type == DHCP_ACK)
​
          {
​
              dhcp_retry_count = 0;
​
              if (OLD_allocated_ip[0] != DHCP_allocated_ip[0] ||
​
              OLD_allocated_ip[1] != DHCP_allocated_ip[1] ||
​
              OLD_allocated_ip[2] != DHCP_allocated_ip[2] ||
​
              OLD_allocated_ip[3] != DHCP_allocated_ip[3])
​
          {
​
              ret = DHCP_IP_CHANGED;
​
              dhcp_ip_update();
​
              #ifdef _DHCP_DEBUG_
​
              printf(">IP changed.\r\n");
​
              #endif                 
​
          }
​
              #ifdef _DHCP_DEBUG_
​
              else printf(">IP is continued.\r\n");
​
              #endif                         
​
              reset_DHCP_timeout();
​
              dhcp_state = STATE_DHCP_LEASED;
​
          }
​
          else if (type == DHCP_NAK)
​
          {
​
              #ifdef _DHCP_DEBUG_
​
              printf("> Receive DHCP_NACK, Failed to maintain ip\r\n");
​
              #endif
​
              reset_DHCP_timeout();
​
              dhcp_state = STATE_DHCP_DISCOVER;
​
          }
​
          else ret = check_DHCP_timeout();
​
      break;
​
      default :
​
      break;
​
      }
​
  return ret;
}

2.6 Code run results

Connect this computer to the router , This machine IP The address is as follows :

take W5500+STM32 Connect the router , from STM32 Serial port output view W5500 Of IP The address is as follows :

This machine ping W5500 result :

Successful connection .

3. W5500 TCP

3.1 Local establishment TCP The server

Turn off the local network , Use TCP Test tool establishment TCP The server

3.2 Use W5500, Set static IP, establish TCP client

Connect W5500 On this machine , Run the program , start-up TCP The server , see W5500 Data sent by the client

int main(void)
{
    System_Initialization();    //STM32 System initialization function ( initialization STM32 Clock and peripherals )
    Load_Net_Parameters();      // Load network parameters     
    W5500_Hardware_Reset();     // Hardware reset W5500
    W5500_Initialization();     //W5500 Initial cargo configuration 
    while (1)
    {
        W5500_Socket_Set();//W5500 Port initialization configuration 
​
        if(W5500_Interrupt)// Handle W5500 interrupt       
        {
            W5500_Interrupt_Process();//W5500 Interrupt handler framework 
        }
        if((S0_Data & S_RECEIVE) == S_RECEIVE)// If Socket0 Data received 
        {
            S0_Data&=~S_RECEIVE;
            Process_Socket_Data(0);//W5500 Receive and send the received data 
        }
        else if(W5500_Send_Delay_Counter >= 500)// Send the string regularly 
        {
            if(S0_State == (S_INIT|S_CONN))
            {
                S0_Data&=~S_TRANSMITOK;
                memcpy(Tx_Buffer, "\r\nWelcome To NiRenElec!\r\n", 23); 
                Write_SOCK_Data_Buffer(0, Tx_Buffer, 23);// Appoint Socket(0~7) Send data processing , port 0 send out 23 Bytes of data 
            }
            W5500_Send_Delay_Counter=0;
        }
    }
}

3.3 experimental result

4. The embedded Web The server

4.1 wiring part

Connect W5500 And routers ,PC Also connected to the router , Easy access to web pages .

4.2 Code section

Use socket encapsulation TCP agreement , The matter is based on TCP Of Socket signal communication

#include "socket.h"
#include "config.h"
#include "stdio.h"
#include "w5500.h"
#include "ult.h"
​
static uint16 local_port;
extern uint16 sent_ptr;
​
/**
@brief   This Socket function initialize the channel in perticular mode, and set the port and wait for W5200 done it.
@return  1 for sucess else 0.
*/
uint8 socket(SOCKET s, uint8 protocol, uint16 port, uint8 flag)
{
   uint8 ret;
   if (
        ((protocol&0x0F) == Sn_MR_TCP)    ||
        ((protocol&0x0F) == Sn_MR_UDP)    ||
        ((protocol&0x0F) == Sn_MR_IPRAW)  ||
        ((protocol&0x0F) == Sn_MR_MACRAW) ||
        ((protocol&0x0F) == Sn_MR_PPPOE)
      )
   {
      close(s);
      IINCHIP_WRITE(Sn_MR(s) ,protocol | flag);
      if (port != 0) {
         IINCHIP_WRITE( Sn_PORT0(s) ,(uint8)((port & 0xff00) >> 8));
         IINCHIP_WRITE( Sn_PORT1(s) ,(uint8)(port & 0x00ff));
      } else {
         local_port++; // if don't set the source port, set local_port number.
         IINCHIP_WRITE(Sn_PORT0(s) ,(uint8)((local_port & 0xff00) >> 8));
         IINCHIP_WRITE(Sn_PORT1(s) ,(uint8)(local_port & 0x00ff));
      }
      IINCHIP_WRITE( Sn_CR(s) ,Sn_CR_OPEN); // run sockinit Sn_CR
​
      /* wait to process the command... */
      while( IINCHIP_READ(Sn_CR(s)) )
         ;
      /* ------- */
      ret = 1;
   }
   else
   {
      ret = 0;
   }
   return ret;
}
​
​
/**
@brief   This function close the socket and parameter is "s" which represent the socket number
*/
void close(SOCKET s)
{
​
   IINCHIP_WRITE( Sn_CR(s) ,Sn_CR_CLOSE);
​
   /* wait to process the command... */
   while( IINCHIP_READ(Sn_CR(s) ) )
      ;
   /* ------- */
        /* all clear */
   IINCHIP_WRITE( Sn_IR(s) , 0xFF);
}
​
​
/**
@brief   This function established  the connection for the channel in passive (server) mode. This function waits for the request from the peer.
@return  1 for success else 0.
*/
uint8 listen(SOCKET s)
{
   uint8 ret;
   if (IINCHIP_READ( Sn_SR(s) ) == SOCK_INIT)
   {
      IINCHIP_WRITE( Sn_CR(s) ,Sn_CR_LISTEN);
      /* wait to process the command... */
      while( IINCHIP_READ(Sn_CR(s) ) )
         ;
      /* ------- */
      ret = 1;
   }
   else
   {
      ret = 0;
   }
   return ret;
}
​
​
/**
@brief   This function established  the connection for the channel in Active (client) mode.
      This function waits for the untill the connection is established.
​
@return  1 for success else 0.
*/
uint8 connect(SOCKET s, uint8 * addr, uint16 port)
{
    uint8 ret;
    if
        (
            ((addr[0] == 0xFF) && (addr[1] == 0xFF) && (addr[2] == 0xFF) && (addr[3] == 0xFF)) ||
            ((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && (addr[3] == 0x00)) ||
            (port == 0x00)
        )
    {
      ret = 0;
    }
    else
    {
        ret = 1;
        // set destination IP
        IINCHIP_WRITE( Sn_DIPR0(s), addr[0]);
        IINCHIP_WRITE( Sn_DIPR1(s), addr[1]);
        IINCHIP_WRITE( Sn_DIPR2(s), addr[2]);
        IINCHIP_WRITE( Sn_DIPR3(s), addr[3]);
        IINCHIP_WRITE( Sn_DPORT0(s), (uint8)((port & 0xff00) >> 8));
        IINCHIP_WRITE( Sn_DPORT1(s), (uint8)(port & 0x00ff));
        IINCHIP_WRITE( Sn_CR(s) ,Sn_CR_CONNECT);
        /* wait for completion */
        while ( IINCHIP_READ(Sn_CR(s) ) ) ;
​
        while ( IINCHIP_READ(Sn_SR(s)) != SOCK_SYNSENT )
        {
            if(IINCHIP_READ(Sn_SR(s)) == SOCK_ESTABLISHED)
            {
                break;
            }
            if (getSn_IR(s) & Sn_IR_TIMEOUT)
            {
                IINCHIP_WRITE(Sn_IR(s), (Sn_IR_TIMEOUT));  // clear TIMEOUT Interrupt
                ret = 0;
                break;
            }
        }
    }
​
   return ret;
}
​
​
​
/**
@brief   This function used for disconnect the socket and parameter is "s" which represent the socket number
@return  1 for success else 0.
*/
void disconnect(SOCKET s)
{
   IINCHIP_WRITE( Sn_CR(s) ,Sn_CR_DISCON);
​
   /* wait to process the command... */
   while( IINCHIP_READ(Sn_CR(s) ) )
      ;
   /* ------- */
}
​
​
/**
@brief   This function used to send the data in TCP mode
@return  1 for success else 0.
*/
uint16 send(SOCKET s, const uint8 * buf, uint16 len)
{
  uint8 status=0;
  uint16 ret=0;
  uint16 freesize=0;
​
  if (len > getIINCHIP_TxMAX(s)) ret = getIINCHIP_TxMAX(s); // check size not to exceed MAX size.
  else ret = len;
​
  // if freebuf is available, start.
  do
  {
    freesize = getSn_TX_FSR(s);
    status = IINCHIP_READ(Sn_SR(s));
    if ((status != SOCK_ESTABLISHED) && (status != SOCK_CLOSE_WAIT))
    {
      ret = 0;
      break;
    }
  } while (freesize < ret);
​
​
  // copy data
  send_data_processing(s, (uint8 *)buf, ret);
  IINCHIP_WRITE( Sn_CR(s) ,Sn_CR_SEND);
​
  /* wait to process the command... */
  while( IINCHIP_READ(Sn_CR(s) ) );
​
  while ( (IINCHIP_READ(Sn_IR(s) ) & Sn_IR_SEND_OK) != Sn_IR_SEND_OK )
  {
    status = IINCHIP_READ(Sn_SR(s));
    if ((status != SOCK_ESTABLISHED) && (status != SOCK_CLOSE_WAIT) )
    {
      printf("SEND_OK Problem!!\r\n");
      close(s);
      return 0;
    }
  }
  IINCHIP_WRITE( Sn_IR(s) , Sn_IR_SEND_OK);
​
#ifdef __DEF_IINCHIP_INT__
   putISR(s, getISR(s) & (~Sn_IR_SEND_OK));
#else
   IINCHIP_WRITE( Sn_IR(s) , Sn_IR_SEND_OK);
#endif
​
   return ret;
}
​
​
​
/**
@brief   This function is an application I/F function which is used to receive the data in TCP mode.
      It continues to wait for data as much as the application wants to receive.
​
@return  received data size for success else -1.
*/
uint16 recv(SOCKET s, uint8 * buf, uint16 len)
{
   uint16 ret=0;
   if ( len > 0 )
   {
      recv_data_processing(s, buf, len);
      IINCHIP_WRITE( Sn_CR(s) ,Sn_CR_RECV);
      /* wait to process the command... */
      while( IINCHIP_READ(Sn_CR(s) ));
      /* ------- */
      ret = len;
   }
   return ret;
}
​
​
/**
@brief   This function is an application I/F function which is used to send the data for other then TCP mode.
      Unlike TCP transmission, The peer's destination address and the port is needed.
​
@return  This function return send data size for success else -1.
*/
uint16 sendto(SOCKET s, const uint8 * buf, uint16 len, uint8 * addr, uint16 port)
{
   uint16 ret=0;
​
   if (len > getIINCHIP_TxMAX(s)) ret = getIINCHIP_TxMAX(s); // check size not to exceed MAX size.
   else ret = len;
​
   if( ((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && (addr[3] == 0x00)) || ((port == 0x00)) )//||(ret == 0) )
   {
      /* added return value */
      ret = 0;
   }
   else
   {
      IINCHIP_WRITE( Sn_DIPR0(s), addr[0]);
      IINCHIP_WRITE( Sn_DIPR1(s), addr[1]);
      IINCHIP_WRITE( Sn_DIPR2(s), addr[2]);
      IINCHIP_WRITE( Sn_DIPR3(s), addr[3]);
      IINCHIP_WRITE( Sn_DPORT0(s),(uint8)((port & 0xff00) >> 8));
      IINCHIP_WRITE( Sn_DPORT1(s),(uint8)(port & 0x00ff));
      // copy data
      send_data_processing(s, (uint8 *)buf, ret);
      IINCHIP_WRITE( Sn_CR(s) ,Sn_CR_SEND);
      /* wait to process the command... */
      while( IINCHIP_READ( Sn_CR(s) ) )
         ;
      /* ------- */
​
      while( (IINCHIP_READ( Sn_IR(s) ) & Sn_IR_SEND_OK) != Sn_IR_SEND_OK )
      {
         if (IINCHIP_READ( Sn_IR(s) ) & Sn_IR_TIMEOUT)
         {
            /* clear interrupt */
            IINCHIP_WRITE( Sn_IR(s) , (Sn_IR_SEND_OK | Sn_IR_TIMEOUT)); /* clear SEND_OK & TIMEOUT */
            return 0;
         }
      }
      IINCHIP_WRITE( Sn_IR(s) , Sn_IR_SEND_OK);
   }
   return ret;
}
​
​
/**
@brief   This function is an application I/F function which is used to receive the data in other then
   TCP mode. This function is used to receive UDP, IP_RAW and MAC_RAW mode, and handle the header as well.
​
@return  This function return received data size for success else -1.
*/
uint16 recvfrom(SOCKET s, uint8 * buf, uint16 len, uint8 * addr, uint16 *port)
{
   uint8 head[8];
   uint16 data_len=0;
   uint16 ptr=0;
   uint32 addrbsb =0;
   if ( len > 0 )
   {
      ptr     = IINCHIP_READ(Sn_RX_RD0(s) );
      ptr     = ((ptr & 0x00ff) << 8) + IINCHIP_READ(Sn_RX_RD1(s));
      addrbsb = (uint32)(ptr<<8) +  (s<<5) + 0x18;
      
      switch (IINCHIP_READ(Sn_MR(s) ) & 0x07)
      {
      case Sn_MR_UDP :
        wiz_read_buf(addrbsb, head, 0x08);        
        ptr += 8;
        // read peer's IP address, port number.
        addr[0]  = head[0];
        addr[1]  = head[1];
        addr[2]  = head[2];
        addr[3]  = head[3];
        *port    = head[4];
        *port    = (*port << 8) + head[5];
        data_len = head[6];
        data_len = (data_len << 8) + head[7];
​
        addrbsb = (uint32)(ptr<<8) +  (s<<5) + 0x18;
        wiz_read_buf(addrbsb, buf, data_len);                
        ptr += data_len;
​
        IINCHIP_WRITE( Sn_RX_RD0(s), (uint8)((ptr & 0xff00) >> 8));
        IINCHIP_WRITE( Sn_RX_RD1(s), (uint8)(ptr & 0x00ff));
        break;
​
      case Sn_MR_IPRAW :
        wiz_read_buf(addrbsb, head, 0x06);        
        ptr += 6;
        addr[0]  = head[0];
        addr[1]  = head[1];
        addr[2]  = head[2];
        addr[3]  = head[3];
        data_len = head[4];
        data_len = (data_len << 8) + head[5];
​
        addrbsb  = (uint32)(ptr<<8) +  (s<<5) + 0x18;
        wiz_read_buf(addrbsb, buf, data_len);        
        ptr += data_len;
​
        IINCHIP_WRITE( Sn_RX_RD0(s), (uint8)((ptr & 0xff00) >> 8));
        IINCHIP_WRITE( Sn_RX_RD1(s), (uint8)(ptr & 0x00ff));
        break;
​
      case Sn_MR_MACRAW :
        wiz_read_buf(addrbsb, head, 0x02);
        ptr+=2;
        data_len = head[0];
        data_len = (data_len<<8) + head[1] - 2;
        if(data_len > 1514)
        {
           printf("data_len over 1514\r\n");
           while(1);
        }
​
        addrbsb  = (uint32)(ptr<<8) +  (s<<5) + 0x18;
        wiz_read_buf(addrbsb, buf, data_len);
        ptr += data_len;
​
        IINCHIP_WRITE( Sn_RX_RD0(s), (uint8)((ptr & 0xff00) >> 8));
        IINCHIP_WRITE( Sn_RX_RD1(s), (uint8)(ptr & 0x00ff));
        break;
​
      default :
            break;
      }
      IINCHIP_WRITE( Sn_CR(s) ,Sn_CR_RECV);
​
      /* wait to process the command... */
      while( IINCHIP_READ( Sn_CR(s)) ) ;
      /* ------- */
   }
   return data_len;
}
​
#ifdef __MACRAW__
void macraw_open(void)
{
  uint8 sock_num;
  uint16 dummyPort = 0;
  uint8 mFlag = 0;
  sock_num = 0;
​
​
  close(sock_num); // Close the 0-th socket
  socket(sock_num, Sn_MR_MACRAW, dummyPort,mFlag);  // OPen the 0-th socket with MACRAW mode
}
​
​
uint16 macraw_send( const uint8 * buf, uint16 len )
{
   uint16 ret=0;
   uint8 sock_num;
   sock_num =0;
​
​
   if (len > getIINCHIP_TxMAX(sock_num)) ret = getIINCHIP_TxMAX(sock_num); // check size not to exceed MAX size.
   else ret = len;
​
   send_data_processing(sock_num, (uint8 *)buf, len);
​
   //W5500 SEND COMMAND
   IINCHIP_WRITE(Sn_CR(sock_num),Sn_CR_SEND);
   while( IINCHIP_READ(Sn_CR(sock_num)) );
   while ( (IINCHIP_READ(Sn_IR(sock_num)) & Sn_IR_SEND_OK) != Sn_IR_SEND_OK );
   IINCHIP_WRITE(Sn_IR(sock_num), Sn_IR_SEND_OK);
​
   return ret;
}
​
uint16 macraw_recv( uint8 * buf, uint16 len )
{
   uint8 sock_num;
   uint16 data_len=0;
   uint16 dummyPort = 0;
   uint16 ptr = 0;
   uint8 mFlag = 0;
   sock_num = 0;
​
   if ( len > 0 )
   {
​
      data_len = 0;
​
      ptr = IINCHIP_READ(Sn_RX_RD0(sock_num));
      ptr = (uint16)((ptr & 0x00ff) << 8) + IINCHIP_READ( Sn_RX_RD1(sock_num) );
      //-- read_data(s, (uint8 *)ptr, data, len); // read data
      data_len = IINCHIP_READ_RXBUF(0, ptr);
      ptr++;
      data_len = ((data_len<<8) + IINCHIP_READ_RXBUF(0, ptr)) - 2;
      ptr++;
​
      if(data_len > 1514)
      {
         printf("data_len over 1514\r\n");
         printf("\r\nptr: %X, data_len: %X", ptr, data_len);
         //while(1);
         /** recommand : close and open **/
         close(sock_num); // Close the 0-th socket
         socket(sock_num, Sn_MR_MACRAW, dummyPort,mFlag);  // OPen the 0-th socket with MACRAW mode
         return 0;
      }
​
      IINCHIP_READ_RXBUF_BURST(sock_num, ptr, data_len, (uint8*)(buf));
      ptr += data_len;
​
      IINCHIP_WRITE(Sn_RX_RD0(sock_num),(uint8)((ptr & 0xff00) >> 8));
      IINCHIP_WRITE(Sn_RX_RD1(sock_num),(uint8)(ptr & 0x00ff));
      IINCHIP_WRITE(Sn_CR(sock_num), Sn_CR_RECV);
      while( IINCHIP_READ(Sn_CR(sock_num)) ) ;
   }
​
   return data_len;
}
#endif
​

Use SPI2 Connect W5500 and STM32

#include "stm32f10x.h"
#include "config.h"
#include "socket.h"
#include "w5500.h"
#include "ult.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
​
void WIZ_SPI_Init(void)
{
    SPI_InitTypeDef   SPI_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB |RCC_APB2Periph_AFIO , ENABLE);  
  // Port B output
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  GPIO_SetBits(GPIOB, GPIO_Pin_12);
  /* Configure SPIy pins: SCK, MISO and MOSI */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
      /* SPI Config -------------------------------------------------------------*/
      SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
      SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
      SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
      SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
      SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
      SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
      SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
      SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
      SPI_InitStructure.SPI_CRCPolynomial = 7;
​
      SPI_Init(SPI2, &SPI_InitStructure);
      SPI_Cmd(SPI2, ENABLE);
}
​
// Connected to Data Flash
void WIZ_CS(uint8_t val)
{
    if (val == LOW) 
    {
        GPIO_ResetBits(GPIOB, WIZ_SCS); 
    }
    else if (val == HIGH)
    {
        GPIO_SetBits(GPIOB, WIZ_SCS); 
    }
}
​
uint8_t SPI2_SendByte(uint8_t byte)
{
      while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
         
      SPI_I2S_SendData(SPI2, byte);
          
      while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
          
      return SPI_I2S_ReceiveData(SPI2);
}

Web HTTP part , take html The content is written into the code , In coordination http The status code of the protocol implements the web page

#ifndef __WEBPAGE_H
#define __WEBPAGE_H
#define INDEX_HTML  "<!DOCTYPE html>"\
"<html>"\
"<head>"\
"<title>Ç峿µÄÍøÒ³ÅäÖÃ</title>"\
"<meta http-equiv='Content-Type' content='text/html; charset=GB2312'/>"\
"<style type='text/css'>"\
"body {text-align:left; background-color:#c0deed;font-family:Verdana;}"\
"#main {margin-right:auto;margin-left:auto;margin-top:30px;}"\
"label{display:inline-block;width:150px;}"\
"#main h3{color:#66b3ff; text-decoration:underline;}"\
"</style>"\
"<script>"\
"function $(id) { return document.getElementById(id); };"\
"function settingsCallback(o) {"\
"if ($('txtVer')) $('txtVer').value = o.ver;"\
"if ($('txtMac')) $('txtMac').value = o.mac;"\
"if ($('txtIp')) $('txtIp').value = o.ip;"\
"if ($('txtSub')) $('txtSub').value = o.sub;"\
"if ($('txtGw')) $('txtGw').value = o.gw;"\
\
"if ($('txtCode')) $('txtCode').value = o.Code;"\
"if ($('txtDTMB_Freq')) $('txtDTMB_Freq').value = o.DTMB_Freq;"\
"if ($('txtFM_Freq')) $('txtFM_Freq').value = o.FM_Freq;"\
"if ($('txtSx_Freq')) $('txtSx_Freq').value = o.Sx_Freq;"\
\
"};"\
"</script>"\
"</head>"\
"<body>"\
"<div id='main'>"\
"<div style='background:snow; display:block;padding:10px 20px;'>"\
"<h3 align='center'>²é¿´ÍøÂç²ÎÊý</h3>"\
"<form id='frmSetting' method='POST' action='config.cgi'>"\
"<p><label for='txtIp'>¹Ì¼þ°æ±¾ºÅ:</label><input type='text' id='txtVer' name='ver' size='16' disabled='disabled' /></p>"\
"<p><label for='txtIp'>MACµØÖ·:</label><input type='text' id='txtMac' name='mac' size='16' disabled='disabled' /></p>"\
"<p><label for='txtIp'>IPµØÖ·:</label><input type='text' id='txtIp' name='ip' size='16' disabled='disabled'/></p>"\
"<p><label for='txtSub'>×ÓÍøÑÚÂë:</label><input type='text' id='txtSub' name='sub' size='16' disabled='disabled'/></p>"\
"<p><label for='txtGw'>ĬÈÏÍø¹Ø:</label><input type='text' id='txtGw' name='gw' size='16' disabled='disabled'/></p>"\
\
"<p><label for='txtCode'>ÐÐÕþ±àÂë:</label><input type='text' id='txtCode' name='Code' size='16' disabled='disabled'/></p>"\
"<p><label for='txtDTMB_Freq'>DTMBƵÂÊ:</label><input type='text' id='txtDTMB_Freq' name='DTMB_Freq' size='16' disabled='disabled' /></p>"\
"<p><label for='txtFM_Freq'>µ÷ƵƵÂÊ:</label><input type='text' id='txtFM_Freq' name='FM_Freq' size='16' disabled='disabled'/></p>"\
"<p><label for='txtSx_Freq'>ÎÞÏ߯µÂÊ:</label><input type='text' id='txtSx_Freq' name='Sx_Freq' size='16' disabled='disabled' /></p>"\
"<p style='color:DarkBlue'><label for='txtPassword'>µÇ¼ÃÜÂë6λ:</label><input type='password' id='txtPassword' name='Password' size='16' enabled='enabled'/></p>"\
"<input type='submit' value=' µÇ  ¼ ' /></p>"\
\
"</form>"\
"</div>"\
"</div>"\
"<div style='margin:5px 5px;'>"\
"&copy;Copyright 2017 by Ç峿"\
"</div>"\
"<script type='text/javascript' src='w5500.js'></script>"\
"</body>"\
"</html>"
​
#endif
/* HTML Doc. for ERROR */
#define ERROR_HTML_PAGE "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 78\r\n\r\n<HTML>\r\n<BODY>\r\nSorry, the page you requested was not found.\r\n</BODY>\r\n</HTML>\r\n\0"
//static char  ERROR_HTML_PAGE[] = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 78\r\n\r\n<HTML>\r\n<BODY>\r\nSorry, the page you requested was not found.\r\n</BODY>\r\n</HTML>\r\n\0";
​
#define ERROR_REQUEST_PAGE "HTTP/1.1 400 OK\r\nContent-Type: text/html\r\nContent-Length: 50\r\n\r\n<HTML>\r\n<BODY>\r\nInvalid request.\r\n</BODY>\r\n</HTML>\r\n\0"
//static char ERROR_REQUEST_PAGE[] = "HTTP/1.1 400 OK\r\nContent-Type: text/html\r\nContent-Length: 50\r\n\r\n<HTML>\r\n<BODY>\r\nInvalid request.\r\n</BODY>\r\n</HTML>\r\n\0";
​
#define RETURN_CGI_PAGE "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 59\r\n\r\n<html><head><title>iWeb - Configuration</title></head><BODY>CGI command was executed.</BODY></HTML>\0"
​
​
/* Response header for HTML*/
#define RES_HTMLHEAD_OK "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: "
//static PROGMEM char RES_HTMLHEAD_OK[] = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: ";
/* Response head for TEXT */
#define RES_TEXTHEAD_OK "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: "
​
/* Response head for GIF */
#define RES_GIFHEAD_OK  "HTTP/1.1 200 OK\r\nContent-Type: image/gif\r\nContent-Length: "
​
/* Response head for JPEG */
#define RES_JPEGHEAD_OK "HTTP/1.1 200 OK\r\nContent-Type: image/jpeg\r\nContent-Length: "       
​
/* Response head for FLASH */
#define RES_FLASHHEAD_OK "HTTP/1.1 200 OK\r\nContent-Type: application/x-shockwave-flash\r\nContent-Length: "
//static PROGMEM char RES_FLASHHEAD_OK[] = "HTTP/1.1 200 OK\r\nContent-Type: application/x-shockwave-flash\r\nContent-Length: ";
​
/* Response head for MPEG */
#define RES_MPEGHEAD_OK "HTTP/1.1 200 OK\r\nContent-Type: video/mpeg\r\nContent-Length: "   
​
/* Response head for PDF */
#define RES_PDFHEAD_OK "HTTP/1.1 200 OK\r\nContent-Type: application/pdf\r\nContent-Length: "
​
//digital I/O out put control result response
#define DOUT_RES_1  "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 1\r\n\r\n1"
#define DOUT_RES_0  "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 1\r\n\r\n0"

4.3 experimental result

5. Reference resources

[1] Get dynamic from the router IP Address

[2] DHCP

[3] STM32 transplant FreeModbus Detailed process

[4] Modbus Communication protocol ( Two )——RTU

[5] STM32F103+W5500 It's done web service

原网站

版权声明
本文为[AnieaLanie]所创,转载请带上原文链接,感谢
https://yzsam.com/2021/12/202112260945331558.html