野火 RT1052 移植网卡功能(LAN8720A)

野火 RT1052 移植网卡功能(LAN8720A)

开发环境

RT-Thread: v4.0.2(master)
SOC: i.MX RT1050
Board: 野火 RT1052

目的

在 RT-Thread 系统上进行网络通讯

背景描述

1.首先调研,找板子,在这过程中发现了野火和正点原子有基于 NXP 的 i.MX RT1050 的开发板。为什么要选择 i.MX RT1050 呢?主要是因为他的主频能达到500MHz。这也是我需要的。
2.搜了很多帖子,都说野火的资料比正点原子的要多,而且较系统。所以就选择了野火的开发。当天就下单了一块“野火 i.MX RT1052开发板 Pro版本”。
3.之前有对 RT-Thread 展开过调研,发现这款国产系统做的很不错,开发方式,代码风格有借鉴 Linux,对于我这个从 Linux过来的人很是熟悉。因为对 RT-Thread 也还是初涉,而野火对于"野火 RT1052"这款开发板有详细的 RT-Thread 资料和示例,所以选择"野火 RT1052"也是基于这个原因。
4.最后要说下野火的问题,其"野火 RT1052"开发板没有PCB,BOM表,需要玩家自己设计底板。野火几乎可以说没有客服渠道,都是要去淘宝上找客服转接技术才能咨询,然后就是野火自家的论坛可以咨询问题。
5.期间我有遇到了很多问题,只在问题一栏简单说明,以及解决办法。移植过程中的坑太多,难言啊!言归正传,开始搞板子

移植过程

1.首先去 Git 下载 RT-Thread 最新的源代码
为什么最新的源代码呢,因为 RT-Thread 团队会更新 bsp, 开发板各功能,开发板各驱动。可能你前段时间下载查询没有的功能,可能再次下载就有你想要的功能,而且只是 menuconfig 简单配置后就可以使用了。
RT-Thread
2.去 RT-Thread 官网下载 Env工具
Env 工具使用来配置编译 RT-Thread 的
按照官方给的安装文档,进行安装配置 Env 用户手册
3.下载好后,进入 “rt-thread\bsp\imxrt\imxrt1052-fire-pro” 这个目录,各种开发板相关的代码都在 bsp 目录下,去找对应的就行。
如果你要用 RT-Thread, 而又不知到选什么板子好,你可以先在源码 bsp 目录下看有没有对应的 bsp ,并且功能是否有你需要的。然后再抉择是否选用这块开发板、SOC
4.按照 网络协议栈驱动移植笔记 文档进行移植。然而进行 “menuconfig” 时,在配置项中并没有 “Enable Ethernet”这个选项。经过阅读
“rt-thread\bsp\imxrt\imxrt1052-fire-pro\board\Kconfig” 文件,发现并没有BSP_USING_ETH配置,绝望了!我在 RT-Thread 论坛上发布了一个问题,第二天就收到了回复, 在这里要感谢这位朋友的回复.
回复中提到: i.MX RT 系列外设驱动添加指南
这里不得不说 RT-Thread 团队很给力,提供的文档很是详细。还有 RT-Thread 官方论坛, 上面有 RT-Thread 团队的维护, 以及希望国产操作系统越来越强大的猿友们.
5.参考"i.MX RT 系列外设驱动添加指南"文档,进行了外设驱动添加。该文档步骤详细,只要按照步骤一步步做就可以了。这里我说下需要注意的问题
5.1 用 MCUXpresso 生成 pin_mux.h pin_mux.c 复用代码时,要根据自己开发板原理图,我们这里是移植的网卡,所以要注意从SOC到PHY芯片之间用到的引脚。然后结合 LAN8720A 芯片手册来进行配置。这里我使用的野火提供的FreeRTOS中lwip例程,这个例程有LAN8720A 芯片的驱动代码 fsl_phy.c,fsl_phy.h。所以直接拿过来使用进行。将 pin_mux.c 中的 “BOARD_InitPins” 函数下有关网卡配置的代码,复制到你的工程中。(NXP官网上没有找到关于LAN8720A的驱动,而且NXP的官方开发板“MIMXRT1050-EVK”使用的是“ksz8081”phy芯片)
5.2 编写 phy 复位函数 imxrt_enet_phy_reset_by_gpio,代码见 board.h board.c
board.h

#ifdef BSP_USING_ETH
void imxrt_enet_phy_reset_by_gpio(void);
#define PHY_ADDRESS     (0x00U)
#endif

board.c

#ifdef BSP_USING_ETH
void imxrt_enet_phy_reset_by_gpio(void)
{
	rt_kprintf("imxrt_enet_phy_reset_by_gpio");

	gpio_pin_config_t gpio_config = {kGPIO_DigitalOutput, 0, kGPIO_NoIntmode};

    GPIO_PinInit(GPIO1, 9, &gpio_config);
    GPIO_PinInit(GPIO1, 10, &gpio_config);
    // pull up the ENET_INT before RESET.
    GPIO_WritePinOutput(GPIO1, 10, 1);
    GPIO_WritePinOutput(GPIO1, 9, 0);
    rt_thread_delay(100);
    GPIO_WritePinOutput(GPIO1, 9, 1);
}
#endif /* BSP_USING_ETH */

5.3 drv_eth.c 文件中 _enet_config 函数下 注释 “ENET_Deinit(imxrt_eth_device.enet_base);”,代码如下

static void _enet_config(void)
{
	...
    /* Set SMI to get PHY link status. */
    sysClock = CLOCK_GetFreq(kCLOCK_AhbClk);

    dbg_log(DBG_LOG, "deinit\n");
    //ENET_Deinit(imxrt_eth_device.enet_base);
    dbg_log(DBG_LOG, "init\n");
    ENET_Init(imxrt_eth_device.enet_base, &imxrt_eth_device.enet_handle, &config, &buffConfig, &imxrt_eth_device.dev_addr[0], sysClock);
    dbg_log(DBG_LOG, "set call back\n");
    ENET_SetCallback(&imxrt_eth_device.enet_handle, _enet_callback, &imxrt_eth_device);
    dbg_log(DBG_LOG, "active read\n");
    ...
}

5.4 menuconfig 配置
在 RT-Thread Components → Network → light weight TCP/IP stack 下
关闭 “Enable alloc ip address through DHCP”
进入 “Static IPv4 Address” 并设置IP,网关,掩码
scons --target=mdk5 -s
5.6 编译并下载,程序正常运行,ping 检测, 网络正常

问题

1."ENET_Deinit(imxrt_eth_device.enet_base);"必须注释。不是注释改行,程序就会死在改行。该问题是通过代码打印调试,才锁定到该行,具体原因还待探索。
2.“cannot open source input file “ulog.h”: No such file or directory”
解: menuconfig 开启 ulog 功能后, 要先关闭该工程, 然后再"scons --target=mdk5 -s"生成工程.
3.“Undefined symbol ulog_flush (referred from drv_eth.o)”, “Undefined symbol ulog_console_backend_output (referred from ulog.o).”
解: 同2,
解: 关闭 ulog 功能后并生成工程, 再开启功能并生成工程

移植过程中思路/日志

RT-Thread RT1052网卡移植思路
1.借助 Freertos 中移植 lwip 的方法来移植 rt-thread中的网卡
2.enet_ethernetif_kinetis.c
    D:\BaiduNetdiskDownload\书籍配套程序\基于i.MXRT1052_开发板\lwip_tcpecho_socket\user\bsp\arch\enet_ethernetif_kinetis.c
    该文件中包含大部分对网卡的操作.
    可以搜索 ENET_ 为前缀的 nxp 提供的 fsl_enet.c 文件中的函数.
    按照对应的方式, 来操作网卡.
    
3.enet_ethernetif.c
    该文件中也有包含 
        #include "fsl_enet.h"
    要注意是否有操作.
    
4.移植网卡驱动, 主要是针对 fsl_enet.c,fsl_enet.h 这两个文件
    E:\firepro\rt-thread-v(master)\rt-thread\bsp\imxrt\libraries\MIMXRT1050\MIMXRT1052\drivers

这是在定位ENET_Deinit卡死过程中, lwip 代码阅读

struct netifapi_msg {
  struct tcpip_api_call_data call;
  struct netif *netif;
  union {
    struct {
#if LWIP_IPV4
      NETIFAPI_IPADDR_DEF(ip4_addr_t, ipaddr);
      NETIFAPI_IPADDR_DEF(ip4_addr_t, netmask);
      NETIFAPI_IPADDR_DEF(ip4_addr_t, gw);
#endif /* LWIP_IPV4 */
      void *state;
      netif_init_fn init;
      netif_input_fn input;
    } add;
    struct {
      netifapi_void_fn voidfunc;
      netifapi_errt_fn errtfunc;
    } common;
  } msg;
};
NETIFAPI_VAR_DECLARE(msg);      //定义了 struct netifapi_msg 结构体
  NETIFAPI_VAR_ALLOC(msg);
NETIFAPI_VAR_REF(msg).netif = netif;
#if LWIP_IPV4
  NETIFAPI_VAR_REF(msg).msg.add.ipaddr  = NETIFAPI_VAR_REF(ipaddr);
  NETIFAPI_VAR_REF(msg).msg.add.netmask = NETIFAPI_VAR_REF(netmask);
  NETIFAPI_VAR_REF(msg).msg.add.gw      = NETIFAPI_VAR_REF(gw);
#endif /* LWIP_IPV4 */
  NETIFAPI_VAR_REF(msg).msg.add.state   = state;        //state 就是 struct eth_device *dev
  NETIFAPI_VAR_REF(msg).msg.add.init    = init;
  NETIFAPI_VAR_REF(msg).msg.add.input   = input;

err = tcpip_api_call(netifapi_do_netif_add, &API_VAR_REF(msg).call);
    err = fn(call);
struct netifapi_msg *msg = (struct netifapi_msg*)(void*)&call;


if (init(netif) != ERR_OK) {        //init 就是 msg->msg.add.init


static err_t eth_netif_device_init(struct netif *netif)     //传进来是struct netifapi_msg*


struct eth_device *ethif;
ethif = (struct eth_device*)netif->state;
rt_device_t device = (rt_device_t) ethif;

(device->init)(device)          //device->init 就是 rt_imxrt_eth_init


netifapi_netif_add(netif, &ipaddr, &netmask, &gw, dev, eth_netif_device_init, tcpip_input);
    //调用了很多层最终调用到,以下代码
        if (init(netif) != ERR_OK) {
            static err_t eth_netif_device_init(struct netif *netif)
                device = (rt_device_t) ethif;

你可能感兴趣的:(#,嵌入式之ARM)