《嵌入式 - 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引脚名 | 封装管脚序号 | 功能 |
---|---|---|
PA5 | 5 | BOOT0/CLK |
PA6 | 6 | MISO |
PB5 | 7 | MOSI |
PD14 | 62 | BOOT1/CS |
PD15 | 63 | INT/BUSY |
PF12 | 252 | RESET |
值得注意的是,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就移植成功了。
4.2双网卡手动切换实现
到目前,板子就有两块网卡。既然有了双网络,必然就会有网络切换,可类比我们的笔记本,可接以太网和WiFi,当WiFi信号不好就使用以太网,当以太网断了,就自动切换到WiFi。ART-Pi也是一样的。
RT-Thread提供了网卡管理和控制的网路组件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配置有两种方式,不清楚的请参看笔者以前的文章。
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,关于该部分内容请参看笔者博文。
代码获取方法
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(无系统)