代码都在 https://github.com/nickfox-taterli/LwIP_STM32F4-Discovery
LwIP是非常复杂的玩意,Lw指的是Light Weight(轻量级),不知道为什么有人会翻译成乐维?
需要移植的文件就两个:
这些基于非常底层的一些操作了.还有底层到寄存器的,在GitHub上我已经注释了(中文),现在大致说下框架.
用户要用LwIP,要调用MX_LWIP_Init这个总入口,他按顺序执行了网络接口的添加,初始化底层,DHCP,然后LwIP就可以用了,发包用的是netif->output,收包用的是netif->input,在netif->input事先就是收到包中断,调用low_level_input把包数据接回来,给netif->input处理,而这个netif->input实际上就是tcpip_input,他就是解包的,发包则是由netif->input交由etharp_output制作数据包,调用low_level_output发出去.
学过USB的都知道,传输的控制数据虽然是Byte的,但是其实每个Bit或者每个Byte含义不同,后续的功能也不同,以太网是一样的,根据IP头解释后面的数据是什么东西,再跟,然后深入解释TCP头里面又是什么东西.所谓的7层OSI模型,头晕.
偷了一张图,用最大白话说.
最下面,就是我们的网卡,连接层也就是我们的MCU,MCU有MAC和LLC,有些还具备PTP(比如STM32F4就具备),这些都是硬件,实际存在的,而LwIP包含的是传输层和网络层,收到网络层的包,就可以进一步解释是不是有传输层协议,比如ICMP他可以携带数据,但是他不是传输层协议.LwIP只做到TCP,UDP就结束了,后面是用户应用,如果开发过Linux的都知道,这其实就是socket,什么服务器软件都是基于socket的嘛.(甚至是个邮件服务器,KMS服务器也可以运行在MCU上.)
分析代码两种方法,自上而下和自下而上,因为很多人(包括我)不知道一个协议包可能都有什么,不如趁机学习协议,就自上而下学习下.LwIP支持两种通信,netconn和socket方式,一开始我也不知道哪个好,好像传递裸数据的,用netconn比较多,那我就用netconn吧.
下一集开始分析这段代码.
void udpecho_thread(void *arg) { struct netconn *udpconn; struct netbuf *udpbuf; struct netbuf *recv_udpbuf; ip_addr_t udpaddr; uint8_t udpdemo_buf[5] = {0xAA, 0x55, 0xFF, 0x5A, 0xA5}; err_t err; err_t recv_err; /* 新建一个连接块 */ udpconn = netconn_new(NETCONN_UDP); udpconn->recv_timeout = 1000; /* 1000毫秒收不到东西,接收函数也不会堵塞. */ if (udpconn != NULL) /* 间接申请了内存 */ { /* 绑定本地所有地址(开发板是本地) */ err = netconn_bind(udpconn, IP_ADDR_ANY, 7001); /* 写目标地址 */ IP4_ADDR(&udpaddr, 10, 0, 1, 35); /* 连接目标端口,UDP是无状态协议,肯定能连接成功的. */ netconn_connect(udpconn, &udpaddr, 7800); if (err == ERR_OK) { while (1) { recv_err = netconn_recv(udpconn, &recv_udpbuf); if (recv_err == ERR_OK) { if(recv_udpbuf->p->len >= 1) udpdemo_buf[4] = ((uint8_t *)recv_udpbuf->p->payload)[0]; netbuf_delete(recv_udpbuf); } /* UDP 缓冲区申请 */ udpbuf = netbuf_new(); /* 申请内存 */ netbuf_alloc(udpbuf, strlen((char *)udpdemo_buf)); /* 把数据复制到payload里去. */ memcpy(udpbuf->p->payload, (void *)udpdemo_buf, strlen((const char *)udpdemo_buf)); /* payload 其实也可以直接修改. */ ((uint32_t *)udpbuf->p->payload)[0] = xTaskGetTickCount(); /* 这一步把数据发送出去. */ err = netconn_send(udpconn, udpbuf); /* UDP总会发成功的. */ netbuf_delete(udpbuf); /* 延迟等下一次再发. */ vTaskDelay(1000); } } else { /* 如果本地地址绑定不了,那么失败. */ netconn_delete(udpconn); } } vTaskDelete(NULL); }