写在前面:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
LwIP是 TCP / IP协议套件的轻量级实现,该套件最初是由瑞典计算机科学研究所的计算机和网络体系结构(CNA)实验室的 Adam Dunkels编写的,但现在正由一组来自世界各地的开发者积极推进开发。
LwIP TCP / IP实现的重点是减少 RAM使用量,同时仍具有完整的 TCP。 这使 LwIP适用于具有数十 KB的可用 RAM且可容纳约 40KB的代码 ROM的嵌入式系统。
LwIP主页:http://savannah.nongnu.org/projects/lwip/
LwIP WiKi:https://lwip.fandom.com/wiki/LwIP_Wiki
目录
ENET MAC 信号引脚定义:
-
ENET_MDC:数据时钟信号。为 PHY提供时序参考,以实现 MDIO 信号上的数据传输
MII / RMII 有该线。
-
ENET_MDIO:数据管理信号。在外部 PHY 和媒体访问控制器之间传输控制信息。数据是与 MDC 同步。此信号是复位后的输入。
MII / RMII 有该线。
-
ENET_TX_DATA0:串行输出,以太网发送数据位(最先传输)。仅在 TX_EN 断言期间有效。
MII / RMII 有该线。
-
ENET_TX_DATA1:串行输出,以太网发送数据位。仅在 TX_EN 断言期间有效。
MII / RMII 有该线。
-
ENET_TX_DATA2:串行输出,以太网发送数据位。仅在 TX_EN 断言期间有效。
MII 有该线。
-
ENET_TX_DATA3:串行输出,以太网发送数据位。仅在 TX_EN 断言期间有效。
MII 有该线。
-
ENET_TX_ER:传输错误(可选)。在断言一个或多个时钟周期的同时也断言 TX_EN,PHY 发送一个或多个非法符号。
MII / RMII 有该线。
-
ENET_TX_EN:传输使能信号。此信号必需与数据前导符的起始位同步出现,并在传输完毕前一直保持。
MII / RMII 有该线。
-
ENET_TX_CLK:在MII 模式下的传输时钟。为 TX_EN、TX_DATA[3:0] 和 TX_ER 提供时序参考。
MII 有该线。
-
ENET_RX_DATA0:以太网接收数据位(最先传输)。当 RX_EN 被断言时,从 PHY 传输到 MAC 控制器。
MII / RMII 有该线。
-
ENET_RX_DATA1:以太网接收数据位。当 RX_EN 被断言时,从 PHY 传输到 MAC 控制器。
MII / RMII 有该线。
-
ENET_RX_DATA2:以太网接收数据位。当 RX_EN 被断言时,从 PHY 传输到 MAC 控制器。
MII 有该线。
-
ENET_RX_DATA3:以太网接收数据位。当 RX_EN 被断言时,从 PHY 传输到 MAC 控制器。
MII 有该线。
-
ENET_RX_ER:接收错误。当用 RX_DV 置位时,表明 PHY 在当前帧中检测到错误。
MII / RMI 有该线。
-
ENET_RX_EN:接收使能信号。断言此输入表示 PHY 在 MII 上存在有效的半字节。从帧的第一个恢复的半字节到最后的半字节,RX_EN 必须保持有效。断言 RX_EN 必须不迟于 SFD 开始并且排除任何 EOF。在 RMII 模式下,此引脚还生成 CRS 信号。
MII / RMI 有该线。
-
ENET_RX_CLK:在 MII 模式下的接收时钟。为 RX_EN、RX_DATA[3:0] 和 RX_ER 提供时序参考。
MII 有该线。
-
ENET_CRS:载波检测。置为有效时,指示发送或接收介质不空闲。 在 RMII 模式下,该信号存在于 RMII_CRS_DV 引脚上。
MII 有该线。
-
ENET_COL:冲突检测。在检测到冲突时断言,并在冲突持续时保持断言。未为全双工模式定义此信号。
MII 有该线。
-
ENET_REF_CLK:在 RMII 模式下的参考时钟。该信号是接收、发送和控制接口的参考时钟。
RMII 有该线。 -
ENET_1588EVENT0_OUT:捕获/比较块输入/输出事件总线信号。当配置为捕获并检测到上升沿时,当前计时器值被锁定并传输到相应的ENET-TCCRN 登记簿软件。当配置为比较时,对应的当计时器达到中编程的比较值注册ENET。中断或DMA 请求可以是如果设置了ENET TCSRN[TIE]或ENET TCSRN[TDRE]中的相应位,则触发。
ENET_1588_EVENT0_OUT 有一个可编程的输出宽度,见 IOMUXC_GPR0[CLK_STRETCH],相对于所有其他事件输出信号延迟一个时钟周期。MII / RMII 有该线。
-
ENET_1588EVENT0_IN:捕获/比较块输入/输出事件总线信号。当配置为捕获并检测到上升沿时,当前计时器值被锁定并传输到相应的ENET-TCCRN 登记簿软件。当配置为比较时,对应的当定时器达到寄存器中编程的比较值ENET_TCCRn。中断或DMA请求可以是如果ENET TCSRN[TIE]中的对应位触发或设置了ENET TCSRN[TDRE]。
MII / RMII 有该线。
-
ENET_1588EVENT1_OUT:捕获/比较块输入/输出事件总线信号。当配置为捕获时,上升沿是检测到时,当前定时器值被锁定并传输到相应的ENET_TCRN 寄存器中,以便软件检查。当配置为比较时,对应的信号1588_事件被断言为1 计时器达到比较值时循环在寄存器ENET 中编程。如果ENET TCSRN[TIE]或ENET U TCSRN[TDRE]已设置。
MII / RMII 有该线。
-
ENET_1588EVENT1_IN:捕获/比较块输入/输出事件总线信号。当被配置为捕捉并检测到上升沿时,当前定时器值被锁定并传送到相应的ENET_TCRN 寄存器中以供软件检查。当配置为比较时,为一个计时器达到比较值时循环在寄存器ENET 中编程。安如果ENET TCSRN[TIE]或ENET U TCSRN[TDRE]已设置。
MII / RMII 有该线。
-
ENET_1588EVENT2_OUT:捕获/比较块输入/输出事件总线信号。当被配置为捕捉并检测到上升沿时,当前定时器值被锁定并传送到相应的ENET_TCRN 寄存器中以供软件检查。当配置为比较时,当定时器达到在寄存器ENET_TCRN 中编程的比较值时,对应的信号1588_EVENT 被断言一个周期。如果设置了ENET TCSRN[TIE]或ENETTCSRN[TDRE]中的相应位,则可以触发中断或DMA 请求。
MII / RMII 有该线。
-
ENET_1588EVENT2_IN:捕获/比较块输入/输出事件总线信号。当被配置为捕捉并检测到上升沿时,当前定时器值被锁定并传送到相应的ENET_TCRN 寄存器中以供软件检查。当配置为比较时,当定时器达到在寄存器ENET_TCRN 中编程的比较值时,对应的信号1588_EVENT 被断言一个周期。如果设置了ENET TCSRN[TIE]或ENETTCSRN[TDRE]中的相应位,则可以触发中断或DMA 请求。
MII / RMII 有该线。
-
ENET_1588EVENT3_OUT:捕获/比较块输入/输出事件总线信号。当被配置为捕捉并检测到上升沿时,当前定时器值被锁定并传送到相应的ENET_TCRN 寄存器中以供软件检查。当配置为比较时,当定时器达到在寄存器ENET_TCRN 中编程的比较值时,对应的信号1588_EVENT 被断言一个周期。如果设置了ENET TCSRN[TIE]或ENETTCSRN[TDRE]中的相应位,则可以触发中断或DMA 请求。
MII / RMII 有该线。
-
ENET_1588EVENT3_IN:捕获/比较块输入/输出事件总线信号。当配置为捕获并检测到上升沿时,当前计时器值被锁存并传送到相应的ENET TCCRN 寄存器中,以供软件检查。当配置为比较时,当定时器达到在寄存器ENET_TCRN 中编程的比较值时,对应的信号1588_EVENT 被断言一个周期。如果设置了ENET TCSRN[TIE]或ENETTCSRN[TDRE]中的相应位,则可以触发中断或DMA 请求。
MII / RMII 有该线。
网络接口:
MII: Media Independent Interface
MII接口信号包括三类,分别为:
- 发送端信号:TXCLK,***TXD[0-***3],TXEN,TXER
- 接收端信号:RXCLK,RXD[0-3],RXDV,RXER,CRS,COL
- 配置信号:MDIO,MDC
信号方向如下图所示,其中 TXER 为选配。MII 共计 18 根信号线,只有 MDIO/MDC 信号可以在不同 PHY 间级联。假定系统中有 8 个 PHY,则 MII 信号总数为 8 * 16 + 2 = 130 根!
因此为减少信号数,RMII 接口应运而生(下一个点有说)。
Transmitter signals:
Signal name | Description | Direction |
---|---|---|
TX_CLK | Transmit clock | PHY to MAC |
TXD0 | Transmit data bit 0 (transmitted first) | MAC to PHY |
TXD1 | Transmit data bit 1 | MAC to PHY |
TXD2 | Transmit data bit 2 | MAC to PHY |
TXD3 | Transmit data bit 3 | MAC to PHY |
TX_EN | Transmit enable | MAC to PHY |
TX_ER | Transmit error (optional) | MAC to PHY |
Receiver signals:
Signal name | Description | Direction |
---|---|---|
RX_CLK | Receive clock | PHY to MAC |
RXD0 | Receive data bit 0 (received first) | PHY to MAC |
RXD1 | Receive data bit 1 | PHY to MAC |
RXD2 | Receive data bit 2 | PHY to MAC |
RXD3 | Receive data bit 3 | PHY to MAC |
RX_DV | Receive data valid | PHY to MAC |
RX_ER | Receive error | PHY to MAC |
CRS | Carrier sense | PHY to MAC |
COL | Collision detect | PHY to MAC |
Management signals:
Signal name | Description | Direction |
---|---|---|
MDIO | Management data | Bidirectional |
MDC | Management data clock | MAC to PHY |
RMII: Reduced Media Independent Interface
相比于MII接口,RMII有以下四处变化:
- TXCLK 和 RXCLK 两个时钟信号,合并为一个时钟 REFCLK
- 时钟速率由 25MHz 上升到 50MHz,单向数据由 4 bits 变为 2 bits
- CRS 和 RXDV 合并为一个信号 CRSDV
- 取消了 COL 信号
RMII信号如下图所示。RMII只要 9 根信号线,相比于MII的 18 根信号可谓有不少的删减,在同一个系统中的多个设备可以共享 MDIO, MDC 和 REFCLK 信号线,每个端口仅留 6 或 7 个引脚。
RMII signals:
Signal name | Description | Direction |
---|---|---|
REF_CLK | Continuous 50 MHz reference clock | Reference clock may be an input on both devices from an external clock source, or may be driven from the MAC to the PHY |
TXD0 | Transmit data bit 0 (transmitted first) | MAC to PHY |
TXD1 | Transmit data bit 1 | MAC to PHY |
TX_EN | When high, clock data on TXD0 and TXD1 to the transmitter | MAC to PHY |
RXD0 | Receive data bit 0 (received first) | PHY to MAC |
RXD1 | Receive data bit 1 | PHY to MAC |
CRS_DV | Carrier Sense (CRS) and RX_Data Valid (RX_DV) multiplexed on alternate clock cycles. In 10 Mbit/s mode, it alternates every 10 clock cycles. | PHY to MAC |
RX_ER | Receive error (optional on switches) | PHY to MAC |
MDIO | Management data | Bidirectional |
MDC | Management data clock. | MAC to PHY |
更多的接口信息可看:https://en.wikipedia.org/wiki/Media-independent_interface
LwIP移植操作:
基础移植
下载 LwIP源码,这里使用 v2.1.2版本:http://download.savannah.nongnu.org/releases/lwip/
要知道在 LwIP协议套件里是支持 OS操作和裸机操作,一般要用到这种网络协议栈都会使用 OS,因此这里也是以带 OS的为例。
首先,准备移植好带 FreeRTOS的工程模板,然后把下载下来的 LwIP源码放到我们的工程文件夹里面:
另外,还需要下载 contrib文件包,选择 v2.1.0就好了,因为 contrib中包含了一些和平台移植相关的代码,到时候要提取一些代码:
接着再把刚下载的 contrib文件夹里的 ./apps
文件夹也同样移植到工程中的中间件文件夹 LwIP里面,然后得到这样的目录(这里先不用看 port接口文件先,而 test文件夹是一些协议栈内核测试程序,在提取完 lwipopts.h
之后就可直接删除了。):
文件分析:
-
当前主目录文件
- CHANGELOG ---- 版本更新记录,从中可以看到 LwIP不同版本的变化
- COPYING ---- 版权说明
- FEATURES ---- LwIP特征 /特点功能说明
- FILES ---- 其中说明了其所在目录下的各目录或文件的用途。在不同的目录下会有不同的该文件
- README ---- 简介文档
- UPGRADING ---- 版本升级后可能出现不兼容,该文档记录了从老版本升级需要修改的地方。对于升级自己使用的 LwIP版本时很有用处。
-
src(核心文件部分)
src ├─api // 高级包装器 API的代码。如果使用低级回调 /原始 API,则不需要。 ├─apps // 用 LwIP低级 /原始 API专门编写的更高级的应用程序。 │ ├─altcp_tls │ ├─http │ │ ├─fs │ │ │ └─img │ │ └─makefsdata │ ├─lwiperf │ ├─mdns │ ├─mqtt │ ├─netbiosns │ ├─smtp │ ├─snmp │ ├─sntp │ └─tftp ├─core // TPC /IP栈的核心;协议的实现,内存和缓冲区管理,以及底层的原始 API。 │ ├─ipv4 │ └─ipv6 ├─include // lwIP include files. │ ├─compat │ │ ├─posix │ │ │ ├─arpa │ │ │ ├─net │ │ │ └─sys │ │ └─stdc │ ├─lwip │ │ ├─apps │ │ ├─priv │ │ └─prot │ └─netif │ └─ppp │ └─polarssl └─netif // 通用网络接口设备驱动程序保存在这里。 └─ppp └─polarssl
port接口移植
以下部分需要根据相应的底层编写对应的驱动处理文件。
系统处理
1、添加 lwipopts.h
:在源码文件夹 ...\lwip-2.1.2\test\fuzz
下可以找到该文件,该文件主要用于 Lwip参数的配置,供用户修改,部分参数配置依赖于 ...\lwip-2.1.2\src\include\lwip\opt.h
文件。
2、添加 sys_arch.c
:在 contrib文件夹的 ...\contrib-2.1.0\ports\freertos
里可以找到该文件,这个主要用来与操作系统对接的接口源文件。
3、添加对应上面 sys_arch.c
所需的头文件 cc.h、perf.h、sys_arch.h
:通用平台移植,适用于 NO_SYS的两种状态。(将调试映射到 printf,从系统时间提供 sys_now和 co功能等),这些文件可以在 ...\contrib-2.1.0\ports\unix\port\include\arch
中提取出来。
物理层处理
一般来说,要进行网络通讯,从硬件的角度看,以太网接口电路主要由MAC(Media Access Control)控制器和物理层接口PHY(Physical Layer,PHY)两大部分构成,如下图所示;而软件上就是基于硬件上面编写相应的驱动程序:
对于上述的三部分,并不一定都是独立的芯片,根据组合形式,可分为下列几种类型:
-
方案一:CPU集成 MAC与 PHY:
-
方案二:CPU集成MAC,PHY采用独立芯片:
-
方案三:CPU不集成MAC与PHY,MAC与PHY采用集成芯片;
添加 PHY驱动
官方 board使用的物理层驱动芯片:KSZ8081RNB
如果使用的是官方一样的 PHY驱动芯片,那么可以在 \SDK_2.8.0_EVKB-IMXRT1050\components\phy\device\phyksz8081
中找到驱动文件。
添加 MAC驱动
在 rt1052中,芯片上是集成了 MAC驱动,看下图:
因此这部分我们是不用管的,全程交给其内部自行处理。
添加 MDIO处理
从下图可以看出来,MDIO用于处理外部 PHY和媒体访问控制器之间传输控制信息,属于 PHY驱动接口的一部分:
其处理文件可以在 ...\SDK_2.8.0_EVKB-IMXRT1050\components\phy\mdio\enet
找到。
网卡驱动处理
网卡驱动部分比较特别,这些文件需要在包含了 Lwip middleware的官方 SDK文件中找到 .../SDK_2.8.0_MIMXRT1052xxxxB module\middleware\lwip\port
:
然后,移植完成后,port文件结构如下:
port
│ lwipopts.h
│ sys_arch.c
│
├─arch
│ cc.h
│ perf.h
│ sys_arch.h
│
├─ethernetif
│ enet_ethernetif.c
│ enet_ethernetif.h
│ enet_ethernetif_kinetis.c
│ enet_ethernetif_priv.h
│
└─temp
│ lwipopts.h
│ sys_arch.c
│
└─arch
cc.h
perf.h
sys_arch.h
最终添加到工程里面如下:
工程文件说明:
通过上面的移植,这里简单分析一下与 LwIP相关的要操作的文件
-
slipif.c - SLIP组件文件
SLIP即串行链路IP,它提供了一种在串行链路上传送 IP 数据包的函数定义。SLIP 协议比较简单,它只是定义了一系列的字符,以实现对链路上的 IP数据包封装和发送,除此之外,它不提供任何寻址、错误检测、包类型识别机制,因此相关驱动程序的实现也比较简单。它需要一个sio(串行 I / O)模块才能工作。(这里并不需要,所以编译屏蔽掉)
-
port文件夹(注意,需要根据平台进行相应的修改)
这个文件夹中主要是针对 TCP/IP 规约的各种定义。
- sys_arch.c文件 - 一般用在有操作系统的移植上
- cc.h文件 - 包含处理器相关的变量类型、数据结构及字节对齐的相关宏
- perf.h文件 - 与系统统计与测量相关的头文件
-
ethernetif文件夹 - 存放着底层的网卡驱动函数
对应的网卡驱动程序,请根据相应的设备进行移植或修改
-
components\lwip文件夹 - 存放着对应的以太网芯片的底层处理程序
对于不同的 PHY to MAC 芯片,请根据相应的底层驱动进行移植或修改
-
lwipopt.h - 该文件主要用于 Lwip 参数的配置(This file is based on …\src\include\lwip\opt.h)
移植时需要根据自己的需要,进行相关的配置,其默认参数配置在 opt.h 文件中配置
1、重写 cc.h、perf.h、sys_arch.h
文件。其实也不算是重写吧,直接把带有 Lwip middleware的官方 SDK文件中 ...\SDK_2.8.0_MIMXRT1052xxxxB module\middleware\lwip\port\arch
的 cc.h、perf.h、sys_arch.h
的内容复制过来或者直接替换。
2、重写 sys_arch.c
文件。同样的,在带有 Lwip middleware的官方 SDK文件中 ...\SDK_2.8.0_MIMXRT1052xxxxB module\middleware\lwip\port
的 sys_arch.c
的内容复制过来或者直接替换。
3、重写 lwipopts.h
文件。修改为以下内容:
/**
******************************************************************************
* @file lwipopts.h
* This file is based on \src\include\lwip\opt.h
******************************************************************************
* Copyright (c) 2013-2016, Freescale Semiconductor, Inc.
* Copyright 2016-2018 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__
#if USE_RTOS
/**
* SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain
* critical regions during buffer allocation, deallocation and memory
* allocation and deallocation.
*/
#define SYS_LIGHTWEIGHT_PROT 1
/**
* NO_SYS==0: Use RTOS
*/
#define NO_SYS 0
/**
* LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c)
*/
#define LWIP_NETCONN 1
/**
* LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)
*/
#define LWIP_SOCKET 1
/**
* LWIP_SO_RCVTIMEO==1: Enable receive timeout for sockets/netconns and
* SO_RCVTIMEO processing.
*/
#define LWIP_SO_RCVTIMEO 1
#else
/**
* NO_SYS==1: Bare metal lwIP
*/
#define NO_SYS 1
/**
* LWIP_NETCONN==0: Disable Netconn API (require to use api_lib.c)
*/
#define LWIP_NETCONN 0
/**
* LWIP_SOCKET==0: Disable Socket API (require to use sockets.c)
*/
#define LWIP_SOCKET 0
#endif
/* ---------- ENET Priority options ---------- */
/* 不打开则使用默认值,基于 enet_ethernetif.h文件修改
注意 configMAX_SYSCALL_INTERRUPT_PRIORITY配置 */
//#define ENET_PRIORITY 13U
//#define ENET_1588_PRIORITY 12U
/* ---------- Core locking ---------- */
#define LWIP_TCPIP_CORE_LOCKING 1 // ../include/lwip/opt.h文件有默认定义
/* ../include/lwip/tcpip.h文件中约 52行默认有定义 */
#if LWIP_TCPIP_CORE_LOCKING
/* 核心互斥操作 */
void sys_lock_tcpip_core(void);
#define LOCK_TCPIP_CORE() sys_lock_tcpip_core() // 相对的加入了线程保护
void sys_unlock_tcpip_core(void);
#define UNLOCK_TCPIP_CORE() sys_unlock_tcpip_core() // 相对的加入了线程保护
void sys_check_core_locking(void);
#define LWIP_ASSERT_CORE_LOCKED() sys_check_core_locking() // 判定是否线程化,原 #define为空
void sys_mark_tcpip_thread(void);
#define LWIP_MARK_TCPIP_THREAD() sys_mark_tcpip_thread() // 检查核心锁定
#endif /* LWIP_TCPIP_CORE_LOCKING */
/* ---------- Memory options ---------- */
/**
* MEM_ALIGNMENT: should be set to the alignment of the CPU
* 4 byte alignment -> #define MEM_ALIGNMENT 4
* 2 byte alignment -> #define MEM_ALIGNMENT 2
*/
#ifndef MEM_ALIGNMENT
#define MEM_ALIGNMENT 4
#endif
/**
* MEM_SIZE: the size of the heap memory. If the application will send
* a lot of data that needs to be copied, this should be set high.
*/
#ifndef MEM_SIZE
#define MEM_SIZE (22 * 1024)
#endif
/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application
sends a lot of data out of ROM (or other static memory), this
should be set high. */
#ifndef MEMP_NUM_PBUF
#define MEMP_NUM_PBUF 15
#endif
/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
per active UDP "connection". */
#ifndef MEMP_NUM_UDP_PCB
#define MEMP_NUM_UDP_PCB 6
#endif
/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP
connections. */
#ifndef MEMP_NUM_TCP_PCB
#define MEMP_NUM_TCP_PCB 10
#endif
/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP
connections. */
#ifndef MEMP_NUM_TCP_PCB_LISTEN
#define MEMP_NUM_TCP_PCB_LISTEN 6
#endif
/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP
segments. */
#ifndef MEMP_NUM_TCP_SEG
#define MEMP_NUM_TCP_SEG 22
#endif
/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active
timeouts. */
#ifndef MEMP_NUM_SYS_TIMEOUT
#define MEMP_NUM_SYS_TIMEOUT 10
#endif
/* ---------- Pbuf options ---------- */
/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */
#ifndef PBUF_POOL_SIZE
#define PBUF_POOL_SIZE 9
#endif
/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */
/* Default value is defined in lwip\src\include\lwip\opt.h as
* LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_ENCAPSULATION_HLEN+PBUF_LINK_HLEN)*/
/* ---------- TCP options ---------- */
#ifndef LWIP_TCP
#define LWIP_TCP 1
#endif
#ifndef TCP_TTL
#define TCP_TTL 255
#endif
/* Controls if TCP should queue segments that arrive out of
order. Define to 0 if your device is low on memory. */
#ifndef TCP_QUEUE_OOSEQ
#define TCP_QUEUE_OOSEQ 0
#endif
/* TCP Maximum segment size. */
#ifndef TCP_MSS
#define TCP_MSS (1500 - 40) /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */
#endif
/* TCP sender buffer space (bytes). */
#ifndef TCP_SND_BUF
#define TCP_SND_BUF (6 * TCP_MSS) // 2
#endif
/* TCP sender buffer space (pbufs). This must be at least = 2 *
TCP_SND_BUF/TCP_MSS for things to work. */
#ifndef TCP_SND_QUEUELEN
#define TCP_SND_QUEUELEN (3 * TCP_SND_BUF) / TCP_MSS // 6
#endif
/* TCP receive window. */
#ifndef TCP_WND
#define TCP_WND (2 * TCP_MSS)
#endif
/* Enable backlog*/
#ifndef TCP_LISTEN_BACKLOG
#define TCP_LISTEN_BACKLOG 1
#endif
/* ---------- Network Interfaces options ---------- */
/* Support netif api (in netifapi.c). */
#ifndef LWIP_NETIF_API
#define LWIP_NETIF_API 1
#endif
/* ---------- ICMP options ---------- */
#ifndef LWIP_ICMP
#define LWIP_ICMP 1
#endif
/* ---------- RAW options ---------- */
#if !defined LWIP_RAW
#define LWIP_RAW 0
#endif
/* ---------- DHCP options ---------- */
/* Define LWIP_DHCP to 1 if you want DHCP configuration of
interfaces. DHCP is not implemented in lwIP 0.5.1, however, so
turning this on does currently not work. */
#ifndef LWIP_DHCP
#define LWIP_DHCP 0
#endif
/* ---------- UDP options ---------- */
#ifndef LWIP_UDP
#define LWIP_UDP 1
#endif
#ifndef UDP_TTL
#define UDP_TTL 255
#endif
/* ---------- Statistics options ---------- */
#ifndef LWIP_STATS
#define LWIP_STATS 0
#endif
#ifndef LWIP_PROVIDE_ERRNO
#define LWIP_PROVIDE_ERRNO 1
#endif
/* ---------- DNS options ---------- */
#ifndef LWIP_DNS
#define LWIP_DNS 0
#endif
/*
--------------------------------------
---------- Checksum options ----------
--------------------------------------
*/
/*
Some MCU allow computing and verifying the IP, UDP, TCP and ICMP checksums by hardware:
- To use this feature let the following define uncommented.
- To disable it and process by CPU comment the the checksum.
*/
//#define CHECKSUM_BY_HARDWARE
#ifdef CHECKSUM_BY_HARDWARE
/* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/
#define CHECKSUM_GEN_IP 0
/* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/
#define CHECKSUM_GEN_UDP 0
/* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/
#define CHECKSUM_GEN_TCP 0
/* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/
#define CHECKSUM_CHECK_IP 0
/* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/
#define CHECKSUM_CHECK_UDP 0
/* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/
#define CHECKSUM_CHECK_TCP 0
#else
/* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/
#define CHECKSUM_GEN_IP 1
/* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/
#define CHECKSUM_GEN_UDP 1
/* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/
#define CHECKSUM_GEN_TCP 1
/* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/
#define CHECKSUM_CHECK_IP 1
/* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/
#define CHECKSUM_CHECK_UDP 1
/* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/
#define CHECKSUM_CHECK_TCP 1
#endif
/**
* DEFAULT_THREAD_STACKSIZE: The stack size used by any other lwIP thread.
* The stack size value itself is platform-dependent, but is passed to
* sys_thread_new() when the thread is created.
*/
#ifndef DEFAULT_THREAD_STACKSIZE
#define DEFAULT_THREAD_STACKSIZE (6 * 128)
#endif
/**
* DEFAULT_THREAD_PRIO: The priority assigned to any other lwIP thread.
* The priority value itself is platform-dependent, but is passed to
* sys_thread_new() when the thread is created.
*/
#ifndef DEFAULT_THREAD_PRIO
#define DEFAULT_THREAD_PRIO 3
#endif
/*
------------------------------------
---------- Debugging options ----------
------------------------------------
*/
#define LWIP_DEBUG
#ifdef LWIP_DEBUG
#define U8_F "c"
#define S8_F "c"
#define X8_F "02x"
#define U16_F "u"
#define S16_F "d"
#define X16_F "x"
#define U32_F "u"
#define S32_F "d"
#define X32_F "x"
#define SZT_F "u"
#endif
#define TCPIP_MBOX_SIZE 32
#define TCPIP_THREAD_STACKSIZE 1024
#define TCPIP_THREAD_PRIO 8
/**
* DEFAULT_RAW_RECVMBOX_SIZE: The mailbox size for the incoming packets on a
* NETCONN_RAW. The queue size value itself is platform-dependent, but is passed
* to sys_mbox_new() when the recvmbox is created.
*/
#define DEFAULT_RAW_RECVMBOX_SIZE 12
/**
* DEFAULT_UDP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a
* NETCONN_UDP. The queue size value itself is platform-dependent, but is passed
* to sys_mbox_new() when the recvmbox is created.
*/
#define DEFAULT_UDP_RECVMBOX_SIZE 12
/**
* DEFAULT_TCP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a
* NETCONN_TCP. The queue size value itself is platform-dependent, but is passed
* to sys_mbox_new() when the recvmbox is created.
*/
#define DEFAULT_TCP_RECVMBOX_SIZE 12
/**
* DEFAULT_ACCEPTMBOX_SIZE: The mailbox size for the incoming connections.
* The queue size value itself is platform-dependent, but is passed to
* sys_mbox_new() when the acceptmbox is created.
*/
#define DEFAULT_ACCEPTMBOX_SIZE 12
#if (LWIP_DNS || LWIP_IGMP || LWIP_IPV6) && !defined(LWIP_RAND)
/* When using IGMP or IPv6, LWIP_RAND() needs to be defined to a random-function returning an u32_t random value*/
#include "lwip/arch.h"
u32_t lwip_rand(void);
#define LWIP_RAND() lwip_rand()
#endif
#endif /* __LWIPOPTS_H__ */
/*****END OF FILE****/
然后,值得注意的是,上面有写到:
/* 不打开则使用默认值,基于 enet_ethernetif.h文件修改
注意 configMAX_SYSCALL_INTERRUPT_PRIORITY配置 */
//#define ENET_PRIORITY 13U
//#define ENET_1588_PRIORITY 12U
这两个宏是用来配置网卡驱动的中断优先级的,所以,宏 ENET_PRIORITY
和 ENET_1588_PRIORITY
定义的中断等级大小必须在 FreeRTOS配置的 configMAX_SYSCALL_INTERRUPT_PRIORITY
和 configKERNEL_INTERRUPT_PRIORITY
优先级之间,否则就会出现错误,其原因是由于任务中断控制不受 FreeRTOS的管理了,具体的 FreeRTOS配置文件分析可看:FreeRTOS篇章之 FreeRTOSConfig.h分析
TCP回显测试:
复制带有 Lwip middleware的官方 SDK文件中 ...\SDK_2.8.0_MIMXRT1052xxxxB module\middleware\lwip\contrib\apps\tcpecho
的文件以及提取 SDK_2.8.0_MIMXRT1052xxxxB module\boards\evkbimxrt1050\lwip_examples\lwip_tcpecho\freertos
中 lwip_tcpecho_freertos.c
文件中的部分代码。
其中,从 lwip_tcpecho_freertos.c
提取出来的代码变更为:
#include "./ENET/enet.h"
#include "lwip/netifapi.h"
#include "enet_ethernetif.h"
#include "board.h"
#include "fsl_iomuxc.h"
#include "pin_mux.h"
#include "fsl_phyksz8081.h"
#include "fsl_enet_mdio.h"
/* IP address configuration. */
#define configIP_ADDR0 192
#define configIP_ADDR1 168
#define configIP_ADDR2 0
#define configIP_ADDR3 102
/* Netmask configuration. */
#define configNET_MASK0 255
#define configNET_MASK1 255
#define configNET_MASK2 255
#define configNET_MASK3 0
/* Gateway address configuration. */
#define configGW_ADDR0 192
#define configGW_ADDR1 168
#define configGW_ADDR2 0
#define configGW_ADDR3 100
/* MAC address configuration. */
#define configMAC_ADDR \
{ \
0x02, 0x12, 0x13, 0x10, 0x15, 0x11 \
}
/* Address of PHY interface. */
#define EXAMPLE_PHY_ADDRESS BOARD_ENET0_PHY_ADDRESS
/* MDIO operations. */
#define EXAMPLE_MDIO_OPS enet_ops
/* PHY operations. */
#define EXAMPLE_PHY_OPS phyksz8081_ops
/* ENET clock frequency. */
#define EXAMPLE_CLOCK_FREQ CLOCK_GetFreq(kCLOCK_IpgClk)
#ifndef EXAMPLE_NETIF_INIT_FN
/*! @brief Network interface initialization function. */
#define EXAMPLE_NETIF_INIT_FN ethernetif0_init
#endif /* EXAMPLE_NETIF_INIT_FN */
static mdio_handle_t mdioHandle = {.ops = &EXAMPLE_MDIO_OPS};
static phy_handle_t phyHandle = {.phyAddr = EXAMPLE_PHY_ADDRESS, .mdioHandle = &mdioHandle, .ops = &EXAMPLE_PHY_OPS};
static void BOARD_InitModuleClock(void)
{
const clock_enet_pll_config_t config = {.enableClkOutput = true, .enableClkOutput25M = false, .loopDivider = 1};
CLOCK_InitEnetPll(&config);
}
static void delay(void)
{
volatile uint32_t i = 0;
for (i = 0; i < 1000000; ++i)
{
__asm("NOP"); /* delay */
}
}
void User_IP_Config(void)
{
static struct netif netif;
#if defined(FSL_FEATURE_SOC_LPC_ENET_COUNT) && (FSL_FEATURE_SOC_LPC_ENET_COUNT > 0)
static mem_range_t non_dma_memory[] = NON_DMA_MEMORY_ARRAY;
#endif /* FSL_FEATURE_SOC_LPC_ENET_COUNT */
ip4_addr_t netif_ipaddr, netif_netmask, netif_gw;
ethernetif_config_t enet_config = {
.phyHandle = &phyHandle,
.macAddress = configMAC_ADDR,
#if defined(FSL_FEATURE_SOC_LPC_ENET_COUNT) && (FSL_FEATURE_SOC_LPC_ENET_COUNT > 0)
.non_dma_memory = non_dma_memory,
#endif /* FSL_FEATURE_SOC_LPC_ENET_COUNT */
};
gpio_pin_config_t gpio_config = {kGPIO_DigitalOutput, 0, kGPIO_NoIntmode};
BOARD_InitModuleClock();
IOMUXC_EnableMode(IOMUXC_GPR, kIOMUXC_GPR_ENET1TxClkOutputDir, true);
GPIO_PinInit(BOARD_INITPINS_ENET_INT_GPIO, BOARD_INITPINS_ENET_INT_GPIO_PIN, &gpio_config);
GPIO_PinInit(BOARD_INITPINS_ENET_RST_GPIO, BOARD_INITPINS_ENET_RST_GPIO_PIN, &gpio_config);
/* pull up the ENET_INT before RESET. */
GPIO_WritePinOutput(BOARD_INITPINS_ENET_INT_GPIO, BOARD_INITPINS_ENET_INT_GPIO_PIN, 1);
GPIO_WritePinOutput(BOARD_INITPINS_ENET_RST_GPIO, BOARD_INITPINS_ENET_RST_GPIO_PIN, 0);
delay();
GPIO_WritePinOutput(BOARD_INITPINS_ENET_RST_GPIO, BOARD_INITPINS_ENET_RST_GPIO_PIN, 1);
mdioHandle.resource.csrClock_Hz = EXAMPLE_CLOCK_FREQ;
IP4_ADDR(&netif_ipaddr, configIP_ADDR0, configIP_ADDR1, configIP_ADDR2, configIP_ADDR3);
IP4_ADDR(&netif_netmask, configNET_MASK0, configNET_MASK1, configNET_MASK2, configNET_MASK3);
IP4_ADDR(&netif_gw, configGW_ADDR0, configGW_ADDR1, configGW_ADDR2, configGW_ADDR3);
tcpip_init(NULL, NULL);
netifapi_netif_add(&netif, &netif_ipaddr, &netif_netmask, &netif_gw, &enet_config, EXAMPLE_NETIF_INIT_FN,
tcpip_input);
netifapi_netif_set_default(&netif);
netifapi_netif_set_up(&netif);
PRINTF("\r\n************************************************\r\n");
PRINTF(" TCP Echo example\r\n");
PRINTF("************************************************\r\n");
PRINTF(" IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t *)&netif_ipaddr)[0], ((u8_t *)&netif_ipaddr)[1],
((u8_t *)&netif_ipaddr)[2], ((u8_t *)&netif_ipaddr)[3]);
PRINTF(" IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t *)&netif_netmask)[0], ((u8_t *)&netif_netmask)[1],
((u8_t *)&netif_netmask)[2], ((u8_t *)&netif_netmask)[3]);
PRINTF(" IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t *)&netif_gw)[0], ((u8_t *)&netif_gw)[1],
((u8_t *)&netif_gw)[2], ((u8_t *)&netif_gw)[3]);
PRINTF("************************************************\r\n");
}
/*---------------------------- END ----------------------------*/
最后,调用 User_IP_Config();函数和 tcpecho_init();,另外,tcpecho_init()函数其实已经内部定向创建了任务了,所以没必要再单独放到任务中执行,只需要先执行 User_IP_Config();函数再到 tcpecho_init();函数的执行就好了。
参考:
https://blog.csdn.net/ZCShouCSDN/article/details/79229728
https://blog.csdn.net/baidu_18848209/article/details/103956031
https://www.jianshu.com/p/dba15e62bcbf
https://www.cnblogs.com/jason-lu/tag/%E7%BD%91%E5%8F%A3/