一、LWIP的移植
1、Lwip内核文件的移植参考正点原子的STM32F407无操作系统的移植,如图工程文件目录结构
2、添加arch文件
由于不使用RTOS实时系统所以,(cc.h、cpu.h、perf.h、sys_arch.h、sys_arch.c)基本不使用,所以也没有正点原子里面的Lwip_Arch文件夹,只是简单地实现了获取时间函数sys_now();代码在lwip_comm.c中。
3、添加LWIP通用文件
Lwip_App为通用文件夹,里面包含五个文件夹lwip_comm (lwip_comm.c、lwip_comm.h是LWIP源码和前面的以太网驱动库结合起来的桥梁。lwipopts.h是用来剪裁和配置LWIP的文件)、tcp_client_demo、tcp_server_demo、udp_demo、web_server_demo,后面这四个是网络应用的功能函数,lwip_comm.h里面定义了本机MAC地址,远端主机IP地址,本机IP地址,子网掩码,默认网关和是否使用DHCP
4、LWIP的剪裁和配置
在LWIP的源码中有一个opt.h的文件,这个文件就是剪裁和配置LWIP的,不过最好不要在这个文件里面修改,我们可以在其他的文件中定义来覆盖opt.h里面的定义,所以前面提过的LWIP->Lwip_App->Lwip_comm里面的lwipopt.h就是重新定义来剪裁配置LWIP的
到这简单LWIP移植就OK了(详细参考正点原子),为了验证是否有效下面我们做UDP和TCP通信来验证
二、UDP接口编程
对于UDP的协议可以网上参考,主要说明一下LWIP中的UDP协议函数(参考正点原子STM32F407LWIP开发手册)在LWIP的源码中有udp.c和udp.h两个和UDP有关的函数(lwip-1.4.1\src\core)
Udp.c中与UDP报文处理有关的函数之间关系图
Lwip的RAW_API编程方式是基于回调机制的,当我们初始化应用的时候我们必须为内核中的不同事件注册相应的回调函数,当事件发生的时候这些回调函数就会被调用,部分函数说明
我们简单做一个demo来测试UPD客户端数据传输功能
#define UDP_DEMO_PORT 8090 //本地端口
uint8_t udp_demo_recvbuf[100]; //UDP接收数据缓冲区
uint8_t udp_demo_sendbuf[50]; //UDP发送数据内容缓冲区
uint8_t UdpRecelen=0;
/******************************************************************************
* 描述 : 创建udp客户端
* 参数 : 无
* 返回 : 无
******************************************************************************/
void udp_echo_init(void)
{
struct udp_pcb *udp_client_pcb;
/* 为udp客户端分配一个udp_pcb结构体 */
udp_client_pcb = udp_new();
/* 绑定本地端号和IP地址 */
udp_bind(udp_client_pcb, IP_ADDR_ANY, UDP_DEMO_PORT);
/* 注册接收回调函数 */
udp_recv(udp_client_pcb,udp_demo_recv,NULL);
}
/******************************************************************************
* 描述 : 接收回调函数
* 参数 : -
* 返回 : 无
******************************************************************************/
void udp_demo_recv(void *arg,struct udp_pcb *upcb,struct pbuf *p,struct ip_addr *addr,u16_t port)
{
uint32_t data_len = 0;
struct pbuf *q;
if(p!=NULL){
memset(udp_demo_recvbuf,0,UDP_DEMO_RX_BUFSIZE);
/* 遍历整个pbuf链表 */
for(q=p;q!=NULL;q=q->next){
//判断要拷贝到UDP_DEMO_RX_BUFSIZE中的数据是否大于
//UDP_DEMO_RX_BUFSIZE的剩余空间,如果大于
//的话就只拷贝UDP_DEMO_RX_BUFSIZE中剩余长度的数据,否则的话就拷贝所有的数据
if(q->len > (UDP_DEMO_RX_BUFSIZE-data_len)){
memcpy(udp_demo_recvbuf+data_len,q->payload,(UDP_DEMO_RX_BUFSIZE- data_len));
}
else memcpy(udp_demo_recvbuf+data_len,q->payload,q->len);
data_len += q->len;
if(data_len > UDP_DEMO_RX_BUFSIZE) break;
}
/* 接收的数据长度 */
UdpRecelen = data_len;
/* 记录远端主机的IP地址 */
upcb->remote_ip=*addr;
/* 记录远端主机的端口号 */
upcb->remote_port=port;
/* 接收数据处理函数 */
UDP_ReceData_Handler(upcb,UdpRecelen);
/* 释放缓冲区数据 */
pbuf_free(p);
}
}
/******************************************************************************
* 描述 : 发送udp数据
* 参数 :
* 返回 : 无
******************************************************************************/
void udp_demo_senddata(struct udp_pcb *upcb,uint8_t *sendbuf)
{
struct pbuf *ptr;
/* 分配缓冲区空间 */
ptr=pbuf_alloc(PBUF_TRANSPORT,strlen((char*)sendbuf),PBUF_POOL);
if(ptr){
/* 填充缓冲区数据 */
ptr->payload=(void*)sendbuf;
/* 发送udp数据 */
udp_send(upcb,ptr);
/* 释放缓冲区空间 */
pbuf_free(ptr);
}
}
/******************************************************************************
* 描述 : udp数据处理函数
* 参数 :
* 返回 : 无
******************************************************************************/
void UDP_ReceData_Handler(upcb,UdpRecelen)
{
/* 将接收到的数据再转发出去 */
udp_demo_senddata(upcb,udp_demo_recvbuf);
/* 也可以做一些判断,在做自己的功能 */
/* 发送 0x2a 0x01 数据*/
if(UdpRecelen>=2){
if((0x01 == udp_demo_recvbuf[1]) && (0x2a==udp_demo_recvbuf[0])){
//Read BMC version
printf("Hello LWIP UDP demo");
}
memset(udp_demo_sendbuf,0,UDP_DEMO_RX_BUFSIZE);
UdpRecelen = 0;
}else{
UdpRecelen = 0 ;
}
}
三、TCP接口编程
对于TCP的协议可以网上参考,主要说明一下LWIP中的TCP协议函数(参考正点原子STM32F407LWIP开发手册)
在LWIP的源码中有tcp.c,tcp.h,tcp_in.c和tcp_out.c函数和TCP有关(lwip-1.4.1\src\core)
TCP层中函数关系图:
LWIP提供的TCP函数,
我们简单做一个demo来测试TCP服务端数据传输功能
/******************************************************************************
* 描述 : 创建tcp服务器
* 参数 : 无
* 返回 : 无
******************************************************************************/
void Tcp_Server_Init(void)
{
struct tcp_pcb *tcp_server_pcb;
/* 为tcp服务器分配一个tcp_pcb结构体 */
tcp_server_pcb = tcp_new();
/* 绑定本地端号和IP地址 */
tcp_bind(tcp_server_pcb, IP_ADDR_ANY, 80);
/* 监听之前创建的结构体tcp_server_pcb */
tcp_server_pcb = tcp_listen(tcp_server_pcb);
/* 初始化结构体接收回调函数 */
tcp_accept(tcp_server_pcb, tcp_server_accept);
}
/******************************************************************************
* 描述 : 客户端接入回调函数
* 参数 : -
* 返回 : -
******************************************************************************/
static err_t tcp_server_accept(void *arg, struct tcp_pcb *pcb, err_t err)
{
/* 确认监听与连接 */
tcp_arg(pcb, mem_calloc(sizeof(struct name), 1));
/* 发送一个建立连接的字符串 */
tcp_write(pcb, "hello my dream \n\r",strlen("hello my dream \n\r "), 1);
/* 配置接收回调函数 */
tcp_recv(pcb, tcp_server_recv);
return ERR_OK;
}
/******************************************************************************
* 描述 : 接收回调函数
* 参数 : -
* 返回 : -
******************************************************************************/
static err_t tcp_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *tcp_recv_pbuf, err_t err)
{
struct pbuf *tcp_send_pbuf;
struct name *name = (struct name *)arg;
if (tcp_recv_pbuf != NULL)
{
/* 扩大收发数据的窗口 */
tcp_recved(pcb, tcp_recv_pbuf->tot_len);
if (!name)
{
pbuf_free(tcp_recv_pbuf);
return ERR_ARG;
}
/* 将接收的数据拷贝给发送结构体 */
tcp_send_pbuf = tcp_recv_pbuf;
/* 换行 */
tcp_write(pcb, "\r\n", strlen("\r\n"), 1);
/* 将接收到的数据再转发出去 */
tcp_write(pcb, tcp_send_pbuf->payload, tcp_send_pbuf->len, 1);
pbuf_free(tcp_recv_pbuf);
}
else if (err == ERR_OK)
{
/* 释放内存 */
mem_free(name);
return tcp_close(pcb);
}
return ERR_OK;
}
这里只是简单的介绍了一下LWIP的UDP和TCP,对于整个LWIP还是非常的复杂,如果想继续深入了解LWIP可以翻看LWIP的源码---http://download.savannah.gnu.org/releases/lwip/