网站开发使用什么运行软件,织梦建站模板,大部分网站是国内虚拟主机和国外虚拟主机,wordpress 访问量过大ENC28J60#xff0c;是一款SPI接口的以太网PHYMAC芯片#xff0c;实现以太网物理层和MAC层硬件通信。uIP是一个TCP/IP软件协议栈#xff0c;实现TCP、UDP、ARP、ICMP等网络协议。STM32F103RCT6通过SPI接口与ENC28J60通讯#xff0c;并移植uIP协议#xff0c;实现一个小型的… ENC28J60是一款SPI接口的以太网PHYMAC芯片实现以太网物理层和MAC层硬件通信。uIP是一个TCP/IP软件协议栈实现TCP、UDP、ARP、ICMP等网络协议。STM32F103RCT6通过SPI接口与ENC28J60通讯并移植uIP协议实现一个小型的UDP服务器。 严格来说UDP是面向无连接的对等通讯协议不存在服务器与客户端之说这里的服务器只是来形容STM32 SERVER系统的UDP的使用方式即STM32充当服务器角色接收同网段的不同IP和端口的UDP数据包并将数据返回。 至于ENC28J60uIP移植相信不少资料与例子可供参考比如奋斗嵌入式正点原子等等本文不再赘述。这里只说我在移植uIP调试软件时发现的几个问题。
1、不要TCP只需要UDP、ARP、ICMP协议移植哪些文件。
下图是移植uIP后实现上述协议的截图没错只要“uip.c”和“uip_arp.c”和必要的头文件就足够了多一个文件都是浪费头文件途径也是添加uip和unix文件夹就好了。 2、UDP服务器接收同网段不同IP和端口要改uip.c文件的位置以及怎么用。
打开“uip.c”文件约1100行左右#endif /* UIP_UDP_CHECKSUMS */和/* Demultiplex this UDP packet between the UDP connections. */之间添加如下代码 //UDP SERVER补丁客户端IP和端口不受限制if((uip_udp_conn ! 0)((uip_udp_conn-rport ! UDPBUF-srcport)||!uip_ipaddr_cmp(uip_udp_conn-ripaddr, UDPBUF-srcipaddr))) //如果是已经连接并且和接收到的端口号或者IP地址不一致{uip_udp_remove(uip_udp_conn); //删除连接uip_udp_conn-rport UDPBUF-srcport; //将目的端口设置为收到的远端UDP包的源端口uip_udp_conn-lport UDPBUF-destport; //将本地端口设置为收到的远端UDP包的目的端口memcpy(uip_udp_conn-ripaddr, UDPBUF-srcipaddr, sizeof(uip_ipaddr_t )); //将目的IP地址设置为收到的远端UDP包的源IP地址}if(uip_udp_conn-rport 0) //如果首次接收到某个远端UDP包{uip_udp_conn-rport UDPBUF-srcport; //将目的端口设置为收到的远端UDP包的端口memcpy(uip_udp_conn-ripaddr,UDPBUF-srcipaddr,sizeof(uip_ipaddr_t )); //将目的IP地址设置为收到的远端UDP包的源IP地址}//uip_udp_conn uip_udp_new(ipaddr, HTONS(1000)); //建立到远程ipaddr,端口为1000的连接 if(uip_udp_conn ! 0) {uip_udp_bind(uip_udp_conn, UDPBUF-destport);//绑定本地端口为LPORT也就是LPORTRPORT 发数据 }//end 补丁
改好后是这样的 这段代码参考了奋斗的程序奋斗的程序有BUG最开始的那个if语句 if((uip_udp_conn ! 0) ((uip_udp_conn-rport ! UDPBUF-srcport) ||!uip_ipaddr_cmp(uip_udp_conn-ripaddr, UDPBUF-srcipaddr))) //如果是已经连接并且和接收到的端口号或者IP地址不一致
红的标记的部分原来的奋斗的程序是“||uip_udp_conn-ripaddr!UDPBUF-srcipaddr)”ripaddr和srcipaddr是u16类型的指针地址判断的是指针的地址是否相等那么必然不等所以即使同样的IP地址发来的包都会判断为真跳进if的执行语句中。改正后已经解决。
最后的if语句中uip_udp_bind(uip_udp_conn, UDPBUF-destport);
目的是将收到的UDP包中目的端口作为服务器的监听端口每次都会根据客户端的UDP端口改变即实现不同的IP、不同的源端口、目的端口发给服务器的UDP包服务器都能处理后原路返回。如果需要固定服务器的监听端口只需要将UDPBUF-destport改为固定的端口号即可例如端口号2000改为uip_udp_bind(uip_udp_conn, HTONS(2000));
再讲讲怎么用。在main函数初始化ENC28J60和uIP之后随便监听一个端口就可以了调用如下函数NetParam.lUdpPort变量自行定义或替换监听端口号但是其他端口也能监听
/*** brief UDP服务器模式* param None* retval None
*/
void udp_server_creat(void)
{uip_listen(HTONS(NetParam.lUdpPort));uip_udp_bind(uip_udp_conn, htons(NetParam.lUdpPort));//绑定本地端口为LPORT
}
3、STM32断电再上电后PC发给STM32的第一包数据PC收不到STM32回复的原因。
阿莫有个帖子为什么uip中udp第一次主动发送数据的时候PC没有收到呢第二次以后后就正常了 (amobbs.com 阿莫电子论坛 - 东莞阿莫电子网站) 之前我也被这个问题困扰了一段时间我做的udp server demo在用PC端用网络调试助手随便发一个什么数据给STM32 SERVERSERVER收到后原路回复上电后收到数据的序号以及收到数据的字节长度但是每次下载程序后测试第一个数据都不回复第二个才开始回复序号也是跳过0直接回1。 原因是STM32 SERVER端的ARP列表上电后被清空而电脑端的ARP列表又是上一次缓存了STM32 SERVER的IP和MAC所以电脑给STM32 SERVER发送UDP包时直接就把UDP发过来了而没有发起ARP这是一方面。另一方面void UipPro(void)这个函数参考奋斗的程序如下
/*** brief 中断触发读取网络接收缓存* param None* retval None
*/
void UipPro(void)
{if(ETH_INT 1){ //当网络接收到数据时会产生中断
rep:;ETH_INT 0;uip_len tapdev_read(); //从网络设备读取一个IP包,返回数据长度if(uip_len 0) //收到数据{/* 处理IP数据包(只有校验通过的IP包才会被接收) */if(BUF-type htons(UIP_ETHTYPE_IP)) //是IP包吗{uip_arp_ipin(); //去除以太网头结构更新ARP表uip_input(); //IP包处理/*当上面的函数执行后如果需要发送数据则全局变量 uip_len 0需要发送的数据在uip_buf, 长度是uip_len (这是2个全局变量)*/if (uip_len 0) //有带外回应数据{uip_arp_out(); //加以太网头结构在主动连接时可能要构造ARP请求tapdev_send(); //发送数据到以太网设备驱动程序}}/* 处理arp报文 */else if (BUF-type htons(UIP_ETHTYPE_ARP)) //是ARP请求包{uip_arp_arpin(); //如是是ARP回应更新ARP表如果是请求构造回应数据包/*当上面的函数执行后如果需要发送数据则全局变量 uip_len 0需要发送的数据在uip_buf, 长度是uip_len (这是2个全局变量)*/if (uip_len 0) //是ARP请求要发送回应{tapdev_send(); //发ARP回应到以太网上}}}}else{ //防止大包造成接收死机,当没有产生中断而ENC28J60中断信号始终为低说明接收死机if(ENC28J60_INT_STA 0) goto rep; }
}
uip_arp_ipin(); //去除以太网头结构更新ARP表
这一行本意是直接从收到的IP包里面获取源IP和源MAC加入到本地ARP表中然而全局搜索一下这个函数发现其在uip_arp.h中是空定义的上面的函数声明被注释了 再看uip_arp.c文件#if 0也是将此函数注释了。 有可能uIP作者调试后忘记将其解除注释因此uip_arp_ipin(); //去除以太网头结构更新ARP表 预期没有实现。
因此对于STM32来说它直接收到一个UDP包要回复给发来这个包的IP地址但是它的ARP列表是空的STM32直接就懵了不知道对方的MAC是啥于是STM32就放弃发送这包数据改为发送ARP查询包询问对方IP。在uip_arp_out()函数中有如下注释就是说了这个意思 了解原因后将其释放出来再编译发现STM32 SERVER重新上电后第一包数据不回复问题解决。 uIP之uip_arp_ipin();函数的作用。-OpenEdv-开源电子网 这个帖子也是问了这个问题我没有ID只能在这里回答了。
4、主动发送UDP数据包的原理及实现。
uIP协议栈用于“收到后发送”的逻辑很好用但是主动发送要费一番周折看一遍uip.c这个文件其发送的大概流程是
uip_process函数会周期调用flag变量如果为“UIP_UDP_TIMER”状态就会进入到回调函数UIP_UDP_APPCALL();然后goto udp_send;进入发送数据流程如果缓存数据长度变量uip_slen非0则发送数据。 因此想要主动发送UDP包需要让uip_process函数进入UIP_UDP_TIMER流程然后在回调函数中注入要发送的数据和长度之后就发出去了。逻辑就是这么简单代码如下
先定义俩个全局变量
u8 *pActiveSendData; //UDP主动发送数据指针
u16 ActiveSendLen; //UDP主动发送数据长度
/*** brief 主动发送udp包* param data 数据* param len 数据长度* retval None
*/
void udp_active_send(u8 *data, u16 len)
{pActiveSendData data;ActiveSendLen len;//设置目标ip和端口uip_udp_conn-rport HTONS(NetParam.rUdpPort); //将目的端口设置为收到的远端UDP包的源端口uip_udp_conn-lport HTONS(NetParam.lUdpPort); //将本地端口设置为收到的远端UDP包的目的端口uip_ipaddr_t ipaddr;uip_ipaddr(ipaddr, NetParam.rIP[0], NetParam.rIP[1], NetParam.rIP[2], NetParam.rIP[3]); memcpy(uip_udp_conn-ripaddr, ipaddr, sizeof(uip_ipaddr_t ));//唤起UDP发送处理uip_process(UIP_UDP_TIMER);/* 如果上面的函数调用导致数据应该被发送出去全局变量uip_len设定值 0 */if(uip_len 0){uip_arp_out(); //加以太网头结构在主动连接时可能要构造ARP请求tapdev_send(); //发送数据到以太网设备驱动程序ActiveSendLen 0;//发送完成数据清零}
}
回调函数向缓冲区写入数据就是用UIP_UDP_APPCALL()宏定义的那个要自己加的函数
/*** brief UDP主函数* param None* retval None
*/
void udp_server_appcall(void)
{//接收到一个新的udp数据包if(uip_newdata())//收到客户端发过来的数据{UDP_newdata();}else if(uip_poll())//主动发送数据{if(ActiveSendLen){udp_send(pActiveSendData, ActiveSendLen);}}
}
发送函数udp_send
/*** brief UDP 数据包发送* param str:数据* retval None
*/
void udp_send(u8 *str, u16 Len)
{struct udp_server_appstate *s (struct udp_server_appstate *)uip_udp_conn-appstate;s-textptr (u8*)str;s-textlen Len;uip_send(s-textptr, s-textlen);//发送udp数据包
// uip_udp_send(s-textlen);
}
怎么用主动发送直接调用udp_active_send如果是先收再发建议还是用udp_send
udp_active_send(OK, 2); 最后附上测试图1S间隔主动发送 两个PC客户端不同IP和源目的端口轮流发送测试。 本例程中ENC28J60与STM32F103RCT6硬件接线
SPI4根线CSPA3 SCK,MISO MOSI PA5 PA6 PA7
ENC28J60中断 INTPC4
IP参数定义位于udp_server.c文件中根据需要自己改lIP就是STM32 SERVER的IPrIP是电脑的IP。 最后的最后放上程序
链接https://pan.baidu.com/s/1JmLrI4zj7Bs5ifaHQ3OxAg?pwdldp9 提取码ldp9
如果链接失效记得评论区喊我更新