最近使用 STM32 测试了一下 lwip 和 esp8266 的网络连接问题,使用 RTThread 的自动网卡时,发现不能很好的自动切换默认网卡,不能满足需求,所以自己简单的改了一下。

一、准备材料

  • MCU:STM32F103ZT6
  • RTThread:5.0.2
  • 工具:RTThread studio
  • 网络芯片:DM9000(FSMC连接)
  • WIFI:ESP8266(uart连接)

二、测试现象

  • 如果默认网卡断开是,无其他网卡联网时,其他网卡重新联网,不会自动切换默认网卡
  • 初始化时,如果默认网卡为初始成功,即时存在能联网的网卡时,也不会切换默认网卡

三、问题原因

遇到问题的时候,我们首先查看一下官方的说明文档: https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/netdev/netdev

找到默认网卡的说明,如下图所示:



从图中不难看出,他只有网卡从 up → down 的时候,才会去切换状态,所以也能解释上面我测试到的现象了

四、解决办法

这里有两个办法可以解决,方式一,直接就该源码;方式二,从新添加相应的处理程序,如下所示:

方式一

程序源码的文件是 rt-thread/components/net/netdev/src/netdev.c,

  1. 先来看一下自动切换网卡的实现方式, 程序如下

    /* Change to the first link_up network interface device automatically */
    static void netdev_auto_change_default(struct netdev *netdev)
    {
    struct netdev *new_netdev = RT_NULL; if (netdev->flags & NETDEV_FLAG_LINK_UP)
    {
    if (!(netdev_default->flags & NETDEV_FLAG_LINK_UP))
    {
    netdev_set_default(netdev);
    }
    return;
    }
    if (rt_memcmp(netdev, netdev_default, sizeof(struct netdev)) == 0)
    {
    new_netdev = netdev_get_first_by_flags(NETDEV_FLAG_LINK_UP);
    if (new_netdev)
    {
    netdev_set_default(new_netdev);
    }
    }
    }

    程序中不难看出切换网卡的思路,确实很厉害,代码也比较简洁,所以切换部分的代码没有问题

  2. 调用的自动切换部分的程序

    /**
    * This function will set network interface device status.
    * @NOTE it can only be called in the network interface device driver.
    *
    * @param netdev the network interface device to change
    * @param is_up the new status
    */
    void netdev_low_level_set_status(struct netdev *netdev, rt_bool_t is_up)
    {
    if (netdev && netdev_is_up(netdev) != is_up)
    {
    if (is_up)
    {
    netdev->flags |= NETDEV_FLAG_UP;
    }
    else
    {
    netdev->flags &= ~NETDEV_FLAG_UP; #ifdef NETDEV_USING_AUTO_DEFAULT
    /* change to the first link_up network interface device automatically */
    netdev_auto_change_default(netdev);
    #endif /* NETDEV_USING_AUTO_DEFAULT */
    } /* execute network interface device status change callback function */
    if (netdev->status_callback)
    {
    netdev->status_callback(netdev, is_up ? NETDEV_CB_STATUS_UP : NETDEV_CB_STATUS_DOWN);
    }
    }
    } /**
    * This function will set network interface device active link status.
    * @NOTE it can only be called in the network interface device driver.
    *
    * @param netdev the network interface device to change
    * @param is_up the new link status
    */
    void netdev_low_level_set_link_status(struct netdev *netdev, rt_bool_t is_up)
    {
    if (netdev && netdev_is_link_up(netdev) != is_up)
    {
    if (is_up)
    {
    netdev->flags |= NETDEV_FLAG_LINK_UP; #ifdef RT_USING_SAL
    /* set network interface device flags to internet up */
    if (netdev_is_up(netdev) && !ip_addr_isany(&(netdev->ip_addr)))
    {
    sal_check_netdev_internet_up(netdev);
    }
    #endif /* RT_USING_SAL */
    }
    else
    {
    netdev->flags &= ~NETDEV_FLAG_LINK_UP; /* set network interface device flags to internet down */
    netdev->flags &= ~NETDEV_FLAG_INTERNET_UP; #ifdef NETDEV_USING_AUTO_DEFAULT
    /* change to the first link_up network interface device automatically */
    netdev_auto_change_default(netdev);
    #endif /* NETDEV_USING_AUTO_DEFAULT */
    } /* execute link status change callback function */
    if (netdev->status_callback)
    {
    netdev->status_callback(netdev, is_up ? NETDEV_CB_STATUS_LINK_UP : NETDEV_CB_STATUS_LINK_DOWN);
    }
    }
    }

    从程序中不难看出,它是只有当网络状态为 down 的时候,才去调用切换网络状态函数

  3. 解决办法

    只需要将 netdev_low_level_set_link_status 函数中的调用位移,移除 down 的判断条件即可解决这个问题。这个问题解决很简单,但是为啥会是这样的设计我还没弄明白,也能是开发人员粗心了(按理说不应该),也有可能是另有深意,但是不满足我得需求,所以还是需要更改一下。这里我没有改源程序,而是从新写了一个函数去实现的,具体流程如下

方式二

通过提供的 API 重写了一下默认网卡切换的方法,程序如下

#include <rtthread.h>
/* 当需要网卡操作是,需要包含这两个头文件 */
#include <arpa/inet.h>
#include <netdev.h> #define DBG_TAG "app_net_monitor"
#include <rtdbg.h> typedef struct net_device_info
{
const char *name; // 设备名称
struct netdev *dev; // 网络设备指针
} net_device_info_t; /* 初始网卡设备,第一个为默认网卡 */
static net_device_info_t net_device[] =
{
{"e0", RT_NULL},
{"esp0", RT_NULL}
}; /* 默认网卡设备 */
struct netdev *network_default = RT_NULL; rt_uint16_t net_dev_number = sizeof(net_device)/sizeof(net_device_info_t); /**
* @brief 自动更改为第一个link_up网络接口设备
*
* @param netdev 网络设备对象
*/
static void network_auto_change_default(struct netdev *netdev)
{
struct netdev *new_netdev = RT_NULL; if (netdev->flags & NETDEV_FLAG_LINK_UP)
{
if (!(network_default->flags & NETDEV_FLAG_LINK_UP))
{
netdev_set_default(netdev);
network_default = netdev;
}
return;
}
if (rt_memcmp(netdev, network_default, sizeof(struct netdev)) == 0)
{
new_netdev = netdev_get_first_by_flags(NETDEV_FLAG_LINK_UP);
if (new_netdev)
{
netdev_set_default(new_netdev);
network_default = new_netdev;
}
}
} /**
* @brief 网络设备状态回调函数
*
* @param netdev 网络设备对象
* @param type 网络状态类型
*/
void network_status_callback(struct netdev *netdev, enum netdev_cb_type type)
{
switch (type)
{
/* 网络断开 */
case NETDEV_CB_STATUS_LINK_DOWN:
network_auto_change_default(netdev);
break; /* 网络连接 */
case NETDEV_CB_STATUS_INTERNET_UP:
network_auto_change_default(netdev);
break; default:
break;
}
} /**
* @brief 返回设备联网状态
*
* @return RT_EOK 表示网络连接
*/
rt_bool_t network_is_up()
{
return netdev_is_link_up(network_default);
} /**
* @brief 初始化自动网卡设置
*
*/
static int app_network_init(void)
{ for (int i = 0; i < net_dev_number; i++)
{
/* 获取网卡设备 */
net_device[i].dev = netdev_get_by_name(net_device[i].name);
if (net_device[i].dev == RT_NULL)
{
LOG_E("not find network interface device name(%s).\n", net_device[i].name);
continue;
} /* 设置网络设备状态变化的回调函数 */
netdev_set_status_callback(net_device[i].dev, network_status_callback); /* 设置一个默认网卡 */
if (network_default == RT_NULL)
{
network_default = net_device[i].dev;
netdev_set_default(network_default);
} } return RT_EOK;
} INIT_APP_EXPORT(app_network_init);

RTThread 自动网卡使用问题的更多相关文章

  1. 从个人的角度谈谈本次GNTC大会的收获

    GNTC资料:from sdnlab 从个人的角度谈谈本次大会的收获 从本次大会的主题演讲来看,目前SDN.NFV的最前沿已经不再像五年前持观望态度以及探讨,各大运营商.各大厂商已经将SDN.NFV具 ...

  2. vmvare入门(1)使用移动,不要使用复制

    1.复制虚拟机会产生新的自动网卡,原来的 System Eth0废了? 2.xftp链接的时候,要选择sftp方式连接,utf8编码.

  3. RT-Thread--内核基础

    内核介绍 内核处于硬件层之上,内核部分包括内核库.实时内核实现. 实时内核的实现包括:对象管理.线程管理及调度器.线程间通信管理.时钟管理及内存管理等等,内核最小的资源占用情况是 3KB ROM,1. ...

  4. Debian、kali类虚拟机网络设置

    Linux发行版分类 先说一下Linux发行版的分类,因为不同的发行版,各自设置的网络的方式就不相同, - 我常用的Linux发行版 -- Debian --- debian --- ubuntu - ...

  5. [dpdk][kernel][driver] 如何让DPDK的UIO开机自动加载到正确的网卡上

    0. 前言 开了虚拟机,开始dpdk之前,我每天都干这几件事: [root@dpdk potatos]# modprobe uio [root@dpdk potatos]# insmod /root/ ...

  6. 解决网卡无法自动获取ip的办法

    解决网卡无法自动获取IP址的方法          为了省钱或者一户多机,很多人都购买宽带路由器共享上网.在架设路由上网的时候,有些“师傅”可能不懂或是偷懒,开启了宽带路由器的DHCP( Dynami ...

  7. Centos 7开启网卡打开DHCP自动获取IP

    在Windows10上安装了CentOS7的Hyper-V虚拟机. 虽然配置了可访问外网的网卡(Win8.1 Hyper-V 共享本机IP上网),但是默认安装的CentOS是没有开启配置网卡信息的,也 ...

  8. CentOS7或CentOS8 开机自动启用网卡的设置方法

    sudo nano /etc/sysconfig/network-scripts/ifcfg-enp0s3(p0s3是网卡,名字不同环境会有差异,输入时可按tab自动补全.) 将最后一行的 ONBOO ...

  9. 【linux】centos6.9设置etc0网卡开机自动获取ip

    在vm新安装的centos系统中,一般选择NAT来设置和主机共享局域网,通过ifconfig etc0 192.168.xx.xx 这种作法机器重启之后就会失效,所以可以使用更改文件的方式完成设置ce ...

  10. 解决eth0网卡无法自动加载的问题

    问题:输入ifup eth0显示无法加载网卡所在的文件 解决办法: vi /etc/rc.d/rc.local 最后一行加入: ifup eth0 然后重启虚拟机即可解决问题. 本文为博主原创文章,未 ...

随机推荐

  1. Sentinel如何持久化数据到Nacos?

    默认情况下 Sentinel 只能接收到 Nacos 推送的消息,但不能将自己控制台修改的信息同步给 Nacos,如下图所示: 但是在生成环境下,我们为了更方便的操作,是需要将 Sentinel 控制 ...

  2. LVGL 日志

    一.启动日志 在 lv_conf.h 中将 LV_USE_LOG 设置为 1,如下图所示: 二.日志级别 在文件 lvgl/src/misc/lv_log.h 中定义了日志等级,等级是从小到大,所以 ...

  3. 前端如何操作动态渲染的多个checkbox列表单选

    input[type=checkbox]:after{     content:"";     display:inline-block;     width:16px;      ...

  4. Intel Pentium III 512MB内存 i815集显上安装Ubuntu Server 14.04

    自己的御用奔腾III PC,接口齐全,准备安装Ubuntu Server 14.04 i386,继续发挥余热,物尽其用. 基本配置: CPU: Intel Pentium III 1000MHz, 2 ...

  5. Codeforces 题解集

    Codeforces Round 940 (Div. 2) and CodeCraft-23 Codeforces Round 940 (Div. 2) and CodeCraft-23 (A-E)

  6. SAP集成技术(五)集成风格

    上一节介绍了各种集成架构,这一节将介绍各种集成风格(Style).我们在日常工作中使用这些集成风格来分类不同类型的集成场景.本文将遵循SAP的集成解决方案咨询方法(ISA - M)使用的方法,将集成风 ...

  7. 【人脸识别】OpenCV获取自己的图像

    思路:先获取10000张自己的图像,然后通过CNN神经网络进行学习. 第一步:先获取自己的脸的数据.如何做? 代码如下: import cv2 import os import sys import ...

  8. STM32F1和STM32F4系列DMA的不同之处——对STM32的DMA的工作机制的一些理解

    喜欢用STM32的DMA功能.一方面STM32的DMA和MPU的DMA一样,可以提高数据传输效率.另一方面,作为一种MCU上的DMA,它可以提高针对外设(peripheral)的数据传输的实时性,改变 ...

  9. C 语言编程 — 运算符

    目录 文章目录 目录 前文列表 运算符 算数运算符 自增.自减运算符 比较运算符 逻辑运算符 位运算符 赋值运算符 逗号运算符 sizeof 运算符 杂项运算符 运算符的优先级 前文列表 <程序 ...

  10. 可以把erp当做一个分支-找自己的方向

    之前一直在寻思自己应该做哪些方面,对所有编程的问题都在研究,又看到自己研究不透.现在,某一时刻看到,可以把erp当做一个分支. 就像游戏里的天赋点一样,进入这个分支... 这是一个专一的方面,和编程普 ...