在硬件工程师完成了PCB和原理图设计后,根据正点原子的教程对例程进行修改,验证了硬件的正确性。
STM32F407和LAN8720调试记录.
正点原子 网络通信实验
下一步的工作需要在产品的板子上实现功能。
原产品采用W5500的硬TCP/IP协议方案。
调试路线尝试先从UDP回环开始。
所有调用接口全部按照 lwip\src\include\lwip\sockets.h中描述,使能LWIP_COMPAT_SOCKETS宏,依照BSD socket风格进行封装。
API接口如下:
#define accept(a,b,c) lwip_accept(a,b,c)
#define bind(a,b,c) lwip_bind(a,b,c)
#define shutdown(a,b) lwip_shutdown(a,b)
#define closesocket(s) lwip_close(s)
#define connect(a,b,c) lwip_connect(a,b,c)
#define getsockname(a,b,c) lwip_getsockname(a,b,c)
#define getpeername(a,b,c) lwip_getpeername(a,b,c)
#define setsockopt(a,b,c,d,e) lwip_setsockopt(a,b,c,d,e)
#define getsockopt(a,b,c,d,e) lwip_getsockopt(a,b,c,d,e)
#define listen(a,b) lwip_listen(a,b)
#define recv(a,b,c,d) lwip_recv(a,b,c,d)
#define recvfrom(a,b,c,d,e,f) lwip_recvfrom(a,b,c,d,e,f)
#define send(a,b,c,d) lwip_send(a,b,c,d)
#define sendto(a,b,c,d,e,f) lwip_sendto(a,b,c,d,e,f)
#define socket(a,b,c) lwip_socket(a,b,c)
#define select(a,b,c,d,e) lwip_select(a,b,c,d,e)
#define ioctlsocket(a,b,c) lwip_ioctl(a,b,c)
第一个问题即发现使用新的硬件时,虽然IP能ping通,串口的console没有任何输出。经检查发现USART2和USART3所使用的GPIO口已经改变,需要另一套板级支持包,即BSP。
例程里用的是RAW API,暂时还没法使用标准化bsd style的接口。按照例程,去掉按键的操作,初始化udp端口。
udppcb=udp_new();
if(udppcb)
{
IP4_ADDR(&rmtipaddr,lwipdev.remoteip[0],lwipdev.remoteip[1],lwipdev.remoteip[2],lwipdev.remoteip[3]);
err=udp_connect(udppcb,&rmtipaddr,UDP_DEMO_PORT);
if(err==ERR_OK)
{
err=udp_bind(udppcb,IP_ADDR_ANY,UDP_DEMO_PORT);
if(err==ERR_OK)
{
udp_recv(udppcb,udp_demo_recv,NULL);
Console::Instance()->printf("====== STATUS:Connected\r\n");
}else res=1;
}else res=1;
}else res=1;
其中UDP_DEMO_PORT为8089,LAN8720 IP地址为192.168.192.30 。
可以从console看到输出”===== STATUS:Connected”。
然而此时向192.168.192.30:8089发送一个UDP字节,程序并没有跳转到接收回调函数中。
通过wireshark抓包,内容如下:
可以看到第一条报文,从192.15发往192.30。确实为UDP协议包。
第二条报文是192.30 回复192.15的报文,协议为ICMP。
ICMP协议的介绍
可以看到协议里指出,错误类型属于端口无法到达。
拿其他程序测试了一下,果然当目标设备的IP正确,而目标设备上没有对应的端口时,确实会回复ICMP报文。
可见问题出在端口初始化上。
无奈之下只能和原子原本的例程对比。能够正常发送报文并接受消息。
在测试过程中无意中截到这样的报文:
可以观察到,只有当原地址和目标地址均为8089时,ICMP才没有回错误报文。
当两者中任意一者不是8089时均会报错。
回去仔细查看代码,确实有两个地方用到了UDP_DEMO_PORT。
分别是
err = udp_connect(udppcb, &rmtipaddr, UDP_DEMO_PORT);
和
err = udp_bind(udppcb, IP_ADDR_ANY, UDP_DEMO_PORT);
一个绑定了本地的端口和IP,另一个绑定了远程设备的IP和端口。
其中udp_connect这个函数是之前没有用过的。LWIP的手册对该函数作了描述:
LwIP Application Developers Manual
Connected pcbs only receive data from the connected remote address, while unconected pcbs receive datagrams from any remote address.
因此经过connect的UDP端口将只接收绑定过的源地址源端口的报文。
尝试把udp_connect这句函数注释掉,果然所有报文都能顺利接收。