简介
随着 5G 的兴起,万物互联成为将来发展的一个方向,更多的设备将具备联网的功能,将数据上传到云端。得益于 TCP/IP 协议的优越性,该协议已成为当前应用的主流网络协议。在嵌入式网络设备中由于受到硬件资源的限制,实现完整的 TCP/IP 协议十分困难,这就需要一种特殊的实现方式,LWIP 作为一种轻量级的 TCP/IP 协议实现方式充分满足了这一要求。
TCP/IP简介
TCP/IP(Transmission Control Protocol/Internet Protocol,传输控制协议/网际协议)体系结构是指能够在多个不同网络间实现的协议簇。该协议簇是在美国国防高级研究计划局(Defense Advanced Research Projects Agency,DARPA)所资助的实验性 ARPARNET 分组交换网络、无线电分组网络和卫星分组网络上研究开发成功的。网络部分瘫痪时仍保持较强的工作能力和灵活性。这种应用环境导致了一系列协议的出现,从而使不同类型的终端和网络间能够进行有效通信。实际上,Internet 已经成为全球计算机互联的主要体系结构,而 TCP/IP 协议是 Internet 的代名词,是将异种网络、不同设备互联起来,进行正常数据通信的格式和大家遵守的约定。
TCP/IP 协议包括两部分:传输控制协议和网际协议。TCP/IP 的通信任务组织成 5 个相对独立的层次:应用层、传输层、互联网层(对应 OSI 的网络层)、网络接口层和物理层,其中网络接口层和物理层常称为物理网层。各层功能如下:
(1)应用层:应用程序通过这一层访问网络,常见 FTP、HTTP、DNS 和 TELNET协议;
(2)传输层:TCP 协议和 UDP 协议;
(3)网络层:IP 协议,ARP、RARP 协议,ICMP 协议等,网络层负责相邻计算机之间的通信
(4)网络接口层:是 TCP/IP 协议的基层(最低层),负责数据帧的发送和接收。
LWIP简介
lwip 是瑞典计算机科学院(SICS)的 Adam Dunkels 开发的一个小型开源的TCP/IP 协议栈。LwIP 是 Light Weight (轻型)IP 协议,有无操作系统的支持都可以运行。LwIP 实现的重点是在保持 TCP 协议主要功能的基础上减少对 RAM 的占用,它只需十几 KB 的 RAM 和 40K 左右的 ROM 就可以运行,这使 LwIP 协议栈适合在低端的嵌入式系统中使用。lwIP 协议栈主要关注的是怎么样减少内存的使用和代码的大小,这样就可以让 lwIP 适用于资源有限的小型平台例如嵌入式系统。
其主要特性如下:
(1)支持多网络接口下的 IP 转发;
(2)支持 ICMP 协议;
(3)包括实验性扩展的 UDP(用户数据报协议);
(4)包括阻塞控制、RTT 估算、快速恢复和快速转发的 TCP(传输控制协议);
(5)提供专门的内部回调接口(Raw API),用于提高应用程序性能;
(6)可选择的 Berkeley 接口 API (在多线程情况下使用) ;
(7)在最新的版本中支持 ppp;
(8) 新版本中增加了的 IP fragment 的支持;
(9) 支持 DHCP 协议,动态分配 ip 地址。
BD设计
这里将网口选上。
串口也选上,即可。
关键部分软件代码
int start_application()
{
struct tcp_pcb *pcb;
err_t err;
unsigned port = 6;
/* create new TCP PCB structure */
pcb = tcp_new();
if (!pcb) {
xil_printf("Error creating PCB. Out of Memory\n\r");
return -1;
}
/* bind to specified @port */
err = tcp_bind(pcb, IP_ADDR_ANY, port);
if (err != ERR_OK) {
xil_printf("Unable to bind to port %d: err = %d\n\r", port, err);
return -2;
}
/* we do not need any arguments to callback functions */
tcp_arg(pcb, NULL);
/* listen for connections */
pcb = tcp_listen(pcb);
if (!pcb) {
xil_printf("Out of memory while tcp_listen\n\r");
return -3;
}
/* specify callback to use for incoming connections */
tcp_accept(pcb, accept_callback);
xil_printf("TCP echo server started @ port %d\n\r", port);
//err = tcp_write(pcb, s, strlen(s), 1);
return 0;
}
可以看到这个板子作为服务器开始监听远程连接。
err_t accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err)
{
static int connection = 1;
/* set the receive callback for this connection */
tcp_recv(newpcb, recv_callback);
/* just use an integer number indicating the connection id as the
callback argument */
tcp_arg(newpcb, (void*)(UINTPTR)connection);
/* increment for subsequent accepted connections */
connection++;
return ERR_OK;
}
这是接收到了数据去绑定相应的回调函数。
err_t recv_callback(void *arg, struct tcp_pcb *tpcb,
struct pbuf *p, err_t err)
{
/* do not read the packet if we are not in ESTABLISHED state */
if (!p) {
tcp_close(tpcb);
tcp_recv(tpcb, NULL);
return ERR_OK;
}
/* indicate that the packet has been received */
tcp_recved(tpcb, p->len);
/* echo back the payload */
/* in this case, we assume that the payload is < TCP_SND_BUF */
if (tcp_sndbuf(tpcb) > p->len) {
err = tcp_write(tpcb, p->payload, p->len, 1);
//printf("%s\n",tpcb->local_ip);
printf("%s\n",p->payload);
} else
xil_printf("no space in tcp_sndbuf\n\r");
/* free the received pbuf */
pbuf_free(p);
return ERR_OK;
}
这里是回调函数,可以看到这里将接收到的数据原封不动的写回到客户端,这里我增加了一个串口打印出数据。
可以看到网口环回了,串口相应的也打印出了数据。