一、創(chuàng)建RT-Thread項目
開始本篇實驗前,需要搭建RT-Thread開發(fā)環(huán)境,具體可以參考:“快來看!先楫芯與RT-Thread碰出火花了”
使用RT-Thread Studio創(chuàng)建名為hpm_net_test的項目:
二、為項目添加RW007支持
2.1 打開RT-Thread Settings
項目創(chuàng)建成功后,打開項目的RT-Thread Settings界面:
可以看到,默認(rèn)情況下SPI驅(qū)動框架已經(jīng)打開了。
BSP中的SPI1驅(qū)動也已經(jīng)打開了:
2.2 添加RW007軟件包
在RT-Thread Settings界面,通過點擊“添加軟件包”按鈕,會彈出RT-Thread Package Center界面:
在中間的搜索框中輸入RW007,回車,可以找到RW007驅(qū)動程序軟件包:
點擊界面“添加”按鈕,即可將RW007軟件包添加到當(dāng)前項目的包配置中了,此時軟件包并沒有真正下載下來。點完添加按鈕后,界面回到了RT-Thread Settings,此時按Ctrl+S保存,則會開始下載。下載過程中,控制臺子窗口中可以看到一些日志輸出:
稍等片刻,可以看到控制臺中間有“RW007 v2.0.1 is downloaded successfully.”輸出。此時rw007軟件包已經(jīng)成功下載到當(dāng)前項目中了,具體代碼位于packages子目錄下:
2.3 配置RW007驅(qū)動
在RT-Thread Settings界面,將鼠標(biāo)移動到RW007組件上,會彈出懸浮菜單:
點擊懸浮菜單中的“配置項”,即可進(jìn)入RW007軟件包的配置界面:
可以看到,默認(rèn)有一個RW007 for stm32的配置,就是說RW007默認(rèn)包含了STM32的驅(qū)動。
這里我們需要修改的就是這個example driver port配置項,點擊下拉菜單改為不使用示例驅(qū)動:
選中后,記得Ctrl+S保存配置。
2.4 編譯、燒錄、運行項目
在RT-Thread Studio中按Ctrl+B快捷鍵或點擊“錘子”圖標(biāo),即可開始編譯項目。編譯完成后,可以看到控制臺輸出了RAM和Flash占用:
此時,將開發(fā)板連接到PC,并使用串口助手或者其他終端工具,連接到新增的串口上。
再到RT-Thread Studio中,按Ctrl+Alt+D快捷鍵或點擊“下載”圖標(biāo)即可進(jìn)行燒錄(或者直接進(jìn)行調(diào)試也可以)。
燒錄完成后,可以看到串口終端上有輸出:
可以看到,輸出了RT-Thread版本信息和RW007模組的序列號以及固件版本信息。這里能夠看到RW007模組的固件版本信息,其實HPM6750芯片和RW007模組之間已經(jīng)可以正常通信了。
三、WiFi測試
接下來,我們進(jìn)行一些簡單的WiFi測試。
添加RW007組件后,默認(rèn)會打開RT-Thread的WiFi驅(qū)動框架,而RT-Thread的WiFi驅(qū)動框架中同時帶有一個測試命令——wifi(對就是這么直接)。
我們可以在RT-Thread的msh交互環(huán)境中使用help查看當(dāng)前已有哪些命令:
可以看到有一個wifi命令。
接下來我們查看wifi命令的使用方式:
3.1 掃描測試
嘗試掃描周圍的WiFi熱點:
可以看到,成功掃描到了周圍的WiFi熱點。
3.2 連接測試
嘗試連接其中的一個熱點:
然而,不幸的是,發(fā)生異常了。
不過,從這里的幾個warning打印信息可以看到,應(yīng)該是因為tcpip線程棧溢出導(dǎo)致的。
3.3 調(diào)大tcpip線程棧大小
接下來,我們通過RT-Thread Settings修改tcpip線程棧的大小。
同樣,首先打開RTT Settings界面,鼠標(biāo)指針放到LwIP組件圖標(biāo)上:
打開配置項,找到RT_LWIP_TCPTHREAD_STACKSIZE配置項,并將其修改為4096:
界面下方可以看到這個LwIP線程棧大小的配置項名稱為RT_LWIP_TCPTHREAD_STACKSIZE。至于這里為什么要改這個配置項,沒有在RT-Thread用過LwIP的同學(xué)可能會疑惑。其實,這里可以根據(jù)線程名“tcpip”,一路搜索代碼,首先可以找到創(chuàng)建名為tcpip線程的代碼位置,然后可以找到線程棧大小參數(shù)的來源。這里是搜索結(jié)果:
PS:因為默認(rèn)使用的是lwip 2.0.3版本,所以這里只搜索了lwip-2.0.3的代碼。
3.4 重新測試
配置修改完成后,Ctrl+S保存,重新編譯項目、燒錄、運行,這次能夠成功連接WiFi熱點了:
可以看到,已經(jīng)成功通過DHCP從熱點獲取到IP地址了。
四、網(wǎng)絡(luò)測試
4.1 RT-Thread網(wǎng)絡(luò)組件
前面提到,添加了RW007軟件包后,會開啟RT-Thread的WiFi驅(qū)動框架;同時,也會開啟系統(tǒng)中網(wǎng)絡(luò)協(xié)議相關(guān)的組件,主要包括套接字抽象層(SAL)、網(wǎng)絡(luò)接口層、輕量級TCP/IP堆棧(LwIP),如下圖所示。
其中,LwIP的默認(rèn)版本用的是v2.0.3,也可以切換為其他版本(RT-Thread系統(tǒng)中同時提供了LwIP的好幾個版本可供選擇)。
4.2 RT-Thread網(wǎng)絡(luò)組件相關(guān)的命令
RT-Thread系統(tǒng)網(wǎng)絡(luò)相關(guān)組件打開后,將會向msh中注冊幾個命令用于測試,具體包括:ifconfig、ping、netstat、dns等,可以在help的輸出中找到:
4.3 ping測試
有IP地址了,我們可以用ping命令測試一下能不能訪問baidu.com:
可以看到,能夠成功ping通baidu.com了。
使用baidu.com的域名能夠訪問,說明DNS整個流程都是OK的,同時網(wǎng)路協(xié)議也是沒問題的。
五、網(wǎng)絡(luò)帶寬測試
5.1 添加netutils軟件包
RT-Thread的netutils軟件包中提供了iperf命令,可以用于測試網(wǎng)絡(luò)帶寬;
和前面類似的方法,為項目添加netutils組件:
打開“配置項”后,打開iperf的配置項:
修改配置后,Ctrl+S保存。
重新編譯、燒錄、運行項目,help的輸出可以看到多了iperf命令。
5.2 iperf命令參數(shù)
在RT-Thread的msh中運行iperf,默認(rèn)輸出幫助信息:
可以看到iperf的命令參數(shù)使用方法。
需要注意:
1.RT-Thread的iperf命令實現(xiàn)中,對參數(shù)的順序有要求,如果使用過程中發(fā)現(xiàn)參數(shù)報錯,需要查看源碼定位原因;
2.RT-Thread的iperf不支持持續(xù)時間選項,一般是先啟動,后通過stop選項停止的方式控制測試時長;
5.3 PC端的iperf
PC端的iperf可以到iperf項目官網(wǎng)下載:https://iperf.fr/iperf-download.php
我使用的mobaxterm,里面自帶了iperf命令,所以就不單獨下載了:
5.4 進(jìn)行iperf測試
進(jìn)行iperf測試之前,需要注意:
1.最好用PC創(chuàng)建熱點,用無線路由器也行,但是需要確保信號強(qiáng)度足夠;
2.確保開發(fā)板和PC直線的距離不要太遠(yuǎn),否則WiFi信號較弱,測試的結(jié)果可能會偏小;
3.最好在WiFi熱點較少的環(huán)境下進(jìn)行測試,否則測出的結(jié)果數(shù)據(jù)也會偏小;
下面進(jìn)行測試,測試步驟如下:
在PC上,創(chuàng)建熱點,例如名為rtt,密碼為12345678
在PC上,啟動iperf服務(wù)端:iperf -s -p 5678
在PC上,使用ipconfig/ifconfig命令查看熱點的IP地址,例如我在Win10上創(chuàng)建的熱點,IP地址是:192.168.137.1
在開發(fā)板上,連接PC啟動熱點:wifi join rtt 12345678
5.在開發(fā)板上,查看IP地址是否已成功分配:ifconfig,另外,可以通過ping命令測試開發(fā)板和PC直接IP是否可達(dá)
6.在開發(fā)板上,啟動iperf客戶端:iperf -c 192.168.137.1 -p 5678
啟動后,可以通過ps命令查看正在運行的線程
7.一段時間后,在開發(fā)板上,停止iperf客戶端:iperf —stop
8.開發(fā)板上iperf停止后,PC端應(yīng)該可以看到iperf的輸出;
開發(fā)板上整個過程的輸出如下:
PC端輸出:
可以看到帶寬為7.45Mbps
5.5 iperf測試小結(jié)
實際上,影響WiFi帶寬測試結(jié)果數(shù)據(jù)的因素很多。我們這里,其中,起決定性的的主要由以下幾個方面:
RW007模組本身支持的最高WiFi傳輸速率;
RW007模組的SPI接口支持的最高工作頻率;
HPM6750 SPI接口最高支持的工作頻率;
熱點(PC或路由器)的WiFi最高傳輸速率;
各種環(huán)境因素,例如開發(fā)板和PC直接的距離、環(huán)境是否有其他熱點干擾等等;
六、業(yè)務(wù)代碼——socket測試
前面的ping測試、iperf測試使用的是系統(tǒng)已有組件或軟件包。除此之外,也可以通過socket連接網(wǎng)絡(luò)。這里以一個簡單的使用socket獲取baidu首頁為例(直接使用web_client軟件包也可以實現(xiàn)該功能):
#include #include #include #include #define DEFAULT_HOST "example.com"#define DEFAULT_PORT 80#define CONTENT_LENGTH "Content-Length:"#define HEADER_END_MARK "\\r\\n\\r\\n"uint32_t get_host_addr(const char *host){ uint32_t dest = 0; struct hostent *he; he = gethostbyname(host); if (he && he->h_addr_list && he->h_addr_list[0]) { dest = ((struct in_addr *)(he->h_addr_list[0]))->s_addr; } return dest;}#define close(fd) closesocket(fd)int fetch(int argc, char* argv[]){ char* host = DEFAULT_HOST; int port = DEFAULT_PORT; int sockfd = -1; int retval = 0; int recved = 0; int content_start = 0; int content_length = 0; struct sockaddr_in server_addr = {0}; static char request[256]; static char response[4096]; if (argc > 1) host = argv[1]; if (argc > 2) port = atoi(argv[2]); sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { rt_kprintf("create socket failed!\\n"); return -1; } rt_kprintf("create socket success!\\n"); rt_memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); server_addr.sin_addr.s_addr = get_host_addr(host); // IP轉(zhuǎn)為 “點分十進(jìn)制” 格式 inet_ntop(AF_INET, &server_addr.sin_addr, response, sizeof(response)); rt_kprintf("server IP: %s\\n", response); rt_kprintf("connect to server...\\n"); retval = connect(sockfd, (const struct sockaddr *)&server_addr, sizeof(server_addr)); if (retval < 0) { rt_kprintf("connect failed!\\n"); close(sockfd); return -1; } rt_snprintf(request, sizeof(request), "GET / HTTP/1.1\\r\\n" "Host: %s\\r\\n" "User-Agent: curl/7.81.0\\r\\n" "Accept: */*\\r\\n" "\\r\\n", host); rt_kprintf("send request...\\n"); retval = send(sockfd, request, rt_strlen(request), 0); if (retval < 0) { rt_kprintf("send failed!\\n"); close(sockfd); return -1; } rt_kprintf("%d bytes sent\\n", retval); rt_kprintf("recv response...\\n"); recved = 0; while ((retval = recv(sockfd, &response[recved], sizeof(response) - recved, 0)) > 0) { if (content_length == 0) { char* content_length_pos = rt_strstr(response, CONTENT_LENGTH); if (content_length_pos) { content_length = atoi(content_length_pos + rt_strlen(CONTENT_LENGTH)); rt_kprintf("found %s %d!\\n", CONTENT_LENGTH, content_length); } } if (content_start == 0) { char* header_end = rt_strstr(response, HEADER_END_MARK); if (header_end) { content_start = header_end + rt_strlen(HEADER_END_MARK) - response; rt_kprintf("content_start: %d\\n", content_start); } } recved += retval; rt_kprintf("recved: %d %d %d\\n", recved, content_start, content_length); if (content_length && content_start && recved - content_start >= content_length) { rt_kprintf("fully recved!\\n"); break; } } response[recved] = '\\0'; rt_kprintf("==== Response Header ====:\\n"); for (int i = 0; i < content_start; i++) { rt_kprintf("%c", response[i]); } rt_kprintf("==== Response Content ====:\\n"); for (int i = content_start; i < recved; i++) { rt_kprintf("%c", response[i]); } if (retval < 0) { rt_kprintf("recv failed!\\n"); close(sockfd); return -1; } shutdown(sockfd, SHUT_RDWR); close(sockfd); return 0;}MSH_CMD_EXPORT(fetch, "fetch home page of a site");
這是一段使用裸socket實現(xiàn)的簡單HTTP客戶端,依次進(jìn)行了請求發(fā)送、回復(fù)接收和回復(fù)解析的過程,測試結(jié)果:
七、原理簡介
以上操作,我們沒有任何底層驅(qū)動相關(guān)代碼,就實現(xiàn)了通過HPM6750EVKMINI開發(fā)板的RW007 WiFi模組實現(xiàn)聯(lián)網(wǎng)功能。這是因為我們基于RT-Thread的項目中,從底到上已經(jīng)有了:
HPM6750EVKIMNI BSP中包含了SPI驅(qū)動(libraries/drivers/drv_spi.c文件);
默認(rèn)打開了spi1的編譯配置;
HPM6750EVKIMNI BSP中包含了網(wǎng)卡初始化代碼(board/rw007_port.c文件);
向系統(tǒng)注冊了啟動時自動執(zhí)行的wifi_spi_device_init函數(shù);
wifi_spi_device_init函數(shù)內(nèi)部會調(diào)用rw007軟件包中的rt_hw_wifi_init函數(shù);
RW007軟件包,包含RW007模組的驅(qū)動代碼;
底層使用SPI驅(qū)動實現(xiàn)主控和RW007模組之間的通訊;
上層向RT-Thread系統(tǒng)注冊WLAN設(shè)備(rt_hw_wifi_init函數(shù)內(nèi)部會調(diào)用rt_wlan_dev_register函數(shù));
RT-Thread的WiFi(也叫WLAN)驅(qū)動框架;
對下連接具體的 WIFI 驅(qū)動,控制 WIFI 的連接斷開,掃描等操作。
對上承載不同的應(yīng)用,為應(yīng)用提供 WIFI 控制,事件,數(shù)據(jù)導(dǎo)流等操作,為上層提供統(tǒng)一的 WIFI 控制接口。
RT-Thread的Socket抽象層(SAL),統(tǒng)一幾種不同的socket實現(xiàn);
RT-Thread的TCP/IP協(xié)議棧(LwIP),具體的TCP/IP協(xié)議實現(xiàn);
(文章摘選自RTT @xusiwei1236)
-
WIFI
+關(guān)注
關(guān)注
81文章
5289瀏覽量
203310 -
RTThread
+關(guān)注
關(guān)注
8文章
132瀏覽量
40811
發(fā)布評論請先 登錄
相關(guān)推薦
評論