《嵌入式 - Lwip开发指南》第4章 移植LWIP(基于RT-Thead系统-以太网+Wifi)

Posted Bruceoxl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《嵌入式 - Lwip开发指南》第4章 移植LWIP(基于RT-Thead系统-以太网+Wifi)相关的知识,希望对你有一定的参考价值。

开发环境:
RT-Thread版本:4.0.3
操作系统:Windows10
Keil版本:V5.30
RT-Thread Studio版本:2.0.1
开发板MCU:STM32F746ZGT6U(NUCLEO-F746ZG开发板)

前面是通过以太网进行网络连接,本文将讲解Wifi联网。

4.1 RW007联网

4.1.1 RW007简介

RW007 是由上海睿赛德电子科技有限公司开发的高速 WiFi 模块,模块基于 Realtek RTL8710BN(Ameba Z 系列) WIFI SOC,使用 SPI/UART 与主机通信 ,支持 IEEE 802.11b/g/n 网络、 WEP/WPA/WPA2 加密方式和 STA 和 AP 模式。

主要特性

1.Cortex-M4 高性能 MCU
2.可自由选择的 AT SPI 双模式,工作模式可由主机配置
3.SPI 时钟高达 30Mbps,UART 波特率高达 6Mbps。
4.SPI 模式下有效以太网带宽高达上传 1MBytes/s,下载 1MBytes/s
5.内置 Bootloader,支持固件升级、安全固件功能。
6.支持快速连接、airkiss 配网
7.支持存储多达 5 条连接信息

4.1.2 RW007硬件电路

RW007与MCU的电路连接示意图如下:

RW007与MCU具体IO连线如下:

从上图可以看出WiFi的接口是SPI。

STM32引脚名封装管脚序号功能
PA55BOOT0/CLK
PA66MISO
PB57MOSI
PD1462BOOT1/CS
PD1563INT/BUSY
PF12252RESET

值得注意的是,PA7已近被以太网占用了,因此需要修改电路。

这里需要将SB122连接,将SB121断开。

4.1.3 RW007相关配置

1.配置 SPI

进行配置 SPI1,并生成代码,保存退出即可 。

2.配置 RW007 软件包

RT-Thread 通过软件包的形式,对 RW007 模块提供配套驱动支持,系统默认选项不包含软件包,用户需手动开启:

RT-Thread online packages —> 、IoT - internet of things —>、Wi-Fi —>,勾选 rw007: SPI WIFI rw007 driver —> 选项

接着进一步设置软件包参数,完成 SPI 总线和 IO 的配置,更改总线设备名称 RW007 BUS NAME 为 spi1。

当然啦,这里需要根据自己的板子来决定使用哪个SPI。还需要配置 SPI 控制 IO。

3.开启 WiFi 框架

RW007 驱动使用了 WLAN 相关的接口,按以下选项路径打开 WiFi 框架:
组件 —>设备驱动程序 —>使用 Wi-Fi 框架:

保存即可配置完成。

值得注意的是,LWIP默认已经勾选,但不一定是最新版本。可以在组件中的进行修改。

4.1.4 WiFi联网测试

一切完成后,编译,下载,打开终端。

这里通过wifi命令来联网。

先对周围的无线网络进行扫描,如果你知道Wii信息可以跳过该步骤:

wifi scan

连接网络:

wifi join ssid passwd

联网成功后会显示IP,接下来可以使用下WiFi相关的的其他命令。

有WiFi信息不代表联网成功,接下来ping下IP。

ping ip/域名

【问题】RW007 初始化失败

【问题分析】由于RW007的软件版本默认选择的是1.1.1,不是最新版本,固件和应用软件不匹配。

【解决办法】选择与固件匹配的软件版本即可。

4.1.5 WiFi联网总结

关于WiFi的底层驱动已经封装了,这部分内容也很多,主要是协议栈内容多,但是大体的流程和其他驱动一样的。

驱动的入口是wifi_spi_device_init函数,主要要RW007硬件使能。然后跳转到rt_hw_wifi_init函数中。

int wifi_spi_device_init(void)
{
    char sn_version[32];
    
    GPIO_TypeDef *cs_gpiox;
    uint16_t cs_pin;

    cs_gpiox = (GPIO_TypeDef *)((rt_base_t)GPIOA + (rt_base_t)(RW007_CS_PIN / 16) * 0x0400UL);
    cs_pin = (uint16_t)(1 << RW007_CS_PIN % 16);
    
    rw007_gpio_init();
    rt_hw_spi_device_attach(RW007_SPI_BUS_NAME, "wspi", cs_gpiox, cs_pin);
    rt_hw_wifi_init("wspi");

    rt_wlan_set_mode(RT_WLAN_DEVICE_STA_NAME, RT_WLAN_STATION);
    rt_wlan_set_mode(RT_WLAN_DEVICE_AP_NAME, RT_WLAN_AP);

    rw007_sn_get(sn_version);
    rt_kprintf("\\nrw007  sn: [%s]\\n", sn_version);
    rw007_version_get(sn_version);
    rt_kprintf("rw007 ver: [%s]\\n\\n", sn_version);

    return 0;
}
INIT_APP_EXPORT(wifi_spi_device_init);

rt_err_t rt_hw_wifi_init(const char *spi_device_name)
{
    static struct rt_wlan_device wlan_sta, wlan_ap;
    rt_err_t ret;
    wifi_sta.wlan = &wlan_sta;
    wifi_sta.hspi = &rw007_spi;
    wifi_ap.wlan = &wlan_ap;
    wifi_ap.hspi = &rw007_spi;
    /* align and struct size check. */
    RT_ASSERT((SPI_MAX_DATA_LEN & 0x03) == 0);

    memset(&rw007_spi, 0, sizeof(struct rw007_spi));

    rw007_spi.spi_device = (struct rt_spi_device *)rt_device_find(spi_device_name);

    if (rw007_spi.spi_device == RT_NULL)
    {
        LOG_E("spi device %s not found!\\r", spi_device_name);
        return -RT_ENOSYS;
    }

    /* config spi */
    {
        struct rt_spi_configuration cfg;
        cfg.data_width = 8;
        cfg.mode = RT_SPI_MODE_0 | RT_SPI_MSB; /* SPI Compatible: Mode 0. */
        cfg.max_hz = RW007_SPI_MAX_HZ;             /* 15M 007 max 30M */
        rt_spi_configure(rw007_spi.spi_device, &cfg);
    }

    /* init spi send mempool */
    rt_mp_init(&rw007_spi.spi_tx_mp,
               "spi_tx",
               &rw007_spi.spi_tx_mempool[0],
               sizeof(rw007_spi.spi_tx_mempool),
               sizeof(struct spi_data_packet));

    /* init spi send mailbox */
    rt_mb_init(&rw007_spi.spi_tx_mb,
               "spi_tx",
               &rw007_spi.spi_tx_mb_pool[0],
               SPI_TX_POOL_SIZE,
               RT_IPC_FLAG_PRIO);
    
    /* init spi recv mempool */
    rt_mp_init(&rw007_spi.spi_rx_mp,
               "spi_rx",
               &rw007_spi.spi_rx_mempool[0],
               sizeof(rw007_spi.spi_rx_mempool),
               sizeof(struct spi_data_packet));

    /* init spi recv mailbox */
    rt_mb_init(&rw007_spi.spi_rx_mb,
               "spi_rx",
               &rw007_spi.spi_rx_mb_pool[0],
               SPI_RX_POOL_SIZE,
               RT_IPC_FLAG_PRIO);

    /* init spi data notify event */
    rt_event_init(&spi_wifi_data_event, "wifi", RT_IPC_FLAG_FIFO);

    rw007_spi.rw007_cmd_event = rt_event_create("wifi_cmd", RT_IPC_FLAG_FIFO);

    /* register wlan device for ap */
    ret = rt_wlan_dev_register(&wlan_ap, RT_WLAN_DEVICE_AP_NAME, &ops, 0, &wifi_ap);
    if (ret != RT_EOK)
    {
        return ret;
    }

    /* register wlan device for sta */
    ret = rt_wlan_dev_register(&wlan_sta, RT_WLAN_DEVICE_STA_NAME, &ops, 0, &wifi_sta);
    if (ret != RT_EOK)
    {
        return ret;
    }

    {
        rt_thread_t tid;

        /* Create package parse thread */
        tid = rt_thread_create("wifi_handle",
                               wifi_data_process_thread_entry,
                               &rw007_spi,
                               2048,
                               8,
                               20);
        if(!tid)
        {
            return -RT_ERROR;
        }
        rt_thread_startup(tid);

        /* Create wifi transfer thread */
        tid = rt_thread_create("wifi_xfer",
                               spi_wifi_data_thread_entry,
                               RT_NULL,
                               2048,
                               9,
                               20);
        if(!tid)
        {
            return -RT_ERROR;
        }
        rt_thread_startup(tid);
    }

    spi_wifi_hw_init();

    return RT_EOK;
}

rt_hw_wifi_init负责创建WIFI数据处理线程,函数如下:

rt_err_t rt_hw_wifi_init(const char *spi_device_name)
{
    static struct rt_wlan_device wlan_sta, wlan_ap;
    rt_err_t ret;
    wifi_sta.wlan = &wlan_sta;
    wifi_sta.hspi = &rw007_spi;
    wifi_ap.wlan = &wlan_ap;
    wifi_ap.hspi = &rw007_spi;
    /* align and struct size check. */
    RT_ASSERT((SPI_MAX_DATA_LEN & 0x03) == 0);

    memset(&rw007_spi, 0, sizeof(struct rw007_spi));

    rw007_spi.spi_device = (struct rt_spi_device *)rt_device_find(spi_device_name);

    if (rw007_spi.spi_device == RT_NULL)
    {
        LOG_E("spi device %s not found!\\r", spi_device_name);
        return -RT_ENOSYS;
    }

    /* config spi */
    {
        struct rt_spi_configuration cfg;
        cfg.data_width = 8;
        cfg.mode = RT_SPI_MODE_0 | RT_SPI_MSB; /* SPI Compatible: Mode 0. */
        cfg.max_hz = RW007_SPI_MAX_HZ;             /* 15M 007 max 30M */
        rt_spi_configure(rw007_spi.spi_device, &cfg);
    }

    /* init spi send mempool */
    rt_mp_init(&rw007_spi.spi_tx_mp,
               "spi_tx",
               &rw007_spi.spi_tx_mempool[0],
               sizeof(rw007_spi.spi_tx_mempool),
               sizeof(struct spi_data_packet));

    /* init spi send mailbox */
    rt_mb_init(&rw007_spi.spi_tx_mb,
               "spi_tx",
               &rw007_spi.spi_tx_mb_pool[0],
               SPI_TX_POOL_SIZE,
               RT_IPC_FLAG_PRIO);
    
    /* init spi recv mempool */
    rt_mp_init(&rw007_spi.spi_rx_mp,
               "spi_rx",
               &rw007_spi.spi_rx_mempool[0],
               sizeof(rw007_spi.spi_rx_mempool),
               sizeof(struct spi_data_packet));

    /* init spi recv mailbox */
    rt_mb_init(&rw007_spi.spi_rx_mb,
               "spi_rx",
               &rw007_spi.spi_rx_mb_pool[0],
               SPI_RX_POOL_SIZE,
               RT_IPC_FLAG_PRIO);

    /* init spi data notify event */
    rt_event_init(&spi_wifi_data_event, "wifi", RT_IPC_FLAG_FIFO);

    rw007_spi.rw007_cmd_event = rt_event_create("wifi_cmd", RT_IPC_FLAG_FIFO);

    /* register wlan device for ap */
    ret = rt_wlan_dev_register(&wlan_ap, RT_WLAN_DEVICE_AP_NAME, &ops, 0, &wifi_ap);
    if (ret != RT_EOK)
    {
        return ret;
    }

    /* register wlan device for sta */
    ret = rt_wlan_dev_register(&wlan_sta, RT_WLAN_DEVICE_STA_NAME, &ops, 0, &wifi_sta);
    if (ret != RT_EOK)
    {
        return ret;
    }

    {
        rt_thread_t tid;

        /* Create package parse thread */
        tid = rt_thread_create("wifi_handle",
                               wifi_data_process_thread_entry,
                               &rw007_spi,
                               2048,
                               8,
                               20);
        if(!tid)
        {
            return -RT_ERROR;
        }
        rt_thread_startup(tid);

        /* Create wifi transfer thread */
        tid = rt_thread_create("wifi_xfer",
                               spi_wifi_data_thread_entry,
                               RT_NULL,
                               2048,
                               9,
                               20);
        if(!tid)
        {
            return -RT_ERROR;
        }
        rt_thread_startup(tid);
    }

    spi_wifi_hw_init();

    return RT_EOK;
}

后面就是进行WIFI 的连接断开,扫描等操作。

关于WiFi的更多信息,请参看官方手册:

WiFi

好了,WiFi就移植成功了。

4.2双网卡手动切换实现

到目前,板子就有两块网卡。既然有了双网络,必然就会有网络切换,可类比我们的笔记本,可接以太网和WiFi,当WiFi信号不好就使用以太网,当以太网断了,就自动切换到WiFi。ART-Pi也是一样的。
RT-Thread提供了网卡管理和控制的网路组件netdev 网卡。netdev 组件主要作用是解决设备多网卡连接时网络连接问题,用于统一管理各个网卡信息与网络连接状态,并且提供统一的网卡调试命令接口。

Netdev官网地址

关于Netdev的原理和API请参看官方手册。笔者这里给出网卡切换的代码。
如下示例,导出命令用于切换默认网卡:

#include <arpa/inet.h>
#include <netdev.h>       /* 当需要网卡操作是,需要包含这两个头文件 */

static int netdev_set_default_test(int argc, char **argv)
{
    struct netdev *netdev = RT_NULL;

    if (argc != 2)
    {
        rt_kprintf("netdev_set_default [netdev_name]   --set default network interface device.\\n");
        return -1;
    }

    /* 通过网卡名称获取网卡对象,名称可以通过 ifconfig 命令查看 */
    netdev = netdev_get_by_name(argv[1]);
    if (netdev == RT_NULL)
    {
        rt_kprintf("not find network interface device name(%s).\\n", argv[1]);
        return -1;
    }

    /* 设置默认网卡对象 */
    netdev_set_default(netdev);

    rt_kprintf("set default network interface device(%s) success.\\n", argv[1]);
    return 0;
}
#ifdef FINSH_USING_MSH
#include <finsh.h>
/* 导出命令到 FinSH 控制台 */
MSH_CMD_EXPORT_ALIAS(netdev_set_default_test, netdev_set_default, set default network interface device);
#endif /* FINSH_USING_MSH */

重点代码就两句,一句是netdev_get_by_name获取网卡,另外一句是netdev_set_default设置获取的网卡作为默认网卡。这两句在后面也会使用。

接下来编译下载。

连接网线,打印信息如下:

接下来查看网络连接状态:

可以看到默认使用的是以太网。接着看看是否连网。

说明网络是正常的。

我们通过netdev_set_status_test命令切换网络。

可以看到网络切换到WiFi,只是WiFi没有连接而已。

关于WiFi配置有两种方式,不清楚的请参看笔者以前的文章。

Wifi的移植使用

4.3双网卡自动切换

在netdev.c文件中static void netdev_auto_change_default(struct netdev *netdev)这个函数进行了默认网卡的设置。

当然,要想实现这个功能,需要配置如下选项,可使能默认网卡自动切换功能:

多网卡模式下,如果开启默认网卡自动切换功能,当前默认网卡状态改变为 down 或 link_down 时,默认网卡会切换到网卡列表中第一个状态为 up 和 link_up 的网卡。这样可以使一个网卡断开后快速切换到另一个可用网卡,简化用户应用层网卡切换操作。如果未开启该功能,则不会自动切换默认网卡。

但是这个自动切换是由缺陷的,默认板子是使用以太网,不管网线插上与断开与否,当没有插网线时,尽管连接了WiFi,还是不会自动切换,需要手动切换,当WiFi连接后,默认也不会切换到以太网。

我们根据实际应用需求,我们知道:

1.默认使用以太网,如果断开以太网,当连接WiFi后,会自动切换到WiFi,WiFi可通过蓝牙配置,并且记录在Flash中,以便下次使用;
2.如果连接了WiFi,再插上网线,则会自动切换到以太网。
因此需要修改两处代码:

第一处:增加WiFi联网自动切换到WiFi

这是在rt_wlan_event_dispatch函数中,当WiFi连接后,就切换到WiFi。

第二处:增加以太网联网自动切换到以太网

这是在phy_linkchange()函数中添加的,当以太网插上后,就切换到以太网。

好了,最后的编译下载即可。

如果想要记住WiFi账号密码,需要开启easyflash,关于该部分内容请参看笔者博文。

WIFI移植




代码获取方法

1.长按下面二维码,关注公众号[嵌入式实验楼]
2.在公众号回复关键词[LWIP]获取资料




欢迎访问我的网站

BruceOu的哔哩哔哩
BruceOu的主页
BruceOu的博客
BruceOu的CSDN博客
BruceOu的简书
BruceOu的知乎

以上是关于《嵌入式 - Lwip开发指南》第4章 移植LWIP(基于RT-Thead系统-以太网+Wifi)的主要内容,如果未能解决你的问题,请参考以下文章

《嵌入式 - Lwip开发指南》第4章 移植LWIP(基于RT-Thead系统-以太网+Wifi)

《嵌入式 - Lwip开发指南》第3章 移植LWIP(无系统)

《嵌入式 - Lwip开发指南》第3章 移植LWIP(无系统)

在LwIP 协议栈移植 Snap 7

《嵌入式 - Lwip开发指南》第1章 LWIP概述

《嵌入式 - Lwip开发指南》第1章 LWIP概述