网口 W5500芯片
1 定义网络服务器的地址和端口
uint8 server_ip[4]={192,168,1,87};
uint16 server_port=5000;
uint16 local_port=6000;
uint16 len=0;
定义了服务器地址 server_ip,服务器端口 server_port,本地端口 local_port,以及缓存字节长度 len
2 对DSP芯片相关功能的初始化
InitSpiaGpio();//初始化W5500的复位的GPIO
gpio_config();//GPIO配置
DINT;//关中断
InitPieCtrl(); //初始化PIE
IER = 0x0000; //清除中断
IFR = 0x0000;
InitPieVectTable();//初始化中断向量
3 对W5500芯片相关功能的初始化
Reset_W5500();//复位W5500
set_default();//设置网络属性
set_network();//对W5500设置,ip那些,到这一步就能Ping通了
3.1 Reset_W5500();
其中
Reset_W5500();
函数,主要对W5500芯片引脚RSTn进行操作
void Reset_W5500(void)
{
GpioDataRegs.GPBDAT.bit.GPIO52 = 0;
delay_loop();
GpioDataRegs.GPBDAT.bit.GPIO52 = 1;
delay_loop();
}
将其至低后拉高,完成W5500的复位。
3.2 set_default();
设置默认的W5500芯片的物理地址 MAC、IP地址 IP、网关 GW、子网掩码 SUB、服务器 DNS。
void set_default(void) // 设置默认MAC、IP、GW、SUB、DNS
{
uint8 mac[6]={0x00,0x08,0xdc,0x11,0x11,0x12};
uint8 lip[4]={192,168,1,150};
uint8 sub[4]={255,255,255,0};
uint8 gw[4]={192,168,1,1};
uint8 dns[4]={8,8,8,8};
memcpy(ConfigMsg.lip, lip, 4);
//printf("lip: %d.%d.%d.%d\r\n",lip[0],lip[1],lip[2],lip[3]);
memcpy(ConfigMsg.sub, sub, 4);
// printf("sub: %d.%d.%d.%d\r\n",sub[0],sub[1],sub[2],sub[3]);
memcpy(ConfigMsg.gw, gw, 4);
//printf("gw: %d.%d.%d.%d\r\n",gw[0],gw[1],gw[2],gw[3]);
memcpy(ConfigMsg.mac, mac,6);
memcpy(ConfigMsg.dns,dns,4);
//printf("dns: %d.%d.%d.%d\r\n",dns[0],dns[1],dns[2],dns[3]);
ConfigMsg.dhcp=0;
ConfigMsg.debug=1;
ConfigMsg.fw_len=0;
ConfigMsg.state=NORMAL_STATE;
ConfigMsg.sw_ver[0]=FW_VER_HIGH;
ConfigMsg.sw_ver[1]=FW_VER_LOW;
}
3.3 set_network();
设置网络,将第二步中的默认设置,通过SPI的方式,将这些数据写入W5500设置。可以理解为,第二步是定义,第三步才是真正的设置。
void set_network(void) // 配置初始化IP信息并打印,初始化8个Socket
{
uint8 ip[4];
setSHAR(ConfigMsg.mac);
//printf("mac:");
setSUBR(ConfigMsg.sub);
setGAR(ConfigMsg.gw);
setSIPR(ConfigMsg.lip);
sysinit(txsize, rxsize); // 初始化8个socket
setRTR(2000); // 设置超时时间
setRCR(3); // 设置最大重新发送次数
getSIPR (ip);
printf("IP : %d.%d.%d.%d\r\n", ip[0],ip[1],ip[2],ip[3]);
getSUBR(ip);
printf("SN : %d.%d.%d.%d\r\n", ip[0],ip[1],ip[2],ip[3]);
getGAR(ip);
printf("GW : %d.%d.%d.%d\r\n", ip[0],ip[1],ip[2],ip[3]);
}
此函数可配置每个socket通道的缓存大小,超时时间,已经最大重新发送次数。
4 状态寄存器Sn_SR 4个状态组成switch函数
此时,芯片W5500有了自己的IP地址,物理地址等。未进行任何操作的情况下,其状态寄存器 Sn_SR 应该为0x00
4.1 SOCK_CLOSED
最初经初始化后,W5500的状态应该是SOCK_CLOSED(0x00),进行第一步操作:
设置W5500的模式。包括TCP UDP等
case SOCK_CLOSED://第一步,设置模式
socket(0,Sn_MR_TCP,local_port,Sn_MR_ND);
break;
此代码含义为,对W5500芯片SOCKET 0 通道,设置为TCP模式。其中会使用到OPEN命令
4.2 SOCK_INIT 0x13
第一步中,设置为了TCP模式,并且使用了OPEN命令。故Sn_SR寄存器将变为SOCK_INIT 0x13。若使用LISTEN命令,则此芯片为服务器,若使用CONNECT,此芯片为客户端。
case SOCK_INIT://第二步,初始化,连接服务器
connect(0, server_ip,server_port);
break;
从代码看出,芯片为TCP模式的客户端。并且使用connect命令,连接服务器。
4.3 SOCK_ESTABLISHED 0x17
当 CONNECT 命令配置成功时,Sn_SR变为 SOCK_ESTABLISHED。这个状态下,就代表可以进行send或者receive了。
4.3 SOCK_CLOSE_WAIT 0x1C
最后一个状态是关闭通道的状态,如文字描述。
case SOCK_CLOSE_WAIT:
close_socket(0);
break;
注意:正常工作时,W5500芯片一般都在SOCK_ESTABLISHED 状态
5 SOCK_ESTABLISHED 状态
5.1 第一步,判断 Sn_IR (Socket n 中断寄存器)
if(getSn_IR(0) & Sn_IR_CON)
{
setSn_IR(0, Sn_IR_CON);
}
Sn_IR_CON=0x01的 也就是说,此时Sn_IR这个八位寄存器的第0位 CON=1。表明是建立了连接。setSn_IR(0, Sn_IR_CON); 这个又相当于初始化了Sn_IR,把他的中断标志初始化。
5.2 第二步,读取接收缓存的字节大小 Sn_RX_RSR
Sn_RX_RSR 显示了 Socket n 接收缓存中已接收和保存的数据大小。
len=getSn_RX_RSR(0);
读取len,如果有缓存,就是电脑服务器端发过来数据了,就会进行处理;如果没有,len=0,就会进行while1循环,继续进行switch判断。并且到这一步只会进行 SOCK_ESTABLISHED 状态。换句话说,就是只会一直判断len是否大于0,len大于0了,就会进行接下来的操作。如果没有大于0,就会一直判断len。
5.3 第三步,len>0 之后的操作
这部分就是真正的对fpga寄存器等的操作,这部分的程序包括对cmddata[]命令的解析,和数据的下发,以及对数据的上传。单开一张说明,保证逻辑性,先跳过这部分的具体操作。
其包括,接收命令,命令解析,设置编码模式,下发命令,设置解码模式,读取数据,上传数据。
6 len>0后的操作
6.1 首先明确命令的组成
一条命令包括:0xaa+cmddata[0] cmddata[1] cmddata[2] cmddata[3] cmddata[4] cmddata[5] cmddata[6] + (cmddata[6])个数据
一般来说一条命令只有8个16位数据,分为两个字节发送,特殊的命令会加上(cmddata[6])个数据
0xaa:数据头
cmddata[0]:采集命令
cmddata[1]:延时时间
cmddata[2]:送数命令
cmddata[3] :M2通道上传个数
cmddata[4] :M5通道上传个数
cmddata[5] :M7通道上传个数
cmddata[6] :送数命令后跟的送数命令。也就是多个送数命令
拿通信测试命令举例:
0x00 0xaa 0x99 0x1e 0x00 0x1e 0x99 0x4e 0x00 0x08 0x00 0x11 0x00 0x11 0x00 0x00 共16个字节,
其实是也就是8个参数:
0xaa:数据头 00aa
cmddata[0]:采集命令
991e
cmddata[1]:延时时间
001e
cmddata[2]:送数命令
994e
cmddata[3] :M2通道上传个数
0008
cmddata[4] :M5通道上传个数
0011
cmddata[5] :M7通道上传个数
0011
cmddata[6] :送数命令后跟的送数命令。也就是多个送数命令
0000
6.2 数据回环(最终时不需要send命令)
memset(buffer,0,sizeof(buffer));
memset(buffer16,0,sizeof(buffer16));//将两个数组清零
recv(0,buffer,len);
send(0,buffer,len);//将W5500的数据写入数组buffer
将buffer数组和buffer16数组清零
接收recv命令,DSP芯片通过SPI接口,将W5500缓存的数据存到buffer数组。
注:send只是测试用,方便在网口观察现象。最终测试时,不能加send命令。
此时数据已经到了buffer数组,为8位的。
6.3 命令解析
for(i=0,j=0;j<1024;i++,j++)//将buffer的八位的数据转换成buffer16数组的16位数据
{
temp1=buffer[i];
temp1=(temp1<<8);
temp1=temp1&0xff00;
temp2=buffer[i+1];
temp2=temp2&0x00ff;
buffer16[j]=temp1|temp2;
i++;
}
if(buffer16[0]!=0x00aa)
{
break;
}
for(i=0,j=1;i<7;i++,j++)//命令第2到8个字,分别为cmddata[0]~cmddata[6],共7个字
{
cmddata[i]=buffer16[j];
}
第一个for循环:将buffer数组的8位的数据,转存到buffer16里,变为16位的数据
if语句:如果命令的头不是0xaa。则说明是无效命令,直接break,然后程序将重新运行switch循环
第二个for循环:解析出cmddata命令,也就是cmddata[0]~cmddata[6]