我这里移植的lwip,都是通过STM32CubeMX直接生成,同时含有FreeRTOS系统。以下是对CubeMX生成的文件直接描述。

1 架构描述

关于源代码文件分为两部分:

  1. 中间层文件,跟STM32和PHY芯片有关。STM32芯片不同,PHY芯片不同,这部分文件都会变化。

  2. lwip源文件,不进行修改部分,这部分是直接复制lwip。

alt text

中间层文件:

  • lwip.cMX_LWIP_Init 初始化函数,tcp_init, netif_add,网卡连接状态线程创建。

  • lan8742.c:PHY芯片寄存器读写函数,设置自动协商,获取网口连接状态,开启关闭PHY中断。

  • ethernetif.c:所有跟STM32 ETH有关的函数,ETH初始化,ETH的读和写,ETH中断。

alt text

主要是 netif.c,这是有关网口描述符定义和相关函数的文件,STM32 ETH的读和写都会在这里赋值给网口描述符对象,从而使用lwip库。

1.1 netif 结构体

  1. netif 是网口的抽象描述符。netif 结构体是在 netif.c 文件中定义的。netif 描述一个网络接口 network interface(网卡)。

  2. 单网卡只需要一个netif即可,lwip也实现了多个netif单链表连接的结构,以实现多网卡功能。netif.c 定义了 struct netif *netif_list


struct netif {

    /* 指向下一块网卡 */
    struct netif *next;

    /* 该网口的ip地址、子网掩码、默认网关配置 */
    ip_addr_t ip_addr;
    ip_addr_t netmask;
    ip_addr_t gw;

    /* 网卡数据的输入(接收),实际指向的是 tcpip_input */
    netif_input_fn input;

    /* 把网络层数据,包装成链路层数据,负责IP->MAC的转换(ARP),实际指向的是 etharp_output */
    netif_output_fn output;
    
    /* 网卡数据底层(物理层)发送,实际指向的是 low_level_output  */
    netif_linkoutput_fn linkoutput;

    /* 当网络接口的 "IP 配置状态" 发生变化时触发。通常打印新IP地址,在界面上更新网络连接信息 */
    netif_status_callback_fn status_callback;

    /* 当网络接口的 "物理链路状态" 发生变化时触发:网线插拔,PHY 链路状态改变(Link Up / Down) */
    netif_status_callback_fn link_callback;

    /**
     * 表示网络接口的主机名(hostname),主要用于:
     * DHCP 协议中的 DHCP Hostname 选项(Option 12)
     * 有些路由器在 DHCP 客户端连接时,会显示主机名
    */
    const char*  hostname;

    /**
     * 是 LwIP 内部用于标识网络接口的两个字符的“接口简名”
     * 配合接口号(num)形成接口标识,例如:en0、st1、et0、lw0 等
    */
    char name[2];

    /* 如果你设备上有多个网络接口(如多个以太网/WiFi/PPP),每个接口会有一个唯一的 num 值(从 0 开始递增)。 */
    u8_t num;  /* netif_num赋值给num, netif_num 由 netif_add 函数进行加一,每调用一次 netif_num 加一 */
}

netif 相关函数

netif_init() /* 网卡抽象描述符(netif)初始化 */
netif_add() /* 添加一个虚拟网卡(netif) */
netif_remove() /* 移除一个虚拟网卡(netif) */
netif_set_default() /* 设置默认虚拟网卡(用于未知的数据包) */

1.2 lwip.c

调用所有相关功能进行初始化

alt text

1.3 ethernetif.c

alt text

1.4 lan8742.c

  1. LAN8742_Init初始化,并不是初始化PHY芯片的,是对PHY对象(lan8742_Objct_t *pObj)的地址号进行赋值。

  2. 实现了PHY读写寄存器相关函数,自动协商,开启和关闭PHY中断函数。

alt text

2 lwip自动创建的3个线程

alt text

该线程周期性检测以太网物理链路状态,并在网线插拔时动态配置 MAC 和更新 LwIP 网络接口的上下线状态。

确保网络断开或重新连接后能自动响应。

2.2 ethernetif_input

alt text

  1. STM32以太网中断,发出 RxPktSemaphore 信号量

  2. ethernetif_input 循环等待该RxPktSemaphore信号量可用,调用 tcpip_input 函数,把以太网数据放入 tcpip_mbox 邮箱,可以让 tcpip_thread 处理以太网输入数据。

2.3 tcpip_thread

alt text

以太网数据读取流程

alt text

3 以太网问题分析和移植总结

  1. PHY芯片复位问题,如果PHY模块没有复位引脚或者复位后,需要等待一定时间,判断PHY是否复位成功。

    例如:我使用的LAN8720芯片,没有复位引脚,复位后,LANxxx_Init直接读取phy地址,可以就会失败。

  2. PHY芯片地址。由于PHY芯片寄存器可能不一样,地址可能读取失败。

    alt text

  3. MPU配置问题,STM32H7系列,以太网DMA描述符和lwip内存池的SRAM空间需要配置。否则会有内存问题。

  4. FreeRTOS默认任务会进行LWIP初始化,所以任务栈大小需要设置为大一些,比如 2048

2 PHY芯片之DM9161

汉泰科兴项目中使用的是该芯片,下面是该芯片电路图:

alt text

关于驱动PHY芯片的经验总结:
  1. CubeMX生成的 lan7842.c 中,LAN8742_Init 初始化函数可能无法找到正确的PHY设备地址,可以直接指定PHY地址。(因为两款芯片读取PHY地址寄存器不一样)

    alt text

3 DM9000

参考

参考1: LWIP学习笔记——网络接口

参考2:使用 LwIP TCP/IP 栈,在 STM32Cube 上开发应用

参考3:ALPHA开发板上PHY网络芯片LAN8720:常用的几个寄存器功能