【笔记分享】nRF54L15 双网卡连接不正常问题以及挂载方法介绍

本专栏由非官方人员 王小小海 所著,其内容主要记录了在开发NCS的过程中遇到的一些问题和解决方法,还有一些应用的例程。作者本人也是在实践应用中遇到的问题,想着把这些问题分享给可能遇到的朋友。仅仅做个人技术交流分享,不做任何商业用途。如有不对之处,请留言,本人及时更改。

本专栏不涉及基础的安装和环境搭建问题,本例程开发使用NCS 2.8.0开发,还请注意!。


所有分享内容

笔记分享

  1. 【笔记分享】NCS下radio_test添加FEM
  2. 【笔记分享】5340基于 BLE LBS 自定义网络核固件点亮LED并合并固件
  3. 【笔记分享】5340基于LBS自定义网络核双核DFU实现
  4. 【笔记分享】5340 设置public address 和 random address
  5. 【笔记分享】NCS nRF52/53 添加LVGL组件驱动屏幕
  6. 【笔记分享】VirtualBox Ubuntu22.04 不能使用nrfjprog问题记录
  7. 【笔记分享】5340使用内部负载电容调频偏
  8. 【笔记分享】基于 LE Audio 例程移植到nRF52840上运行思路
  9. 【nRF52/53】【笔记分享】基于 BLE LBS DFU使用内部外部Flash 升级
  10. 【nRF54H20】基础介绍与使用介绍

应用分享

暂无


前言

最近在使用 nRF54L15 开发蓝牙网关的时候,SPIM挂了一个W5500,UART挂了一个 ESP8266。
.
两个网卡都能正常在Zephyr的网络协议中挂载网卡,并且也能通过 Zephyr的网络函数收发数据。
.
使用条件是两个网卡不会同时使用。
.
文章末尾有详细 挂载方法和配置方法。

问题描述

  1. 双网卡IP为DHCP分配的IP时候 TCP连接同一个服务器,不管怎么去切换网卡,都能正常收发数据。
  2. 当设置为静态IP时,假设当前IP地址为:192.168.1.100,设置W5500 为192.168.1.100 首次使用TCP连接服务器,能够正常首发数据,然后切换到WiFi 网卡 设置IP地址为:192.168.1.100 使用TCP连接服务器,也能正常收发数据。
  3. 先把WiFi网卡断开路由器,在切换到W5500网卡时。结果发现TCP无法正常连接服务器。一直提示 TCP Port number reused。
  4. 只要按照 W5500 —> ESP8266 —> W5500 的使用方式,必现问题。
  5. 每次复位又能正常连接服务器了。
    问题截图

问题分析

  1. 已经在切换网卡之前,断开了WiFi,那么网卡应该是W5500,通过打印查看确实是。
  2. 可以通过上面的图片确认,本地端口确实没有被用到,每次分配的都是新的端口。

以上分析,基本确认确实是代码里面的网卡处理的逻辑有问题。但是一时半会儿发现不了问题。
最后打开Wireshark 将与服务器的连接过程的抓包数据,一句一句的点开分析,最后找到了问题。

问题解析

第一次通过 W5500 进行通信,可以看到TCP 的SYN 的MAC地址确实是W5500的MAC地址。

在这里插入图片描述

切换到 ESP8266 进行通信,TCP 的SYN确实是 ESP8266的MAC地址。

在这里插入图片描述

切换到W5500 通信,SYN发送的MAC地址是 W5500的MAC地址,但是服务器回复 SYN,ACK的MAC地址竟然是之前 ESP8266的MAC地址。

在这里插入图片描述

在这里插入图片描述

通过分析问题可以总结为网卡切换时,即使ESP8266 断开了路由,也无法让服务器 底层的ARP池忘记 之前IP 的MAC地址。
.
那么双网卡设置为同样的IP时,需要将 活动的网卡 去使能,然后在 使能 需要活动的网卡才能避免此问题。
.
其核心原理还是因为没有arp去更新,新的IP和Mac之间的关系。重新使能网卡会产生一次ARP。
.
具体见下面代码:


int net_mgmt_restart(void)
{
    int ret;
    struct net_if *iface = net_if_get_default();

    if (!net_if_is_admin_up(iface))
    {
        ret = net_if_up(iface);
        if (ret)
        {
            LOG_ERR("Cannot bring up iface (%d)", ret);
            return ret;
        }

        LOG_INF("Interface up");
    }

    return 0;
}

int net_mgmt_stop(void)
{
    int ret;
    struct net_if *iface = net_if_get_default();

    if (net_if_is_admin_up(iface))
    {
        ret = net_if_down(iface);
        if (ret)
        {
            LOG_ERR("Cannot bring up iface (%d)", ret);
            return ret;
        }

        LOG_INF("Interface Down");
    }

    return 0;
}
static int net_mgmt_ip_addr_remove(struct net_if *iface, struct in_addr *ip)
{
    if (net_ipv4_is_addr_mcast(ip))
    {
        int ret = net_ipv4_igmp_leave(iface, ip);
        if (ret < 0)
        {
            LOG_ERR("Cannot %s multicast group %s (%d)\n",
                    "leave", net_utils_sprint_addr(ip), ret);
            return ret;
        }
    }
    else
    {
        if (!net_if_ipv4_addr_rm(iface, ip))
        {
            LOG_ERR("Failed to delete %s\n", net_utils_sprint_addr(ip));
            return -ENOEXEC;
        }
    }

    return 0;
}

static int net_mgmt_clear_info(struct net_if *iface)
{
    int ret = 0;

    for (uint8_t i = 0; i < NET_IF_MAX_IPV4_ADDR; i++)
    {
        if (iface->config.ip.ipv4 != NULL)
        {
            ret = net_mgmt_ip_addr_remove(iface,
                &iface->config.ip.ipv4->unicast[i].ipv4.address.in_addr);
            if (ret != 0)
            {
                // 重启网卡.
                net_mgmt_stop();
                LOG_INF("net restart!");

                k_sleep(K_MSEC(1000));

                net_mgmt_restart();
                LOG_INF("net restart done!");
            }
        }
    }

    return 0;
}

int netif_init(net_if_t interface)
{
    struct net_if *iface = NULL;

    // 停止DHCPv4.
    net_dhcpv4_stop(net_if_get_default());

    // 清除网络信息, 如果ETH和WLAN都设置为相同的IP会默认选择ETH接口.
    // 导致HARDFAULT.
    net_mgmt_clear_info(net_if_get_default());

    // 如果当前设置为静态IP时, 以太网和WiFi使用同一个IP, 即使WiFi断开了连接,
    // 依然也会导致无法正常连接, 解决这个问题需要将不使用的网卡停止.

    // 停止当前网卡.
    net_mgmt_stop();

    // 选择当前网卡.
    switch (interface)
    {
        case NET_IF_ETH:

            // 断开WiFi连接.
            net_mgmt_link_stop();

            iface = net_if_get_by_index(net_if_get_by_name(ETH_MGMT_IFACE_NAME));
            break;

        case NET_IF_WLAN:

            iface = net_if_get_by_index(net_if_get_by_name(WIFI_MGMT_IFACE_NAME));
            break;

        default:
            return -EINVAL;
    }

    // 重置当前网络接口.
    if (iface != NULL)
    {
        // 设置当前网络接口为默认接口.
        net_if_set_default(iface);

        // 重启网卡.
        net_mgmt_restart();
        return 0;
    }

    return -EINVAL;
}


附加

看了上面的文章 大家也知道了具体问题,避免了踩坑的风险。有朋友也想通过这种方式去挂载 双网卡,那么下面我们开始介绍。

不管是使用的什么芯片,在Zephyr下方法都是通过设备树使能。我使用的nRF54Lxx,不过不限于nRF54L,可以是nRF52/nRF53,或者是其他MCU。


&pinctrl {

	spi3_default: spi3_default {
		group1 {
			psels = <NRF_PSEL(SPIM_MISO, 0, 8)>,
					<NRF_PSEL(SPIM_MOSI, 0, 14)>,
					<NRF_PSEL(SPIM_SCK, 0, 6)>;
		   	nordic,drive-mode = <NRF_DRIVE_H0H1>;
		};
	};

	spi3_sleep: spi3_sleep {
		group1 {
			psels = <NRF_PSEL(SPIM_MISO, 0, 8)>,
					<NRF_PSEL(SPIM_MOSI, 0, 14)>,
					<NRF_PSEL(SPIM_SCK, 0, 6)>;
			low-power-enable;
		};
	};

	uart1_default: uart1_default {
		group1 {
			psels = <NRF_PSEL(UART_TX, 0, 20)>;
			nordic,drive-mode = <NRF_DRIVE_H0H1>;
		};
		group2 {
			psels = <NRF_PSEL(UART_RX, 0, 17)>;
			nordic,drive-mode = <NRF_DRIVE_H0H1>;
		};
	};

	uart1_sleep: uart1_sleep {
		group1 {
			psels = <NRF_PSEL(UART_TX, 0, 20)>,
					<NRF_PSEL(UART_RX, 0, 17)>;
			low-power-enable;
		};
	};
};


&spi3 {
	compatible = "nordic,nrf-spim";
	status = "okay";
	pinctrl-0 = <&spi3_default>;
	pinctrl-1 = <&spi3_sleep>;
	pinctrl-names = "default", "sleep";
	max-frequency = <DT_FREQ_M(32)>;
	cs-gpios = <&gpio0 4 GPIO_ACTIVE_LOW>;
	ethernet: w5500@0 {
		compatible = "wiznet,w5500";
		reg = <0x0>;
		spi-max-frequency = <DT_FREQ_M(32)>;
		int-gpios = <&gpio0 16 GPIO_ACTIVE_LOW>;
		reset-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
	};
};

&uart1 {
	status = "okay";
	current-speed = <115200>;
	pinctrl-0 = <&uart1_default>;
	pinctrl-1 = <&uart1_sleep>;
	pinctrl-names = "default", "sleep";

	esp_at: esp_at {
		compatible = "espressif,esp-at";
		status = "okay";
		target-speed = <1000000>;
		reset-gpios = <&gpio0 15 GPIO_ACTIVE_LOW>;
	};
};

将以上的内容添加到对应板子设备树文件即可,如果用的 Nordic 芯片建议 串口开启硬件异步功能。

CONFIG_UART_0_ASYNC=n
CONFIG_UART_1_NRF_HW_ASYNC=y
CONFIG_UART_1_NRF_HW_ASYNC_TIMER=2

prj.conf 文件配置

# 自定义ETH网络接口.
CONFIG_ETH_DRIVER=y

CONFIG_MODEM=y
CONFIG_WIFI=y
CONFIG_WIFI_ESP_AT_DNS_USE=y
CONFIG_WIFI_ESP_AT_PASSIVE_MODE=y
CONFIG_WIFI_ESP_AT_MDM_RING_BUF_SIZE=4096
CONFIG_WIFI_ESP_AT_RX_STACK_SIZE=2048

CONFIG_MODEM_IFACE_UART_ASYNC=y
CONFIG_MODEM_IFACE_UART_ASYNC_RX_BUFFER_SIZE=1400
CONFIG_MODEM_IFACE_UART_ASYNC_RX_NUM_BUFFERS=3

其他的就按照正常网络配置添加就好了。参考如下:

####################### NETWORK ###################

CONFIG_NETWORKING=y
CONFIG_NET_SOCKETS=y
CONFIG_NET_SOCKETS_POSIX_NAMES=y

CONFIG_NET_IPV4=y
CONFIG_NET_IPV4_IGMP=y
CONFIG_NET_IPV6=n
CONFIG_NET_DHCPV4=y
CONFIG_NET_TCP=y
CONFIG_NET_TCP_KEEPALIVE=y
CONFIG_NET_TCP_KEEPINTVL_DEFAULT=120

CONFIG_NET_UDP=y

CONFIG_DNS_RESOLVER=y

CONFIG_NET_CONTEXT_RCVTIMEO=y
CONFIG_NET_CONTEXT_SNDTIMEO=y

CONFIG_NET_CONFIG_SETTINGS=y
CONFIG_NET_CONFIG_AUTO_INIT=n
CONFIG_NET_CONFIG_INIT_TIMEOUT=0

CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.168.1.2"
CONFIG_NET_CONFIG_MY_IPV4_NETMASK="255.255.255.0"
CONFIG_NET_CONFIG_MY_IPV4_GW="192.168.1.1"

CONFIG_NET_IF_MAX_IPV4_COUNT=2

CONFIG_NET_HOSTNAME_ENABLE=y
CONFIG_NET_HOSTNAME_DYNAMIC=y

CONFIG_NET_PKT_RX_COUNT=16
CONFIG_NET_PKT_TX_COUNT=16
CONFIG_NET_BUF_RX_COUNT=32
CONFIG_NET_BUF_TX_COUNT=32
CONFIG_NET_BUF_DATA_SIZE=512
CONFIG_NET_TC_RX_COUNT=0

CONFIG_NET_L2_ETHERNET=y
CONFIG_NET_L2_ETHERNET_MGMT=y
CONFIG_NET_MGMT_EVENT_STACK_SIZE=1024

CONFIG_ZVFS_OPEN_MAX=5
CONFIG_NET_MAX_CONN=5
CONFIG_NET_MAX_CONTEXTS=5
CONFIG_NET_SOCKETS_POLL_MAX=5

结束语

好的,本次分享基本上就是这些。

有不明白的地方欢迎提问,也厚脸皮要个赞或者关注,谢谢各位啦。

如果有哪位朋友需要定制方案,也可以联系私信我。感谢大家的浏览。


本系列文章大多数是本人遇到和解决过的问题,难有疏忽之处,有什么问题或者不明白的地方,欢迎留言询问!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值