在i.MX RT微控制器上初始化LWIP协议栈是一个复杂但有趣的过程,它涉及多个步骤和关键组件的配置。以下是该初始化流程的介绍:
LWIP协议栈与开发平台简介
LWIP(Light Weight IP),是一种轻量化且开源的TCP/IP协议栈。LwIP在有限的RAM和ROM条件下,实现了一个完整的TCP/IP 协议栈,并且LwIP在MCU平台上得到了非常广泛的应用。此外,它既可以基于操作系统运行,也可以在裸机情况下运行。
TCP/IP协议栈的模型结构如下图所示:
本篇文章基于i.MX RT四位数跨界MCU平台,RT四位数跨界MCU最高主频可达1Ghz,并且搭载了很多性能强劲的外设,广泛的应用于工业,自动化,IoT,消费电子等领域,并且NXP官方提供免费的IDE,开发工具以及SDK软件包等,为开发者提供了全面的支持。
以太网接口及PHY管理接口硬件初始化
Ethernet MAC与PHY之间通过以太网接口连接,常见的接口有RMII,MII等。与此同时,RT四位数上的Ethernet MAC外设也配备了PHY管理接口,可通过MDC MDIO来实现PHY相关寄存器的读写。
此处以RT1060举例,在RT1060 EVK上默认使用的是RMII以太网接口,对照原理图完成RMII相关管脚的初始化。
MDC,MDIO初始化:
PHY INIT以及RESET管脚初始化,配成GPIO输出即可:
RMII的TX_CLK由MCU提供,因此要将TX_CLK的方向配置为输出:
至此,以太网接口,PHY管理接口等硬件配置基本初始化完成。
LWIP时基初始化与超时事件注册
在LWIP中,经常会进行一些超时判定,例如ARP缓存表的时间管理,IP分片数据报的重装等待超时等等,并且LwIP也提供了超时事件注册函数sys_timeout,在RT1060官方SDK的ping bm demo中就是通过超时事件来发送ping请求。
而超时的判定需要一个时基,MCU中一般会用系统滴答定时器来作为时基,且时间间隔设置为1ms并开启中断。每一次进中断都会将当前时间加1。设置滴答定时器的代码如下图所示。
在LWIP协议栈初始化时,也需要注册一些超时事件,通过调用sys_timeout函数,该函数中又会调用sys_timeout_abs函数。
在sys_timeout_abs函数中会计算出超时事件即将超时的时间,并且根据超时时间将这些超时事件连接成一个链表,如下图所示。当超时发生时就会调用对应的处理函数。
SDK中会把需要注册的超时事件都放在一个数组中,在初始化时调用注册函数去一个个注册这些超时事件。超时事件数组如下图所示。
LWIP内存堆内存池初始化
在LwIP中,内存分配策略一般有两种,一种是分配固定大小的内存块。如TCP 首部、UDP 首部,IP 首部,以太网首部等都是固定的数据结构,其大小就是一个固定的值,那么我们就能采用这种方式分配这些固定大小的内存空间,这样子的效率就会大大提高。另一种是利用内存堆进行动态分配,属于可变长度的内存块。
在LWIP协议栈初始化时一定要对这两种内存分配方式进行初始化,方便后续协议栈进行相关内存分配。内存堆初始化代码如下所示,其中LWIP_RAM_HEAP_POINTER实际上就是分配的内存堆数组首地址。
内存堆数组大小为想要分配的内存堆大小对齐后再加上两倍的mem结构体对齐后的大小,mem结构体中会存放一些内存堆相关管理信息,宏定义如下图所示。
不难看出在内存堆初始化代码中实际上就是初始化了两个mem结构体。第一个mem结构体在内存堆起始地址处,next成员为MEM_SIZE_ALIGNED, prev和used成员皆初始化为0。
第二个mem结构体ram_end设置为内存堆首地址偏移MEM_SIZE_ALIGNED处,used变量设置为1,next和prev皆指向偏移MEM_SIZE_ALIGNED的位置。
内存池初始化函数为memp_init,如下图所示。它使用轮询的方式调用memp_init_pool去初始化每一类内存池,memp_pools数组中存放了初始化过的memp结构体。
在memp_init_pool中会根据初始化过的memp结构体中的一些参数,比如下图中所示的num,num代表有多少个内存块,memp_init_pool中会根据num将内存块连接成单链表。
网卡挂载及初始化
调用netif_add来挂载网卡,netif结构体是抽象出来的网卡结构体,IP地址,网关,子网掩码等都会保存在该结构体中。网卡初始化函数也会作为参数之一传入netif_add函数,并在netif_add函数中被调用。在该初始化函数中最终会完成以下几部分初始化:
1.以太网相关数据结构的初始化,包括rx_buffer, tx_buffer, buffer descriptor,buffer descriptor ring。初始化这些数据结构,以便在接收发送以太网数据时使用。
2.phy的初始化,初始化以太网外设中的MDC MDIO,如下图所示。
通过MDC MDIO去操作PHY相关的寄存器,例如去配置PHY的百兆千兆模式,软复位PHY,检查自动协商,连接状态以及配置LED等等。确保PHY工作在想要的状态下,部分初始化PHY的代码如下所示。
3.设置netif相关参数,例如MAC地址长度,MTU,flags,以及网络接口层输入,网络接口层输出函数等。
4.初始化Ethernet MAC外设,去配置以太网外设中的接口类型,速度,工作模式,中断等等。部分配置代码如下所示。
LWIP不同API初始化
在完成上述初始化流程之后,还需要调用一些LwIP提供的API,LwIP常用的API有RAW API, Socket API, NETCONN API三种,前者是不需要基于操作系统的,后两者需要基于操作系统运行。这三种API在初始化时也是不同的,Socket API和NETCONN API类似,此处以RT1060 SDK中的ping demo来举例说明。
首先是RAW API,在raw.c中定义了一个raw_pcb结构体,初始化时会定义一个新的raw_pcb结构体并插入raw_pcbs链表。并且给新定义的raw_pcb赋初值,如下图所示,通过raw_recv绑定ping_recv函数。通过raw_bind绑定IP地址,并且注册一个超时事件,超时时就调用ping_timeout函数,参数为ping_pcb。在ping_timeout函数中会发送ping请求。
当LWIP跑在操作系统上时,LWIP协议栈是作为一个独立线程存在的。因此,在初始化时要创建tcpip_thread线程。用户代码与tcpip_thread线程之间是通过邮箱进行数据的交互的。因此,在初始化时也需要创建一个邮箱。
在使用Socket API时,首先要调用lwip_socket函数向内核申请一个套接字,然后调用setsockopt设置套接字的一些选项。接着就可以调用lwip_sendto函数去发送数据包。调用recvfrom函数接收数据包。
至此,在i.MX RT使用LwIP协议栈初始化流程介绍完毕,通过理解和实践这些步骤,开发者可以在i.MX RT微控制器上成功初始化LWIP协议栈,并实现网络通信功能。
总体看来,整个初始化流程还是相对复杂的,这个过程不仅涉及硬件驱动编程和TCP/IP协议栈的配置,还需要对内存管理、中断处理、超时检查等关键知识点有深入的理解。感兴趣的读者可以下载RT四位数的SDK深入了解。
来源:恩智浦MCU加油站
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。