文章目录

  • LWIP 简介
  • LWIP 文件说明
    • core内核文件简介
  • LWIP 三种编程接口
    • RAW
    • NETCONN
    • SOCKET API
  • ETH(以太网)
    • 介绍
    • SMI接口
    • MII接口
    • RMII接口
    • 以太网DMA描述符
  • LWIP数据包和网络接口管理
    • netif 结构体
    • netif 使用
    • 与netif相关的底层函数
    • LWIP 数据包
    • LWIP网络接口管理
      • 添加中间文件
  • 开始移植
    • 在Cubemx中选用ETH

LWIP 简介

LwIP 全名:Light weight IP,意思是轻量化的 TCP/IP 协议。LwIP 的设计初衷是:用少量的资源消耗实现一个较为完整的 TCP/IP 协议栈,其中“完整”主要指的是 TCP 协议的完整性,实现的重点是在保持 TCP 协议主要功能的基础上减少对 RAM 的占用。此外 LwIP 既可以移植到操作系统上运行,也可以在无操作系统的情况下独立运行。

LWIP 文件说明

最关心的是src文件中的内容
api :文件装的是NETCONN API 和SOCKET API 源文件,只有在操作系统中才会编译
apps:文件夹里面装的是应用程序的源文件,包括常见的应用程序,如 httpd、mqtt、tftp、sntp、snmp等
core:文件夹里面是 LwIP 的内核源文件
include:文件夹里面是 LwIP 所有模块对应的头文件。
netif:文件夹里面是与网卡移植有关的文件,这些文件为我们移植网卡提供了模板,我们可以直接使用。

core内核文件简介

altcp.c、altcp_alloc.c、altcp_tcp.c 等文件是应用程序分层 TCP 连接 API,从 TCPIP 线程使用,是一个抽象层,可以模拟应用程序的 tcp 回调 API,同时防止直接链接,没有使用。
def.c:文件定义了一些基础类函数,比如主机序和网络序的转换、字符串的查找和比较、整数转换成字符串等,这些函数会被 LwIP 内核的很多模块所调用。在 include 目录里面的 def.h 文件对外声明了 def.c 所实现的函数,同时定义了许多宏,能实现一些基础操作,比如取最大值、取最小值、计算数组长度等,这些宏同样也被内核的许多模块所调用。
我们经常可以看到某个内核的源文件在开始的地方 #include “def.h”
dns:文件实现了域名解析的功能
inet_chksum.c:文件提供了 LwIP 所需的校验和功能
init.c:文件对 LwIP 的用户宏配置进行了检查,会将配置错误和不合理的地方,通过编译器的 #error和 #warning 功能表示出来。另外,init.c 定义了lwip_init 初始化函数,这个函数会依次对 LwIP 的各个模块进行初始化。
ip.c:文件实现了 IP 协议相关的函数
mem.c:文件实现了内存堆(heap)管理机制
memp.c:文件实现了动态内存池(Dynamic)管理机制
netif.c文件实现了网卡的操作,比如注册/删除网卡、使能/禁能网卡、设置网卡 IP 地址等等
pbuf.c:文件实现了 LwIP 对网络数据包的各种操作
raw.c:文件实现了一个传输层协议的框架
stat.c:文件实现了 LwIP 内核的统计功能,使用户可以实时地查看 LwIP 内核对网络数据包的处理情况
sys.c:它提供了与临界区相关的操作
tcp.c、tcp_in.c 和 tcp_out.c:文件实现了 TCP 协议,包括对 TCP 连接的操作、对 TCP 数据包的输入输出操作和 TCP 定时器
timeouts.c:定义了 LwIP 内核的超时处理机制
udp.c:文件实现了 UDP 协议,包括对 UDP 连接的操作和 UDP 数据包的操作。

LWIP 三种编程接口

RAW

RAW/Callback API 是指内核回调型的 API,这在许多通信协议的 C 语言实现中都有所应用

NETCONN

可以使用 NETCONN API 或者 Socket API 进行网络应用程序的开发。NETCONN API 是基于操作系统的 IPC 机制(即信号量和邮箱机制)实现。在操作系统环境中,LwIP 内核会被实现为一个独立的线程,名为tcpip_thread
函数的调用 lwip_system_init->tcpip_init(tcpip_init_done_callback, (void *)&done_sem);

void tcpip_init(tcpip_init_done_fn initfunc, void *arg)
{lwip_init();tcpip_init_done = initfunc;tcpip_init_done_arg = arg;if (sys_mbox_new(&tcpip_mbox, TCPIP_MBOX_SIZE) != ERR_OK) {LWIP_ASSERT("failed to create tcpip_thread mbox", 0);}
#if LWIP_TCPIP_CORE_LOCKINGif (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {LWIP_ASSERT("failed to create lock_tcpip_core", 0);}
#endif /* LWIP_TCPIP_CORE_LOCKING */sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
}

SOCKET API

Socket,即套接字,它对网络连接进行了高级的抽象,使得用户可以像操作文件一样操作网络连接

ETH(以太网)

介绍

  TCP/IP 定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准,PHY 层芯片 LAN8720 相当于物理层,STM32F407 自带的MAC 层相当于数据链路层,而 LWIP 提供的就是网络层、传输层的功能,应用层是需要用户自己根据自己想要的功能去实现的。
  STM32F407自带有10/100Mbit/s的以太网MAC(这个是内核,介子控制器)内核,这个以太网MAC内核有 如下特性:

  1. 支持外部PHY(物理层这个是纯模拟电路)接口实现10/100Mbit/s数据传输速率。
  2. 通过符合IEEE802.3的(MII接口)介子独立接口和(RMII接口)简化介子独立接口与外接快速以太网PHY进行通信
  3. 支持全双工和半双工操作
  4. 报头和帧起始数据(SFD)在发送路径中插入、在接收路径中删除
  5. 可逐帧控制CRC和pad自动生成
  6. 可编程帧长度,支持高大16KB的巨型帧
  7. 可编程帧间隔
  8. SMI接口是站管理接口,通过这个接口来访问PHY寄存器,数据线MDIO和时钟线MDC,可以访问32个PHY设备
#mermaid-svg-VD9mk3S1X3LDRPeS {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-VD9mk3S1X3LDRPeS .error-icon{fill:#552222;}#mermaid-svg-VD9mk3S1X3LDRPeS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VD9mk3S1X3LDRPeS .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-VD9mk3S1X3LDRPeS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VD9mk3S1X3LDRPeS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VD9mk3S1X3LDRPeS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VD9mk3S1X3LDRPeS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VD9mk3S1X3LDRPeS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VD9mk3S1X3LDRPeS .marker.cross{stroke:#333333;}#mermaid-svg-VD9mk3S1X3LDRPeS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VD9mk3S1X3LDRPeS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VD9mk3S1X3LDRPeS .cluster-label text{fill:#333;}#mermaid-svg-VD9mk3S1X3LDRPeS .cluster-label span{color:#333;}#mermaid-svg-VD9mk3S1X3LDRPeS .label text,#mermaid-svg-VD9mk3S1X3LDRPeS span{fill:#333;color:#333;}#mermaid-svg-VD9mk3S1X3LDRPeS .node rect,#mermaid-svg-VD9mk3S1X3LDRPeS .node circle,#mermaid-svg-VD9mk3S1X3LDRPeS .node ellipse,#mermaid-svg-VD9mk3S1X3LDRPeS .node polygon,#mermaid-svg-VD9mk3S1X3LDRPeS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VD9mk3S1X3LDRPeS .node .label{text-align:center;}#mermaid-svg-VD9mk3S1X3LDRPeS .node.clickable{cursor:pointer;}#mermaid-svg-VD9mk3S1X3LDRPeS .arrowheadPath{fill:#333333;}#mermaid-svg-VD9mk3S1X3LDRPeS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VD9mk3S1X3LDRPeS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VD9mk3S1X3LDRPeS .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-VD9mk3S1X3LDRPeS .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-VD9mk3S1X3LDRPeS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VD9mk3S1X3LDRPeS .cluster text{fill:#333;}#mermaid-svg-VD9mk3S1X3LDRPeS .cluster span{color:#333;}#mermaid-svg-VD9mk3S1X3LDRPeS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VD9mk3S1X3LDRPeS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

网络数据交换
设备配置
MAC
MII
SMII
PHY

SMI接口

  站管理接口(SMI) 允许应用程序通过 2 条线:时钟(MDC)和数据线(MDIO)访问任意 PHY 寄存器。该接口支持访问多达 32 个PHY。应用程序可以从 32 个PHY 中选择一个PHY,然后从任意PHY 包含的 32 个寄存器中选择一个寄存器,发送控制数据或接收状态信息。

MII接口

  MII接口:介质独立接口 (MII) 定义了 10 Mbit/s 和 100 Mbit/s 的数据传输速率下 MAC 子层与 PHY 之间的互连。

  TX_CLK和RX_CLK为发送和接收连续时钟,当速率为10Mbit/s时为2.5MHZ,速率为100Mbit/s时为25MHZ,外部晶振提供25MHZ的时钟。

RMII接口

  介质独立接口 (RMII) 规范降低了 10/100 Mbit/s 下微控制器以太网外设与外部 PHY 间的 引脚数。根据 IEEE 802.3u 标准, MII 包括 16 个数据和控制信号的引脚。 RMII 规范将引脚 数减少为 7个(引脚数减少 62.5%)。不过RMII接口的参考时钟必须是50MHZ!

REF_CLK:参考时钟
TX_EN: 发送使能
TXD[1:0]:发送数据
RXD[1:0]:接收数据

以太网DMA描述符

  F407有一个以太网专用的DAM,DMA可以在CPU完全不干预的情况下,通过描述符有效地将数据从源传送到目标,接收缓冲区和发送缓冲区的数据都可以通过以太网DMA来传送。429可以直接从HAL库中把32f4xx_hal_eth.c添加进来。

typedef struct
{__IO uint32_t   Status;           /*!< Status */uint32_t   ControlBufferSize;     /*!< Control and Buffer1, Buffer2 lengths */uint32_t   Buffer1Addr;           /*!< Buffer1 address pointer 这个是缓冲区的地址 */uint32_t   Buffer2NextDescAddr;   /*!< Buffer2 or next descriptor address pointer下一个描述符的地址 *//*!< Enhanced ETHERNET DMA PTP Descriptors */uint32_t   ExtendedStatus;        /*!< 增强型描述符状态 */uint32_t   Reserved1;             /*!< Reserved */uint32_t   TimeStampLow;          /*!< Time Stamp 时间戳 Low value for transmit and receive */uint32_t   TimeStampHigh;         /*!< Time Stamp 时间错High value for transmit and receive */} ETH_DMADescTypeDef;
//DMA描述符数组
ETH_DMADescTypeDef  DMARxDscrTab[ETH_RXBUFNB] //DMA接收描述符
ETH_DMADescTypeDef  DMATxDscrTab[ETH_TXBUFNB] //DMA发送描述符
//数据缓冲区
uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]
uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]

以太网驱动库 stm32f4xx_hal_eth.c 中我们采用的是 DMA 链接结构,但是DMARxDscrTabDMATxDscrTab 是两个指针(或数组),那么我们必然要将这两个指针(或数组)改为链接结构。在 stm32f4xx_hal_eth.c 文件中有两个函数 HAL_ETH_DMATxDescListInit()HAL_ETH_DMARxDescListInit (),通过这两个函数我们就可以将上面两个指针(或数组)变为链接结构。

//在应用层定义指针,用于发送和接收数据的缓冲区
uint8_t *Rx_Buff; //以太网底层驱动接收 buffers 指针
uint8_t *Tx_Buff; //以太网底层驱动发送 buffers 指针
//这两个值,在stm32f4xx_hal_conf.h 定义
uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE];
uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE];

LWIP数据包和网络接口管理

netif 结构体

如何使硬件和软件无缝的连接起来。LWIP使用一个数据结构体netif来描述网卡。
单个网卡中,netif 结构体只有一个,多个网卡的时候,LwIP 会将每个用 netif 描述的网卡连接成一个链表(单向链表),该链表就记录每个网卡的 netif。

struct netif {#if !LWIP_SINGLE_NETIF/** pointer to next in linked list *//** 指向netif 链表的下一个 */struct netif *next;
#endif#if LWIP_IPV4/** IP address configuration in network byte order *//** IP 地址 子网掩码 默认网关 */ip_addr_t ip_addr;ip_addr_t netmask;ip_addr_t gw;
#endif /* LWIP_IPV4 *//** This function is called by the network device driver*  to pass a packet up the TCP/IP stack. * 此函数由网络设备驱动程序调用,将数据包传递到 TCP/IP 协议栈。* 对于以太网物理层,这通常是 ethernet_input() */netif_input_fn input;
#if LWIP_IPV4/** This function is called by the IP module when it wants*  to send a packet on the interface. This function typically*  first resolves the hardware address, then sends the packet.*  For ethernet physical layer, this is usually etharp_output() * 此函数由 IP 层调用,在接口上发送数据包。通常这个功能,* 首先解析硬件地址,然后发送数据包。* 对于以太网物理层,这通常是 etharp_output()/* * */netif_output_fn output;
#endif /* LWIP_IPV4 *//** This function is called by ethernet_output() when it wants*  to send a packet on the interface. This function outputs*  the pbuf as-is on the link medium.*  这个函数被ethernet_output()调用,当需要在网卡上发送一个数据包的时候,*  底层硬件输出数据,*/netif_linkoutput_fn linkoutput;#if LWIP_NETIF_STATUS_CALLBACK/** This function is called when the netif state is set to up or down状态 */netif_status_callback_fn status_callback;
#endif /* LWIP_NETIF_STATUS_CALLBACK */
#if LWIP_NETIF_LINK_CALLBACK/** This function is called when the netif link is set to up or down网络连接*/netif_status_callback_fn link_callback;
#endif /* LWIP_NETIF_LINK_CALLBACK */
#if LWIP_NETIF_REMOVE_CALLBACK/** This function is called when the netif has been removed 网卡将要删除的时候*/netif_status_callback_fn remove_callback;
#endif /* LWIP_NETIF_REMOVE_CALLBACK *//** This field can be set by the device driver and could point*  to state information for the device. */void *state;
#ifdef netif_get_client_datavoid* client_data[LWIP_NETIF_CLIENT_DATA_INDEX_MAX + LWIP_NUM_NETIF_CLIENT_DATA];
#endif
#if LWIP_NETIF_HOSTNAME/* the hostname for this netif, NULL is a valid value netif 的主机名 NULL也是一个有效值
*/const char*  hostname;
#endif /* LWIP_NETIF_HOSTNAME */
#if LWIP_CHECKSUM_CTRL_PER_NETIFu16_t chksum_flags;
#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*//** maximum transfer unit (in bytes) */u16_t mtu;
#if LWIP_IPV6 && LWIP_ND6_ALLOW_RA_UPDATES/** maximum transfer unit (in bytes), updated by RA */u16_t mtu6;
#endif /* LWIP_IPV6 && LWIP_ND6_ALLOW_RA_UPDATES *//** link level hardware address of this interface */u8_t hwaddr[NETIF_MAX_HWADDR_LEN];/** number of bytes used in hwaddr */u8_t hwaddr_len;/** flags (@see @ref netif_flags)网卡状态信息标志位,是很重要的控制字段,
* 它包括网卡功能使能、广播使能、 ARP 使能等等重要控制位。 */
* */u8_t flags;/** descriptive abbreviation */char name[2];/** number of this interface. Used for @ref if_api and @ref netifapi_netif, * as well as for IPv6 zones */u8_t num;
#if LWIP_IPV6_AUTOCONFIG/** is this netif enabled for IPv6 autoconfiguration */u8_t ip6_autoconfig_enabled;
#endif /* LWIP_IPV6_AUTOCONFIG */
#if LWIP_IPV6_SEND_ROUTER_SOLICIT/** Number of Router Solicitation messages that remain to be sent. */u8_t rs_count;
#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
#if MIB2_STATS/** link type (from "snmp_ifType" enum from snmp_mib2.h) */u8_t link_type;/** (estimate) link speed */u32_t link_speed;/** timestamp at last change made (up/down) */u32_t ts;/** counters */struct stats_mib2_netif_ctrs mib2_counters;
#endif /* MIB2_STATS */
#if LWIP_IPV4 && LWIP_IGMP/** This function could be called to add or delete an entry in the multicastfilter table of the ethernet MAC.*/netif_igmp_mac_filter_fn igmp_mac_filter;
#endif /* LWIP_IPV4 && LWIP_IGMP */
#if LWIP_IPV6 && LWIP_IPV6_MLD/** This function could be called to add or delete an entry in the IPv6 multicastfilter table of the ethernet MAC. */netif_mld_mac_filter_fn mld_mac_filter;
#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
#if LWIP_NETIF_USE_HINTSstruct netif_hint *hints;
#endif /* LWIP_NETIF_USE_HINTS */
#if ENABLE_LOOPBACK/* List of packets to be queued for ourselves. */struct pbuf *loop_first;struct pbuf *loop_last;
#if LWIP_LOOPBACK_MAX_PBUFSu16_t loop_cnt_current;
#endif /* LWIP_LOOPBACK_MAX_PBUFS */
#endif /* ENABLE_LOOPBACK */
};

netif 使用

将网卡定义一个 netif结构体变量 struct netif gnetif,然后要把网卡挂载到 netif_list链表上才能够使用。
netif_add()函数,把网卡进行挂载。

struct netif *netif_add(struct netif *netif,
#if LWIP_IPV4const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
#endif /* LWIP_IPV4 */void *state, netif_init_fn init, netif_input_fn input)
{LWIP_ASSERT_CORE_LOCKED();LWIP_ERROR("netif_add: invalid netif", netif != NULL, return NULL);LWIP_ERROR("netif_add: No init function given", init != NULL, return NULL);#if LWIP_IPV4if (ipaddr == NULL) {ipaddr = ip_2_ip4(IP4_ADDR_ANY);}if (netmask == NULL) {netmask = ip_2_ip4(IP4_ADDR_ANY);}if (gw == NULL) {gw = ip_2_ip4(IP4_ADDR_ANY);}/* reset new interface configuration state */   第一步 地址清零ip_addr_set_zero_ip4(&netif->ip_addr);ip_addr_set_zero_ip4(&netif->netmask);ip_addr_set_zero_ip4(&netif->gw);netif->output = netif_null_output_ip4;
#endif /* LWIP_IPV4 */NETIF_SET_CHECKSUM_CTRL(netif, NETIF_CHECKSUM_ENABLE_ALL);netif->mtu = 0;netif->flags = 0;
#ifdef netif_get_client_datamemset(netif->client_data, 0, sizeof(netif->client_data));
#endif /* LWIP_NUM_NETIF_CLIENT_DATA */#if LWIP_NETIF_STATUS_CALLBACKnetif->status_callback = NULL;
#endif /* LWIP_NETIF_STATUS_CALLBACK */
#if LWIP_NETIF_LINK_CALLBACKnetif->link_callback = NULL;
#endif /* LWIP_NETIF_LINK_CALLBACK */
#if LWIP_IGMPnetif->igmp_mac_filter = NULL;
#endif /* LWIP_IGMP */#if ENABLE_LOOPBACKnetif->loop_first = NULL;netif->loop_last = NULL;
#endif /* ENABLE_LOOPBACK *//* remember netif specific state information data */netif->state = state;           第二步根据传递进来的参数填写网卡 state、input 等字段的相关信息netif->num = netif_num;netif->input = input;NETIF_RESET_HINTS(netif);
#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFSnetif->loop_cnt_current = 0;
#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */#if LWIP_IPV4netif_set_addr(netif, ipaddr, netmask, gw); 第三步调用网卡设置函数 netif_set_addr() 设置网卡 IP 地址、子网掩码、网关等信息
#endif /* LWIP_IPV4 *//* call user specified initialization function for netif */if (init(netif) != ERR_OK) {  第四步通过传递进来的回调函数 init() 进行网卡真正的初始化操作,所以该函数是由用户实现的return NULL;}{struct netif *netif2;int num_netifs;do {if (netif->num == 255) {netif->num = 0;}num_netifs = 0;for (netif2 = netif_list; netif2 != NULL; netif2 = netif2->next) {LWIP_ASSERT("netif already added", netif2 != netif);num_netifs++;LWIP_ASSERT("too many netifs, max. supported number is 255", num_netifs <= 255);if (netif2->num == netif->num) {netif->num++;break;}}} while (netif2 != NULL);}if (netif->num == 254) {netif_num = 0;} else {netif_num = (u8_t)(netif->num + 1); 第5步初始化网卡成功,则遍历当前设备拥有多少个网卡,并为当前网卡分配维一标识 num}/* add this netif to the list */netif->next = netif_list;  第6步将当前网卡插入 netif_list 链表中。netif_list = netif;
#endif /* "LWIP_SINGLE_NETIF */mib2_netif_added(netif);#if LWIP_IGMP/* start IGMP processing */if (netif->flags & NETIF_FLAG_IGMP) {igmp_start(netif);}
#endif /* LWIP_IGMP */LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP",netif->name[0], netif->name[1]));
#if LWIP_IPV4LWIP_DEBUGF(NETIF_DEBUG, (" addr "));ip4_addr_debug_print(NETIF_DEBUG, ipaddr);LWIP_DEBUGF(NETIF_DEBUG, (" netmask "));ip4_addr_debug_print(NETIF_DEBUG, netmask);LWIP_DEBUGF(NETIF_DEBUG, (" gw "));ip4_addr_debug_print(NETIF_DEBUG, gw);
#endif /* LWIP_IPV4 */LWIP_DEBUGF(NETIF_DEBUG, ("\n"));netif_invoke_ext_callback(netif, LWIP_NSC_NETIF_ADDED, NULL);return netif;
}

与netif相关的底层函数

在ethernetifi.c中常用的几个底层函数

err_t ethernetif_init(struct netif *netif);

ethernetif_init() 函数是在上层管理网卡 netif 的到时候会被调用的函数,如使用 netif_add() 添加网卡的时候,就会调用 ethernetif_init() 函数对网卡进行初始化,其实该函数的最终调用的初始化函数就是low_level_init() 函数

static void low_level_init(struct netif *netif);

网卡初始化话函数,它主要完成网卡的复位及参数初始化,根据实际的网卡属性
进行配置 netif 中与网卡相关的字段,例如网卡的 MAC 地址、长度,最大发送单元等。调用我们自己实现的以太网驱动初始化函数 Bsp_Eth_Init(),这是根据网卡的驱动所编写的函数,不同的网卡是不一样的,由用户实现,
初始化完成就需要启动网卡,才能进行数据的收发操作。

static err_t low_level_output(struct netif *netif, struct pbuf *p);

函数为网卡的发送函数,它主要将内核的数据包发送出去,数据包采用 pbuf 数据结构进行描述

static struct pbuf * low_level_input(struct netif *netif);

函数为网卡的数据接收函数,该函数会接收一个数据包,为了内核易于对数据包的管理,该函数必须将接收的数据封装成 pbuf 的形式。

LWIP 数据包

  利用pbuf来描述数据包,这个结构体在pbuf.h中。

struct pbuf {struct pbuf *next; //指向下一个 pbuf 结构体,可以构成链表void *payload; //指向该 pbuf 真正的数据区u16_t tot_len; //当前 pbuf 和链表中后面所有 pbuf 的数据长度,它们属于一个数据包u16_t len; //当前 pbuf 的数据长度u8_t type; //当前 pbuf 的类型u8_t flags; //状态为,保留u16_t ref; //该 pbuf 被引用的次数
};

  next:指向下一个 pbuf 结构体,每个 pbuf 能存放的数据有限,如果应用有大量的数据的话,我们就需要多个 pbuf 来存放,我们将同一个数据包的 pbuf 连接在一起形成一个链表,那么 next字段就是实现这个链表的关键。
  payload:指向该 pbuf 的数据存储区的首地址,STM32F407 内部网络模块接收到数据,并将数据提交给 LWIP 的时候,就是将数据存储在 payload 指定的存储区中。同样在发送数据的时候将 payload 所指向的存储区数据转给 STM32F407 的网络模块去发送。
  tot_len:我们在接收或发送数据的时候数据会存放在 pbuf 链表中,tot_len 字段就表示当前pbuf 和链表中以后所有 pbuf 总的数据长度。
  len:当前 pbuf 总数据的长度
  type:当前 pbuf 类型,一共有四种:PBUF_RAMPBUF_ROMPBUF_REFPBUF_POOL
  flag:保留位
  ref:该 pbuf 被引用的次数,当还有其他指针指向这个 pbuf 的时候 ref 字段就加一。

LWIP网络接口管理

  在 LWIP 中对于网络接口的描述是通过一个 netif 结构体完成的,netif 结构体在netif.h 文件中有定义。

struct netif { struct netif *next; //指向下过一个netif 结构体 ip_addr_t ip_addr; //网络接口IP 地址 ip_addr_t netmask; //子网掩码 ip_addr_t gw; //默认网关 netif_input_fn input; //IP 层接收数据函数 netif_output_fn output; //IP 层发送数据包调用 netif_linkoutput_fn linkoutput; //底层数据包发送 void *state; //设备的状态信息 u16_t mtu; //该网络接口最大允许传输的数据长度 u8_t hwaddr_len; //物理地址长度 u8_t hwaddr[NETIF_MAX_HWADDR_LEN]; //该网络接口的物理地址 u8_t flags; //该网络接口的状态和属性 char name[2]; //该网络接口的名字 u8_t num; //该网络接口的编号
}

添加中间文件

  LWIP 文件夹可以发现有一个 arch 文件夹,在 arch文件中有 5 个文件 cc.h、cpu.h、perf.h、sys_arch.hsys_arch.c
  cc.h :主要完成协议内部使用的数据类型的定义
  cpu.h :用来定义CPU 的大小端模式,因为STM32 是小端模式,因此这里定义BYTE_ORDER=LITTLE_ENDLAN//小端模式
  perf.h :是和系统测量与统计相关的文件,我们不使用任何的测量和统计
  sys_arch.h :是在使用操作系统的时候才使用到的文件,获取时间的函数 sys_now(),为LWIP提供时钟。
  sys_arch.c :用来定义CPU 的大小端模式,因为STM32 是小端模式,因此这里定义BYTE_ORDER=LITTLE_ENDLAN//小端模式

开始移植

硬件 STM32F407VET6 PHY是DP83848(外接50Mhz)用RMII模式
REF_CLK: 参考时钟
TX_EN:发送使能
TXD[1:0]:发送数据
RXD[1:0]:接收数据
CRS_DV:载波监听,接收数据有效
RX_ED:接收错误(可选)
MDC:周期性时钟,提供以最大频率 2.5 MHz 传输数据时的参考时序,在空闲 状态下, SMI 管理接口将 MDC 时钟信号驱动为低电平。
MDIO:数据输入/输出比特流,用于通过 MDC 时钟信号向/从 PHY 设备同步传输状态 信息。
ETH_INT:这个引脚自己写的

在Cubemx中选用ETH


LWIP学习 (1) LWIP简介相关推荐

  1. 【LWIP】LWIP协议|相关知识汇总|LWIP学习笔记

    这里作为一个汇总帖把,把以前写过的LWIP相关的博客文章汇总到一起,方便自己这边查找一些资料. 收录于: [LWIP]LWIP协议|相关知识汇总|LWIP学习笔记 LWIP协议 [LWIP]LWIP网 ...

  2. LwIP学习笔记——STM32 ENC28J60移植与入门

    0.前言 去年(2013年)的整理了LwIP相关代码,并在STM32上"裸奔"成功.一直没有时间深入整理,在这里借博文整理总结.LwIP的移植过程细节很多,博文也不可能一一详解个别 ...

  3. .NetCore微服务Surging新手傻瓜式 入门教程 学习日志---结构简介(二)

    .NetCore微服务Surging新手傻瓜式 入门教程 学习日志---结构简介(二) 原文:.NetCore微服务Surging新手傻瓜式 入门教程 学习日志---结构简介(二) 先上项目解决方案图 ...

  4. DL:深度学习(神经网络)的简介、基础知识(神经元/感知机、训练策略、预测原理)、算法分类、经典案例应用之详细攻略

    DL:深度学习(神经网络)的简介.基础知识(神经元/感知机.训练策略.预测原理).算法分类.经典案例应用之详细攻略 目录 深度学习(神经网络)的简介 1.深度学习浪潮兴起的三大因素 深度学习(神经网络 ...

  5. 深度学习及TensorFlow简介

    深度学习及TensorFlow简介 深度学习目前已经被应用到图像识别,语音识别,自然语言处理,机器翻译等场景并取得了很好的行业应用效果.至今已有数种深度学习框架,如TensorFlow.Caffe.T ...

  6. 国内外学习科学研究机构简介…

    原文地址:国内外学习科学研究机构简介(排名不分先后)作者:ET_郭光武 学习能力被称为是21世纪人类最基本的生存能力.有关"学习"的研究逐渐得到世界各国的重视,"学习&q ...

  7. 深圳Python培训学习:Python3 简介--[千锋]

    深圳Python培训学习:Python3 简介–[千锋] Python 是一个高层次的结合了解释性.编译性.互动性和面向对象的脚本语言. Python 的设计具有很强的可读性,相比其他语言经常使用英文 ...

  8. RISC-V学习笔记【简介】

    本学习笔记参考<手把手教你设计CPU--RISC-V处理器>一书,该书出版日期为2018年,可能部分内容已经过时,仅作为学习用途 RISC-V简介 RISC-V的设计理念就是"简 ...

  9. OptiX资料学习笔记1——简介

    OptiX资料学习笔记1--简介 OptiX引擎的现状 目前有三种开源的API支持NVIDIA的光线追踪功能,分别为: DirectX Raytracing (DXR) DX的光线追踪API Vulk ...

最新文章

  1. java 字符串大小比较
  2. Quartz.Net分布式任务管理平台
  3. vue-cli安装步骤
  4. 当不同公司的产品经理在一块聊天,会聊什么?
  5. 为什么Nginx的性能要比Apache高得多?
  6. OpenVSLAM:日本先进工业科技研究所新开源视觉SLAM框架
  7. 快速恢复检测 恢复 故障服务器方法
  8. 将Sublime Text 2配置为C#代码编辑器(附配置文件)
  9. python工资高还是java-深圳python工资高还是java
  10. 学习Unix其实就这样简单
  11. 在串口调试助手上使用AT命名控制GPRS模块发送短信
  12. throw在java用法_throw()使用小结
  13. CorelDRAW VBA - 发布(导出)PDF文档
  14. 一文彻底解决An error occurred while creating the AVD. See idea.log for details问题
  15. 一本通 1273:货币系统
  16. discuz X程序目录和文件列表 详细中文说明
  17. tushare接口get_realtime_quotes报错:AssertionError: 33 columns passed, passed data had 34 columns
  18. 沉浸式WebXR开发技术架构
  19. 记swagger离线文档乱码解决
  20. 一道输出超限nnnn次的题

热门文章

  1. 使用document解析xml文件
  2. 【Vuforia】制作简单的AR--demo(有卡识别)
  3. 2012/5/9武汉佰钧成
  4. 【附源码】Python计算机毕业设计软件学院社团管理系统
  5. windows下安装mingw-w64
  6. HTML+JS+CSS+xml快速入门
  7. 绿色专利数据集 1985-2020年上市公司绿色专利申请获得授权量数据 2011-2019地级市绿色专利数据
  8. 三步跳过wegame登录
  9. Android APP安全之APK完整性校验
  10. Shell语言(一)