W5500芯片复盘总结

此次项目中涉及到W5500芯片的功能如下:

        1.利用一个硬件Socket创建UDP作为与上位机通讯的接口

        2.通过提供底层函数帮助上位机实现CreateUDP,UDPSend,UDPRecv的功能

在项目中所遇到的难点:

        1.在前期不明白如何在初始化函数结束后,继续创建UDP端口,看了许多例程都没有这样的需求和代码实现。

        2.数据发送会无法正常发送

初始化第一个UDP端口用于与上位机通讯

伪代码如下

void W5500_Udp_Init()
{
    GPIO_Configuration();
    W5500SpiInit();    //MCU与W5500之间通过SPI进行通讯,做初始化操作
    W5500SwReset();    //硬件重启W5500芯片
    Load_Net_Parameters();        //装载网络相关的参数
    W5500_Initialization();        //W5500初始化配置
    W5500_Socket_Set();            //端口硬件初始化配置
}

W5500SwReset()将W5500芯片进行复位操作

Load_Net_Parameters()函数中主要对以下参数进行赋值

1.网关参数        Wirte_W5500_nByte(GAR,Gateway_IP,4);       

                                //将四个长度的Gateway_IP数组数据写入GAR通用寄存器中

2.子网掩码        Wirte_W5500_nByte(SUBR,Sub_Mask,4);

3.物理地址        Wirte_W5500_nByte(SHAR,Phy_Addr,6);

4.本机IP地址        Wirte_W5500_nByte(SIPR,IP_Addr,4);

5.端口0的端口号

注:以上数据都保存在Uint8_t类型的数组中

 W5500_Initialization()函数将上述参数写入对应通用寄存器绿色操作部分

完成上述绿色字体操作后

设置发送缓冲区和接收缓冲区的大小       Sn_RXBUF_SIZE&&Sn_TXBUF_SIZE寄存器

设置重试时间                                           RTR寄存器

设置重设次数                                           RCR寄存器

至此就已经完成了W5500芯片的通用寄存器的初始化工作,下面就是对于具体Socket的初始化以及发送和接收。

下述函数展示了如何创建Udp端口以及实现接收到的数据就发送的功能。

void do_udp()
{  
	switch(getSn_SR(0))			//获取Socket0的状态																	
	{
		case SOCK_UDP:			//如果当前已经是UDP模式,则实现以下功能																				
				Delay_ms(100);
				if(getSn_IR(0) & Sn_IR_RECV)    //若该端口的Sn_IR寄存器中Recv寄存器被置位
				{
					setSn_IR(0, Sn_IR_RECV);	//那么再将其手动置1														
				}
    //数据回环测试程序,数据从上位机发送给W5500芯片,W5500接收到数据后再次发送给上位机
				if((len=getSn_RX_RSR(0))>0)
				{ 
					memset(buffer,0,len+1);
					recvfrom(0,buffer);			
					sendto(0,buffer,len, remote_ip, remote_port);		  		
				}
		break;
		case SOCK_CLOSED:		//如果当前该端口处于关闭状态								
				socket(0,Sn_MR_UDP,local_port,0);				//则将其初始化为端口号为local_port的UDP形式Socket硬件接口																				
		break;
	}
}

接收函数中需要注意的是:

UDPRecvBuf接收到的数组前8个数组分别是

1.UDPRecvBuf[0]-UDPRecvBuf[3] 为远端IP地址

2.UDPRecvBuf[4]-UDPRecvBuf[5] 为远端Port端口号

3.UDPRecvBuf[6]-UDPRecvBuf[7] 为数据长度

因此从第九个数组开始UDPRecvBuf[8]开始才是真正的数据部分。

发送数组中需要注意的是:

因为是UDP的形式,此时向不同的IP地址和端口号发送数据的时候不需要再次配置目标IP地址和端口号。但是需要将目标IP地址和端口号直接包含在数据包中!(来自gpt3.5的分析还需自行判断,我根据可实现代码来看还是需要对准备发送数据的Socket端口对应的Sn_DIPR寄存器写入目标IP地址,向准备发送数据的Socket端口对应的Sn_DPORT寄存器写入目标Port端口号)并且此时Socket已经成功初始化,那么数据就可以正常发送。

此处参考WIZnet官方例程

uint16 sendto(SOCKET s, const uint8 * buf, uint16 len, uint8 * addr, uint16 port)
{
.../*主要操作如下*/
    IINCHIP_WRITE( Sn_DIPR0(s), addr[0]);    //向对应reg中写入目标ip地址第一个字节
    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);          //根据此标志位进行发送操作
...
}
void send_data_processing(SOCKET s, uint8 *data, uint16 len)
{
  uint16 ptr =0;
  uint32 addrbsb =0;
 
  ptr = IINCHIP_READ( Sn_TX_WR0(s) );
  ptr = ((ptr & 0x00ff) << 8) + IINCHIP_READ(Sn_TX_WR1(s));

  addrbsb = (uint32)(ptr<<8) + (s<<5) + 0x10;
  wiz_write_buf(addrbsb, data, len);
  
  ptr += len;
  IINCHIP_WRITE( Sn_TX_WR0(s) ,(uint8)((ptr & 0xff00) >> 8));
  IINCHIP_WRITE( Sn_TX_WR1(s),(uint8)(ptr & 0x00ff));
}

uint16 wiz_write_buf(uint32 addrbsb,uint8* buf,uint16 len)	// W5500将通过SPI获取的数据写入相关寄存器,并返回写入的数据长度
{
   uint16 idx = 0;																			// idx定义为正在写入的第几个数
   if(len == 0) printf("Unexpected2 length 0\r\n");			// 写入数据为空;len表示写入数据的长度
   IINCHIP_CSoff();                              				// CS=0, SPI数据帧开始
   IINCHIP_SpiSendData( (addrbsb & 0x00FF0000)>>16);		// 地址段,提供16位偏移地址(0000 0000 0000 0000)
   IINCHIP_SpiSendData( (addrbsb & 0x0000FF00)>> 8);		// 控制段,共8位(0000 0000 高5位BSB位为00000表示通用寄存器)
   IINCHIP_SpiSendData( (addrbsb & 0x000000F8) + 4);    // 控制段+4(0000 0100 RWB位置1表示写入,OM位为00表示SPI工作模式为VDM)
   for(idx = 0; idx < len; idx++)                				// 数据段,写入数据值
   {
     IINCHIP_SpiSendData(buf[idx]);											// MCU通过SPI发送数据
   }
   IINCHIP_CSon();                               				// CS=1, SPI数据帧结束
   return len;  																				// 返回写入的数据长度值
}

后续继续初始化新的UDP端口,是可以的,不需要复位芯片,不需要重新配置通用寄存器内容。只需要给他一个正常范围内的Port端口号即可,具体函数实现可以参考do_udp()函数中所调用的socket()函数。

uint8 socket(SOCKET s, uint8 protocol, uint16 port, uint8 flag)
{
   uint8 ret;
   if (
        ((protocol&0x0F) == Sn_MR_TCP)    ||        //判断Socket创建类型
        ((protocol&0x0F) == Sn_MR_UDP)    ||
        ((protocol&0x0F) == Sn_MR_IPRAW)  ||
        ((protocol&0x0F) == Sn_MR_MACRAW) ||
        ((protocol&0x0F) == Sn_MR_PPPOE)
      )
   {
      close(s);                                    //先关闭该Socket
      IINCHIP_WRITE(Sn_MR(s) ,protocol | flag);    //对该Socket对应的reg中写入协议类型
      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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三工言吾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值