开发环境
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
board.c
5.3 drv_eth.c 文件中 _enet_config 函数下 注释 “ENET_Deinit(imxrt_eth_device.enet_base);”,代码如下
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;
原作者:returningprodigal