i.MX - RT1052 LwIP移植

写在前面:

本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。

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]TXENTXER
  • 接收端信号:RXCLKRXD[0-3]RXDVRXERCRSCOL
  • 配置信号:MDIOMDC

信号方向如下图所示,其中 TXER 为选配。MII 共计 18 根信号线,只有 MDIO/MDC 信号可以在不同 PHY 间级联。假定系统中有 8 个 PHY,则 MII 信号总数为 8 * 16 + 2 = 130 根!

因此为减少信号数,RMII 接口应运而生(下一个点有说)。

在这里插入图片描述

Transmitter signals:

Signal nameDescriptionDirection
TX_CLKTransmit clockPHY to MAC
TXD0Transmit data bit 0 (transmitted first)MAC to PHY
TXD1Transmit data bit 1MAC to PHY
TXD2Transmit data bit 2MAC to PHY
TXD3Transmit data bit 3MAC to PHY
TX_ENTransmit enableMAC to PHY
TX_ERTransmit error (optional)MAC to PHY

Receiver signals:

Signal nameDescriptionDirection
RX_CLKReceive clockPHY to MAC
RXD0Receive data bit 0 (received first)PHY to MAC
RXD1Receive data bit 1PHY to MAC
RXD2Receive data bit 2PHY to MAC
RXD3Receive data bit 3PHY to MAC
RX_DVReceive data validPHY to MAC
RX_ERReceive errorPHY to MAC
CRSCarrier sensePHY to MAC
COLCollision detectPHY to MAC

Management signals:

Signal nameDescriptionDirection
MDIOManagement dataBidirectional
MDCManagement data clockMAC to PHY

RMII: Reduced Media Independent Interface

相比于MII接口,RMII有以下四处变化:

  • TXCLKRXCLK 两个时钟信号,合并为一个时钟 REFCLK
  • 时钟速率由 25MHz 上升到 50MHz,单向数据由 4 bits 变为 2 bits
  • CRSRXDV 合并为一个信号 CRSDV
  • 取消了 COL 信号

RMII信号如下图所示。RMII只要 9 根信号线,相比于MII的 18 根信号可谓有不少的删减,在同一个系统中的多个设备可以共享 MDIO, MDCREFCLK 信号线,每个端口仅留 67 个引脚。

在这里插入图片描述

RMII signals:

Signal nameDescriptionDirection
REF_CLKContinuous 50 MHz reference clockReference clock may be an input on both devices from an external clock source, or may be driven from the MAC to the PHY
TXD0Transmit data bit 0 (transmitted first)MAC to PHY
TXD1Transmit data bit 1MAC to PHY
TX_ENWhen high, clock data on TXD0 and TXD1 to the transmitterMAC to PHY
RXD0Receive data bit 0 (received first)PHY to MAC
RXD1Receive data bit 1PHY to MAC
CRS_DVCarrier 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_ERReceive error (optional on switches)PHY to MAC
MDIOManagement dataBidirectional
MDCManagement 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\archcc.h、perf.h、sys_arch.h 的内容复制过来或者直接替换。

2、重写 sys_arch.c 文件。同样的,在带有 Lwip middleware的官方 SDK文件中 ...\SDK_2.8.0_MIMXRT1052xxxxB module\middleware\lwip\portsys_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_PRIORITYENET_1588_PRIORITY 定义的中断等级大小必须在 FreeRTOS配置的 configMAX_SYSCALL_INTERRUPT_PRIORITYconfigKERNEL_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\freertoslwip_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/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值