本帖最后由 a976209770 于 2024-11-28 15:05 编辑
lwIP 是一个轻量级的 TCP/IP 协议栈,适用于嵌入式系统,支持 TCP、UDP、DNS 和 DHCP 等常用网络协议。本文将以 APM32 系列为例,介绍如何移植 lwIP 并实现完整的网络通信流程,包括发送和接收数据的详细代码示例。
1. lwIP 的基础配置lwIP 的配置通过 lwipopts.h 文件完成,该文件定义了协议栈的功能模块和资源分配。以下是推荐的配置:
1.1 配置文件:lwipopts.h#ifndef LWIPOPTS_H
#define LWIPOPTS_H
// 基本功能启用
#define LWIP_TCP 1 // 启用TCP
#define LWIP_UDP 1 // 启用UDP
#define LWIP_DNS 1 // 启用DNS
#define LWIP_DHCP 1 // 启用DHCP
// 内存配置
#define MEM_SIZE 1600 // 内存池大小
#define MEMP_NUM_PBUF 16 // PBUF内存块数量
#define PBUF_POOL_SIZE 16 // PBUF池大小
#define PBUF_POOL_BUFSIZE 512 // 单个PBUF大小
// TCP配置
#define TCP_MSS 1460 // 最大报文段长度
#define TCP_SND_BUF (4 * TCP_MSS) // 发送缓冲区
#define TCP_WND (4 * TCP_MSS) // 接收窗口
// 系统配置
#define NO_SYS 0 // 使用操作系统
#define SYS_LIGHTWEIGHT_PROT 1 // 启用轻量级保护
// 网络接口
#define LWIP_NETIF_STATUS_CALLBACK 1 // 启用网络接口状态回调
#endif // LWIPOPTS_H
2. lwIP 的移植实现网络接口是 lwIP 移植的核心。以下代码示例展示了基本的以太网初始化、数据接收和发送。 以太网接口初始化
#include "lwip/err.h"
#include "lwip/netif.h"
#include "ethernetif.h"
#include "apm32f4xx_eth.h"
static uint8_t rx_buffer[1536];
static uint8_t tx_buffer[1536];
err_t ethernetif_init(struct netif *netif)
{
netif->hwaddr_len = ETH_HWADDR_LEN;
netif->hwaddr[0] = 0x02; // 示例MAC地址
netif->hwaddr[1] = 0x00;
netif->hwaddr[2] = 0x00;
netif->hwaddr[3] = 0x00;
netif->hwaddr[4] = 0x00;
netif->hwaddr[5] = 0x01;
netif->mtu = 1500;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
ETH_Config(); // 硬件以太网初始化
return ERR_OK;
}
以太网数据接收
void ethernetif_input(struct netif *netif)
{
struct pbuf *p = NULL;
if (ETH_CheckFrameReceived())
{
uint16_t len = ETH_GetReceivedFrame(rx_buffer, sizeof(rx_buffer));
if (len > 0)
{
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
if (p != NULL)
{
memcpy(p->payload, rx_buffer, len);
if (netif->input(p, netif) != ERR_OK)
{
pbuf_free(p);
}
}
}
}
}
以太网数据发送
err_t ethernetif_output(struct netif *netif, struct pbuf *p)
{
struct pbuf *q;
uint16_t offset = 0;
for (q = p; q != NULL; q = q->next)
{
memcpy(&tx_buffer[offset], q->payload, q->len);
offset += q->len;
}
ETH_SendFrame(tx_buffer, offset);
return ERR_OK;
}
3. 主程序初始化以下代码展示如何基于 lwIP 实现 TCP 数据的发送和接收。 3.1 数据发送
static void tcp_send_data(struct tcp_pcb *pcb, const char *data, uint16_t len)
{
err_t err = tcp_write(pcb, data, len, TCP_WRITE_FLAG_COPY);
if (err == ERR_OK)
{
tcp_output(pcb);
}
}
3.2 数据接收
static err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
if (p != NULL)
{
printf("Received: %s\n", (char *)p->payload);
tcp_send_data(tpcb, (char *)p->payload, p->tot_len);
tcp_recved(tpcb, p->tot_len);
pbuf_free(p);
}
else if (err == ERR_OK)
{
tcp_close(tpcb);
}
return ERR_OK;
}
3.3 TCP 服务器
void tcp_server_init(void)
{
struct tcp_pcb *pcb = tcp_new();
tcp_bind(pcb, IP_ADDR_ANY, 12345);
pcb = tcp_listen(pcb);
tcp_accept(pcb, tcp_server_accept);
}
static err_t tcp_server_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
{
tcp_recv(newpcb, tcp_server_recv);
return ERR_OK;
}
4. 测试和调试- 测试方法:
- 启动 lwIP TCP 服务器。
- 使用客户端工具(如 telnet 或自定义工具)连接到服务器并发送数据。
- 调试要点:
- 确认网络初始化是否成功。
- 确保 lwIP 的内存配置足够。
- 检查接收数据的完整性。
5.主要模块分析
1. 核心协议栈(src/core)- core/ipv4 和 core/ipv6:实现 IP 协议栈的核心逻辑。
- TCP、UDP 协议:
- tcp.c:TCP 协议的实现,包括连接管理、数据传输和状态机。
- udp.c:UDP 协议的实现,提供无连接的轻量级数据传输功能。
- 内存管理:
- mem.c:lwIP 的动态内存分配。
- memp.c:提供固定大小内存池的管理。
- 计时器:
2. 应用接口层(src/api)- 提供标准的 BSD Sockets API,便于用户开发:
- sockets.c:BSD 套接字实现。
- tcpip.c:实现协议栈与应用接口之间的线程同步。
3. 网络接口(src/netif)- ethernetif.c:以太网接口的实现。
- 提供硬件抽象层,负责接收和发送数据帧。
4. 系统架构(doc/sys_arch.txt)- 描述了与操作系统的接口,主要定义线程、信号量和消息队列的使用方式。
6. 总结以上内容适用于 APM32 平台的网络开发,能够快速实现基于 lwIP 的网络通信功能。
附件为基于APM32F407的 lwIP 的示例代码,供参考
|